【问题标题】:Problem with converting time according to timezone in C#在 C# 中根据时区转换时间的问题
【发布时间】:2019-11-12 11:57:51
【问题描述】:

我正在尝试将 DateTime 从源时区转换为 UTC。日期、时间和时区作为用户的输入(显示为下拉菜单选项)。

以下是用于转换的部分代码:

string inputDateString = "2019-11-12T09:00:00.000"; //Taken as input from user
string inputTimeZoneString = "(UTC-03:00) Brasilia"; // Taken as input from user

DateTime dtStartdatetime = DateTime.Parse(inputDateString);

string sourceTimeZone = string.Empty;
foreach (TimeZoneInfo a in TimeZoneInfo.GetSystemTimeZones())
{
     if (a.DisplayName == objCalendar.timezone)
     {
          sourceTimeZone = a.Id;
     }
     string strTimeZone = a.DisplayName.Substring(a.DisplayName.IndexOf(')') + 1);
     string strTimeZone1 = objCalendar.timezone.Substring(objCalendar.timezone.IndexOf(')') + 1);
     if (strTimeZone.Trim() == strTimeZone1.Trim())
     {
          sourceTimeZone = a.Id;
     }
}

DateTime utc_time_start = TimeZoneInfo.ConvertTimeToUtc(dtStartdatetime, TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZone));
Console.WriteLine(utc_time_start.ToString("yyyyMMddTHHmmssZ"));

问题是这段代码在开发系统(基于 IST 时区)上运行时将 20191112T120000Z 作为输出,而相同的代码在运行时会产生 20191112T110000Z 作为输出在服务器上(基于 EST)。这种行为是由于运行它的系统的时区不同造成的吗?对于这种情况,可能的解决方案是什么?来自特定时区的特定时间应产生相同的 UTC 时间,而与代码执行的机器无关。

【问题讨论】:

  • 首先,在 DateTime.Parse 中,您可以在此字符串中指定您可以预期的时区。如果你没有指定这个 - 它将使用本地机器时区并相应地转换
  • 要使用时区,最好使用 DateTimeOffset,因为它有更多使用时区的方法
  • @MichaelKokorin 我不确定说“您可以在解析时指定时区”是否准确 - 您可以指定 偏移量,但这与时区
  • 请记住,默认情况下,大多数服务器都以 UTC 运行。 IMO 您应该假设服务器以 UTC 运行,并针对它进行编码。在解析和显示时始终指定时区是解决问题的合理方法。
  • @neil 就我而言,服务器位于东部时间(东部时间),我需要能够将任何时区的时间转换为 UTC。我怎样才能做到这一点?如果您可以分享一些代码,将会有所帮助。

标签: c# datetime timezone


【解决方案1】:

您的服务器运行所在的时区不会影响此代码。

差异是由于the end of DST for Brazil in 2019

Windows 发布了an update in July 2019 来涵盖这种情况。具体来说,KB4507704Windows 10 Build 17763.652 解决了此更改。你的开发环境有这个更新,服务器没有。您应该确保您的服务器正在接收 Windows 更新。如果缺少此(发布后 5 个月),则可能还缺少更重要的安全更新。

此外,出于以下几个原因,我强烈不鼓励使用显示名称匹配时区:

  • 显示名称由操作系统的主要语言本地化,因此它们会有所不同,例如,在设置为英语的服务器上与设置为葡萄牙语的服务器上。 (.NET 中的全球化和本地化设置不用于此。)

  • 显示名称可能不稳定。也就是说,如果在以后的更新中需要更改显示名称,则该字符串将与您之前使用的内容有所不同。

相反,将时区的Id 作为输入传递给TimeZoneInfo.FindSystemTimeZoneById,并完全跳过中间的匹配位。

【讨论】:

  • 感谢您分享您的见解。此信息非常有帮助。我会按照您的建议进行更改。不过只有一个问题 - 当你说我应该通过 Id 而不是比较显示名称时;我假设您指的是 E。南美洲标准时间和其他时区的类似值。
  • 是的,这是来自TimeZoneInfo.Id 的值,对应于Windows 注册表中的时区键。保证不会改变。您应该将其存储在数据库中。显示相应的DisplayName 以显示给您的最终用户,但不要保存它或在 API 中传递它。
【解决方案2】:

找到源时区后,您不会离开循环。

在第二部分中,您搜索时区的部分字符串。这部分很可能会在您的服务器上找到第二个时区。如果操作系统或 .net 版本不同,就会发生这种情况。

试试:

string inputDateString = "2019-11-12T09:00:00.000"; //Taken as input from user
string inputTimeZoneString = "(UTC-03:00) Brasilia"; // Taken as input from user

var dtStartdatetime = DateTime.Parse(inputDateString);

string sourceTimeZone = string.Empty;
foreach (TimeZoneInfo a in TimeZoneInfo.GetSystemTimeZones())
{
     if (a.DisplayName == inputTimeZoneString)
     {
          sourceTimeZone = a.Id;
         break;
     }
     string strTimeZone = a.DisplayName.Substring(a.DisplayName.IndexOf(')') + 1);
     string strTimeZone1 = inputTimeZoneString.Substring(inputTimeZoneString.IndexOf(')') + 1);
     if (strTimeZone.Trim() == strTimeZone1.Trim())
     {
          sourceTimeZone = a.Id;
         break;
     }
}

DateTime utc_time_start = TimeZoneInfo.ConvertTimeToUtc(dtStartdatetime, TimeZoneInfo.FindSystemTimeZoneById(sourceTimeZone));
Console.WriteLine(utc_time_start.ToString("yyyyMMddTHHmmssZ"));

【讨论】:

  • 我尝试添加中断但无济于事。至于关于部分字符串搜索的第二点;我尝试检查 sourceTimeZone 值并获得 E。两台机器上的南美标准时间,但结果时间仍然不同。
  • 似乎是配置差异。我认为您必须开始将错误的系统与正确的系统进行比较。找到的 TimeZoneInfo 的基本 UTC 偏移量是否相同?两台机器的操作系统和操作系统版本相同吗?两者的时间设置相同?
猜你喜欢
  • 1970-01-01
  • 2017-03-05
  • 1970-01-01
  • 2017-11-05
  • 1970-01-01
  • 2017-03-30
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多