77

[更新格式说明符与格式字符串不同;格式说明符是自定义格式字符串的一部分,其中格式字符串是“库存”并且不提供自定义。我的问题是说明符不是格式]

我一直在尝试使用使用“zzz”格式说明符的格式字符串执行往返日期时间转换,我知道它与本地时间绑定。因此,如果我尝试使用 UTC 日期时间往返,它会抛出 DateTimeInvalidLocalFormat 异常,它应该使用以下文本:

UTC 日期时间正在转换为仅适用于当地时间的格式的文本。这可能在使用“z”格式说明符调用 DateTime.ToString 时发生,该说明符将在输出中包含本地时区偏移量。在这种情况下,要么使用指定 UTC 时间的“Z”格式说明符,要么使用“o”格式字符串,这是在文本中保存 DateTime 的推荐方法。当传递要由 XmlConvert 或 DataSet 序列化的 DateTime 时,也会发生这种情况。如果使用 XmlConvert.ToString,则传入 XmlDateTimeSerializationMode.RoundtripKind 以正确序列化。如果使用 DataSet,请将 DataColumn 对象上的 DateTimeMode 设置为 DataSetDateTime.Utc。

根据这个建议,我需要做的就是让我的代码工作是用 'ZZZ' 替换 'zzz' 这样我就可以采用 UTC 格式。问题是,在文档中的任何地方都找不到“Z”,我尝试的任何“Z”格式组合,即“Z”、“ZZ”、“ZZZ”,总是只是将 DateTime 实例转换为那些 Z 被视为文字.

是否有人忘记在没有告诉异常消息作者的情况下实现“Z”,或者我错过了如何在没有黑客攻击的情况下用“+0000”替换有效的本地时间偏移?

代码示例:

// This is the format with 'zzzzz' representing local time offset
const string format = "ddd MMM dd HH:mm:ss zzzzz yyyy";

// create a UTC time
const string expected = "Fri Dec 19 17:24:18 +0000 2008";
var time = new DateTime(2008, 12, 19, 17, 24, 18, 0, DateTimeKind.Utc);

// If you're using a debugger this will rightfully throw an exception
// with .NET 3.5 SP1 because 'z' is for local time only; however, the exception
// asks me to use the 'Z' specifier for UTC times, but it doesn't exist, so it
// just spits out 'Z' as a literal.
var actual = time.ToString(format, CultureInfo.InvariantCulture);

Assert.AreEqual(expected, actual);
4

8 回答 8

82

也许“K”格式说明符会有一些用处。这是唯一一个似乎提到使用大写“Z”的地方。

“Z”是 DateTimes 的一种独特情况。文字“Z”实际上是 UTC 时间的 ISO 8601 日期时间标准的一部分。当“Z”(祖鲁语)被添加到时间的末尾时,它表示那个时间是 UTC,所以实际上字面的 Z 是时间的一部分。这可能会给 .NET 中的日期格式库带来一些问题,因为它实际上是文字,而不是格式说明符。

于 2009-05-07T06:23:17.160 回答
12

当您使用 DateTime 时,您可以在变量中存储日期和时间。

日期可以是本地时间或 UTC 时间,这取决于您。

例如,我在意大利(+2 UTC)

var dt1 = new DateTime(2011, 6, 27, 12, 0, 0); // store 2011-06-27 12:00:00
var dt2 = dt1.ToUniversalTime()  // store 2011-06-27 10:00:00

那么,当我打印包含时区的 dt1 和 dt2 时会发生什么?

dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 12:00:00 +2

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 10:00:00 +2

dt1 和 dt2 仅包含日期和时间信息。dt1 和 dt2 不包含时区偏移量。

那么如果“+2”不包含在 dt1 和 dt2 变量中,那么它是从哪里来的呢?

它来自您的机器时钟设置。

编译器告诉您,当您使用 'zzz' 格式时,您正在编写一个组合“DATE + TIME”(存储在 dt1 和 dt2 中)+ “TIMEZONE OFFSET”(不包含在 dt1 和 dt2 中的字符串,因为它们是 DateTyme 类型),它将使用正在执行代码的服务器计算机的偏移量。

编译器告诉您“警告:代码的输出取决于机器时钟偏移”

如果我在位于伦敦(+1 UTC)的服务器上运行此代码,结果将完全不同:而不是“ +2 ”,它将写成“ +1

...
dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 12:00:00 +1

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 10:00:00 +1

正确的解决方案是使用 DateTimeOffset 数据类型代替 DateTime。从 2008 版本开始在 sql Server 中可用,从 3.5 版本开始在 .Net 框架中可用

于 2011-06-29T10:39:58.867 回答
7

我正在处理,DateTimeOffset不幸的是“o”打印出“+0000”而不是“Z”。

所以我最终得到:

dateTimeOffset.UtcDateTime.ToString("o")
于 2014-03-13T02:35:26.677 回答
5

通过字符串往返日期一直很痛苦......但是文档表明'o'说明符是用于捕获UTC状态的往返的说明符。解析时,如果原始结果是 UTC,则结果通常具有 Kind == Utc。我发现最好的办法总是在序列化之前将日期标准化为 UTC 或本地,然后指示解析器选择哪种标准化。

DateTime now = DateTime.Now;
DateTime utcNow = now.ToUniversalTime();

string nowStr = now.ToString( "o" );
string utcNowStr = utcNow.ToString( "o" );

now = DateTime.Parse( nowStr );
utcNow = DateTime.Parse( nowStr, null, DateTimeStyles.AdjustToUniversal );

Debug.Assert( now == utcNow );
于 2009-05-07T06:41:09.893 回答
4

MSDN 上的此页面列出了标准的 DateTime 格式字符串,不包括使用“Z”的字符串。

更新:您需要确保日期字符串的其余部分也遵循正确的模式(您没有提供发送内容的示例,因此很难说您是否这样做)。要使 UTC 格式正常工作,它应该如下所示:

// yyyy'-'MM'-'dd HH':'mm':'ss'Z'
DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z");
于 2009-05-07T06:14:08.417 回答
3
Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");

将输出:

07 Mai 2009 | 08:16 | 13 | +02:00 | +02 | +2

我在丹麦,我与 GMT 的偏移量是 +2 小时,女巫是正确的。

如果您需要获取CLIENT Offset,我建议您检查我所做的一个小技巧。该页面位于英国的服务器中,GMT 为 +00:00,如您所见,您将获得当地的 GMT 偏移量。


关于你的评论,我做了:

DateTime dt1 = DateTime.Now;
DateTime dt2 = dt1.ToUniversalTime();

Label1.Text = dt1.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");
Label2.Text = dt2.ToString("dd MMM yyyy | hh:mm | FF | ZZZ | ZZ | Z");

我明白了:

07 Mai 2009 | 08:24 | 14 | +02:00 | +02 | +2
07 Mai 2009 | 06:24 | 14 | ZZZ | ZZ | Z 

我没有例外,只是......它对大写 Z 没有任何作用 :(

对不起,我错过了什么吗?


仔细阅读有关自定义日期和时间格式字符串的 MSDN

不支持大写“Z”。

于 2009-05-07T06:17:36.287 回答
0

如果您需要强制+00:00而不是Z指示UTC,这将得到您需要的东西:

string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss.ffffff'+00:00'");
于 2021-07-01T22:31:48.533 回答
0

看起来 +00.00 发生在 DateTimeKind 不是 UTC 时。指定它使“o”正常工作:

DateTime.SpecifyKind(new DateTime(2011, 6, 27, 12, 0, 0), DateTimeKind.Utc).ToString("o");

这将返回 Z 时间

于 2022-03-04T15:20:31.493 回答