#c# #linux #macos #datetime #timezone
#c# #linux #macos #datetime #Часовой пояс
Вопрос:
У меня есть код, который преобразует длинные числа в даты.
DateTimeOffset value =
DateTimeOffset.FromUnixTimeSeconds(1597325462);
DateTime showTime = value.DateTime;
string easternZoneId = "America/New_York";
TimeZoneInfo easternZone =
TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
DateTime targetTime =
TimeZoneInfo.ConvertTime(showTime, easternZone);
Console.WriteLine("DateTime is {0}", targetTime);
На моем Mac вывод "DateTime is 8/13/2020 6:31:02 AM"
На моем сервере вывод "DateTime is 8/13/2020 9:31:02 AM"
Идентичный код на обоих.
Значение поля Linux является точным. Как я могу получить тот же результат на моем Mac?
Комментарии:
1. UTC, это «2020-08-13T13:31:02 00:00» , просто чтобы попасть на ту же страницу. Можете ли вы как-то проверить, разрешают ли обе системы одно и то же
TimeZoneInfo
?2. Проблема в том, что они этого не делают, и я не знаю, как обойти эту проблему.
Ответ №1:
Используемая TimeZone.ConvertTime
вами перегрузка принимает DateTime
значение и назначение TimeZoneInfo
. Там нет упоминания об исходном часовом поясе, поэтому он выводится из .Kind
свойства DateTime
передаваемого.
В вашем случае это так DateTimeKind.Unspecified
, потому что .DateTime
свойство a DateTimeOffset
всегда возвращает неопределенное значение, независимо от того, каково смещение.
В ConvertTime
вызове, если вид имеет значение DateTimeKind.Unspecified
, предполагается, что это местное время (как если бы это было DateTimeKind.Local
). (Прокрутите вниз до раздела примечания в документах здесь.) Таким образом, вы преобразуете, как если бы временная метка Unix была основана на локальном времени, а не на том, что она основана на UTC. Вы получаете разные результаты между вашей рабочей станцией и сервером, потому что у них разные системные локальные часовые пояса, а не потому, что они работают под управлением разных операционных систем.
Существует несколько различных способов переписать это для решения проблемы. Я дам вам несколько на выбор, в порядке моих предпочтений:
-
Вы могли бы оставить все как
DateTimeOffset
на протяжении всего процесса преобразования:DateTimeOffset value = DateTimeOffset.FromUnixTimeSeconds(1597325462); string easternZoneId = "America/New_York"; TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId); DateTimeOffset targetTime = TimeZoneInfo.ConvertTime(value, easternZone);
-
Вы могли бы использовать
.UtcDateTime
свойство вместо.DateTime
свойства:DateTimeOffset value = DateTimeOffset.FromUnixTimeSeconds(1597325462); DateTime showTime = value.UtcDateTime; string easternZoneId = "America/New_York"; TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId); DateTime targetTime = TimeZoneInfo.ConvertTime(showTime, easternZone);
-
Вы могли бы использовать
ConvertTimeFromUtc
вместоConvertTime
:DateTimeOffset value = DateTimeOffset.FromUnixTimeSeconds(1597325462); DateTime showTime = value.DateTime; string easternZoneId = "America/New_York"; TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId); DateTime targetTime = TimeZoneInfo.ConvertTimeFromUtc(showTime, easternZone);
-
Вы могли бы указать UTC в качестве исходного часового пояса в
ConvertTime
вызове:DateTimeOffset value = DateTimeOffset.FromUnixTimeSeconds(1597325462); DateTime showTime = value.DateTime; string easternZoneId = "America/New_York"; TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId); DateTime targetTime = TimeZoneInfo.ConvertTime(showTime, TimeZoneInfo.Utc, easternZone);
Есть несколько других вариантов, таких как явная настройка вида, но я думаю, что приведенного выше достаточно, чтобы продолжить.
Если сомневаетесь, выберите первый вариант. DateTimeOffset
это гораздо проще рационализировать, чем DateTime
в большинстве случаев.
Комментарии:
1. Кроме того, если вам нужна поддержка систем Windows, вам нужно либо перейти на использование
"Eastern Standard Time"
, либо использоватьGetTimeZoneInfo
из моей библиотеки TimeZoneConverter , которая делает это за вас.
Ответ №2:
Проблема, похоже, в том, что в библиотеке ICU нет такого часового пояса, который я бы использовал для просмотра в обеих ОС.
Проверьте пример в документах Microsoft: https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.converttimebysystemtimezoneid?view=netcore-3.1
DateTime currentTime = DateTime.Now;
Console.WriteLine("New York: {0}",
TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "Eastern Standard Time"));
Таким образом, похоже, что идентификатор, который вы должны искать, — это «Восточное стандартное время»
Если вы не можете его найти, запустите следующий код, чтобы проверить доступные часовые пояса на вашем компьютере
foreach (TimeZoneInfo z in TimeZoneInfo.GetSystemTimeZones())
Console.WriteLine(z.Id);
Параметр destinationTimeZoneId должен точно соответствовать идентификатору часового пояса по длине, но не по регистру, для успешного совпадения; то есть сравнение destinationTimeZoneId с идентификаторами часовых поясов не зависит от регистра.
Итак, проверьте часовые пояса и скопируйте правильную строку для использования на основе того, что существует на компьютере.
Комментарии:
1. Обе машины имеют часовой пояс «Америка / Нью-Йорк», и ни на одной из них нет «Восточного стандартного времени»
2. Хорошая догадка, но проблема не в этом. Не стесняйтесь удалять это, чтобы избежать законных отзывов «это бесполезно».