在 unix (nginx) 上托管时 .NET Core 中的 TimeZoneInfo

Posted

技术标签:

【中文标题】在 unix (nginx) 上托管时 .NET Core 中的 TimeZoneInfo【英文标题】:TimeZoneInfo in .NET Core when hosting on unix (nginx) 【发布时间】:2017-05-24 19:10:22 【问题描述】:

例如,当我尝试执行以下操作时。

TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time")

我收到错误消息,TimeZone 在本地计算机上不可用。当我在本地运行它时,它可以工作,但我在 Windows 上运行它。部署后,它在 nginx 中的 Unix 机器上运行。我可以看到FindSystemTimeZoneById 在涉及 Unix 时正在查找错误的文件夹。有什么办法可以做到这一点?

【问题讨论】:

我认为这个问题听起来应该在 StackExchange 的 UNIX 区域结束,因为听起来您需要在 Unix 机器上适当地安装/配置时区数据库。 但似乎错误是 FindSystemTimeZoneById 在涉及到 unix 时正在查找错误的文件夹,时区已安装但 ofc 与 Windows 机器上的目录不同。 .NET Core on Unix 在/usr/share/zoneinfo/ 中查找时区文件。如果您希望它在不同的目录中查找,您可以将 TZDIR 环境变量设置为包含您的 tzfiles 的目录。有关更多信息,请参阅Unix implementation。在 Windows 上,时区信息是从注册表中读取的,而不是从磁盘上的文件中读取的。 搜索引擎的额外关键字:System.TimeZoneNotFoundException:在本地计算机上找不到时区 ID '...'。 System.IO.FileNotFoundException:找不到文件'/usr/share/zoneinfo/...' 【参考方案1】:

你能试试这个吗?

   TimeZoneInfo easternZone;
        try
        
            easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
        
        catch (TimeZoneNotFoundException)
        
            easternZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
        

您可以在此处查看 IANA 时区列表https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

【讨论】:

请解释如何以及为什么这可以解决所描述的问题。谢谢! 我同意,这不是问题。 这不是最好的解决方案,但如果它失败了,他会使用另一个假设你正在使用 Linux 或反之亦然的解决方案,并且它可以工作。【参考方案2】:

.Net Core 使用系统时区。不幸的是 Windows 和 Linux 有不同的时区系统。现在你有两种方法:

使用其他(和通用)时区实现,例如Noda time Translate between Windows and IANA time zones,例如使用TimeZoneConverter micro-library.

【讨论】:

这是 .NET Core 在 Unix 上的当前限制。见the GitHub issue here。您需要在 Windows 上传递与在 Unix 上不同的时区 ID。该问题被标记为“待命”。欢迎提交;)。【参考方案3】:

如果您想尝试 Windows 时区,然后在 Windows 时区不存在时回退到 IANA 时区:

var tzi  = TimeZoneInfo.GetSystemTimeZones().Any(x => x.Id == "Eastern Standard Time") ? 
    TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") : 
    TimeZoneInfo.FindSystemTimeZoneById("America/New_York");

【讨论】:

【参考方案4】:

使用previous answer,我们可以通过检查我们正在运行的操作系统来避免昂贵的try/catch

using System;
using System.Runtime.InteropServices;

TimeZoneInfo easternStandardTime;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))

  easternStandardTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))

  easternStandardTime = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))

  throw new NotImplementedException("I don't know how to do a lookup on a Mac.");

【讨论】:

嗨,我在哪里可以找到 Windows 和 Linux 时区 ID 的横向比较?很难找到。我只想找到'Mountain Standard Time'的linux等价物 如果它的 OSX 时区称为 Place of Birth of Our Dear Prophet and Visionary +08:30 谢谢,节省了我的时间。【参考方案5】:

通过执行以下操作,我能够在我的开发 docker 映像中支持此用例:

cp /usr/share/zoneinfo/America/Los_Angeles "/usr/share/zoneinfo/Pacific Standard Time"

显然,我认为这对于生产部署来说不是一个好主意。但在某些情况下它可能会有所帮助。

【讨论】:

感谢,此解决方案使开发人员能够在 Linux 下对我们的 .NET 项目进行编程。 这是最简单的方法,不会损坏功能【参考方案6】:

快速而肮脏的解决方案:在 Windows 上的虚拟应用程序中使用 ToSerializedString 序列化您的 TimeZoneInfo,保存输出,然后在需要的地方使用 FromSerializedString 反序列化。

在 Windows 上:

Console.WriteLine(TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"));

输出:

Eastern Standard Time;-300;(UTC-05:00) Eastern Time (US & Canada);Eastern Standard Time;Eastern Daylight Time;[01:01:0001;12:31:2006;60;[0;02:00:00;4;1;0;];[0;02:00:00;10;5;0;];][01:01:2007;12:31:9999;60;[0;02:00:00;3;2;0;];[0;02:00:00;11;1;0;];];

然后:

// TimeZoneInfo is immutable
public static readonly TimeZoneInfo EST = TimeZoneInfo.FromSerializedString(
            "Eastern Standard Time;-300;(UTC-05:00) Eastern Time (US & Canada);Eastern Standard Time;Eastern Daylight Time;[01:01:0001;12:31:2006;60;[0;02:00:00;4;1;0;];[0;02:00:00;10;5;0;];][01:01:2007;12:31:9999;60;[0;02:00:00;3;2;0;];[0;02:00:00;11;1;0;];];");

【讨论】:

【参考方案7】:

从 .NET 6 Preview 4 开始,it is finally possible 以跨平台方式处理时区。

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");

请注意,如链接中所述,基于 .NET Core Alpine Linux 的 Docker 映像 will not have the necessary tzdata installed by default,因此必须将其安装在您的 Dockerfile 中才能正常工作。

【讨论】:

以上是关于在 unix (nginx) 上托管时 .NET Core 中的 TimeZoneInfo的主要内容,如果未能解决你的问题,请参考以下文章

Java 聊天程序在本地主机上工作,但在 Heroku 上托管时不能

在 Netlify 上托管时设置电子邮件

为啥我的 discord.py 机器人在 Heroku 上托管时没有声音?

在 AWS 上托管时,拒绝为目标域生成登录提示的权限

在 Azure 上托管时找不到位于我的根项目文件夹中的文件

在 000webhost 上托管时无法使用 jwt 连接到 laravel api