Bagikan melalui


Pilih antara DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly, dan TimeZoneInfo

Aplikasi .NET dapat menggunakan informasi tanggal dan waktu dalam beberapa cara. Penggunaan informasi tanggal dan waktu yang lebih umum meliputi:

  • Untuk mencerminkan tanggal saja, sehingga informasi waktu tidak penting.
  • Untuk mencerminkan waktu saja, sehingga informasi tanggal tidak penting.
  • Untuk mencerminkan tanggal dan waktu abstrak yang tidak terkait dengan waktu dan tempat tertentu (misalnya, sebagian besar toko dalam rantai internasional terbuka pada hari kerja pada pukul 09:00 PAGI).
  • Untuk mengambil informasi tanggal dan waktu dari sumber di luar .NET, biasanya di mana informasi tanggal dan waktu disimpan dalam jenis data sederhana.
  • Untuk mengidentifikasi satu titik waktu secara unik dan tidak ambigu. Beberapa aplikasi mengharuskan tanggal dan waktu tidak ambigu hanya pada sistem host. Aplikasi lain mengharuskan aplikasi tersebut tidak ambigu di seluruh sistem (yaitu, tanggal yang diserialisasikan pada satu sistem dapat dideserialisasi secara bermakna dan digunakan pada sistem lain di mana saja di dunia).
  • Untuk mempertahankan beberapa waktu terkait (seperti waktu lokal pemohon dan waktu penerimaan server untuk permintaan web).
  • Untuk melakukan aritmatika tanggal dan waktu, mungkin dengan hasil yang secara unik dan tidak ambigu mengidentifikasi satu titik waktu.

.NET mencakup jenis DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly, dan TimeZoneInfo, yang semuanya dapat digunakan untuk membangun aplikasi yang bekerja dengan tanggal dan waktu.

Nota

Artikel ini tidak membahas TimeZone karena fungsionalitasnya hampir sepenuhnya tergabung dalam kelas TimeZoneInfo. Jika memungkinkan, gunakan kelas TimeZoneInfo alih-alih kelas TimeZone.

Struktur DateTimeOffset

Struktur DateTimeOffset mewakili nilai tanggal dan waktu, bersama dengan offset yang menunjukkan berapa banyak nilai tersebut berbeda dari UTC. Dengan demikian, nilai selalu secara tidak ambigu mengidentifikasi satu titik waktu.

Jenis DateTimeOffset mencakup semua fungsionalitas jenis DateTime bersama dengan kesadaran zona waktu. Ini membuatnya cocok untuk aplikasi yang:

  • Secara unik dan tidak ambigu mengidentifikasi satu titik waktu. Jenis DateTimeOffset dapat digunakan untuk secara tidak ambigu menentukan arti "sekarang", untuk mencatat waktu transaksi, untuk mencatat waktu peristiwa sistem atau aplikasi, dan untuk merekam waktu pembuatan dan modifikasi file.
  • Lakukan aritmatika tanggal dan waktu umum.
  • Pertahankan beberapa waktu yang saling terkait, selama waktu tersebut disimpan sebagai dua nilai terpisah atau sebagai dua elemen dari sebuah struktur.

Nota

Penggunaan ini untuk nilai DateTimeOffset jauh lebih umum daripada yang untuk nilai DateTime. Akibatnya, pertimbangkan DateTimeOffset sebagai jenis tanggal dan waktu default untuk pengembangan aplikasi.

