Сохранить DateTimeOffset в строке даты после преобразования даты UTC в TimeZoneInfo

#c# #datetime

#c# #дата и время

Вопрос:

Дана следующая строка даты UTC: «2020-10-10T12:00:00»

Почему при преобразовании в строку я теряю смещение даты и времени, несмотря на указание его в формате?

 string parseString = "2020-10-10T12:00:00";

if (DateTime.TryParseExact(parseString, "s", null, DateTimeStyles.None, out DateTime parsedDate))
{
    TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Honolulu");

    DateTime atUtc = DateTime.SpecifyKind(parsedDate, DateTimeKind.Utc);
    DateTimeOffset dateAtTimeZone = TimeZoneInfo.ConvertTime(atUtc, timeZoneInfo);
    Console.WriteLine(dateAtTimeZone.ToString("O"));
    Console.WriteLine(dateAtTimeZone.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK"));
}
  

Почему я возвращаю следующее: 2020-10-10T02:00:00.0000000 13:00

Вместо: 2020-10-10T02:00:00.0000000-10:00

Комментарии:

1. Тогда не используйте DateTime ( learn.microsoft.com/en-us/dotnet/api /… ).

2. @JeremyLakeman — вы хотите сказать, что я должен вручную указать смещение? Это то, что я получил по вашей ссылке. Я предположил, что если вы преобразуете объект DateTime с использованием часового пояса, он будет содержать некоторую информацию о локализации / смещении, и я мог бы применить, например, следующие форматы: learn.microsoft.com/en-us/dotnet/standard/base-types /…

3. @nealsu «по сути, это будет содержать некоторую информацию о локализации / смещении» Нет, это не так. Это задача DateTimeOffset .

4. Дата-время четко определено только для UTC и часового пояса этого компьютера. Слишком много методов сделают что-то удивительное, если вид не указан. ИМХО использует его только для хранения UTC. Как только вам понадобятся «локальные» или «пользовательские» значения времени, используйте DateTimeOffset .

5. Потому TimeZoneInfo.ConvertTime что все еще работает на DateTime s. На самом деле вы ничего не изменили. Вы только что преобразовали неверный результат из before в an DateTimeOffset . Вы также должны сделать atUtc DateTimeOffset .

Ответ №1:

Есть два TimeZoneInfo.ConvertTime параметра, которые принимают 2 параметра — один, который принимает a DateTime и возвращает a DateTime , и один, который принимает an DateTimeOffset и возвращает a DateTimeOffset . Прямо сейчас вы вызываете тот, который принимает DateTime :

 DateTime atUtc = DateTime.SpecifyKind(parsedDate, DateTimeKind.Utc);
DateTimeOffset dateAtTimeZone = TimeZoneInfo.ConvertTime(atUtc, timeZoneInfo); // atUtc is a DateTime here!
  

Тот факт, что вы присваиваете результат DateTimeOffset , не имеет значения, поскольку тип возвращаемого значения не участвует в разрешении перегрузки.

Перегрузка, которая принимает a DateTime , просто изменяет переданные части даты и времени DateTime , поскольку информация о часовом поясе / смещении не является частью DateTime s . Когда вы преобразуете его в DateTimeOffset , ваше локальное смещение добавляется к нему во время преобразования, и это то, что печатается.

То, что вы должны вызывать, — это перегрузка, которая принимает DateTimeOffset . Поскольку он принимает a DateTimeOffset , он может изменять часть смещения DateTimeOffset , а также части даты и времени, до желаемых значений. Вам также необходимо объявить atUtc как DateTimeOffset :

 DateTimeOffset atUtc = DateTime.SpecifyKind(parsedDate, DateTimeKind.Utc);
DateTimeOffset dateAtTimeZone = TimeZoneInfo.ConvertTime(atUtc, timeZoneInfo);
  

Или лучше использовать DateTimeOffset.TryParseExact в первую очередь!