我正在使用来自多个来源的数据,我需要整理一个准确的日期时间。
我有:
A) 代表一天中某个时间的string
,例如:"下午 4:00">
B) 一个旨在表示纯日期的DateTime
对象,通过创建为特定日期的午夜祖鲁时间(偏移量为 00:00)。
C) 代表时区区域设置的string
,例如:"美国/Los_Angeles">
如何获得具有正确刻度数的精确DateTime
对象,表示该日期 (B) 在该区域设置 (C) 中经历的时间 (A)?
这里有一个使用 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;
}
使用DateTime
、DateTimeOffset
和TimeZoneInfo
时的注意事项
如果您不想使用 NodaTime,请注意内置类可能存在的陷阱:
- Windows 中的
TimeZoneInfo
使用与 IANA/TZDB 不兼容的不同说明符。如果您在使用前未转换America/Los_Angeles
时区字符串,则它将无法正常工作(请参阅 https://stackoverflow.com/tags/timezone/info) DateTimeOffset
(比DateTime
更可靠)在创建时仍然会丢失信息。时区数据无法持久保存,您将只有一个与 UTC 有偏移量的日期。- 您需要手动解析自定义时间字符串(可能使用正则表达式)。
以下是我最终在没有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.
};