C# DateTime - преобразование DateTimeOffset в другой часовой пояс - PullRequest
1 голос
/ 17 июня 2020

При преобразовании DateTimeOffset в другой часовой пояс OffSet неверен.

Я прочитал много статей и экспериментировал слишком много часов, но не вижу, что мне здесь не хватает:

// It's June in the UK and we're in British Summer Time, which is 1 hour ahead of UTC (GMT)
var UKoffsetUtc = new TimeSpan(1, 0, 0);

// It's 4pm - declare local time as a DateTimeOffset
var UKdateTimeOffset = new DateTimeOffset(2020, 6, 17, 16, 0, 0, UKoffsetUtc);

// Convert to UTC as a date
var utc = DateTime.SpecifyKind(UKdateTimeOffset.UtcDateTime, DateTimeKind.Utc);

// Get Aus TimeZoneInfo
var AUSTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");

// Check the Aus offset from UTC
var AUSOffset = AUSTimeZone.GetUtcOffset(utc);
Console.WriteLine(AUSOffset); // Output is 10 as expected

// Declare Aus Time as DateTimeOffset
var AUSDateTimeOffset = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);

// The Aus Offset from UTC is not correct 
Console.WriteLine(AUSDateTimeOffset.ToString("dd MM yyyy HH:mm zzz"));

Результат: 18 06 2020 01:00 + 01: 00

Aus на 10 часов опережает UT C (на 9 часов опережает время по Гринвичу), поэтому дата и время верны, но не смещение.

Как мне получить правильное смещение в AUSDateTimeOffset?

Ответы [ 2 ]

2 голосов
/ 17 июня 2020

Ошибка находится в этой части:

var AUSDateTimeOffset = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);

В этом коде utc - это DateTime, и, таким образом, полученный AUSDateTimeOffset на самом деле DateTime. Его Kind будет DateTimeKind.Unspecified.

Преобразование будет выполнено правильно, и, таким образом, вы увидите правильные дату и время в результате. Однако смещение неверно, потому что оно не является частью DateTime. В документации по спецификатору zzz говорится:

Со значениями DateTime описатель настраиваемого формата «zzz» представляет смещение со знаком часового пояса локальной операционной системы от UT C, измеряется в часах и минутах. Он не отражает значение свойства экземпляра DateTime.Kind. По этой причине спецификатор формата "zzz" не рекомендуется использовать со значениями DateTime.

Таким образом, +01:00 исходит из вашего местного часового пояса, а не из целевого часового пояса. .

Есть несколько способов исправить это:

  • Вы можете сделать AUSDateTimeOffset a DateTimeOffset с правильным смещением:

    DateTime AUSDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);
    TimeSpan AUSOffset = AUSTimeZone.GetUtcOffset(utc);
    DateTimeOffset AUSDateTimeOffset = new DateTimeOffset(AUSDateTime, AUSOffset);
    
  • Вы можете использовать UT C DateTimeOffset вместо UT C DateTime:

    DateTimeOffset utc = UKdateTimeOffset.ToUniversalTime();
    DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTime(utc, AUSTimeZone);
    
  • Вы можете просто преобразовать исходный DateTimeOffset, так как нет необходимости преобразовывать в UT C сначала:

    DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTime(UKdateTimeOffset, AUSTimeZone);
    
  • Как сказал Джими в комментариях, вы даже можете преобразовать без построение объекта TimeZoneInfo вообще:

    DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(UKdateTimeOffset, "AUS Eastern Standard Time");
    

Любое из вышеперечисленных даст такой же правильный ответ.

1 голос
/ 17 июня 2020

Вы можете создать новое смещение и использовать его -

    // Create new offset for UTC
    var AUSOffset = new DateTimeOffset(utc, TimeSpan.Zero);

    // Declare Aus Time as DateTimeOffset
    var AUSDateTimeOffset = UKdateTimeOffset.ToOffset(AUSTimeZone.GetUtcOffset(AUSOffset));                              
    Console.WriteLine(AUSDateTimeOffset.ToString("dd MM yyyy HH:mm zzz"));

Или:

Используйте ConvertTimeBySystemTimeZoneId как было предложено Джими в комментарии!

    var finalDate = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(UKdateTimeOffset, "AUS Eastern Standard Time");
    Console.WriteLine(finalDate.ToString("dd MM yyyy HH:mm zzz"));
...