【问题标题】:Creating a DateTime in a specific Time Zone in c#在 C# 中的特定时区创建日期时间
【发布时间】:2020-04-28 14:57:51
【问题描述】:

我正在尝试创建一个单元测试来测试机器上时区何时更改的情况,因为它已被错误地设置然后更正。

在测试中,我需要能够在非本地时区创建 DateTime 对象,以确保运行测试的人无论身在何处都可以成功地执行此操作。

从 DateTime 构造函数中可以看出,我可以将 TimeZone 设置为本地时区、UTC 时区或未指定。

如何创建具有特定时区(如 PST)的 DateTime?

【问题讨论】:

标签: c# .net datetime timezone .net-3.5


【解决方案1】:

对于特定时区(既不是本地时间,也不是 UTC)的日期/时间,您可以使用 DateTimeOffset 类:

  var time = TimeSpan.Parse("9:00");
  var est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
  var nationalDateTime = new DateTimeOffset(DateTime.Today.Ticks + time.Ticks, est.BaseUtcOffset);

【讨论】:

  • DateTimeOffset 指定时区。 @Tryinko 在他的 cmets 中很好地解释了这一点。
【解决方案2】:

试试TimeZoneInfo.ConvertTime(dateTime, sourceTimeZone, destinationTimeZone)

