Melakukan operasi aritmetika dengan tanggal dan waktu

Meskipun struktur DateTime dan DateTimeOffset menyediakan anggota yang melakukan operasi aritmetika pada nilainya, hasil operasi aritmetika sangat berbeda. Artikel ini membahas perbedaan tersebut, menghubungkan perbedaan dengan derajat kesadaran zona waktu dalam data tanggal dan waktu, serta membahas cara melakukan operasi yang sadar zona waktu sepenuhnya menggunakan data tanggal dan waktu.

Perbandingan dan operasi aritmetika dengan nilai DateTime

Properti DateTime.Kind memungkinkan nilai DateTimeKind ditetapkan ke tanggal dan waktu untuk menunjukkan apakah properti tersebut mewakili waktu lokal, Waktu Universal Terkoordinasi (UTC), atau waktu di zona waktu yang tidak ditentukan. Tetapi, informasi zona waktu terbatas ini diabaikan saat membandingkan atau melakukan aritmetika tanggal dan waktu pada nilai DateTimeKind. Contoh berikut, yang membandingkan waktu lokal saat ini dengan waktu UTC saat ini, menggambarkan bagaimana informasi zona waktu diabaikan.

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,
                        utcTime.Kind,
                        (localTime - utcTime).Hours,
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The {0} time is {1} the {2} time.",
                        localTime.Kind,
                        Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)),
                        utcTime.Kind);
   }
}
// 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.
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

Metode CompareTo(DateTime) melaporkan bahwa waktu lokal lebih awal dari (atau kurang dari) waktu UTC, dan operasi pengurangan menunjukkan bahwa perbedaan antara UTC dan waktu lokal untuk sistem di zona Waktu Standar Pasifik AS adalah tujuh jam. Tetapi karena kedua nilai ini memberikan representasi yang berbeda dari satu titik waktu, jelas dalam kasus ini bahwa interval waktu sepenuhnya disebabkan oleh offset zona waktu lokal dari UTC.

Secara umum, properti DateTime.Kind tidak memengaruhi hasil yang ditampilkan oleh metode perbandingan dan aritmetika Kind (seperti yang ditunjukkan oleh perbandingan dua titik waktu yang identik), meskipun dapat memengaruhi interpretasi hasil tersebut. Contohnya:

  • Hasil dari setiap operasi aritmetika yang dilakukan pada dua nilai tanggal dan waktu yang memiliki properti DateTime.Kind yang sama dengan DateTimeKind mencerminkan interval waktu sebenarnya antara kedua nilai tersebut. Demikian pula, perbandingan dua nilai tanggal dan waktu tersebut secara akurat mencerminkan hubungan antar waktu.

  • Hasil dari operasi aritmetika atau perbandingan yang dilakukan pada dua nilai tanggal dan waktu yang memiliki properti DateTime.Kind yang sama dengan DateTimeKind atau pada dua nilai tanggal dan waktu dengan nilai properti DateTime.Kind yang berbeda mencerminkan perbedaan waktu jam antara kedua nilai tersebut.

  • Operasi aritmetika atau perbandingan pada nilai tanggal dan waktu lokal tidak mempertimbangkan apakah nilai tertentu ambigu atau tidak valid, juga tidak memperhitungkan pengaruh aturan penyesuaian yang dihasilkan dari transisi zona waktu lokal ke atau dari waktu musim panas.

  • Operasi apa pun yang membandingkan atau menghitung perbedaan antara UTC dan waktu lokal menyertakan interval waktu yang sama dengan offset zona waktu lokal dari UTC dalam hasil.

  • Setiap operasi yang membandingkan atau menghitung perbedaan antara waktu yang tidak ditentukan dan waktu UTC atau lokal mencerminkan waktu jam sederhana. Perbedaan zona waktu tidak dipertimbangkan, dan hasilnya tidak mencerminkan penerapan aturan penyesuaian zona waktu.

  • Setiap operasi yang membandingkan atau menghitung perbedaan antara dua waktu yang tidak ditentukan dapat mencakup interval yang tidak diketahui yang mencerminkan perbedaan antara waktu di dua zona waktu yang berbeda.

Ada banyak skenario saat perbedaan zona waktu tidak memengaruhi penghitungan tanggal dan waktu (untuk diskusi tentang beberapa skenario ini, lihat Memilih antara DateTime, DateTimeOffset, TimeSpan, dan TimeZoneInfo) atau saat konteks data tanggal dan waktu mendefinisikan arti dari perbandingan atau operasi aritmetika.

Perbandingan dan operasi aritmetika dengan nilai DateTimeOffset

Nilai DateTimeOffset tidak hanya mencakup tanggal dan waktu, tetapi juga offset yang secara jelas mendefinisikan tanggal dan waktu tersebut relatif terhadap UTC. Offset ini memungkinkan untuk mendefinisikan kesetaraan secara berbeda dari nilai DateTime. Sedangkan nilai DateTime sama jika memiliki nilai tanggal dan waktu yang sama, nilai DateTimeOffset sama jika keduanya merujuk ke titik waktu yang sama. Saat digunakan dalam perbandingan dan dalam sebagian besar operasi aritmetika yang menentukan interval antara dua tanggal dan waktu, nilai DateTimeOffset lebih akurat dan tidak memerlukan interpretasi. Contoh berikut, dengan DateTimeOffset yang setara dengan contoh sebelumnya yang membandingkan nilai DateTimeOffset lokal dan UTC, menggambarkan perbedaan perilaku ini.

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.
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)

Dalam contoh ini, metode CompareTo menunjukkan bahwa waktu lokal saat ini dan waktu UTC saat ini sama, dan pengurangan nilai CompareTo(DateTimeOffset) menunjukkan bahwa perbedaan antara kedua waktu tersebut adalah TimeSpan.Zero.

Batasan utama dalam menggunakan nilai DateTimeOffset dalam aritmetika tanggal dan waktu adalah bahwa meskipun nilai DateTimeOffset memiliki beberapa kesadaran zona waktu, nilai tersebut tidak sepenuhnya menyadari zona waktu. Meskipun offset nilai DateTimeOffset mencerminkan offset zona waktu dari UTC saat variabel DateTimeOffset pertama kali diberi nilai, variabel tersebut akan dipisahkan dari zona waktu setelahnya. Karena tidak lagi secara langsung terkait dengan waktu yang dapat diidentifikasi, penambahan dan pengurangan interval tanggal dan waktu tidak mempertimbangkan aturan penyesuaian zona waktu.

Sebagai ilustrasi, transisi ke waktu musim panas di zona Waktu Standar Tengah AS terjadi pada pukul 02.00 pada tanggal 9 Maret 2008. Dengan mengingat hal itu, menambahkan interval dua setengah jam ke waktu Standar Tengah 01.30 pada tanggal 9 Maret 2008, akan menghasilkan tanggal dan waktu 05.00 pada tanggal 9 Maret 2008. Tetapi, seperti yang ditunjukkan contoh berikut, hasil penambahannya adalah pukul 04.00 pada tanggal 9 Maret 2008. Hasil operasi ini memang mewakili titik waktu yang benar, meskipun ini bukan waktu di zona waktu yang kita minati (yaitu, tidak memiliki offset zona waktu yang diharapkan).

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
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

Operasi aritmetika dengan waktu dalam zona waktu

Kelas TimeZoneInfo menyertakan metode konversi yang secara otomatis menerapkan penyesuaian saat metode konversi mengonversi waktu dari satu zona waktu ke zona waktu lainnya. Metode konversi ini meliputi:

Untuk detailnya, lihat Mengonversi waktu antar zona waktu.

Kelas TimeZoneInfo tidak menyediakan metode apa pun yang secara otomatis menerapkan aturan penyesuaian saat Anda melakukan aritmetika tanggal dan waktu. Tetapi, Anda dapat menerapkan aturan penyesuaian dengan mengonversi waktu di zona waktu ke UTC, melakukan operasi aritmetika, lalu mengonversi dari UTC kembali ke waktu di zona waktu. Untuk detailnya, lihat Cara: Menggunakan zona waktu dalam aritmetika tanggal dan waktu.

Misalnya, kode berikut ini mirip dengan kode sebelumnya yang menambahkan dua setengah jam ke pukul 02.00 pada tanggal 9 Maret 2008. Tetapi, karena kode ini mengubah waktu Standar Tengah ke UTC sebelum melakukan aritmetika tanggal dan waktu, dan kemudian mengonversi hasil dari UTC kembali ke waktu Standar Tengah, waktu yang dihasilkan mencerminkan transisi Zona Waktu Standar Tengah ke waktu musim panas.

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
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

Lihat juga