2012-03-01 2 views
18

Мои местный часовой пояс (UTC + 10: 00) Канберра, Мельбурн, СиднейDateTime - Странное поведение декретного

сб 31-Mar-2012 15:59 UTC = Вс 01-Апрель-2012 2:59 + 11:00
Сб 31-Мар-2012 16:00 UTC = Солнце 01-апр-2012 02:00 +10: 00

Переход на летнее время в 3 часа ночи в первое воскресенье апреля и часовое движение назад 1 час ,

Учитывая следующий код ....

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); 

DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal).AddMinutes(1); 
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); 

Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1); 
Console.WriteLine("{0:yyyy-MMM-dd HH:mm:ss.ffff K} ({1}) = {2:yyyy-MMM-dd HH:mm:ss.ffff K} ({3})", dt2, dt2.Kind, dt3, dt3.Kind); 
Console.WriteLine("{0} : {1} : {2}", dt1.ToUniversalTime().Hour, dt2.ToUniversalTime().Hour, dt3.ToUniversalTime().Hour); 

я получаю следующие выходные данные

2012-Апрель-01 02: 59: 00,0000 +11: 00
2012-Апрель-01 03 : 00: 00,0000 +10: 00 (местный) = 2012-Apr-01 02: 00: 00,0000 +10: 00 (местный)
15: 17: 16

Добавление 1 минуту к первоначальному DateTime делает местный время 3AM, но также установить смещение на +10 часов. Добавление 1 минуты к дате UTC и синтаксический анализ правильно устанавливает локальное время в 2 часа с +10 UTC смещением.

повторитель

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc); 

DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).AddMinutes(1); 
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc); 

или

DateTime dt1 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 

DateTime dt2 = DateTime.Parse("31-Mar-2012 15:59", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).AddMinutes(1); 
DateTime dt3 = DateTime.Parse("31-Mar-2012 16:00", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); 

дает

2012-Mar-31 15: 59: 00,0000 Z
2012-Mar-31 16: 00: 00.0000 Z (Utc) = 2012-марта-31 16: 00: 00.0000 Z (Utc)
15: 16: 16

как ожидалось

Повторяя снова

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime(); 

DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime().AddMinutes(1); 
DateTime dt3 = new DateTime(2012, 03, 31, 16, 0, 0, DateTimeKind.Utc).ToLocalTime(); 

дает оригинальную

2012-Apr-01 02: 59: 00,0000 +11: 00
2012-Апрель-01 3:00: 00.0000 +10: 00 (местный) = 2012-апр-01 02: 00: 00.0000 +10: 00 (местный)
15: 17: 16

Может ли это объяснить это?

Неприлично, если я использую TimeZoneInfo для преобразования из UTC в AUS по восточному поясному времени я получаю правильное время, но я теряю информацию о сдвиге в экземпляре DateTime как DateTime.Kind == DateTimeKind.Unspecified

= = Дополнительный сценарий для выделения

Это просто добавленное время, начиная с неопределенной даты UTC, за 1 минуту до окончания дневного света.

DateTime dt1 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc); 
DateTime dt2 = new DateTime(2012, 03, 31, 15, 59, 0, DateTimeKind.Utc).ToLocalTime(); 