Nilai DateTimeOffset tidak terkait dengan zona waktu tertentu, tetapi dapat berasal dari berbagai zona waktu. Contoh berikut mencantumkan zona waktu tempat sejumlah nilai DateTimeOffset (termasuk Waktu Standar Pasifik lokal) dapat berada.

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($"   {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

Output menunjukkan bahwa setiap nilai tanggal dan waktu dalam contoh ini dapat termasuk dalam setidaknya tiga zona waktu yang berbeda. Nilai DateTimeOffset dari 10/6/2007 menunjukkan bahwa jika nilai tanggal dan waktu mewakili waktu hemat siang, perbedaan waktunya dari UTC tidak selalu cocok dengan perbedaan waktu UTC dasar zona waktu asal atau perbedaan waktu UTC yang ditemukan dalam nama tampilan. Karena satu nilai DateTimeOffset tidak digabungkan erat dengan zona waktunya, nilai tersebut tidak dapat mencerminkan transisi zona waktu ke dan dari waktu musim panas. Ini bisa bermasalah ketika aritmatika tanggal dan waktu digunakan untuk memanipulasi nilai DateTimeOffset. Untuk diskusi tentang cara melakukan aritmatika tanggal dan waktu dengan cara yang mempertimbangkan aturan penyesuaian zona waktu, lihat Melakukan operasi aritmatika dengan tanggal dan waktu.

Struktur DateTime

Nilai DateTime menentukan tanggal dan waktu tertentu. Ini termasuk properti Kind yang memberikan informasi terbatas tentang zona waktu di mana tanggal dan waktu tersebut berada. Nilai DateTimeKind yang dikembalikan oleh properti Kind menunjukkan apakah nilai DateTime mewakili waktu lokal (DateTimeKind.Local), Waktu Universal Terkoordinasi (UTC) (DateTimeKind.Utc), atau waktu yang tidak ditentukan (DateTimeKind.Unspecified).

Struktur DateTime cocok untuk aplikasi dengan satu atau beberapa karakteristik berikut:

  • Bekerja dengan tanggal dan waktu abstrak.
  • Bekerja dengan tanggal dan waktu yang tidak memiliki informasi zona waktu.
  • Bekerja dengan tanggal dan waktu UTC saja.
  • Lakukan aritmatika tanggal dan waktu, tetapi fokus pada hasil umum. Misalnya, dalam operasi penambahan yang menambahkan enam bulan ke tanggal dan waktu tertentu, sering kali tidak penting apakah hasilnya disesuaikan untuk waktu hemat siang hari.

Kecuali nilai DateTime tertentu mewakili UTC, nilai tanggal dan waktu tersebut sering ambigu atau terbatas dalam portabilitasnya. Misalnya, jika nilai DateTime mewakili waktu lokal, nilai tersebut portabel dalam zona waktu lokal tersebut (yaitu, jika nilai dideserialisasi pada sistem lain di zona waktu yang sama, nilai tersebut masih secara tidak ambigu mengidentifikasi satu titik waktu). Di luar zona waktu lokal, nilai DateTime tersebut dapat memiliki beberapa interpretasi. Jika properti Kind dari nilai DateTimeKind.Unspecified, maka itu menjadi kurang portabel: sekarang menjadi ambigu dalam zona waktu yang sama dan bahkan mungkin pada sistem yang sama tempat pertama kali diserialisasikan. Hanya jika nilai DateTime mewakili UTC, nilai tersebut secara tidak ambigu mengidentifikasi satu titik waktu, terlepas dari sistem atau zona waktu tempat nilai tersebut digunakan.

Penting

Saat menyimpan atau berbagi data DateTime, gunakan UTC dan atur properti DateTime dari nilai Kind ke DateTimeKind.Utc.

Struktur DateOnly

Struktur DateOnly mewakili tanggal tertentu, tanpa waktu. Karena tidak memiliki komponen waktu, itu menunjukkan tanggal dari awal hari hingga akhir hari. Struktur ini sangat ideal untuk menyimpan tanggal tertentu, seperti tanggal lahir, tanggal ulang tahun, hari libur, atau tanggal terkait bisnis.

Meskipun Anda dapat menggunakan DateTime saat mengabaikan komponen waktu, ada beberapa manfaat untuk menggunakan DateOnly melalui DateTime:

  • Struktur DateTime dapat bergulir ke hari sebelumnya atau berikutnya jika diimbangi oleh zona waktu. DateOnly tidak dapat diimbangi oleh zona waktu, dan selalu mewakili tanggal yang ditetapkan.
  • Menserialisasikan struktur DateTime mencakup komponen waktu, yang dapat mengaburkan niat data. Selain itu, DateOnly menserialisasikan lebih sedikit data.
  • Saat kode berinteraksi dengan database, seperti SQL Server, seluruh tanggal umumnya disimpan sebagai jenis data date, yang tidak menyertakan waktu. DateOnly lebih cocok dengan jenis database.

Untuk informasi selengkapnya tentang DateOnly, lihat Cara menggunakan struktur DateOnly dan TimeOnly.

Penting

DateOnly tidak tersedia untuk .NET Framework.

Struktur TimeSpan

Struktur TimeSpan mewakili interval waktu. Dua penggunaan khasnya adalah:

  • Mencerminkan interval waktu antara dua nilai tanggal dan waktu. Misalnya, mengurangi satu nilai DateTime dari yang lain mengembalikan nilai TimeSpan.
  • Mengukur waktu yang berlalu. Misalnya, properti Stopwatch.Elapsed mengembalikan nilai TimeSpan yang mencerminkan interval waktu yang telah berlalu sejak panggilan ke salah satu metode Stopwatch yang mulai mengukur waktu yang berlalu.

Nilai TimeSpan juga dapat digunakan sebagai pengganti nilai DateTime ketika nilai tersebut mencerminkan waktu tanpa referensi ke hari tertentu. Penggunaan ini mirip dengan properti DateTime.TimeOfDay dan DateTimeOffset.TimeOfDay, yang mengembalikan nilai TimeSpan yang mewakili waktu tanpa mengacu pada tanggal. Misalnya, struktur TimeSpan dapat digunakan untuk mencerminkan waktu pembukaan atau penutupan harian toko, atau dapat digunakan untuk mewakili waktu di mana peristiwa reguler terjadi.

Contoh berikut mendefinisikan struktur StoreInfo yang mencakup objek TimeSpan untuk waktu buka dan tutup penyimpanan, serta objek TimeZoneInfo yang mewakili zona waktu penyimpanan. Struktur ini juga mencakup dua metode, IsOpenNow dan IsOpenAt, yang menunjukkan apakah penyimpanan terbuka pada waktu yang ditentukan oleh pengguna, yang diasumsikan berada di zona waktu lokal.

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

Struktur StoreInfo kemudian dapat digunakan oleh kode klien seperti berikut.

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

Struktur TimeOnly

Struktur TimeOnly mewakili nilai waktu sehari-hari, seperti jam alarm harian atau jam berapa Anda makan siang setiap hari. TimeOnly dibatasi pada rentang 00:00:00.0000000 - 23:59:59.9999999, waktu tertentu dalam sehari.

Sebelum jenis TimeOnly diperkenalkan, pemrogram biasanya menggunakan jenis DateTime atau jenis TimeSpan untuk mewakili waktu tertentu. Namun, menggunakan struktur ini untuk mensimulasikan waktu tanpa tanggal dapat menimbulkan beberapa masalah, yang TimeOnly memecahkan:

  • TimeSpan mewakili waktu yang berlalu, seperti waktu yang diukur dengan stopwatch. Rentang atas lebih dari 29.000 tahun, dan nilainya bisa negatif untuk menunjukkan bergerak mundur dalam waktu. TimeSpan negatif tidak menunjukkan waktu tertentu dalam sehari.
  • Jika TimeSpan digunakan sebagai waktu dalam sehari, ada risiko bahwa itu dapat dimanipulasi ke nilai di luar hari 24 jam. TimeOnly tidak memiliki risiko ini. Misalnya, jika shift kerja karyawan dimulai pada pukul 18.00 dan berlangsung selama 8 jam, menambahkan 8 jam ke struktur TimeOnly digulirkan ke 2:00.
  • Menggunakan DateTime untuk waktu sehari mengharuskan tanggal sewenang-wenang dikaitkan dengan waktu, dan kemudian diabaikan. Praktik umum untuk memilih DateTime.MinValue (0001-01-01) sebagai tanggal, namun, jika jam dikurangi dari nilai DateTime, pengecualian OutOfRange mungkin terjadi. TimeOnly tidak memiliki masalah ini saat waktu bergulir maju dan mundur di sekitar jangka waktu 24 jam.
  • Menserialisasi struktur DateTime mencakup komponen tanggal, yang dapat mengaburkan niat data. Selain itu, TimeOnly menserialisasikan lebih sedikit data.

Untuk informasi selengkapnya tentang TimeOnly, lihat Cara menggunakan struktur DateOnly dan TimeOnly.

Penting

TimeOnly tidak tersedia untuk .NET Framework.

Kelas TimeZoneInfo

Kelas TimeZoneInfo mewakili salah satu zona waktu Bumi, dan memungkinkan konversi tanggal dan waktu apa pun dalam satu zona waktu ke zona waktu yang setara di zona waktu lain. Kelas TimeZoneInfo memungkinkan untuk bekerja dengan tanggal dan waktu sehingga nilai tanggal dan waktu apa pun secara tidak ambigu mengidentifikasi satu titik waktu. Kelas TimeZoneInfo juga dapat diperluas. Meskipun tergantung pada informasi zona waktu yang disediakan untuk sistem Windows dan didefinisikan dalam registri, itu mendukung pembuatan zona waktu kustom. Ini juga mendukung serialisasi dan deserialisasi informasi zona waktu.

Dalam beberapa kasus, mengambil keuntungan penuh dari kelas TimeZoneInfo mungkin memerlukan pekerjaan pengembangan lebih lanjut. Jika nilai tanggal dan waktu tidak digabungkan erat dengan zona waktu tempat nilai tersebut berada, diperlukan pekerjaan lebih lanjut. Kecuali aplikasi Anda menyediakan beberapa mekanisme untuk menautkan tanggal dan waktu dengan zona waktu terkait, mudah bagi nilai tanggal dan waktu tertentu untuk dibongkar dari zona waktunya. Salah satu metode menautkan informasi ini adalah menentukan kelas atau struktur yang berisi nilai tanggal dan waktu dan objek zona waktu terkait.

Untuk memanfaatkan dukungan zona waktu di .NET, Anda harus mengetahui zona waktu tempat nilai tanggal dan waktu berada saat objek tanggal dan waktu tersebut dibuat. Zona waktu sering tidak diketahui, terutama di aplikasi web atau jaringan.

Lihat juga