代码之家  ›  专栏  ›  技术社区  ›  Alexander Staroselsky

替换时区

  •  4
  • Alexander Staroselsky  · 技术社区  · 6 年前

    我们正在尝试构建基本的事件日历功能,允许用户创建事件并在给定的月、日、年、小时、分钟以及时区指定开始时间( System.TimeZoneInfo.Id ).CMS系统生成结果 System.DateTime 根据我们服务器的位置 TimeZoneInfo.Id 山地标准时间 .CMS不提供日期选择器组件选项来指定时区。但是,我们可以控制SQL datetime精度,默认设置为 7

    这个 DateTime yyyyMMddTHHmmssZ 为了在中填充开始/结束时间。ics/ical。采用这种格式,2018年5月25日下午7:00( 20180508T192840Z )始终看起来像服务器的山区标准时间(MST),而不是2018年5月25日东部标准时间(EST)晚上7:00。

    如何“替换”的时区 日期时间 在不更改年/月/日/小时/分钟的情况下生成 日期时间 ,则, DateTimeOffset TimeZoneInfo ,则, NodaTime ,甚至 string 要格式化为的函数 YYYYMMDDTHHMMSZ ?

    以下内容:

    TimeZoneInfo destinationTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    var converted = TimeZoneInfo.ConvertTime(dateTime1, destinationTimeZone);
    

    或:

    LocalDateTime fromLocal = LocalDateTime.FromDateTime(dateTime1);
    DateTimeZone fromZone = DateTimeZoneProviders.Tzdb["America/Denver"];
    ZonedDateTime fromZoned = fromLocal.InZoneLeniently(fromZone);
    
    DateTimeZone toZone = DateTimeZoneProviders.Tzdb["America/Chicago"];
    ZonedDateTime toZoned = fromZoned.WithZone(toZone);
    LocalDateTime toLocal = toZoned.LocalDateTime;
    var result = toLocal.ToDateTimeUnspecified();
    

    创建新的 日期时间 将小时数从CST调整为EST,因为目标是 日期时间 使用原始小时值,但使用 TimeZoneInfo。身份证件 东部标准时间

    日期时间 构造函数似乎没有指定 时区信息 只有 DateTimeKind

    如何才能做到这一点,即使是 日期时间 创建自 DateTime.Now ?

    2 回复  |  直到 6 年前
        1
  •  3
  •   Matt Johnson-Pint    6 年前

    有几件事:

    • 您的格式说明符包括 Z 在最后。这由处理。Net的字符串格式设置为 字符文字 ,因为它不是有效的 datetime formatting specifier 。请注意,格式化标记区分大小写。作为文本,它只是复制到输出中,就像 T 是因此,您生成的这个字符串总是会被任何解析它的东西解释为UTC,因为这是 Z 指ISO 8601标准中的。这最终是您面临的问题的根本原因。

      如果您希望它反映一个模糊的本地时间(因为时区可能在您的.ics中的其他地方?),然后省略 Z 完全但是,如果打算包括时区偏移,则可以使用 K 的说明符 DateTime 价值观,或者 zzz 说明符与 DateTimeOffset 价值观-取决于您的具体需求。

    • 正如其他人指出的那样, 日期时间 不知道时区,但请注意,两者都不知道 日期时间偏移量 因为它只跟踪UTC的偏移量,而不是特定的时区。例如,它可以跟踪 -07:00 ,但它不能告诉你它是在山区。这就是野田佳彦时代 ZonedDateTime 类型Net本身没有任何这样的内置类型。

    • 在代码中,而不是调用 TimeZoneInfo.ConvertTime 这个 .Kind dateTime1 将考虑变量。如果是 DateTimeKind.Utc ,则结果将具有确定性。但如果是 DateTimeKind.Unspecified DateTimeKind.Local ,则将其视为本地计算机的时区,即 服务器 你的情况是时区。

    • 请注意,最好以行为相同的方式编写代码 不管 服务器时区的设置。这通常意味着避免 DateTimeKind。地方的 例如 DateTime.Now ,则, TimeZoneInfo.Local ,以及其他。相反,使用 DateTime.UtcNow 用于获取当前 日期时间 。或者,您可以使用 DateTimeOffset.Now DateTimeOffset.UtcNow ,或Noda Time的 IClock 实施。

    归根结底,尽管有几种可能的解决方案,但要将当前时间生成为特定时区中的字符串,最简单的方法是:

    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    DateTime utcNow = DateTime.UtcNow;
    DateTime converted = TimeZoneInfo.ConvertTime(utcNow, destinationTimeZone);
    string s = converted.ToString("yyyyMMddTHHmmss");
    

    或者您可能需要:

    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    DateTimeOffset utcNow = DateTimeOffset.UtcNow;
    DateTimeOffset converted = TimeZoneInfo.ConvertTime(utcNow, destinationTimeZone);
    string s = converted.ToString("yyyyMMddTHHmmsszzz").Replace(":","");
    

    请注意 : 通过 Replace 最后,这是因为在ISO 8601中 基本的 格式,偏移量应如下所示 -0500 而不是 -05:00 。遗憾的是,没有格式说明符可以直接获取。(仅ISO 8601 扩展的 格式使用冒号)。

        2
  •  0
  •   UladzislaÅ­    6 年前

    DateTime 类型不知道时区,它所知道的关于区域的一切都是 DateTimeKind ,可以是 Local ,则, Utc Unspecified 。包含在字符串表示中的区域信息将基于 Kind 值和服务器时区。

    您应该使用 DateTimeOffset 对于在单个值中存储日期时间和时区信息的场景:

    var dateTime = DateTime.Now; /*your date time here*/
    var destinationTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    var zonedDateTime = new DateTimeOffset(DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), destinationTimeZone.BaseUtcOffset);
    var dateTimeStr = zonedDateTime.ToString("o"/*your format goes here*/);