Kiezen tussen DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly en TimeZoneInfo
.NET-toepassingen kunnen op verschillende manieren datum- en tijdgegevens gebruiken. De meest voorkomende toepassingen van datum- en tijdgegevens zijn:
- Als u alleen een datum wilt weergeven, zodat tijdgegevens niet belangrijk zijn.
- Als u alleen een tijd wilt weergeven, zodat datumgegevens niet belangrijk zijn.
- Om een abstracte datum en tijd weer te geven die niet is gekoppeld aan een specifieke tijd en plaats (bijvoorbeeld de meeste winkels in een internationale keten zijn geopend op weekdagen om 9:00 uur).
- Voor het ophalen van datum- en tijdgegevens uit bronnen buiten .NET, meestal waar datum- en tijdgegevens worden opgeslagen in een eenvoudig gegevenstype.
- Om uniek en ondubbelzinnig een enkel tijdstip te identificeren. Voor sommige toepassingen is vereist dat een datum en tijd alleen ondubbelzinnig zijn op het hostsysteem. Andere apps vereisen dat het eenduidig is tussen systemen (dat wil zeggen dat een datum die op het ene systeem is geserialiseerd, zinvol kan worden gedeserialiseerd en op een ander systeem overal ter wereld kan worden gebruikt).
- Als u meerdere gerelateerde tijden wilt behouden (zoals de lokale tijd van de aanvrager en de tijd van ontvangst van een webaanvraag).
- Om rekenkundige datum- en tijdberekeningen uit te voeren, mogelijk met een resultaat dat uniek en ondubbelzinnig één bepaald tijdstip identificeert.
.NET bevat de typen DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyen TimeZoneInfo, die allemaal kunnen worden gebruikt om toepassingen te bouwen die werken met datums en tijden.
Notitie
In dit artikel wordt niet TimeZone besproken omdat de functionaliteit ervan bijna volledig is opgenomen in de TimeZoneInfo-klasse. Gebruik waar mogelijk de TimeZoneInfo-klasse in plaats van de TimeZone klasse.
De structuur DateTimeOffset
De DateTimeOffset structuur vertegenwoordigt een datum- en tijdwaarde, samen met een offset die aangeeft hoeveel die waarde verschilt van UTC. De waarde identificeert dus altijd ondubbelzinnig een enkel punt in de tijd.
Het DateTimeOffset type bevat alle functionaliteit van het DateTime type, samen met tijdzonebewustzijn. Dit maakt het geschikt voor toepassingen die:
- Uniek en ondubbelzinnig een single point in time identificeren. Het DateTimeOffset type kan worden gebruikt om ondubbelzinnig de betekenis van 'nu' te definiëren, om transactietijden te registreren, de tijden van systeem- of toepassingsevenementen te registreren, en het maken en wijzigen van bestanden vast te leggen.
- Algemene datum- en tijdberekeningen uitvoeren.
- Bewaar meerdere gerelateerde tijden, zolang deze tijden worden opgeslagen als twee afzonderlijke waarden of als twee leden van een structuur.
Notitie
Deze toepassingen voor DateTimeOffset waarden komen veel vaker voor dan voor DateTime waarden. Als gevolg hiervan kunt u overwegen DateTimeOffset als het standaardtype datum en tijd voor toepassingsontwikkeling.
Een DateTimeOffset waarde is niet gekoppeld aan een bepaalde tijdzone, maar kan afkomstig zijn van verschillende tijdzones. In het volgende voorbeeld ziet u de tijdzones waartoe een aantal DateTimeOffset waarden (inclusief een lokale Pacific Standard Time) behoren.
using System;
using System.Collections.ObjectModel;
public class TimeOffsets
{
public static void Main()
{
DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
DateTimeOffset thisTime;
thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
ShowPossibleTimeZones(thisTime);
thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));
ShowPossibleTimeZones(thisTime);
thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
ShowPossibleTimeZones(thisTime);
}
private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
{
TimeSpan offset = offsetTime.Offset;
ReadOnlyCollection<TimeZoneInfo> timeZones;
Console.WriteLine("{0} could belong to the following time zones:",
offsetTime.ToString());
// Get all time zones defined on local system
timeZones = TimeZoneInfo.GetSystemTimeZones();
// Iterate time zones
foreach (TimeZoneInfo timeZone in timeZones)
{
// Compare offset with offset for that date in that time zone
if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
Console.WriteLine(" {0}", timeZone.DisplayName);
}
Console.WriteLine();
}
}
// This example displays the following output to the console:
// 6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
// (GMT-07:00) Arizona
// (GMT-08:00) Pacific Time (US & Canada)
// (GMT-08:00) Tijuana, Baja California
//
// 3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
// (GMT-06:00) Central America
// (GMT-06:00) Central Time (US & Canada)
// (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
// (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
// (GMT-06:00) Saskatchewan
//
// 3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
// (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
// (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
// (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
// (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
// (GMT+01:00) West Central Africa
Imports System.Collections.ObjectModel
Module TimeOffsets
Public Sub Main()
Dim thisTime As DateTimeOffset
thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
ShowPossibleTimeZones(thisTime)
thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))
ShowPossibleTimeZones(thisTime)
thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
ShowPossibleTimeZones(thisTime)
End Sub
Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
Dim offset As TimeSpan = offsetTime.Offset
Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)
Console.WriteLine("{0} could belong to the following time zones:", _
offsetTime.ToString())
' Get all time zones defined on local system
timeZones = TimeZoneInfo.GetSystemTimeZones()
' Iterate time zones
For Each timeZone As TimeZoneInfo In timeZones
' Compare offset with offset for that date in that time zone
If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
Console.WriteLine(" {0}", timeZone.DisplayName)
End If
Next
Console.WriteLine()
End Sub
End Module
' This example displays the following output to the console:
' 6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
' (GMT-07:00) Arizona
' (GMT-08:00) Pacific Time (US & Canada)
' (GMT-08:00) Tijuana, Baja California
'
' 3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
' (GMT-06:00) Central America
' (GMT-06:00) Central Time (US & Canada)
' (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
' (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
' (GMT-06:00) Saskatchewan
'
' 3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
' (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
' (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
' (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
' (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
' (GMT+01:00) West Central Africa
In de uitvoer ziet u dat elke datum- en tijdwaarde in dit voorbeeld tot ten minste drie verschillende tijdzones kan behoren. De DateTimeOffset waarde van 10-6-2007 geeft aan dat als een datum- en tijdwaarde een zomertijd vertegenwoordigt, de offset van UTC niet eens noodzakelijkerwijs overeenkomt met de basis-UTC-offset van de oorspronkelijke tijdzone of de offset van UTC die in de weergavenaam is gevonden. Omdat één DateTimeOffset waarde niet nauw is gekoppeld aan de tijdzone, kan deze de overgang van een tijdzone naar en van zomertijd niet weerspiegelen. Dit kan problematisch zijn wanneer rekenkundige berekeningen met datum en tijd worden gebruikt om een DateTimeOffset-waarde te manipuleren. Zie Rekenkundige bewerkingen uitvoeren met datums en tijdenvoor een discussie over het uitvoeren van datum- en tijdberekeningen op een manier die rekening houdt met de aanpassingsregels van een tijdzone.
De structuur Datum/tijd
Een DateTime-waarde definieert een bepaalde datum en tijd. Het bevat een Kind eigenschap die beperkte informatie biedt over de tijdzone waartoe die datum en tijd behoort. De DateTimeKind waarde die wordt geretourneerd door de eigenschap Kind geeft aan of de DateTime waarde de lokale tijd (DateTimeKind.Local), Coordinated Universal Time (UTC) (DateTimeKind.Utc) of een niet-opgegeven tijd (DateTimeKind.Unspecified) vertegenwoordigt.
De DateTime structuur is geschikt voor toepassingen met een of meer van de volgende kenmerken:
- Werken met abstracte datums en tijden.
- Werk met datums en tijden waarvoor tijdzonegegevens ontbreken.
- Werk alleen met UTC-datums en -tijden.
- Rekenkundige datum- en tijdbewerkingen uitvoeren, maar hebben betrekking op algemene resultaten. Bij een optellingsbewerking die zes maanden toevoegt aan een bepaalde datum en tijd, is het vaak niet belangrijk of het resultaat wordt aangepast voor zomertijd.
Tenzij een bepaalde DateTime waarde UTC vertegenwoordigt, is die datum- en tijdwaarde vaak dubbelzinnig of beperkt in de draagbaarheid ervan. Als een DateTime waarde bijvoorbeeld de lokale tijd vertegenwoordigt, is deze draagbaar binnen die lokale tijdzone (dat wil gezegd, als de waarde wordt gedeserialiseerd op een ander systeem in dezelfde tijdzone, identificeert die waarde nog steeds een enkel punt in de tijd). Buiten de lokale tijdzone kan die DateTime waarde meerdere interpretaties hebben. Als de eigenschap Kind van de waarde DateTimeKind.Unspecifiedis, is deze nog minder draagbaar: het is nu dubbelzinnig binnen dezelfde tijdzone en mogelijk zelfs op hetzelfde systeem waar deze voor het eerst werd geserialiseerd. Alleen als een DateTime waarde UTC vertegenwoordigt, wordt die waarde ondubbelzinnig geïdentificeerd op één bepaald tijdstip, ongeacht het systeem of de tijdzone waarin de waarde wordt gebruikt.
Belangrijk
Wanneer u DateTime gegevens opslaat of deelt, gebruikt u UTC en stelt u de eigenschap DateTime van de Kind waarde in op DateTimeKind.Utc.
De DateOnly-structuur
De DateOnly structuur vertegenwoordigt een specifieke datum, zonder tijd. Omdat het geen tijdcomponent heeft, vertegenwoordigt het een datum vanaf het begin van de dag tot het einde van de dag. Deze structuur is ideaal voor het opslaan van specifieke datums, zoals een geboortedatum, een jubileumdatum, een feestdag of een zakelijke datum.
Hoewel u DateTime
kunt gebruiken terwijl u het tijdonderdeel negeert, zijn er enkele voordelen om DateOnly
te gebruiken via DateTime
:
- De
DateTime
-structuur kan mogelijk overgaan in de vorige of volgende dag als dit wordt beïnvloed door een tijdzone.DateOnly
kan niet worden verschoven door een tijdzone en vertegenwoordigt altijd de datum die is ingesteld. - Het serialiseren van een
DateTime
structuur bevat het tijdonderdeel, waardoor de intentie van de gegevens mogelijk wordt verborgen.DateOnly
serialiseert ook minder gegevens. - Wanneer code communiceert met een database, zoals SQL Server, worden hele datums meestal opgeslagen als het
date
gegevenstype, dat geen tijd bevat.DateOnly
komt beter overeen met het databasetype.
Zie DateOnly
voor meer informatie over .
Belangrijk
DateOnly
is niet beschikbaar voor .NET Framework.
De structuur TimeSpan
De TimeSpan structuur vertegenwoordigt een tijdsinterval. De twee typische toepassingen zijn:
- Het tijdsinterval tussen twee datum- en tijdwaarden weergeven. Als u bijvoorbeeld een DateTime waarde van een andere waarde aftrekken, wordt een TimeSpan waarde geretourneerd.
- Verstreken tijd meten. De eigenschap Stopwatch.Elapsed retourneert bijvoorbeeld een TimeSpan waarde die overeenkomt met het tijdsinterval dat is verstreken sinds de aanroep van een van de Stopwatch methoden die beginnen met het meten van verstreken tijd.
Een TimeSpan-waarde kan ook worden gebruikt als vervanging voor een DateTime waarde wanneer die waarde een tijd weerspiegelt zonder verwijzing naar een bepaalde dag. Dit gebruik is vergelijkbaar met de eigenschappen DateTime.TimeOfDay en DateTimeOffset.TimeOfDay, die een TimeSpan waarde retourneren die de tijd aangeeft zonder te verwijzen naar een datum. De TimeSpan structuur kan bijvoorbeeld worden gebruikt om de dagelijkse openings- of sluitingstijd van een winkel weer te geven, of kan worden gebruikt om het tijdstip aan te geven waarop een gewone gebeurtenis plaatsvindt.
In het volgende voorbeeld wordt een StoreInfo
-structuur gedefinieerd met TimeSpan-objecten voor de openingstijden en sluitingstijden van de winkel, evenals een TimeZoneInfo-object dat de tijdzone van de winkel vertegenwoordigt. De structuur bevat ook twee methoden, IsOpenNow
en IsOpenAt
, die aangeven of de winkel is geopend op een tijdstip dat door de gebruiker is opgegeven, waarvan wordt aangenomen dat deze zich in de lokale tijdzone bevindt.
using System;
public struct StoreInfo
{
public String store;
public TimeZoneInfo tz;
public TimeSpan open;
public TimeSpan close;
public bool IsOpenNow()
{
return IsOpenAt(DateTime.Now.TimeOfDay);
}
public bool IsOpenAt(TimeSpan time)
{
TimeZoneInfo local = TimeZoneInfo.Local;
TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;
// Is the store in the same time zone?
if (tz.Equals(local)) {
return time >= open & time <= close;
}
else {
TimeSpan delta = TimeSpan.Zero;
TimeSpan storeDelta = TimeSpan.Zero;
// Is it daylight saving time in either time zone?
if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;
if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;
TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
return comparisonTime >= open && comparisonTime <= close;
}
}
}
Public Structure StoreInfo
Dim store As String
Dim tz As TimeZoneInfo
Dim open As TimeSpan
Dim close As TimeSpan
Public Function IsOpenNow() As Boolean
Return IsOpenAt(Date.Now.TimeOfDay)
End Function
Public Function IsOpenAt(time As TimeSpan) As Boolean
Dim local As TimeZoneInfo = TimeZoneInfo.Local
Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset
' Is the store in the same time zone?
If tz.Equals(local) Then
Return time >= open AndAlso time <= close
Else
Dim delta As TimeSpan = TimeSpan.Zero
Dim storeDelta As TimeSpan = TimeSpan.Zero
' Is it daylight saving time in either time zone?
If local.IsDaylightSavingTime(Date.Now.Date + time) Then
delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
End If
If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
End If
Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
Return (comparisonTime >= open AndAlso comparisonTime <= close)
End If
End Function
End Structure
De StoreInfo
structuur kan vervolgens worden gebruikt door clientcode zoals hieronder.
public class Example
{
public static void Main()
{
// Instantiate a StoreInfo object.
var store103 = new StoreInfo();
store103.store = "Store #103";
store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
// Store opens at 8:00.
store103.open = new TimeSpan(8, 0, 0);
// Store closes at 9:30.
store103.close = new TimeSpan(21, 30, 0);
Console.WriteLine("Store is open now at {0}: {1}",
DateTime.Now.TimeOfDay, store103.IsOpenNow());
TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
foreach (var time in times)
Console.WriteLine("Store is open at {0}: {1}",
time, store103.IsOpenAt(time));
}
}
// The example displays the following output:
// Store is open now at 15:29:01.6129911: True
// Store is open at 08:00:00: True
// Store is open at 21:00:00: True
// Store is open at 04:59:00: False
// Store is open at 18:31:00: True
Module Example
Public Sub Main()
' Instantiate a StoreInfo object.
Dim store103 As New StoreInfo()
store103.store = "Store #103"
store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
' Store opens at 8:00.
store103.open = new TimeSpan(8, 0, 0)
' Store closes at 9:30.
store103.close = new TimeSpan(21, 30, 0)
Console.WriteLine("Store is open now at {0}: {1}",
Date.Now.TimeOfDay, store103.IsOpenNow())
Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
New TimeSpan(21, 0, 0),
New TimeSpan(4, 59, 0),
New TimeSpan(18, 31, 0)}
For Each time In times
Console.WriteLine("Store is open at {0}: {1}",
time, store103.IsOpenAt(time))
Next
End Sub
End Module
' The example displays the following output:
' Store is open now at 15:29:01.6129911: True
' Store is open at 08:00:00: True
' Store is open at 21:00:00: False
' Store is open at 04:59:00: False
' Store is open at 18:31:00: False
De TimeOnly-structuur
De TimeOnly structuur vertegenwoordigt een tijd-van-dag-waarde, zoals een dagelijkse wekker of welke tijd u elke dag luncht.
TimeOnly
is beperkt tot het bereik van 00:00:00.0000000 - 23:59:59.9999999, een bepaald tijdstip van de dag.
Voordat het TimeOnly
type werd geïntroduceerd, gebruikten programmeurs meestal het DateTime type of het TimeSpan type om een specifieke tijd aan te geven. Als u deze structuren echter gebruikt om een tijd zonder datum te simuleren, kunnen er enkele problemen ontstaan, die TimeOnly
oplost:
-
TimeSpan
vertegenwoordigt verstreken tijd, zoals de tijd die wordt gemeten met een stopwatch. Het maximum bereik is meer dan 29.000 jaar en de waarde ervan kan negatief zijn om aan te geven dat men achteruit in de tijd gaat. Een negatieveTimeSpan
geeft geen specifiek tijdstip van de dag aan. - Als
TimeSpan
wordt gebruikt als een tijdstip van de dag, is er een risico dat deze kan worden gemanipuleerd naar een waarde buiten de 24-uurs dag.TimeOnly
heeft dit risico niet. Als de werkdienst van een werknemer bijvoorbeeld begint om 18:00 uur en 8 uur duurt, wordt het toevoegen van 8 uur aan deTimeOnly
structuur overgerold tot 2:00 uur. - Als u
DateTime
gebruikt voor een tijdstip van de dag, moet een willekeurige datum worden gekoppeld aan de tijd en later genegeerd. Het is gebruikelijk omDateTime.MinValue
(0001-01-01) te kiezen als de datum, maar als uren worden afgetrokken van deDateTime
-waarde, kan er eenOutOfRange
uitzondering optreden.TimeOnly
heeft dit probleem niet omdat de tijd rond de periode van 24 uur vooruit en achteruit rolt. - Het serialiseren van een
DateTime
structuur bevat het datumonderdeel, waardoor de intentie van de gegevens mogelijk wordt verborgen.TimeOnly
serialiseert ook minder gegevens.
Zie TimeOnly
voor meer informatie over .
Belangrijk
TimeOnly
is niet beschikbaar voor .NET Framework.
De klasse TimeZoneInfo
De TimeZoneInfo klasse vertegenwoordigt een van de tijdzones van de aarde en maakt het mogelijk om een datum en tijd in één tijdzone te converteren naar het equivalent ervan in een andere tijdzone. De TimeZoneInfo-klasse maakt het mogelijk om met datums en tijden te werken, zodat elke datum- en tijdwaarde ondubbelzinnig één punt in de tijd identificeert. De TimeZoneInfo-klasse is ook uitbreidbaar. Hoewel deze afhankelijk is van tijdzone-informatie die is opgegeven voor Windows-systemen en gedefinieerd in het register, ondersteunt het maken van aangepaste tijdzones. Het ondersteunt ook de serialisatie en deserialisatie van tijdzone-informatie.
In sommige gevallen kan het volledig profiteren van de TimeZoneInfo klasse verdere ontwikkeling vereisen. Als datum- en tijdwaarden niet nauw zijn gekoppeld aan de tijdzones waartoe ze behoren, is verder werk vereist. Tenzij uw toepassing een mechanisme biedt voor het koppelen van een datum en tijd aan de bijbehorende tijdzone, is het eenvoudig dat een bepaalde datum- en tijdwaarde wordt ontkoppeld van de tijdzone. Een methode voor het koppelen van deze informatie is het definiëren van een klasse of structuur die zowel de datum- als tijdwaarde en het bijbehorende tijdzoneobject bevat.
Als u wilt profiteren van de ondersteuning voor tijdzones in .NET, moet u de tijdzone kennen waartoe een datum- en tijdwaarde behoort wanneer dat datum- en tijdobject wordt geïnstantieerd. De tijdzone is vaak niet bekend, met name in web- of netwerk-apps.