对于任何处理日期和时间并处理时区差异的应用程序来说,这变得越来越重要。 应用程序不能再假定所有时间都可以以本地时间表示,即 DateTime 结构中可用的时间。 例如,显示美国东部当前时间的网页将缺乏对东亚客户的信任。 本文介绍如何将时间从一个时区转换为另一个时区,以及如何转换具有有限时区识别能力的 DateTimeOffset 值。
转换为协调世界时
协调世界时(UTC)是一种高精度的原子时间标准。 世界的时区表示为相对于 UTC 的正/负偏移量。 因此,UTC 提供不受时区限制或与时区无关的时间。 当日期和时间跨计算机可移植性非常重要时,建议使用 UTC。 有关使用日期和时间的详细信息和其他最佳做法,请参阅 .NET Framework中使用 DateTime 编码最佳做法。 将单个时区转换为 UTC 使时间比较变得简单。
注释
还可以序列化 DateTimeOffset 结构,以明确表示单个时间点。 由于 DateTimeOffset 对象存储日期和时间值及其与 UTC 的偏移量,因此它们始终表示与 UTC 相关的特定时间点。
将时间转换为 UTC 的最简单方法是调用 visual Basic TimeZoneInfo.ConvertTimeToUtc(DateTime) 方法中的 static
(Shared
)。 该方法执行的确切转换取决于 dateTime
参数 Kind 属性的值,如下表所示:
DateTime.Kind |
转换 |
---|---|
DateTimeKind.Local |
将本地时间转换为 UTC。 |
DateTimeKind.Unspecified |
假定 dateTime 参数是本地时间,并将本地时间转换为 UTC。 |
DateTimeKind.Utc |
返回的 dateTime 参数保持不变。 |
以下代码将当前本地时间转换为 UTC,并将结果显示到控制台:
DateTime dateNow = DateTime.Now;
Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(dateNow)} UTC.");
Dim dateNow As Date = Date.Now
Console.WriteLine("The date and time are {0} UTC.", _
TimeZoneInfo.ConvertTimeToUtc(dateNow))
如果日期和时间值不表示本地时间或 UTC,ToUniversalTime 方法可能会返回错误的结果。 但是,可以使用 TimeZoneInfo.ConvertTimeToUtc 方法从指定的时区转换日期和时间。 有关检索表示目标时区的 TimeZoneInfo 对象的详细信息,请参阅 查找在本地系统上定义的时区。 以下代码使用 TimeZoneInfo.ConvertTimeToUtc 方法将东部标准时间转换为 UTC:
DateTime easternTime = new DateTime(2007, 01, 02, 12, 16, 00);
string easternZoneId = "Eastern Standard Time";
try
{
TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
Console.WriteLine($"The date and time are {TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone)} UTC.");
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine($"Unable to find the {easternZoneId} zone in the registry.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine($"Registry data on the {easternZoneId} zone has been corrupted.");
}
Dim easternTime As New Date(2007, 01, 02, 12, 16, 00)
Dim easternZoneId As String = "Eastern Standard Time"
Try
Dim easternZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(easternZoneId)
Console.WriteLine("The date and time are {0} UTC.", _
TimeZoneInfo.ConvertTimeToUtc(easternTime, easternZone))
Catch e As TimeZoneNotFoundException
Console.WriteLine("Unable to find the {0} zone in the registry.", _
easternZoneId)
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the {0} zone has been corrupted.", _
easternZoneId)
End Try
如果 DateTime 对象的 Kind 属性与时区不匹配,TimeZoneInfo.ConvertTimeToUtc 方法将引发 ArgumentException。 如果 Kind 属性 DateTimeKind.Local 但 TimeZoneInfo 对象不表示本地时区,或者 Kind 属性 DateTimeKind.Utc 但 TimeZoneInfo 对象不等于 TimeZoneInfo.Utc,则会发生不匹配。
所有这些方法将 DateTime 值作为参数,并返回 DateTime 值。 对于 DateTimeOffset 值,DateTimeOffset 结构具有 ToUniversalTime 实例方法,该方法将当前实例的日期和时间转换为 UTC。 以下示例调用 ToUniversalTime 方法将本地时间和其他时间转换为 UTC:
DateTimeOffset localTime, otherTime, universalTime;
// Define local time in local time zone
localTime = new DateTimeOffset(new DateTime(2007, 6, 15, 12, 0, 0));
Console.WriteLine($"Local time: {localTime}");
Console.WriteLine();
// Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero);
Console.WriteLine($"Other time: {otherTime}");
Console.WriteLine($"{localTime} = {otherTime}: {localTime.Equals(otherTime)}");
Console.WriteLine($"{localTime} exactly equals {otherTime}: {localTime.EqualsExact(otherTime)}");
Console.WriteLine();
// Convert other time to UTC
universalTime = localTime.ToUniversalTime();
Console.WriteLine($"Universal time: {universalTime}");
Console.WriteLine($"{otherTime} = {universalTime}: {universalTime.Equals(otherTime)}");
Console.WriteLine($"{otherTime} exactly equals {universalTime}: {universalTime.EqualsExact(otherTime)}");
Console.WriteLine();
// The example produces the following output to the console:
// Local time: 6/15/2007 12:00:00 PM -07:00
//
// Other time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
//
// Universal time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
Dim localTime, otherTime, universalTime As DateTimeOffset
' Define local time in local time zone
localTime = New DateTimeOffset(#6/15/2007 12:00:00PM#)
Console.WriteLine("Local time: {0}", localTime)
Console.WriteLine()
' Convert local time to offset 0 and assign to otherTime
otherTime = localTime.ToOffset(TimeSpan.Zero)
Console.WriteLine("Other time: {0}", otherTime)
Console.WriteLine("{0} = {1}: {2}", _
localTime, otherTime, _
localTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
localTime, otherTime, _
localTime.EqualsExact(otherTime))
Console.WriteLine()
' Convert other time to UTC
universalTime = localTime.ToUniversalTime()
Console.WriteLine("Universal time: {0}", universalTime)
Console.WriteLine("{0} = {1}: {2}", _
otherTime, universalTime, _
universalTime.Equals(otherTime))
Console.WriteLine("{0} exactly equals {1}: {2}", _
otherTime, universalTime, _
universalTime.EqualsExact(otherTime))
Console.WriteLine()
' The example produces the following output to the console:
' Local time: 6/15/2007 12:00:00 PM -07:00
'
' Other time: 6/15/2007 7:00:00 PM +00:00
' 6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
' 6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM +00:00: False
'
' Universal time: 6/15/2007 7:00:00 PM +00:00
' 6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
' 6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM +00:00: True
将 UTC 转换为指定的时区
若要将 UTC 转换为本地时间,请参阅以下 将 UTC 转换为本地时间 部分。 若要将 UTC 转换为指定的任何时区中的时间,请调用 ConvertTimeFromUtc 方法。 该方法采用两个参数:
以下代码将 UTC 转换为中央标准时间:
DateTime timeUtc = DateTime.UtcNow;
try
{
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
Console.WriteLine("The date and time are {0} {1}.",
cstTime,
cstZone.IsDaylightSavingTime(cstTime) ?
cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Central Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.");
}
Dim timeUtc As Date = Date.UtcNow
Try
Dim cstZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim cstTime As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone)
Console.WriteLine("The date and time are {0} {1}.", _
cstTime, _
IIf(cstZone.IsDaylightSavingTime(cstTime), _
cstZone.DaylightName, cstZone.StandardName))
Catch e As TimeZoneNotFoundException
Console.WriteLine("The registry does not define the Central Standard Time zone.")
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.")
End Try
将 UTC 转换为本地时间
若要将 UTC 转换为本地时间,请调用要转换 DateTime 对象的 ToLocalTime 方法。 方法的确切行为取决于对象的 Kind 属性的值,如下表所示:
DateTime.Kind |
转换 |
---|---|
DateTimeKind.Local |
返回 DateTime 值不变。 |
DateTimeKind.Unspecified |
假定 DateTime 值为 UTC,并将 UTC 转换为本地时间。 |
DateTimeKind.Utc |
将 DateTime 值转换为本地时间。 |
注释
TimeZone.ToLocalTime 方法的行为与 DateTime.ToLocalTime
方法相同。 它采用单个参数(即日期和时间值)进行转换。
还可以使用 static
(Visual Basic 中的Shared
)TimeZoneInfo.ConvertTime 方法将任何指定时区中的时间转换为本地时间。 下一部分将讨论此方法。
在任意两个时区之间进行转换
可以使用 TimeZoneInfo 类的以下两个 static
(Shared
)方法之一在任意两个时区之间进行转换:
-
此方法的参数是要转换的日期和时间值、表示日期和时间值的时区的
TimeZoneInfo
对象,以及表示要转换日期和时间值的时区的TimeZoneInfo
对象。 -
此方法的参数包括要转换的日期和时间值、该日期时间值所在时区的标识符,以及要将该日期时间值转换至的目标时区的标识符。
这两种方法都需要日期和时间值的 Kind 属性进行转换,以及表示其时区的 TimeZoneInfo 对象或时区标识符彼此对应。 否则会引发 ArgumentException。 例如,如果日期和时间值的 Kind
属性 DateTimeKind.Local
,则当作为参数传递给该方法的 TimeZoneInfo
对象不等于 TimeZoneInfo.Local
时,将引发异常。 如果作为参数传递给方法的标识符不等于 TimeZoneInfo.Local.Id
,也会引发异常。
以下示例使用 ConvertTime 方法从夏威夷标准时间转换为本地时间:
DateTime hwTime = new DateTime(2007, 02, 01, 08, 00, 00);
try
{
TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
Console.WriteLine("{0} {1} is {2} local time.",
hwTime,
hwZone.IsDaylightSavingTime(hwTime) ? hwZone.DaylightName : hwZone.StandardName,
TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.");
}
Dim hwTime As Date = #2/01/2007 8:00:00 AM#
Try
Dim hwZone As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time")
Console.WriteLine("{0} {1} is {2} local time.", _
hwTime, _
IIf(hwZone.IsDaylightSavingTime(hwTime), hwZone.DaylightName, hwZone.StandardName), _
TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local))
Catch e As TimeZoneNotFoundException
Console.WriteLine("The registry does not define the Hawaiian Standard Time zone.")
Catch e As InvalidTimeZoneException
Console.WriteLine("Registry data on the Hawaiian Standard Time zone has been corrupted.")
End Try
转换 DateTimeOffset 值
由 DateTimeOffset 对象表示的日期和时间值并不完全感知时区,因为对象在实例化时与时区取消关联。 但是,在许多情况下,应用程序只需根据 UTC 的两个不同的偏移量(而不是特定时区的时间)转换日期和时间。 若要执行此转换,可以调用当前实例的 ToOffset 方法。 方法的单个参数是该方法将返回的新日期和时间值的偏移量。
例如,如果用户对网页的请求的日期和时间是已知的,并且以 MM/dd/yyyy hh:mm:ss zzh 格式序列化为字符串,则以下 ReturnTimeOnServer
方法将此日期和时间值转换为 Web 服务器上的日期和时间:
public DateTimeOffset ReturnTimeOnServer(string clientString)
{
string format = @"M/d/yyyy H:m:s zzz";
TimeSpan serverOffset = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);
try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture);
DateTimeOffset serverTime = clientTime.ToOffset(serverOffset);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
Dim format As String = "M/d/yyyy H:m:s zzz"
Dim serverOffset As TimeSpan = TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now)
Try
Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
Dim serverTime As DateTimeOffset = clientTime.ToOffset(serverOffset)
Return serverTime
Catch e As FormatException
Return DateTimeOffset.MinValue
End Try
End Function
如果该方法传递字符串“9/1/2007 5:32:07 -05:00”,代表一个比协调世界时早五小时的时区中的日期和时间,那么它会为位于美国太平洋标准时区的服务器返回“9/1/2007 3:32:07 AM -07:00”。
TimeZoneInfo 类还包括 TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) 方法的重载,该方法使用 ToOffset(TimeSpan) 值执行时区转换。 该方法有两个参数:一个是 DateTimeOffset 值,另一个是对时间要转换到的目标时区的引用。 方法调用返回 DateTimeOffset 值。 例如,可以按如下方式重写上一示例中的 ReturnTimeOnServer
方法,以调用 ConvertTime(DateTimeOffset, TimeZoneInfo) 方法。
public DateTimeOffset ReturnTimeOnServer(string clientString)
{
string format = @"M/d/yyyy H:m:s zzz";
try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString, format,
CultureInfo.InvariantCulture);
DateTimeOffset serverTime = TimeZoneInfo.ConvertTime(clientTime,
TimeZoneInfo.Local);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}
Public Function ReturnTimeOnServer(clientString As String) As DateTimeOffset
Dim format As String = "M/d/yyyy H:m:s zzz"
Try
Dim clientTime As DateTimeOffset = DateTimeOffset.ParseExact(clientString, format, CultureInfo.InvariantCulture)
Dim serverTime As DateTimeOffset = TimeZoneInfo.ConvertTime(clientTime, TimeZoneInfo.Local)
Return serverTime
Catch e As FormatException
Return DateTimeOffset.MinValue
End Try
End Function
另请参阅
- TimeZoneInfo
- 日期、时间和时区
- 查找本地系统 上定义的时区