Instrukcje: Obustronne wartości daty i godziny

W wielu aplikacjach wartość daty i czasu ma na celu jednoznaczną identyfikację pojedynczego punktu w czasie. W tym artykule DateTime pokazano, jak zapisać i przywrócić wartość, DateTimeOffset wartość oraz wartość daty i czasu z informacjami o strefie czasowej, aby przywrócona wartość identyfikuje ten sam czas co zapisana wartość.

Rundy wartości DateTime

  1. Przekonwertuj DateTime wartość na jej reprezentację w postaci ciągu, wywołując DateTime.ToString(String) metodę ze specyfikatorem formatu "o".

  2. Zapisz reprezentację ciągu wartości DateTime w pliku lub przekaż ją przez proces, domenę aplikacji lub granicę maszyny.

  3. Pobierz ciąg reprezentujący wartość DateTime .

  4. Wywołaj DateTime.Parse(String, IFormatProvider, DateTimeStyles) metodę i przekaż DateTimeStyles.RoundtripKind jako wartość parametru styles .

W poniższym przykładzie pokazano, jak zaokrąglić wartość DateTime .

const string fileName = @".\DateFile.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.
DateTime dateToSave = DateTime.SpecifyKind(new DateTime(2008, 6, 12, 18, 45, 15),
                                           DateTimeKind.Local);
string dateString = dateToSave.ToString("o");
Console.WriteLine("Converted {0} ({1}) to {2}.",
                  dateToSave.ToString(),
                  dateToSave.Kind.ToString(),
                  dateString);
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.
DateTime restoredDate;

StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
inFile.Close();
restoredDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} ({2}) from {1}.", restoredDate.ToString(),
                                              fileName,
                                              restoredDate.Kind.ToString());
// The example displays the following output:
//    Converted 6/12/2008 6:45:15 PM (Local) to 2008-06-12T18:45:15.0000000-05:00.
//    Wrote 2008-06-12T18:45:15.0000000-05:00 to .\DateFile.txt.
//    Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.
Const fileName As String = ".\DateFile.txt"

Dim outFile As New StreamWriter(fileName)

' Save DateTime value.
Dim dateToSave As Date = DateTime.SpecifyKind(#06/12/2008 6:45:15 PM#, _
                                              DateTimeKind.Local)
Dim dateString As String = dateToSave.ToString("o")
Console.WriteLine("Converted {0} ({1}) to {2}.", dateToSave.ToString(), _
                  dateToSave.Kind.ToString(), dateString)
outFile.WriteLine(dateString)
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName)
outFile.Close()

' Restore DateTime value.
Dim restoredDate As Date

Dim inFile As New StreamReader(fileName)
dateString = inFile.ReadLine()
inFile.Close()
restoredDate = DateTime.Parse(dateString, Nothing, DateTimeStyles.RoundTripKind)
Console.WriteLine("Read {0} ({2}) from {1}.", restoredDate.ToString(), _
                  fileName, restoredDAte.Kind.ToString())
' The example displays the following output:
'    Converted 6/12/2008 6:45:15 PM (Local) to 2008-06-12T18:45:15.0000000-05:00.
'    Wrote 2008-06-12T18:45:15.0000000-05:00 to .\DateFile.txt.
'    Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.

W przypadku rundy wartości DateTime ta technika pomyślnie zachowuje czas dla wszystkich czasów lokalnych i uniwersalnych. DateTime Jeśli na przykład wartość lokalna jest zapisywana w systemie w strefie czasowej Pacyfiku Stanów Zjednoczonych i jest przywracana w systemie w strefie czasowej Standardowej Stanów Zjednoczonych, przywrócona data i godzina będą dwie godziny późniejsza niż oryginalny czas, co odzwierciedla różnicę czasu między dwiema strefami czasowymi. Jednak ta technika nie musi być dokładna w przypadku nieokreślonych godzin. Wszystkie DateTime wartości, których Kind właściwość jest Unspecified traktowana tak, jakby były czasem lokalnymi. Jeśli nie jest to czas lokalny, oznacza to, DateTime że punkt w czasie nie jest poprawnie identyfikowany. Obejście tego ograniczenia to ścisłe zesunięcie wartości daty i czasu ze strefą czasową operacji zapisywania i przywracania.

Rundy wartości DateTimeOffset

  1. Przekonwertuj DateTimeOffset wartość na jej reprezentację w postaci ciągu, wywołując DateTimeOffset.ToString(String) metodę ze specyfikatorem formatu "o".

  2. Zapisz reprezentację ciągu wartości DateTimeOffset w pliku lub przekaż ją przez proces, domenę aplikacji lub granicę maszyny.

  3. Pobierz ciąg reprezentujący wartość DateTimeOffset .

  4. Wywołaj DateTimeOffset.Parse(String, IFormatProvider, DateTimeStyles) metodę i przekaż DateTimeStyles.RoundtripKind jako wartość parametru styles .

W poniższym przykładzie pokazano, jak zaokrąglić wartość DateTimeOffset .

const string fileName = @".\DateOff.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.
DateTimeOffset dateToSave = new DateTimeOffset(2008, 6, 12, 18, 45, 15,
                                               new TimeSpan(7, 0, 0));
string dateString = dateToSave.ToString("o");
Console.WriteLine("Converted {0} to {1}.", dateToSave.ToString(),
                  dateString);
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.
DateTimeOffset restoredDateOff;

StreamReader inFile = new StreamReader(fileName);
dateString = inFile.ReadLine();
inFile.Close();
restoredDateOff = DateTimeOffset.Parse(dateString, null,
                                       DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} from {1}.", restoredDateOff.ToString(),
                  fileName);
// The example displays the following output:
//    Converted 6/12/2008 6:45:15 PM +07:00 to 2008-06-12T18:45:15.0000000+07:00.
//    Wrote 2008-06-12T18:45:15.0000000+07:00 to .\DateOff.txt.
//    Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.
Const fileName As String = ".\DateOff.txt"

Dim outFile As New StreamWriter(fileName)

' Save DateTime value.
Dim dateToSave As New DateTimeOffset(2008, 6, 12, 18, 45, 15, _
                                     New TimeSpan(7, 0, 0))
Dim dateString As String = dateToSave.ToString("o")
Console.WriteLine("Converted {0} to {1}.", dateToSave.ToString(), dateString)
outFile.WriteLine(dateString)
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName)
outFile.Close()

' Restore DateTime value.
Dim restoredDateOff As DateTimeOffset

Dim inFile As New StreamReader(fileName)
dateString = inFile.ReadLine()
inFile.Close()
restoredDateOff = DateTimeOffset.Parse(dateString, Nothing, DateTimeStyles.RoundTripKind)
Console.WriteLine("Read {0} from {1}.", restoredDateOff.ToString(), fileName)
' The example displays the following output:
'    Converted 6/12/2008 6:45:15 PM +07:00 to 2008-06-12T18:45:15.0000000+07:00.
'    Wrote 2008-06-12T18:45:15.0000000+07:00 to .\DateOff.txt.
'    Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.

Ta technika zawsze jednoznacznie identyfikuje DateTimeOffset wartość jako pojedynczy punkt w czasie. Wartość można następnie przekonwertować na wartość Uniwersalny czas koordynowany (UTC) DateTimeOffset.ToUniversalTimeDateTimeOffset.ToOffset przez wywołanie metody lub przekonwertować ją na czas w określonej strefie czasowej przez wywołanie metody lub TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) . Głównym ograniczeniem tej techniki jest to, że arytmetyka daty i czasu wykonywana DateTimeOffset na wartości reprezentującej czas w określonej strefie czasowej może nie dawać dokładnych wyników dla tej strefy czasowej. Jest to spowodowane tym, że DateTimeOffset gdy jest skojarzona wartość, jest ona skojarzona ze swoją strefą czasową. W związku z tym reguły korekty strefy czasowej nie mogą być już stosowane podczas wykonywania obliczeń daty i czasu. Ten problem można rozwiązać, definiując typ niestandardowy, który zawiera zarówno wartość daty i godziny, jak i towarzyszący jej strefę czasową.

