如何以UTC格式打印DateTime?

3

我只是想以UTC格式打印DateTime时间。我做错了什么?

var utcEpoch = DateTime.Parse("1970-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); //This specifies the time I provided is in UTC
Console.WriteLine(utcEpoch.ToString("yyyy-MM-dd HH:mm:ss zzz")); //This properly shows my UTC offset of -6, so it's not wrong
Console.WriteLine(utcEpoch.ToString("u")); //This just flat out seems wrong because it doesn't specify a timezone or offset in its output

> 1969-12-31 18:00:00 -06:00
> 1969-12-31 18:00:00Z

我预期看到最后一个的时间是1970-01-01 00:00:00Z

2个回答

4
通用可排序("u")格式说明符

虽然结果字符串应该将时间表示为协调 世界时(UTC),但在格式化操作期间不会对原始DateTime值进行转换。 因此,在格式化之前,必须通过调用DateTime.ToUniversalTime方法将DateTime值转换为UTC。相比之下,DateTimeOffset值会自动执行此转换;在格式化操作之前,无需调用DateTimeOffset.ToUniversalTime方法。

您的utcEpoch.Kind并非UTC,而是LocalDateTime比你想象的要棘手。您希望它将返回Kind属性作为UTC,但事实并非如此。它返回Local

这种情况也在Phil Haack的博客文章上讨论过,Matt Johnson对此发表了相当好的评论;

AssumeLocalAssumeUniversal都与输入有关 字符串的解释方式。 它们本身不会改变输出的类型。

默认的输出类型为Local。要让它成为Utc,可以使用 AdjustToUniversal样式。

DateTimeStyles枚举是基于标志的,因此可以以某种有意义的方式组合这些枚举。要实现最初的目标(将输入解析为UTC并将其作为UTC输出),则应使用:

DateTime utcDate = DateTime.Parse("10/01/2006 19:30", culture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);

正如其他人指出的那样,单独调用ToUniversalTime()也可以起到作用,但这在技术上更正确。

您也可以在引用源上查看它;

case 'u':   // Universal time in sortable format.
if (offset != NullOffset)
{
    // Convert to UTC invariants mean this will be in range
    dateTime = dateTime - offset;
}
else if (dateTime.Kind == DateTimeKind.Local)
{
    InvalidFormatForLocal(format, dateTime);
}

1
这个答案(带编辑)是一个完美的解释。现在感觉更有意义了 :) - Joe Phillips
@JoePhillips 我尝试给出更多的解释。看看吧。 - Soner Gönül
1
++ 使用 DateTime 值时,"zzz" 自定义格式说明符表示本地操作系统时区相对于 UTC 的有符号偏移量,以小时和分钟为单位。它不反映实例的 DateTime.Kind 属性的值。因此,不建议将 "zzz" 格式说明符与 DateTime 值一起使用。 - Soner Gönül
这里的“zzz”注释非常关键。我肯定忽视了它,并基于此做出了一些错误的假设。 - Joe Phillips
@JoePhillips 很高兴能帮忙。 - Soner Gönül
显示剩余2条评论

2
我认为你误解了API的作用。
首先要注意的是,DateTimeStyles.AssumeUniversalDateTimeStyles.AssumeLocal仍将返回一种Kind = Local的DateTime。
> DateTime.Parse("1970-01-01 00:00:00", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).Kind
=> Local
> DateTime.Parse("1970-01-01 00:00:00", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).Kind
=> Local

无论如何,我们将获得一个本地日期。这意味着该API很可能是为了使从UTC日期获取本地时间成为可能。让我们尝试一下是否正确。
我在瑞典,所以在标准时间期间我们是UTC + 1。因此,如果我使用DateTimeStyles.AssumeUniversal并输入今天的日期,我应该得到一个本地日期,即今天的01:00。
在C# Interactive中运行:
> DateTime.Parse("2018-03-03", .CultureInfo.InvariantCulture, .DateTimeStyles.AssumeUniversal)
=> [2018-03-03 01:00:00]

C#假设我输入的字符串是UTC时间,并且我想转换为本地时间,因此它为我“修正”了它。
使用AssumeLocal同样适用。
DateTime.Parse("2018-03-03", .CultureInfo.InvariantCulture, .DateTimeStyles.AssumeLocal)
=> [2018-03-03 00:00:00]

作为预期,我们现在将输入视为本地字符串,并获得相同的值。
要将日期作为UTC获取,您可以指定类型。
DateTime.SpecifyKind(DateTime.Parse("2018-03-03", CultureInfo.InvariantCulture), DateTimeKind.Utc).ToString("o")
=> "2018-03-03T00:00:00.0000000Z"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接