Как переводить между часовыми поясами Windows и IANA?
Как описано в теге часового пояса вики, есть два разных стиля часовых поясов.
Предоставленные Microsoft для использования с Windows и.Net
TimeZoneInfo
класс идентифицируется значением, таким какEastern Standard Time
,Те, которые предоставляются IANA в TZDB, обозначаются такими значениями, как
America/New_York
,
Многие интернет-API используют часовые пояса IANA, но по многим причинам может потребоваться преобразовать их в идентификатор часового пояса Windows или наоборот.
Как это можно сделать в.Net?
3 ответа
Основным источником данных для преобразования между идентификаторами часовых поясов Windows и IANA является windowsZones.xml
файл, распространяемый в рамках проекта Unicode CLDR.
Тем не менее, 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"
, известная как "золотая зона", если вы не указали код страны, и в этой стране нет совпадения с другой зоной.
Примечание. Этот ответ развивался годами, поэтому комментарии ниже могут относиться или не относиться к текущей редакции. Посмотрите историю изменений для деталей. Благодарю.
Начиная с .NET 6 Preview 4, наконец-то появилась возможность работать с часовыми поясами кроссплатформенным образом, поэтому эти ручные обходные пути больше не нужны.
В
TimeZoneInfo.FindSystemTimeZoneById(string)
Метод автоматически принимает часовые пояса Windows или IANA на любой платформе и при необходимости преобразует их.
// Both of these will now work on any supported OS where ICU and time zone data are available.
TimeZoneInfo tzi1 = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
TimeZoneInfo tzi2 = TimeZoneInfo.FindSystemTimeZoneById("Australia/Sydney");
Обратите внимание, что, как указано в ссылке, образы Docker для .NET Core Alpine на базе Linux не имеют необходимых
tzdata
установлен по умолчанию , поэтому он должен быть установлен в вашем
Dockerfile
чтобы это работало правильно.
Я знаю, что это старый вопрос, но у меня был вариант использования, которым я хотел бы поделиться здесь, так как это наиболее релевантный пост, который я нашел при поиске. Я разрабатывал приложение.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")