.Net C # DateTime на Mac OSX против Debian Linux

#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. Хорошая догадка, но проблема не в этом. Не стесняйтесь удалять это, чтобы избежать законных отзывов «это бесполезно».