C# Datetime ISO 8601 格式给出了错误的值

C# Datetime ISO 8601 format gives incorrect value

我正在尝试获取格式正确的 ISO 8601 日期,但我一直遇到问题。我的初始代码似乎可以正常工作,但我发现 DST 日期未按预期 returning。我在 Whosebug 上创建了一个 .NET fiddle 来询问这个问题,但似乎“系统”时区的工作方式会在我部署代码时给我带来更多问题。

这是一个 dotnet fiddle 显示完全错误的东西:

using System;
                
public class Program
{
    public static void Main()
    {
        var val3 = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(2021, 10, 13, 18, 0, 0), TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time"));
        Console.WriteLine(val3.ToString("yyyy-MM-ddTHH:mm:sszzz"));
    
    }
}

如果我 运行 这个,我得到以下信息:

2021-10-13T13:00:00+00:00

所以 CST 的时间是正确的,但偏移量看起来反映了服务器上的“系统”时区。综合来看,日期和输入的日期完全不一样

如果我在本地时区为 CST 的开发系统上 运行 此代码,我会得到另一个不同的答案:

2021-10-13T08:00:00-06:00

鉴于所讨论的日期是夏令时,我希望以上两者都 return 以下内容:

2021-10-13T13:00:00-05:00

我做错了什么?

让我看看我的理解是否正确(我仍然不完全确定 SOAP 在问题中何时发挥作用)。

您有一个 UTC 日期时间:

var dt = new DateTime(2021, 10, 13, 18, 0, 0, DateTimeKind.Utc);

并且您想在 CST 中对其进行格式化 -- 在 10 月 13 日,实际上是 CDT (-05:00)?

如果那是正确的,那么你想利用 DateTimeOffset,它可以更好地理解..好吧,时间偏移。

// Your original value. This will have an offset of "00:00" 
// as the DateTime above was created as `DateTimeKind.Utc`
var timeAtUtc = new DateTimeOffset(dt);

// Alternatively:
// var timeAtUtc = new DateTimeOffset(2021, 10, 13, 18, 0, 0, TimeSpan.Zero);

// Find out the correct offset on that day (-5:00)
var cstOffsetAtTheTime = TimeZoneInfo
    .FindSystemTimeZoneById("Central Standard Time")
    .GetUtcOffset(timeAtUtc);

// And now apply the offset to your original value
var timeAtCst = timeAtUtc.ToOffset(cstOffsetAtTheTime);

// You will now get "2021-10-13T13:00:00-05:00" 
// no matter where you run this from.
Console.WriteLine(timeAtCst.ToString("yyyy-MM-ddTHH:mm:sszzz"));

// Edit: If you pass it a date in Feb, you'll see that it will correctly be at -06:00.


根据以下评论编辑 2022-03-07。如果你不需要关心偏移值,你可以简单地做:

var timeAtUtc = ...;
var timeAtCst = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(
  timeAtUtc, 
  "Central Standard Time");

很好,但只是为了回答有关原始代码有什么问题的问题:

  • A DateTime 对象不存储时区偏移信息。它只存储一个 Kind 属性,它可以是三个 DateTimeKind 值之一,UtcLocalUnspecified

  • 由于您的输入是 DateTime,输出也必须是 DateTime,没有时区偏移。因此,与您的目标时区相关的偏移量将被丢弃。使用 DateTimeOffset 允许目标偏移量持续存在。

  • 在最终的控制台输出中,您使用了 zzz 来显示偏移量。 .NET documentation 解释了为什么这使用了服务器的时区:

    With DateTime values, the "z" custom format specifier represents the signed offset of the local operating system's time zone from Coordinated Universal Time (UTC), measured in hours. It doesn't reflect the value of an instance's DateTime.Kind property. For this reason, the "z" format specifier is not recommended for use with DateTime values.

    With DateTimeOffset values, this format specifier represents the DateTimeOffset value's offset from UTC in hours.

顺便说一句,如果您确实需要显示 DateTime 的偏移量,其 KindUtcLocal,您应该使用 the K specifier(K 代表种类)而不是 zzz。使用 K 说明符,Utc 种将显示“Z”,Local 值将显示本地偏移量,例如“-05:00”,而 Unspecified 值将显示两者都不显示(空字符串)。