替换时区
Replace TimeZone
我们正在尝试构建基本的事件日历功能,允许用户创建事件并在给定的月、日、年、小时和分钟以及时区指定开始时间(System.TimeZoneInfo.Id
). CMS 系统根据我们服务器的位置生成结果 System.DateTime
,比方说 TimeZoneInfo.Id
山地标准时间 。 CMS 的日期选择器组件不提供指定时区的选项。但是我们确实可以控制 SQL 日期时间精度,默认设置为 7
.
为了在 .ics/ical 中填充 start/end 次,DateTime
被格式化为 yyyyMMddTHHmmssZ
。使用这种格式,它使 2018 年 5 月 25 日 7:00PM (20180508T192840Z
) 始终看起来像服务器的山地标准时间 (MST),而不是选定的东部标准时间 (EST) 中的 2018 年 5 月 25 日 7:00PM ).
如何 "replace" 在不更改 year/month/day/hour/minute 的情况下生成 DateTime
的时区 DateTime
、DateTimeOffset
、TimeZoneInfo
、NodaTime
,甚至 string
函数格式化为 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
来做到这一点?
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*/);
几件事:
您的格式说明符末尾包含一个 Z
。这被 .Net 的字符串格式视为 字符文字 ,因为它不是有效的 datetime formatting specifier。请注意,格式化标记区分大小写。作为文字,它只是被复制到输出 - 就像 T
一样。因此,您生成的这个字符串总是会被解析它的任何东西解释为 UTC,因为这就是 Z
在 ISO 8601 标准中的含义。这最终是您所面临问题的根本原因。
如果您想让它反映一个不明确的本地时间(因为时区可能在您的 .ics 中的其他地方?),则完全省略 Z
。但是,如果您打算包括时区偏移量,那么您可以将 K
说明符用于 DateTime
值,或者可能将 zzz
说明符与 DateTimeOffset
值结合使用 - 取决于根据您的具体需求。
正如其他人指出的那样,DateTime
不是时区感知的,但也要注意 DateTimeOffset
也不是,因为它只跟踪与 UTC 的偏移量而不是特定的时区。例如,它可以跟踪 -07:00
,但不能告诉您它处于山地时间。这就是为什么 Noda Time 有它的 ZonedDateTime
类型。 .Net 本身没有任何此类内置类型。
在您的代码中,而不是在对 TimeZoneInfo.ConvertTime
的调用中,将考虑 dateTime1
变量的 .Kind
。如果它是 DateTimeKind.Utc
,那么结果将是确定性的。但如果它是 DateTimeKind.Unspecified
,或 DateTimeKind.Local
,那么它将被视为就好像是根据本地计算机的时区 - 即 server 时区在你的情况下。
请注意,无论服务器的时区设置如何, 最好以相同的方式编写代码。这通常意味着避免使用 DateTimeKind.Local
,例如 DateTime.Now
、TimeZoneInfo.Local
等。相反,使用 DateTime.UtcNow
获取当前的 DateTime
。或者,您可以使用 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 basic 格式中,偏移量应该像 -0500
而不是 -05:00
。不幸的是,没有格式说明符可以直接获取它。 (只有 ISO 8601 extended 格式使用冒号)。
我们正在尝试构建基本的事件日历功能,允许用户创建事件并在给定的月、日、年、小时和分钟以及时区指定开始时间(System.TimeZoneInfo.Id
). CMS 系统根据我们服务器的位置生成结果 System.DateTime
,比方说 TimeZoneInfo.Id
山地标准时间 。 CMS 的日期选择器组件不提供指定时区的选项。但是我们确实可以控制 SQL 日期时间精度,默认设置为 7
.
为了在 .ics/ical 中填充 start/end 次,DateTime
被格式化为 yyyyMMddTHHmmssZ
。使用这种格式,它使 2018 年 5 月 25 日 7:00PM (20180508T192840Z
) 始终看起来像服务器的山地标准时间 (MST),而不是选定的东部标准时间 (EST) 中的 2018 年 5 月 25 日 7:00PM ).
如何 "replace" 在不更改 year/month/day/hour/minute 的情况下生成 DateTime
的时区 DateTime
、DateTimeOffset
、TimeZoneInfo
、NodaTime
,甚至 string
函数格式化为 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
来做到这一点?
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*/);
几件事:
您的格式说明符末尾包含一个
Z
。这被 .Net 的字符串格式视为 字符文字 ,因为它不是有效的 datetime formatting specifier。请注意,格式化标记区分大小写。作为文字,它只是被复制到输出 - 就像T
一样。因此,您生成的这个字符串总是会被解析它的任何东西解释为 UTC,因为这就是Z
在 ISO 8601 标准中的含义。这最终是您所面临问题的根本原因。如果您想让它反映一个不明确的本地时间(因为时区可能在您的 .ics 中的其他地方?),则完全省略
Z
。但是,如果您打算包括时区偏移量,那么您可以将K
说明符用于DateTime
值,或者可能将zzz
说明符与DateTimeOffset
值结合使用 - 取决于根据您的具体需求。正如其他人指出的那样,
DateTime
不是时区感知的,但也要注意DateTimeOffset
也不是,因为它只跟踪与 UTC 的偏移量而不是特定的时区。例如,它可以跟踪-07:00
,但不能告诉您它处于山地时间。这就是为什么 Noda Time 有它的ZonedDateTime
类型。 .Net 本身没有任何此类内置类型。在您的代码中,而不是在对
TimeZoneInfo.ConvertTime
的调用中,将考虑dateTime1
变量的.Kind
。如果它是DateTimeKind.Utc
,那么结果将是确定性的。但如果它是DateTimeKind.Unspecified
,或DateTimeKind.Local
,那么它将被视为就好像是根据本地计算机的时区 - 即 server 时区在你的情况下。请注意,无论服务器的时区设置如何, 最好以相同的方式编写代码。这通常意味着避免使用
DateTimeKind.Local
,例如DateTime.Now
、TimeZoneInfo.Local
等。相反,使用DateTime.UtcNow
获取当前的DateTime
。或者,您可以使用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 basic 格式中,偏移量应该像 -0500
而不是 -05:00
。不幸的是,没有格式说明符可以直接获取它。 (只有 ISO 8601 extended 格式使用冒号)。