【讨论】:

    【解决方案3】:

    使用 TimeZones 类可以轻松创建特定时区的日期。

    TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById(TimeZones.Paris.Id));
    

    【讨论】:

    • 抱歉,这里的 Asp .NET Core 2.2 上不可用,VS2017 建议我安装 Outlook Nuget 包。
    • example => TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById("太平洋标准时间"))
    【解决方案4】:

    Jon's answer 谈论TimeZone,但我建议改用TimeZoneInfo

    我个人喜欢尽可能保持 UTC 格式(至少在过去是这样;storing UTC for the future has potential issues),所以我建议采用这样的结构:

    public struct DateTimeWithZone
    {
        private readonly DateTime utcDateTime;
        private readonly TimeZoneInfo timeZone;
    
        public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
        {
            var dateTimeUnspec = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
            utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTimeUnspec, timeZone); 
            this.timeZone = timeZone;
        }
    
        public DateTime UniversalTime { get { return utcDateTime; } }
    
        public TimeZoneInfo TimeZone { get { return timeZone; } }
    
        public DateTime LocalTime
        { 
            get 
            { 
                return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); 
            }
        }        
    }
    

    您可能希望将“TimeZone”名称更改为“TimeZoneInfo”以使事情更清晰 - 我自己更喜欢更简短的名称。

    【讨论】:

    • 恐怕我不知道任何等效的 SQL Server 构造。我建议将时区名称作为一列,将 UTC 值作为另一列。分别获取它们,然后您可以相当轻松地创建实例。
    • 不确定使用 DateTime 和 TimeZoneInfo 的构造函数的预期用途,但鉴于您正在调用 dateTime.ToUniversalTime() 方法,我怀疑您猜测它“可能”是在当地时间。在这种情况下,我认为你真的应该使用传入的 TimeZoneInfo 将其转换为 UTC,因为他们告诉你它应该在那个时区。
    • @ChrisMoschini:那时你只是在发明自己的 ID 方案——世界上没有其他人使用的方案。我会坚持使用行业标准的 zoneinfo,谢谢。 (例如,很难看出“欧洲/伦敦”是如何毫无意义的。)
    • @ChrisMoschini:不同的例子:CST。是 UTC-5 还是 UTC-6? IST 怎么样——你的数据库中是以色列、印度还是爱尔兰? (即使您现在知道偏移量,观察相同缩写的不同国家/地区可能会在不同时间改变。因此,它所指的实际时区仍然存在歧义。时区!=偏移量。)回到您的案例:您声称使用缩写最能解决您的问题。使用行业标准时区 ID 会变得更糟吗?
    • @ChrisMoschini:我会继续推荐使用行业标准、明确的 zoneinfo ID,而不是模糊的缩写。这不是首选谁的图书馆的问题——图书馆的作者身份真的不是问题。如果有人希望使用具有 good 标识符选择的另一个库,那很好。不过,时区标识符的选择很重要,我认为让读者意识到缩写 不明确是非常重要的,正如我在 IST 示例中所展示的那样。
    【解决方案5】:

    这里的其他答案很有用,但它们没有具体介绍如何访问太平洋 - 你去吧:

    public static DateTime GmtToPacific(DateTime dateTime)
    {
        return TimeZoneInfo.ConvertTimeFromUtc(dateTime,
            TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
    }
    

    奇怪的是,虽然“太平洋标准时间”通常与“太平洋夏令时间”不同,但在这种情况下,它指的是一般的太平洋时间。事实上,如果您使用FindSystemTimeZoneById 来获取它,其中一个可用属性是一个布尔值,告诉您该时区当前是否处于夏令时。

    您可以在一个库中看到更通用的示例,我最终根据用户的要求等来处理我在不同时区中需要的日期时间:

    https://github.com/b9chris/TimeZoneInfoLib.Net

    这在 Windows 之外不起作用(例如 Linux 上的 Mono),因为时间列表来自 Windows 注册表: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\

    您会在下面找到键(注册表编辑器中的文件夹图标);这些键的名称是您传递给FindSystemTimeZoneById 的名称。在 Linux 上,您必须使用一组单独的 Linux 标准时区定义,我没有充分探索。

    【讨论】:

    • 另外还有 ConvertTimeBySystemTimeZoneId() 例如:TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, "Central Standard Time")
    • 在windows中TimeZone Id List也可以看到这个答案:stackoverflow.com/a/24460750/4573839
    【解决方案6】:

    我使用扩展方法为 web 更改了一点 Jon Skeet answer。它也像魅力一样适用于 azure。

    public static class DateTimeWithZone
    {
    
    private static readonly TimeZoneInfo timeZone;
    
    static DateTimeWithZone()
    {
    //I added web.config <add key="CurrentTimeZoneId" value="Central Europe Standard Time" />
    //You can add value directly into function.
        timeZone = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["CurrentTimeZoneId"]);
    }
    
    
    public static DateTime LocalTime(this DateTime t)
    {
         return TimeZoneInfo.ConvertTime(t, timeZone);   
    }
    }
    

    【讨论】:

      【解决方案7】:

      我喜欢 Jon Skeet 的回答,但想补充一件事。我不确定 Jon 是否期望 ctor 总是在本地时区传递。但我想将它用于其他地方的情况。

      我正在从数据库中读取值,并且我知道该数据库所在的时区。所以在 ctor 中,我将传入数据库的时区。但是我想要当地时间的价值。 Jon 的 LocalTime 不会返回转换为本地时区日期的原始日期。它返回转换为原始时区的日期(无论您传递给ctor)。

      我认为这些属性名称清楚...

      public DateTime TimeInOriginalZone { get { return TimeZoneInfo.ConvertTime(utcDateTime, timeZone); } }
      public DateTime TimeInLocalZone    { get { return TimeZoneInfo.ConvertTime(utcDateTime, TimeZoneInfo.Local); } }
      public DateTime TimeInSpecificZone(TimeZoneInfo tz)
      {
          return TimeZoneInfo.ConvertTime(utcDateTime, tz);
      }
      

      【讨论】:

        【解决方案8】:

        DateTimeOffset 结构正是为这种用途而创建的。

        见: http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx

        以下是创建具有特定时区的 DateTimeOffset 对象的示例:

        DateTimeOffset do1 = new DateTimeOffset(2008, 8, 22, 1, 0, 0, new TimeSpan(-5, 0, 0));

        【讨论】:

        • 谢谢,这是完成它的好方法。在正确的时区内获取 DateTimeOffset 对象后,您可以使用 .UtcDateTime 属性来获取您创建的时间的 UTC 时间。如果您将日期存储在 UTC 中,那么将它们转换为每个用户的本地时间没什么大不了的 :)
        • 我认为这不能正确处理夏令时,因为有些时区会尊重它,而另一些则不会。同样“当天” DST 开始/结束,当天的部分时间将关闭。
        • 课程。 DST 是特定时区的规则。 DateTimeOffset 不不不不与任何时区相关联。不要将 UTC 偏移值(例如 -5)与时区混淆。这不是时区,而是偏移量。许多时区通常共享相同的偏移量,因此它是指时区的一种模棱两可的方式。由于 DateTimeOffset 与偏移量相关联,而不是时区,因此它不可能应用 DST 规则。所以凌晨 3 点将是一年中的每一天的凌晨 3 点,在 DateTimeOffset 结构中无一例外(例如在它的 Hours 和 TimeOfDay 属性中)。
        • 如果您查看 DateTimeOffset 的 LocalDateTime 属性,您可能会感到困惑。该属性不是 DateTimeOffset,而是类型为 DateTimeKind.Local 的 DateTime 实例。该实例与时区相关联......无论本地系统时区是什么。该属性将反映夏令时。
        • 所以,DateTimeOffset 的真正问题在于它没有包含足够的信息。它包括偏移量,而不是时区。多个时区的偏移量不明确。
        【解决方案9】:

        您必须为此创建一个自定义对象。您的自定义对象将包含两个值:

        不确定是否已经存在 CLR 提供的数据类型,但至少 TimeZone 组件已经可用。

        【讨论】:

        • 仅供参考:TimeZone 类很久以前就被弃用了。它太有限了,就像 DateTimeKind 太有限一样。 TimeZoneInfo 是一项重大改进,但未能确定何时应用 - 何时不应用 - 夏令时调整。
        猜你喜欢
        • 2018-10-03
        • 2014-10-28
        • 2011-05-03
        • 2016-03-12
        • 2012-12-10
        • 1970-01-01
        • 1970-01-01
        • 2013-02-16
        相关资源
        最近更新 更多