简体   繁体   English

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

[英]C# Parse String into DateTime with Specific TimeZone

I've been struggling with this for a bit so I'll punt to SO.我一直在为此苦苦挣扎,所以我会选择 SO。

I have an app where I need to parse strings into datetimes.我有一个应用程序,需要将字符串解析为日期时间。 The strings look like this:字符串如下所示:

"201503131557"

(they will always be this format "yyyyMMddHHmm") and always be in Central Standard Time (or Central Daylight Time depending on the date)even though they don't specify it. (它们将始终采用这种格式“yyyyMMddHHmm”)并且始终采用中部标准时间(或中部夏令时,具体取决于日期),即使它们没有指定。

When I parse them I understand that it parses them into local time.当我解析它们时,我明白它会将它们解析为本地时间。 I'm running on Azure PaaS and don't want to change it to run in CST just to support this operation.我在 Azure PaaS 上运行,不想为了支持此操作而将其更改为在 CST 中运行。

How do i write code that works both locally and in Azure PaaS that will correctly parse these dates into DateTimes setting their timezone to CST?我如何编写同时在本地和 Azure PaaS 中工作的代码,以便将这些日期正确解析为 DateTimes,并将其时区设置为 CST?

Here is a quick unit test I wrote to prove Matt Johnson's answer.这是我为证明 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);
    }

To parse the string, since you have only the date and time, and you know the specific format the strings will be in, do this:要解析字符串,因为您只有日期和时间,并且您知道字符串将采用的特定格式,请执行以下操作:

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

The resulting value will have its Kind property set to DateTimeKind.Unspecified (not local time, as you thought).结果值将其Kind属性设置为DateTimeKind.Unspecified (不是您认为的本地时间)。 That is to be expected, as you provided no information regarding how this timestamp is related to UTC or local time.这是意料之中的,因为您没有提供有关此时间戳如何与 UTC 或本地时间相关的信息。

You said the value represents time in Central Standard Time.您说该值代表中部标准时间的时间。 You'll need a TimeZoneInfo object that understands that time zone.您将需要一个了解该时区的TimeZoneInfo对象。

TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");

Note that this identifier represents the Central Time observed in the USA, including both CST or CDT depending on which is in effect for the given value (despite the word "Standard" in its name).请注意,此标识符表示在美国观察到的中部时间,包括 CST 或 CDT,具体取决于哪个对给定值有效(尽管其名称中有“标准”一词)。 Also note that it valid on Windows operating systems only.另请注意,它仅在 Windows 操作系统上有效。 If you wanted to use .NET Core on some other OS, then you'd need to pass the IANA identifier "America/Chicago" instead (or use my TimeZoneConverter library to use either identifier on any platform).如果您想在其他操作系统上使用 .NET Core,则需要传递 IANA 标识符"America/Chicago" (或使用我的TimeZoneConverter库在任何平台上使用任一标识符)。

The next step is to figure out what you want to do with this value.下一步是弄清楚你想用这个值什么。 You might do a few different things with it:你可以用它做一些不同的事情:

  • If you want to convert it to the equivalent UTC value, represented as a DateTime , then you can do this:如果要将其转换为等效的 UTC 值(表示为DateTime ,则可以执行以下操作:

     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:如果您想要一个DateTimeOffset表示,它包含您提供的输入以及与美国中部时间相关的 UTC 偏移量,那么您可以执行以下操作:

     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.请记住,如果输入时间接近 DST 转换,则输入时间可能无效或不明确。 The GetUtcOffset method will return the standard offset in such cases.在这种情况下, GetUtcOffset方法将返回标准偏移量。 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 theTimeZoneInfo class.您还可以执行其他操作,所有这些都由TimeZoneInfo类提供。

Note that "Azure PaaS" might refer to a few different things, and while there is a setting called WEBSITE_TIME_ZONE in Azure App Service - I don't recommend you lean on it.请注意,“Azure PaaS”可能指的是一些不同的东西,虽然 Azure 应用服务中有一个名为WEBSITE_TIME_ZONE的设置 - 我不建议您依赖它。 Consider it a last resort to be used only when you can't control the code.仅当您无法控制代码时才将其视为最后的手段。 In most cases, it is better off writing your code to never depend on the time zone setting of the system it runs on.在大多数情况下,编写代码时最好不要依赖于它运行的系统的时区设置。 That means never calling DateTime.Now , or TimeZoneInfo.Local , DateTime.ToLocalTime , or even DateTime.ToUniversalTime (since it converts from the local time zone), etc. Instead, rely upon methods that work explicitly with either UTC or a specific time zone or offset.这意味着永远不要调用DateTime.NowTimeZoneInfo.LocalDateTime.ToLocalTime ,甚至DateTime.ToUniversalTime (因为它本地时区转换)等。相反,依赖于明确使用 UTC 或特定时间的方法区域或偏移。 Then you will never need to care about where your app is hosted.那么您将永远不需要关心您的应用程序的托管位置。

Lastly, understand that neither the DateTime or DateTimeOffset types have any capability of understanding that a value is tied to a specific time zone.最后,请了解DateTimeDateTimeOffset类型都无法理解值与特定时区相关联。 For that, you'd need to either write your own class, or look to the Noda Time library whose ZonedDateTime class provides such functionality.为此,您需要编写自己的类,或者查看其ZonedDateTime类提供此类功能的Noda Time库。

You can use:您可以使用:

DateTime.ParseExact(...) 日期时间.ParseExact(...)


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

This datetime do not have any metadata about timezone.此日期时间没有关于时区的任何元数据。 You can convert to UTC and then you will be sure that you are able to convert to all timezones.您可以转换为 UTC,然后您将确保能够转换为所有时区。

Use the DateTime.TryParseExact(...) method with DateTimeStyles.RoundtripKind to avoid conversion.DateTime.TryParseExact(...)方法与DateTimeStyles.RoundtripKind一起使用以避免转换。

DateTime dt;
DateTime.TryParseExact("201503131557", "yyyyMMddHHmm", null, System.Globalization.DateTimeStyles.RoundtripKind, out dt);

If you need to convert you will need to go to UTC then use the conversion methods of TimeZoneInfo with TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time") .如果您需要转换,则需要转到 UTC,然后使用TimeZoneInfoTimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")的转换方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM