對於處理時區間差異的任何應用程式而言,它變得越來越重要。 應用程式無法再假設所有時間都可以在當地時間表示,這是從 DateTime 結構取得的時間。 例如,顯示美國東部目前時間的網頁將缺乏對東亞客戶的信任度。 本文說明如何將時間從某個時區轉換到另一個時區,並轉換具有有限時區感知的DateTimeOffset值。
轉換成國際標準時間
世界協調時間(UTC)是高精確度的原子時間標準。 世界時區會以UTC的正或負位移表示。 因此,UTC 提供不受時區限制或時區中立的時間。 當日期和時間跨計算機可移植性很重要時,建議使用UTC。 如需使用日期和時間的詳細數據和其他最佳做法,請參閱 在 .NET Framework 中使用 DateTime 撰寫最佳做法。 將個別時區轉換成UTC可讓時間比較變得簡單。
備註
您也可以串行化 DateTimeOffset 結構,以明確表示單一時間點。 由於 DateTimeOffset 物件會儲存日期和時間值及其與UTC的位移,因此它們一律代表與UTC相關的特定時間點。
將時間轉換成 UTC 最簡單的方式是呼叫 static (Shared 在 Visual Basic 中) TimeZoneInfo.ConvertTimeToUtc(DateTime) 方法。 方法所執行的確切轉換取決於 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
如果TimeZoneInfo.ConvertTimeToUtc物件的ArgumentException屬性與時區不符,方法DateTime會擲回Kind。 如果 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 轉換為當地時間,請呼叫 ToLocalTime 您要轉換其時間的物件 方法 DateTime 。 方法的確切行為取決於物件 Kind 屬性的值,如下表所示:
DateTime.Kind |
轉換 |
|---|---|
DateTimeKind.Local |
返回未變更的 DateTime 值。 |
DateTimeKind.Unspecified |
假設 DateTime 此值為 UTC,並將 UTC 轉換為當地時間。 |
DateTimeKind.Utc |
將 DateTime 值轉換為當地時間。 |
備註
方法 TimeZone.ToLocalTime 的行為與 DateTime.ToLocalTime 方法相同。 它需要單一參數,也就是日期和時間值來轉換。
您也可以使用 static (Shared 在 Visual Basic 中) TimeZoneInfo.ConvertTime 方法,將任何指定時區中的時間轉換為當地時間。 下一節將討論這項技術。
在任意兩個時區之間進行轉換
您可以使用static類別的下列兩種方法之一(在 Visual Basic 中為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 方法會將這個日期和時間值轉換成網頁伺服器上的日期和時間:
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」,代表一個比 UTC 早五個小時的時區中的日期和時間,對於位於美國太平洋標準時間區的伺服器,它會傳回「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