Как переводить между часовыми поясами Windows и IANA?

149

Как описано в теге часового пояса вики , есть два разных стиля часовых поясов.

  • Те, что предоставляются Microsoft для использования с Windows, и TimeZoneInfoкласс .Net (при работе в Windows) идентифицируются по значению, например "Eastern Standard Time".

  • Те, которые предоставляются IANA в TZDB и используются TimeZoneInfoклассом .NET при работе в Linux или OSX, идентифицируются по значению, например "America/New_York".

Многие интернет-API используют часовые пояса IANA, но по многим причинам может потребоваться преобразовать их в идентификатор часового пояса Windows или наоборот.

Как это можно сделать в .Net?

Мэтт Джонсон-Пинт
источник

Ответы:

198

Основным источником данных для преобразования между идентификаторами часовых поясов Windows и IANA является windowsZones.xmlфайл, распространяемый в рамках проекта Unicode CLDR . Последнюю версию Dev можно найти здесь .

Однако CLDR выпускается только дважды в год. Это, наряду с периодической частотой обновлений Windows и нерегулярными обновлениями базы данных часовых поясов IANA, усложняет прямое использование данных CLDR. Имейте в виду, что сами изменения часового пояса производятся по прихоти различных правительств мира, и не все изменения вносятся с достаточным уведомлением, чтобы сделать это в этих циклах выпуска до их соответствующих дат вступления в силу.

Есть несколько других крайних случаев, которые необходимо обработать, которые не охватываются строго CLDR, и время от времени появляются новые. Поэтому я инкапсулировал сложность решения в микробиблиотеке TimeZoneConverter , которую можно установить из Nuget.

Использовать эту библиотеку просто. Вот несколько примеров конвертации:

string tz = TZConvert.IanaToWindows("America/New_York");
// Result:  "Eastern Standard Time"

string tz = TZConvert.WindowsToIana("Eastern Standard Time");
// result:  "America/New_York"

string tz = TZConvert.WindowsToIana("Eastern Standard Time", "CA");
// result:  "America/Toronto"

На сайте проекта есть еще примеры .

Важно понимать, что хотя часовой пояс IANA можно сопоставить с одним часовым поясом Windows, обратное неверно. Один часовой пояс Windows может быть сопоставлен с несколькими часовыми поясами IANA. Это можно увидеть в приведенных выше примерах, где Eastern Standard Timeсопоставляется как с America/New_York, так и с America/Toronto. TimeZoneConverter доставит тот, который помечает CLDR "001", известный как «золотая зона», если вы не укажете код страны отдельно, и в этой стране не будет совпадения с другой зоной.

Примечание: этот ответ развивался годами, поэтому комментарии ниже могут относиться или не относиться к текущей редакции. Посмотрите историю изменений для деталей. Спасибо.

Мэтт Джонсон-Пинт
источник
1
использование этого метода при конвертации (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhiдает Asia/Calcuttaэто должно быть Asia/Kolkata. кажется, TzdbDateTimeZoneSourceсодержит старые значения.
Анто Субаш
1
@MattJohnson при преобразовании метода Asia/Kolkatausing IanaToWindowsне работает. но он работает со Asia/Calcuttaстарым именем. Вы обновили метод, WindowsToIanaно IanaToWindowsу вас возникла та же проблема. несколько других зон , которые не работают в America/Argentina/Buenos_Aires, America/Indiana/Indianapolis, Asia/Kathmandu.
Анто Субаш
1
@AntoJSubash - Опять же, отличное наблюдение! Я отредактировал IanaToWindowsметод компенсации. Огромное спасибо!
Мэтт Джонсон-Пинт
2
@MattJohnson I второе замечание @ sirrocco. Использование канонического идентификатора тоже var canonical = tzdbSource.CanonicalIdMap[ ianaZoneId ]; links = Enumerable.Repeat( canonical, 1 ).Concat( links );помогло мне.
Йоханнес Рудольф
2
@sirrocco - Извините, я не видел ваш комментарий раньше. Обновлены функции. Спасибо!
Мэтт Джонсон-Пинт
4

Я знаю, что это старый вопрос, но у меня был вариант использования, которым я хотел бы поделиться здесь, так как это наиболее релевантный пост, который я нашел при поиске. Я разрабатывал приложение .NET Core, используя Docker Linux-контейнер, но для развертывания на сервере Windows. Так что мне нужен был только контейнер Docker Linux для поддержки имен часовых поясов Windows. Я получил это без изменения кода моего приложения, выполнив следующие действия:

cp /usr/share/zoneinfo/America/Chicago "/usr/share/zoneinfo/Central Standard Time"
cp /usr/share/zoneinfo/America/New_York "/usr/share/zoneinfo/Eastern Standard Time"
cp /usr/share/zoneinfo/America/Denver "/usr/share/zoneinfo/Mountain Standard Time"
cp /usr/share/zoneinfo/America/Los_Angeles "/usr/share/zoneinfo/Pacific Standard Time"

Затем в моем .NET-коде следующее работало без каких-либо изменений: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")

EverPresent
источник
1
Это довольно замечательное нестандартное мышление! Кажется, что все должно быть в порядке, если вы покрываете несколько часовых поясов. Имейте в виду, что в США их больше, чем четыре. Для того, чтобы покрыть 50 штатов в настоящее время, вам также необходимо добавить ссылки для , America/Phoenixчтобы "US Mountain Standard Time", Pacific/Honoluluчтобы "Hawaiian Standard Time", America/Anchorageчтобы "Alaskan Standard Time", и America/Adakв "Aleutian Standard Time". Это не распространяется на территории США или исторические противоречия, но поможет вам начать.
Мэтт Джонсон-
2
Я не рекомендовал бы такой подход, если мы хотим охватить весь мир или иметь дело с любым действительным идентификатором часового пояса. Список слишком длинный и слишком изменчивый для этого.
Мэтт Джонсон-