C# 替换时区

C# 替换时区,c#,datetime,timezone,icalendar,nodatime,C#,Datetime,Timezone,Icalendar,Nodatime,我们正在尝试构建基本的事件日历功能,该功能允许用户创建事件,并在给定的月、日、年、小时、分钟以及时区指定开始时间(System.TimeZoneInfo.Id)。CMS系统根据服务器的位置生成结果system.DateTime,比如TimeZoneInfo.Id山地标准时间。CMS不提供带有日期选择器组件的选项来指定时区。但是,我们可以控制SQL日期时间精度,默认设置为7 DateTime格式为yyyymmddthhmmszas,用于填充.ics/ical中的开始/结束时间。使用这种格式,201

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

DateTime
格式为
yyyymmddthhmmsz
as,用于填充.ics/ical中的开始/结束时间。使用这种格式,2018年5月25日7:00PM(
20180508T192840Z
)总是看起来像服务器的山区标准时间(MST),而不是2018年5月25日选定东部标准时间(EST)的7:00PM

如何使用
日期时间
日期时间偏移量
时区信息
节点时间
,甚至
字符串
函数来格式化为
yyyymmddthhmmsz
,来“替换”生成的
日期时间
的时区

以下是:

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

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

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


如何使用一些甚至是从
DateTime.Now
创建的
DateTime
来实现这一点呢?

DateTime
类型不知道时区,它所知道的关于区域的一切都是
DateTimeKind
,可以是
本地
Utc
未指定的
。字符串表示中包含的区域信息将基于
种类
值和服务器时区

您应该为您的场景使用
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的字符串格式将其视为字符文字,因为它不是有效的。请注意,格式化标记区分大小写。作为一个文本,它只是复制到输出,就像
    T
    一样。因此,您生成的这个字符串总是会被任何解析它的东西解释为UTC,因为在ISO 8601标准中,
    Z
    就是这个意思。这是你所面临问题的根本原因

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

  • 正如其他人所指出的,
    DateTime
    不支持时区,但也请注意,
    DateTimeOffset
    也不支持时区,因为它只跟踪UTC的偏移量,而不跟踪特定时区。例如,它可以跟踪
    -07:00
    ,但不能告诉您它在山区时间。这就是野田佳彦时间有其
    zoneDateTime
    类型的原因。Net本身没有任何这样的内置类型

  • 在代码中,而不是在调用
    TimeZoneInfo.ConvertTime
    时,将考虑
    dateTime1
    变量的
    .Kind
    。如果是
    DateTimeKind.Utc
    ,则结果将是确定的。但是如果它是
    DateTimeKind.Unspecified
    ,或者
    DateTimeKind.Local
    ,那么它将被视为本地计算机的时区,在您的情况下,这就是服务器时区

  • 请注意,无论服务器的时区设置为什么,以行为相同的方式编写代码要好得多。这通常意味着避免使用
    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基本格式中,偏移量应该类似于
-0500
,而不是
-05:00
。不幸的是,没有格式说明符可以直接得到它。(只有ISO 8601扩展格式使用冒号)。

对于此应用程序,服务器时间实际上应该设置为GMT。
DateTime
类型不知道时区,它知道的所有关于区域的信息都是
DateTimeKind
,可以是
Local
Utc
Unspecified
。因此,我不太确定在不更改ticks值的情况下替换
DateTime
的时区意味着什么。你能进一步解释一下吗?对于包含区域信息的日期,应使用
DateTimeOffset
。它有一个带有
TimeSpan
的构造函数,因此您可以提供时区偏移。是否要将所选日期时间转换为sele
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTimeOffset utcNow = DateTimeOffset.UtcNow;
DateTimeOffset converted = TimeZoneInfo.ConvertTime(utcNow, destinationTimeZone);
string s = converted.ToString("yyyyMMddTHHmmsszzz").Replace(":","");