提问者:小点点

替换时区


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

datetime被格式化为yyyymmddthhmmssz,以便在.ics/ical中填充开始/结束时间。使用这种格式,它会使2018年5月25日7:00PM(20180508T192840Z)始终看起来像服务器的山区标准时间(MST),而不是选择的东部标准时间(EST)中的2018年5月25日7:00PM。

如何将在不更改年/月/日/小时/分钟的情况下生成的datetimedatetimeoffsettimezoneinfonodatime或甚至string函数“替换”datetime的时区,以将其格式化为yyyymmddthhmmssz

以下内容:

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

创建一个新的DateTime,将小时从CST调整为EST,这将不起作用,因为目标是使DateTime具有原始小时值,但具有TimeZoneInfo.id东部标准时间。

DateTime构造函数似乎没有指定TimeZoneInfo的构造函数,只有指定DateTimeKind的构造函数。

如何使用从datetime.now创建的datetime来完成这一任务?


共1个答案

匿名用户

几件事:

>

  • 您的格式说明符在末尾包含z。这被.NET的字符串格式视为字符文本,因为它不是有效的日期时间格式说明符。请注意,格式标记区分大小写。作为文字,它只是复制到输出--就像t一样。因此,您生成的字符串总是会被解析它的任何东西解释为UTC,因为这是ISO8601标准中z的意思。这是你所面临问题的根本原因。

    如果您希望它反映一个不明确的本地时间(因为时区可能在.ics的其他地方?),那么就完全省略z。但是,如果您打算包含时区偏移量,则可以将k说明符用于datetime值,或者将zzz说明符与datetimeoffset值结合使用-这取决于您的具体需要。

    正如其他人指出的,datetime不知道时区,但也要注意,datetimeoffset也不知道,因为它只跟踪与UTC的偏移量,而不跟踪特定时区。例如,它可以跟踪-07:00,但它不能告诉你它处于山区时间。这就是Noda Time具有zoneddatetime类型的原因。.NET本身没有任何这样的内置类型。

    在您的代码中,并不是在对TimeZoneInfo.ConvertTime的调用中,将考虑DateTime1变量的.kind。如果是datetimekind.utc,那么结果将是确定性的。但如果它是DateTimeKind.unspecifiedDateTimeKind.local,则它将被视为本地计算机的时区-在您的情况下是服务器的时区。

    请注意,无论服务器的时区设置为什么,以行为相同的方式编写代码要好得多。这通常意味着避免DateTimeKind.Local,如DateTime.NowTimeZoneInfo.Local等。相反,使用datetime.utcNow获取当前的datetime。或者,您可以使用datetimeoffset.nowdatetimeoffset.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扩展格式使用冒号)。