Console.WriteLine("Original in UTC  : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1); 
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.ToLocalTime()); 
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1).ToLocalTime()); 
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt1.AddMinutes(1)); 
Console.WriteLine("====================================================="); 
Console.WriteLine("Original in UTC  : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.ToUniversalTime()); 
Console.WriteLine("Original in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2); 
Console.WriteLine("+ 1 Minute in Local : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1)); 
Console.WriteLine("+ 1 Minute in UTC : {0:yyyy-MMM-dd HH:mm:ss.ffff K}", dt2.AddMinutes(1).ToUniversalTime()); 

дает

Оригинал в формате UTC: 2012-Mar-31 15:59:00.0000 Z
Оригинал Местно: 2012-Апрель-01 02: 59: 00,0000 +11: 00
+ 1 минута в Местно: 2012-Апрель-01 : 00: 00,0000 +10: 00
+ 1 Минута UTC: 2012-Mar-31 : 00: 00,0000 Z

============================= ========================

Оригинал в формате UTC: 2012-Mar-31 15: 59: 00,0000 Z
Оригинал Местно: 2012 -Apr-01 02: 59: 00.0000 +11: 00
+ 1 минута i п Местно: 2012-Апрель-01 : 00: 00,0000 +10: 00
+ 1 минута в UTC: 2012-Mar-31 : 00: 00,0000 Z

+0

DateTime НЕ «хранит» локальное смещение. Я просто показываю смещение, которое будет действовать в то время. Так как dt2 всегда находится в локальном времени, то он имеет текущий час IS «true». Вы должны использовать DateTimeOffset, если вы хотите носить с собой «как примененное» смещение http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx – IDisposable

+0

... если это правда, тогда я ожидал, что третий результат от dt2 в последнем сценарии будет 03:00:00 +11: 00, но он знает, что DST закончил. Он правильно переключился на +10: 00, но не снял час. DateTimeOffset показывает время как 03:00:00 +11: 00, что не соответствует моему местному часовому поясу. –

+0

Нет, он знает, что ВЫ СКАЗАЛИ это было 3:00 по состоянию на 4/1/2012, так что смещение в тот момент +10: 00 – IDisposable

ответ

29

Я считаю, что проблема заключается в условия при выполнении конверсий.

Вы разбираетесь в режиме универсального времени, но затем неявно преобразуетесь в «локальный» вид - со значением 2:59:59. Когда вы запрашиваете «местное» значение, чтобы добавить минуту, это просто добавляет минуту к локальному значению, без учета часового пояса. Когда вы печатаете смещение, система пытается выработать смещение по местному времени 3 часа ..., которое равно +10.

Так эффективно вы получили:

  • Анализировать шаг 1: лакомство строка как универсальный (15:59 UTC)
  • Анализировать шаг 2: преобразовать результат локального (2:59 местного)
  • Добавление
  • не: по местному времени, никакие значения часового пояса применяются (3:00 местное)
  • Формат шаг 1: смещение запрашивается, поэтому выяснить, что местные карты, что время до 17:00 (UTC)
  • Формат шага 2: вычисление смещения как разница между локальным и унив. ersal (+10)

Да, все это немного больно - DateTime is painful in general, который является главной причиной, я пишу Noda Time, где есть отдельные типы для «даты/времени в зоне» против «локальной даты/time "(или" local date "или" local time "), и это очевидно, что вы используете в любой момент.

Мне непонятно, что вы на самом деле пытаетесь достичь здесь - если вы можете быть более конкретным, я могу показать вам, что вы будете делать в Noda Time, хотя могут быть некоторые присущие двусмысленности (конверсии из локальной даты/times to "zoned" дата/время может иметь 0, 1 или 2 результата).

EDIT: Если цель просто помнить, часовой пояс, а также мгновенный, в Нода времени вы хотите ZonedDateTime, как это:

using System; 
using NodaTime; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var zone = DateTimeZone.ForId("Australia/Melbourne"); 
     ZonedDateTime start = Instant.FromUtc(2012, 3, 31, 15, 59, 0) 
            .InZone(zone); 
     ZonedDateTime end = start + Duration.FromMinutes(1); 

     Console.WriteLine("{0} ({1})", start.LocalDateTime, start.Offset); 
     Console.WriteLine("{0} ({1})", end.LocalDateTime, end.Offset); 
    } 
} 

Посмотреть ноты на calendar arithmetic для более определенной информации об этом.

+0

. Я видел сообщение на вашем блог и был в середине написания некоторых модульных тестов, касающихся летнего времени, для проверки обработки Noda Time DST, но застрял, когда я увидел эти результаты только с DateTime struct –

+0

. Отметьте этот ответ как правильный, так как в различных обсуждениях подчеркивается, что арифметика DateTime на не- -UTC datetime принципиально нарушена в .NET. Единственный способ борьбы с этим - обернуть или заменить DateTime –

0

Мой способ справиться с этим заключается в том, чтобы рассматривать DateTime немного как Floats - для них требуется специальная обработка, когда вы манипулируете ими, или когда вы показываете их пользователю.Я использую небольшую библиотеку, которую я написал, чтобы обернуть их:

https://github.com/b9chris/TimeZoneInfoLib.Net

И всегда относиться к ним как UTC + TimeZoneInfo. Таким образом, вы можете выполнить всю типичную математику, которую вы обычно делаете, работая только с UTC до UTC, и только обрабатываете локальные DateTimes на последнем этапе, показывая их пользователю в каком-то хорошем формате. Еще одним преимуществом этой структуры является то, что вы можете более точно отображать чистый часовой пояс для пользователя в том формате, в котором они используются, а не каждый раз соскабливать в классе TimeZoneInfo.

Смежные вопросы