Rundy wartości daty i czasu z jej strefą czasową

  1. Zdefiniuj klasę lub strukturę z dwoma polami. Pierwsze pole jest obiektem DateTime lub , a DateTimeOffset drugie obiektem TimeZoneInfo . Poniższy przykład to prosta wersja tego typu.

    [Serializable] public class DateInTimeZone
    {
       private TimeZoneInfo tz;
       private DateTimeOffset thisDate;
    
       public DateInTimeZone() {}
    
       public DateInTimeZone(DateTimeOffset date, TimeZoneInfo timeZone)
       {
          if (timeZone == null)
             throw new ArgumentNullException("The time zone cannot be null.");
    
          this.thisDate = date;
          this.tz = timeZone;
       }
    
       public DateTimeOffset DateAndTime
       {
          get {
             return this.thisDate;
          }
          set {
             if (value.Offset != this.tz.GetUtcOffset(value))
                this.thisDate = TimeZoneInfo.ConvertTime(value, tz);
             else
                this.thisDate = value;
          }
       }
    
       public TimeZoneInfo TimeZone
       {
          get {
             return this.tz;
          }
       }
    }
    
    <Serializable> Public Class DateInTimeZone
        Private tz As TimeZoneInfo
        Private thisDate As DateTimeOffset
    
        Public Sub New()
        End Sub
    
        Public Sub New(date1 As DateTimeOffset, timeZone As TimeZoneInfo)
            If timeZone Is Nothing Then
                Throw New ArgumentNullException("The time zone cannot be null.")
            End If
            Me.thisDate = date1
            Me.tz = timeZone
        End Sub
    
        Public Property DateAndTime As DateTimeOffset
            Get
                Return Me.thisDate
            End Get
            Set
                If Value.Offset <> Me.tz.GetUtcOffset(Value) Then
                    Me.thisDate = TimeZoneInfo.ConvertTime(Value, tz)
                Else
                    Me.thisDate = Value
                End If
            End Set
        End Property
    
        Public ReadOnly Property TimeZone As TimeZoneInfo
            Get
                Return tz
            End Get
        End Property
    End Class
    
  2. Oznacz klasę atrybutem SerializableAttribute .

  3. Serializuj obiekt przy użyciu BinaryFormatter.Serialize metody .

  4. Przywróć obiekt przy użyciu Deserialize metody .

  5. Rzutować (w języku C#) lub konwertować (w Visual Basic) zdeserializowany obiekt na obiekt odpowiedniego typu.

Poniższy przykład ilustruje sposób rundy obiektu, który przechowuje zarówno informacje o strefie czasowej, jak i o dacie i godzinie.

const string fileName = @".\DateWithTz.dat";

DateTime tempDate = new DateTime(2008, 9, 3, 19, 0, 0);
TimeZoneInfo tempTz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateInTimeZone dateWithTz = new DateInTimeZone(new DateTimeOffset(tempDate,
                                tempTz.GetUtcOffset(tempDate)),
                                tempTz);

// Store DateInTimeZone value to a file
FileStream outFile = new FileStream(fileName, FileMode.Create);
try
{
   BinaryFormatter formatter = new BinaryFormatter();
   formatter.Serialize(outFile, dateWithTz);
   Console.WriteLine("Saving {0} {1} to {2}", dateWithTz.DateAndTime,
                     dateWithTz.TimeZone.IsDaylightSavingTime(dateWithTz.DateAndTime) ?
                     dateWithTz.TimeZone.DaylightName : dateWithTz.TimeZone.DaylightName,
                     fileName);
}
catch (SerializationException)
{
   Console.WriteLine("Unable to serialize time data to {0}.", fileName);
}
finally
{
   outFile.Close();
}

// Retrieve DateInTimeZone value
if (File.Exists(fileName))
{
   FileStream inFile = new FileStream(fileName, FileMode.Open);
   DateInTimeZone dateWithTz2 = new DateInTimeZone();
   try
   {
      BinaryFormatter formatter = new BinaryFormatter();
      dateWithTz2 = formatter.Deserialize(inFile) as DateInTimeZone;
      Console.WriteLine("Restored {0} {1} from {2}", dateWithTz2.DateAndTime,
                        dateWithTz2.TimeZone.IsDaylightSavingTime(dateWithTz2.DateAndTime) ?
                        dateWithTz2.TimeZone.DaylightName : dateWithTz2.TimeZone.DaylightName,
                        fileName);
   }
   catch (SerializationException)
   {
      Console.WriteLine("Unable to retrieve date and time information from {0}",
                        fileName);
   }
   finally
   {
      inFile.Close();
   }
}
// This example displays the following output to the console:
//    Saving 9/3/2008 7:00:00 PM -05:00 Central Daylight Time to .\DateWithTz.dat
//    Restored 9/3/2008 7:00:00 PM -05:00 Central Daylight Time from .\DateWithTz.dat
Const fileName As String = ".\DateWithTz.dat"

Dim tempDate As Date = #9/3/2008 7:00:00 PM#
Dim tempTz As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
Dim dateWithTz As New DateInTimeZone(New DateTimeOffset(tempDate, _
                                         tempTz.GetUtcOffset(tempDate)), _
                                     tempTz)

' Store DateInTimeZone value to a file
Dim outFile As New FileStream(fileName, FileMode.Create)
Try
    Dim formatter As New BinaryFormatter()
    formatter.Serialize(outFile, dateWithTz)
    Console.WriteLine("Saving {0} {1} to {2}", dateWithTz.DateAndTime, _
            IIf(dateWithTz.TimeZone.IsDaylightSavingTime(dateWithTz.DateAndTime), _
                dateWithTz.TimeZone.DaylightName, dateWithTz.TimeZone.DaylightName), _
            fileName)
Catch e As SerializationException
    Console.WriteLine("Unable to serialize time data to {0}.", fileName)
Finally
    outFile.Close()
End Try

' Retrieve DateInTimeZone value
If File.Exists(fileName) Then
    Dim inFile As New FileStream(fileName, FileMode.Open)
    Dim dateWithTz2 As New DateInTimeZone()
    Try
        Dim formatter As New BinaryFormatter()
        dateWithTz2 = DirectCast(formatter.Deserialize(inFile), DateInTimeZone)
        Console.WriteLine("Restored {0} {1} from {2}", dateWithTz2.DateAndTime, _
                          IIf(dateWithTz2.TimeZone.IsDaylightSavingTime(dateWithTz2.DateAndTime), _
                          dateWithTz2.TimeZone.DaylightName, dateWithTz2.TimeZone.DaylightName), _
                          fileName)
    Catch e As SerializationException
        Console.WriteLine("Unable to retrieve date and time information from {0}", _
                          fileName)
    Finally
        inFile.Close
    End Try
End If
' This example displays the following output to the console:
'    Saving 9/3/2008 7:00:00 PM -05:00 Central Daylight Time to .\DateWithTz.dat
'    Restored 9/3/2008 7:00:00 PM -05:00 Central Daylight Time from .\DateWithTz.dat      

Ta technika powinna zawsze jednoznacznie odzwierciedlać prawidłowy punkt czasu przed zapisaniem i przywróceniem, pod warunkiem, że implementacja połączonego obiektu daty i godziny i strefy czasowej nie zezwala na synchronizowanie wartości daty z wartością strefy czasowej.

Kompilowanie kodu

Te przykłady wymagają:

Zobacz też