C# 将字符串解析为具有特定时区的 DateTime

5

我已经在这方面挣扎了一段时间,所以我会求救于Stack Overflow。

我有一个应用程序,需要将字符串解析为日期时间。 这些字符串看起来像这样:

"201503131557"

日期格式始终为"yyyyMMddHHmm",且始终为中央标准时间(或者根据日期而定的中央夏令时间),即使它们没有指定。当我解析它们时,我知道它们被解析成了本地时间。我正在 Azure PaaS 上运行,不想更改它以在 CST 中运行只为支持此操作。

如何编写既可在本地工作又可在 Azure PaaS 上正确解析这些日期并将其时区设置为CST的代码?

以下是我编写的快速单元测试,用于验证Matt Johnson的答案。

    [TestMethod]
    public void DateTests()
    {
        var unSpecDt = DateTime.ParseExact("201503131557", "yyyyMMddHHmm", CultureInfo.InvariantCulture);
        var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");


        var utcDt = TimeZoneInfo.ConvertTimeToUtc(unSpecDt, tz);

        var offset = tz.GetUtcOffset(unSpecDt);
        var dto =new DateTimeOffset(unSpecDt, offset);


        var cstDt = dto.DateTime;

        Assert.IsTrue(cstDt.Hour - utcDt.Hour == offset.Hours);
    }

@PrasadTelkikar,我不理解这个问题。这些字符串将始终是CST / CDT日期时间。我需要在我的笔记本电脑上运行的应用程序中将它们解析为DateTime(该应用程序在CST / CDT中运行),但也需要在Azure PaaS中运行。 - cobolstinks
我认为这个问题很清楚,但我理解为什么有些人会对它进行负投票,因为它没有展示出研究的努力。如果你想改进这个问题,你可以编辑它来展示你尝试过什么,并解释你想要用结果做什么。 - Matt Johnson-Pint
3个回答

12

要解析字符串,因为你只有日期和时间,并且知道字符串的特定格式,所以请按照以下步骤进行:

DateTime dt = DateTime.ParseExact("201503131557", "yyyyMMddHHmm", CultureInfo.InvariantCulture);

生成的值将其Kind属性设置为DateTimeKind.Unspecified(不是本地时间,正如您所认为的那样)。这是可以预料的,因为您没有提供有关此时间戳与UTC或本地时间的关系的任何信息。
您说该值表示中央标准时间中的时间。 您将需要一个理解该时区的TimeZoneInfo对象。
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");

请注意,此标识符表示美国观察到的中央时间,包括CST或CDT,具体取决于给定值的有效性(尽管其名称中有“标准”一词)。还要注意,它仅在Windows操作系统上有效。如果您想在其他操作系统上使用.NET Core,则需要传递IANA标识符“America/Chicago”(或使用我的TimeZoneConverter库在任何平台上使用任一标识符)。
下一步是确定您想要对此值执行什么操作。您可以对其进行几种不同的操作:
  • If you want to convert it to the equivalent UTC value, represented as a DateTime, then you can do this:

    DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);
    
  • If you want a DateTimeOffset representation which holds the input you gave and the offset from UTC as it related to US Central Time, then you can do this:

    TimeSpan offset = tz.GetUtcOffset(dt);
    DateTimeOffset dto = new DateTimeOffset(dt, offset);
    

    Keep in mind that it's possible for the input time is invalid or ambiguous if it falls near a DST transition. The GetUtcOffset method will return the standard offset in such cases. If you want different behavior, you have more code to write (out of scope for this post).

  • There are other things you might do, all of which are provided by the TimeZoneInfo class.

请注意,“Azure PaaS”可能指的是几个不同的东西,虽然在Azure App Service中有一个名为WEBSITE_TIME_ZONE的设置,但我不建议您依赖它。将其视为最后的手段,仅在您无法控制代码时使用。在大多数情况下,最好编写您的代码,使其永远不依赖于运行系统的时区设置。这意味着永远不要调用DateTime.NowTimeZoneInfo.LocalDateTime.ToLocalTime甚至DateTime.ToUniversalTime(因为它从本地时区转换),等等。相反,依赖于明确使用UTC或特定时区或偏移量的方法。然后您就不需要关心应用程序托管的位置。
最后,请了解DateTime类型和DateTimeOffset类型都没有理解值与特定时区相关联的能力。为此,您需要编写自己的类,或查看Noda Time库,其ZonedDateTime类提供了这样的功能。

哇,谢谢!这正是我需要的信息。感谢您澄清了我的误解。 - cobolstinks

0
使用 DateTime.TryParseExact(...) 方法,并使用 DateTimeStyles.RoundtripKind 以避免转换。
DateTime dt;
DateTime.TryParseExact("201503131557", "yyyyMMddHHmm", null, System.Globalization.DateTimeStyles.RoundtripKind, out dt);

如果您需要进行转换,您需要先转换为UTC时间,然后使用TimeZoneInfo的转换方法,并使用TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")


0

你可以使用:

DateTime.ParseExact(...)


public static DateTime ParseExact (string s, string format, IFormatProvider provider);

这个日期时间没有任何有关时区的元数据。你可以转换为UTC,然后你就可以确信你能够将其转换为所有时区。


如果我在本地使用它(在一个CST的机器上),而不是在Azure PaaS上运行(我的理解是本地时区为UTC),那么我会得到不同的日期吗? - cobolstinks
如果您知道要解析的日期在UTC上是正确的,那么您必须将其解析为UTC,然后它就可以正常工作了。我不知道是否有更有效的方法,但例如,您可以将其解析为任何日期时间,然后创建一个新的日期时间,在其中设置从首次解析的日期时间获取的刻度,并将DateTimeKind设置为UtcKind......或者另一种方法是使用NuGet包noda time - Peter M.

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