【问题标题】:Get DateTime object given local time as string, date as UTC DateTime and a timezone-locale string?获取给定本地时间的 DateTime 对象作为字符串,日期作为 UTC DateTime 和时区区域设置字符串?
【发布时间】:2017-05-22 20:00:54
【问题描述】:

我正在处理来自多个来源的数据,我需要汇总一个准确的日期时间。

我有:

A) string 代表一天中的某个时间,例如:“4:00 pm”
B) 一个 DateTime 对象,旨在表示一个纯日期,通过创建为特定日期的午夜、祖鲁时间(00:00 的偏移量)。
C) string 代表时区语言环境,例如:“America/Los_Angeles”

如何获得精确的DateTime 对象,具有正确的刻度数,表示在该日期 (B) 在该区域设置 (C) 经历的时间 (A)??

【问题讨论】:

  • 您应该考虑使用DateTimeOffset 而不是DateTime
  • 在 C# 中处理日期和时间时,我总是建议考虑使用 NodaTime。它避免了内置 DateTimeDateTimeOffset 类的许多谬误。
  • 好的,我可以使用 DateTimeOffset 作为我的输出,但我无法控制这些输入类型(A、B、C)。此外,NodaTime 在这里可以提供任何答案,但我更喜欢没有它也可以工作的正确答案。
  • 这里有一个漂亮、简单的时区转换器库:github.com/mj1856/TimeZoneConverter,它将在 TZDB 和 Windows 时区之间进行转换。然后您可以将其与 .NET Timezone 函数一起使用(IIRC 至少需要 .NET 4.5,尽管它们也可能存在于 4.0 中)。
  • 请澄清 - 您是说日期是基于 UTC 日期,但时间是指定时区的本地时间?

标签: c# datetime timezone


【解决方案1】:

这里有一个使用NodaTime 的示例,它在处理日历、时区、日期和时间时比任何框架类都更可靠:

var timeString = "4:00 pm";
var pureDate = new DateTime(2017, 5, 22, 0, 0, 0, DateTimeKind.Utc);
var timezoneString = "America/Los_Angeles";

var localTime = ParseTimeString(timeString);

var localDate = LocalDate.FromDateTime(pureDate);

var localDateTime = localDate.At(localTime);

var zone = DateTimeZoneProviders.Tzdb[timezoneString];

var zonedDateTime = localDateTime.InZoneStrictly(zone);

zonedDateTime 中,您将找到您的完整日期:

"2017-05-22T16:00:00 America/Los_Angeles (-07)"

您可以使用 zonedDateTime.ToDateTimeUtc() 来获取 UTC 格式的 System.DateTime 实例。

ParseTimeString 使用您的格式说明符解析您的时间字符串:

public static LocalTime ParseTimeString(string timeString)
{
    var pattern = LocalTimePattern.CreateWithInvariantCulture("h:mm tt");
    return pattern.Parse(timeString).Value;
}

使用DateTimeDateTimeOffsetTimeZoneInfo 时的注意事项

如果您不想使用 NodaTime,请注意内置类可能存在的缺陷:

  1. Windows 中的TimeZoneInfo 使用与 IANA/TZDB 不兼容的不同说明符。如果您在使用前不进行转换,您的America/Los_Angeles 时区字符串将不起作用(请参阅https://stackoverflow.com/tags/timezone/info
  2. DateTimeOffset(比DateTime 更可靠)在创建时仍然会丢失信息。时区数据无法持久保存,您将只有一个与 UTC 有偏差的日期。
  3. 您需要手动解析自定义时间字符串(可能使用正则表达式)。

【讨论】:

  • 这个答案目前假设日期也是本地日期,但我认为 OP 是说他的日期值基于 UTC 日期。
  • @MattJohnson 我也不清楚。我打算将纯日期作为仅包含日期部分的实例。也许OP可以澄清。
  • 将创建输入 (B),例如:new DateTime(2017, 6, 22, 0, 0, 0, DateTimeKind.Utc)
  • @Faust 我当时的意图是正确的。上述代码应按要求工作。
  • @MattJohnson 如果他在他的问题中添加所需的输出,一切都会更清楚。
【解决方案2】:

这是我最终在没有 NodaTime 的情况下如何解决的:

    public static DateTime Combine(DateTime date, string time, string timeZone)
    {
        TimeZoneInfo tzInfo = TimeZoneInfo.FindSystemTimeZoneById(TimezoneDictionary[timeZone]);
        var timeOfDay = DateTime.ParseExact(time, "h:mm tt", null).TimeOfDay;
        var combined = date.Add(timeOfDay).Subtract(tzInfo.BaseUtcOffset);
        if (tzInfo.IsDaylightSavingTime(combined))
            combined = combined.Subtract(TimeSpan.FromHours(1));

        return combined;
    }

我还需要这本字典来将 IANA 时区转换为 Microsoft:

    private static Dictionary<string, string> TimezoneDictionary = new Dictionary<string, string>
    {
        { "America/Los_Angeles", "Pacific Standard Time" },
        { "America/Denver", "Mountain Standard Time" },
        { "America/Chicago", "Central Standard Time" },
        { "America/New_York", "Eastern Standard Time" },
        //many more of course, just clipping this list for brevity.
    };

【讨论】:

    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 2012-08-13
    • 2013-05-04
    • 1970-01-01
    相关资源
    最近更新 更多