使用日期和時間執行算術運算
雖然 DateTime 和 DateTimeOffset 結構都提供成員來對其值執行算術運算,但算術運算得到的結果極為不同。 本主題將說明這些差異、這些差異與日期和時間資料的時區感知程度有何關係,並討論如何使用日期和時間資料執行完整的時區感知運算。
對 DateTime 值進行比較和算術運算
從 .NET Framework 2.0 版開始,DateTime 值加入了有限的時區感知程度。 DateTime.Kind 屬性可讓 DateTimeKind 值指派給日期和時間,指出其表示的是本地時間、Coordinated Universal Time (UTC) 或未指定時區的時間。 不過,若對 DateTime 值進行比較或執行日期和時間算術,此有限的時區資訊就會被忽略。 下列範例將以比較目前本地時間和目前 UTC 時間來說明此點。
Public Enum TimeComparison As Integer
EarlierThan = -1
TheSameAs = 0
LaterThan = 1
End Enum
Module DateManipulation
Public Sub Main()
Dim localTime As Date = Date.Now
Dim utcTime As Date = Date.UtcNow
Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours", _
localTime.Kind.ToString(), _
utcTime.Kind.ToString(), _
(localTime - utcTime).Hours, _
(localTime - utcTime).Minutes)
Console.WriteLine("The {0} time is {1} the {2} time.", _
localTime.Kind.ToString(), _
[Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)), _
utcTime.Kind.ToString())
' If run in the U.S. Pacific Standard Time zone, the example displays
' the following output to the console:
' Difference between Local and Utc time: -7:0 hours
' The Local time is EarlierThan the Utc time.
End Sub
End Module
using System;
public enum TimeComparison
{
EarlierThan = -1,
TheSameAs = 0,
LaterThan = 1
}
public class DateManipulation
{
public static void Main()
{
DateTime localTime = DateTime.Now;
DateTime utcTime = DateTime.UtcNow;
Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours",
localTime.Kind.ToString(),
utcTime.Kind.ToString(),
(localTime - utcTime).Hours,
(localTime - utcTime).Minutes);
Console.WriteLine("The {0} time is {1} the {2} time.",
localTime.Kind.ToString(),
Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)),
utcTime.Kind.ToString());
}
}
// If run in the U.S. Pacific Standard Time zone, the example displays
// the following output to the console:
// Difference between Local and Utc time: -7:0 hours
// The Local time is EarlierThan the Utc time.
CompareTo(DateTime) 方法會報告本地時間早於 (或少於) UTC 時間,接著減法運算會指出 UTC 與位於 美國太平洋標準時間的系統本地時間有七個小時的時差。 但由於這兩個值提供不同的單一時間點表示,因此,從此例可以清楚得知,這段時間間隔完全是本地時間與 UTC 之間的位移。
簡單地說,就是 DateTime.Kind 屬性並不會影響 DateTime 比較和計算後傳回的結果 (就如同兩個完全相同的時間點比較的結果),雖然這可能會影響結果的解讀。 例如:
對兩個 DateTime.Kind 屬性都等於 Utc 的日期和時間值執行的任何算術運算所得到的結果,才會反映出兩個值之間的實際時間間隔。 同樣地,比較這兩個日期和時間值,才能準確反映出兩個時間之間的關係。
對兩個 DateTime.Kind 屬性都等於 Local 的日期和時間值,或對兩個有不同 DateTime.Kind 屬性值的日期和時間值,執行的任何算術或比較運算所得結果反映的是兩個值之間的時鐘時間。
對本地日期和時間值所做的算術或比較運算,不會考慮到特定值是否為模稜兩可或無效,也不會將本地時區與日光節約時間的來回轉換調整規則的影響列入考量。
任何比較或計算 UTC 與本地時間之時差的運算所得的時間間隔,就等於是本地時區與 UTC 之間的位移。
任何比較或計算未指定時間與 UTC 或本地時間之時差的運算,反映的是簡單的時鐘時間。 時區差異不會列入考量,而結果也不會反映出套用時區調整規則。
任何比較或計算兩個未指定時間之時差的運算,可能會加入一段未知的時間間隔,以反映兩個不同時區的時間之間的差異。
在很多情節中,時區差異並不會影響日期和時間計算 (如需部分案例討論,請參閱在 DateTime、DateTimeOffset 和 TimeZoneInfo 之間選擇),或是日期和時間資料的內容會定義比較或算術運算的意義。
對 DateTimeOffset 值進行比較和算術運算
DateTimeOffset 值不僅包含日期和時間,也包含位移,它會明確定義相對於 UTC 的日期和時間。 因此,相等的定義與 DateTime 值稍有不同。 對於 DateTime 值,如果兩個值有相同的日期和時間值,就表示相等,但對於 DateTimeOffset 值,則要兩個值都是指相同的時間點,才算相等。 這可讓 DateTimeOffset 值在進行比較或大多數算術運算,以判斷兩個日期和時間值之間的時間間隔時,結果更為準確,也不需要太多的解讀。 下列範例是前面比較本地和 UTC DateTime 值的範例,但將值改為了 DateTimeOffset,以說明此行為差異。
Public Enum TimeComparison As Integer
EarlierThan = -1
TheSameAs = 0
LaterThan = 1
End Enum
Module DateTimeOffsetManipulation
Public Sub Main()
Dim localTime As DateTimeOffset = DateTimeOffset.Now
Dim utcTime As DateTimeOffset = DateTimeOffset.UtcNow
Console.WriteLine("Difference between local time and UTC: {0}:{1:D2} hours.", _
(localTime - utcTime).Hours, _
(localTime - utcTime).Minutes)
Console.WriteLine("The local time is {0} UTC.", _
[Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)))
End Sub
End Module
' Regardless of the local time zone, the example displays
' the following output to the console:
' Difference between local time and UTC: 0:00 hours.
' The local time is TheSameAs UTC.
' Console.WriteLine(e.GetType().Name)
using System;
public enum TimeComparison
{
EarlierThan = -1,
TheSameAs = 0,
LaterThan = 1
}
public class DateTimeOffsetManipulation
{
public static void Main()
{
DateTimeOffset localTime = DateTimeOffset.Now;
DateTimeOffset utcTime = DateTimeOffset.UtcNow;
Console.WriteLine("Difference between local time and UTC: {0}:{1:D2} hours",
(localTime - utcTime).Hours,
(localTime - utcTime).Minutes);
Console.WriteLine("The local time is {0} UTC.",
Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)));
}
}
// Regardless of the local time zone, the example displays
// the following output to the console:
// Difference between local time and UTC: 0:00 hours.
// The local time is TheSameAs UTC.
在這個範例中,CompareTo 方法指出目前本地時間與目前 UTC 時間相等,而 DateTimeOffset 值相減的結果顯示兩個時間的時差為 TimeSpan.Zero。
在日期和時間的計算中使用 DateTimeOffset 值的主要限制在於,雖然 DateTimeOffset 值具有某些程度的時區感知,但卻不是完全的時區感知。 雖然在一開始指派值給 DateTimeOffset 變數時,能夠讓 DateTimeOffset 值的位移反映出時區與 UTC 之間的位移,但稍後就會與時區切斷關聯。 由於不再與可識別的時間直接相關聯,日期和時間間隔的加減就不會將時區的調整規則列入考量。
舉例而言,美國中部標準時間時區會在 2008 年 3 月 9 日 2:00 A.M. 轉換成日光節約時間 。 這表示,在中部標準時間 2008 年 3 月 9 日 1:30 A.M. 加上兩個半小時, 應會得出的日期和時間為 2008 年 3 月 9 日 5:00 A.M. 。 不過,如下列範例所示,加上兩個半小時後為 2008 年 3 月 9 日 4:00 A.M.。 。 請注意,這個運算結果並不代表正確的時間點,不過這也不是我們所要時區的時間 (也就是說,沒有得到預期的時區位移)。
Module IntervalArithmetic
Public Sub Main()
Dim generalTime As Date = #03/09/2008 1:30AM#
Const tzName As String = "Central Standard Time"
Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)
' Instantiate DateTimeOffset value to have correct CST offset
Try
Dim centralTime1 As New DateTimeOffset(generalTime, _
TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime))
' Add two and a half hours
Dim centralTime2 As DateTimeOffset = centralTime1.Add(twoAndAHalfHours)
' Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
twoAndAHalfHours.ToString(), _
centralTime2)
Catch e As TimeZoneNotFoundException
Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
End Try
End Sub
End Module
' The example displays the following output to the console:
' 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00
using System;
public class IntervalArithmetic
{
public static void Main()
{
DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
const string tzName = "Central Standard Time";
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);
// Instantiate DateTimeOffset value to have correct CST offset
try
{
DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime));
// Add two and a half hours
DateTimeOffset centralTime2 = centralTime1.Add(twoAndAHalfHours);
// Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1,
twoAndAHalfHours.ToString(),
centralTime2);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
}
}
}
// The example displays the following output to the console:
// 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00
對時區時間進行算術運算
TimeZoneInfo 類別包含數個轉換方法,將時間從某個時區轉換成另一個時區時,會自動套用調整。 這些需求包括下列各項:
ConvertTime 和 ConvertTimeBySystemTimeZoneId 方法,這兩個方法會在任何兩個時區之間轉換時間。
ConvertTimeFromUtc 和 ConvertTimeToUtc 方法,這兩個方法會將特定時區的時間轉換成 UTC,或將 UTC 轉換成特定時區的時間。
如需詳細資訊,請參閱 在各時區間轉換時間。
TimeZoneInfo 類別並沒有提供任何自動套用調整規則的方法,可供執行日期和時間算術使用。 不過,您可以這麼做:將時區的時間轉換成 UTC、執行算術運算,然後再將 UTC 轉換回該時區的時間。 如需詳細資訊,請參閱 HOW TO:在日期和時間運算中使用時區。
例如,下列程式碼如同前面的程式碼,也是將兩個半小時加到 2008 年 3 月 9 日 2:00 A.M.。 。 不過,由於它是先將中部標準時間轉換成 UTC,然後才執行日期和時間算術,接著再將結果從 UTC 轉換回中部標準時間,因此所得時間就會反映轉換成日光節約時間之後的中部標準時區。
Module TimeZoneAwareArithmetic
Public Sub Main()
Const tzName As String = "Central Standard Time"
Dim generalTime As Date = #03/09/2008 1:30AM#
Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName)
Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)
' Instantiate DateTimeOffset value to have correct CST offset
Try
Dim centralTime1 As New DateTimeOffset(generalTime, _
cst.GetUtcOffset(generalTime))
' Add two and a half hours
Dim utcTime As DateTimeOffset = centralTime1.ToUniversalTime()
utcTime += twoAndAHalfHours
Dim centralTime2 As DateTimeOffset = TimeZoneInfo.ConvertTime(utcTime, cst)
' Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
twoAndAHalfHours.ToString(), _
centralTime2)
Catch e As TimeZoneNotFoundException
Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
End Try
End Sub
End Module
' The example displays the following output to the console:
' 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00
using System;
public class TimeZoneAwareArithmetic
{
public static void Main()
{
const string tzName = "Central Standard Time";
DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);
// Instantiate DateTimeOffset value to have correct CST offset
try
{
DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
cst.GetUtcOffset(generalTime));
// Add two and a half hours
DateTimeOffset utcTime = centralTime1.ToUniversalTime();
utcTime += twoAndAHalfHours;
DateTimeOffset centralTime2 = TimeZoneInfo.ConvertTime(utcTime, cst);
// Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1,
twoAndAHalfHours.ToString(),
centralTime2);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
}
}
}
// The example displays the following output to the console:
// 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00