DateTime vs DateTimeOffset

742

Saat ini, kami memiliki cara standar untuk berurusan dengan .NET DateTimedalam cara sadar TimeZone: Setiap kali kami memproduksi, DateTimekami melakukannya di UTC (mis. Menggunakan DateTime.UtcNow), dan setiap kali kami menampilkannya, kami mengonversi kembali dari UTC ke waktu setempat pengguna. .

Itu bekerja dengan baik, tapi saya sudah membaca tentang DateTimeOffsetdan bagaimana ia menangkap waktu lokal dan UTC di objek itu sendiri. Jadi pertanyaannya adalah, apa manfaat menggunakan DateTimeOffsetdibandingkan dengan apa yang telah kita lakukan?

David Reis
sumber
3
Ada beberapa jawaban bagus di bawah ini. Tapi saya masih bertanya-tanya apa, jika ada, yang bisa meyakinkan Anda untuk mulai menggunakan DateTimeOffset.
HappyNomad
Ketika datang ke penyimpanan, stackoverflow.com/questions/4715620/… juga menarik.
Dejan

Jawaban:

1169

DateTimeOffsetadalah representasi waktu sesaat (juga dikenal sebagai waktu absolut ). Maksud saya adalah momen dalam waktu yang bersifat universal untuk semua orang (tidak memperhitungkan detik kabisat , atau efek relativistik dari pelebaran waktu ). Cara lain untuk mewakili waktu sesaat adalah dengan DateTimemana .Kindadalah DateTimeKind.Utc.

Ini berbeda dari waktu kalender (juga dikenal sebagai waktu sipil ), yang merupakan posisi pada kalender seseorang, dan ada banyak kalender berbeda di seluruh dunia. Kami menyebut zona waktu kalender ini . Waktu kalender diwakili oleh DateTimemana .Kindadalah DateTimeKind.Unspecified, atau DateTimeKind.Local. Dan .Localhanya bermakna dalam skenario di mana Anda memiliki pemahaman tersirat di mana komputer yang menggunakan hasil diposisikan. (Misalnya, stasiun kerja pengguna)

Jadi, mengapa DateTimeOffsetbukan UTC DateTime? Ini semua tentang perspektif. Mari kita gunakan analogi - kita akan berpura-pura menjadi fotografer.

Bayangkan Anda berdiri di garis waktu kalender, mengarahkan kamera ke seseorang di garis waktu sesaat yang diletakkan di depan Anda. Anda mengatur kamera berdasarkan aturan zona waktu Anda - yang berubah secara berkala karena waktu musim panas, atau karena perubahan lain pada definisi hukum zona waktu Anda. (Anda tidak memiliki tangan yang mantap, jadi kamera Anda goyah.)

Orang yang berdiri di foto akan melihat dari sudut mana kamera Anda berasal. Jika orang lain mengambil foto, mereka bisa dari sudut yang berbeda. Inilah yang diwakili oleh Offsetbagian itu DateTimeOffset.

Jadi jika Anda memberi label pada kamera "Waktu Timur", terkadang Anda menunjuk dari -5, dan kadang-kadang Anda menunjuk dari -4. Ada kamera di seluruh dunia, semuanya berlabel hal yang berbeda, dan semua menunjuk pada garis waktu instan yang sama dari sudut yang berbeda. Beberapa dari mereka tepat di sebelah (atau di atas) satu sama lain, jadi hanya mengetahui offset tidak cukup untuk menentukan zona waktu yang terkait dengan waktu.

Dan bagaimana dengan UTC? Nah, itu satu-satunya kamera di luar sana yang dijamin memiliki tangan mantap. Ada di atas tripod, dengan kuat berlabuh ke tanah. Itu tidak ke mana-mana. Kami menyebut sudut pandangnya sebagai titik nol.

Waktu Seketika vs Visualisasi Waktu Kalender

Jadi - apa yang dikatakan analogi ini kepada kita? Ini memberikan beberapa panduan intuitif-

  • Jika Anda merepresentasikan waktu relatif terhadap tempat tertentu, wakili waktu kalender dengan a DateTime. Pastikan Anda tidak pernah mengacaukan satu kalender dengan yang lain. Unspecifiedharus menjadi asumsi Anda. Localhanya bermanfaat dari DateTime.Now. Sebagai contoh, saya mungkin mendapatkan DateTime.Nowdan menyimpannya dalam database - tetapi ketika saya mengambilnya, saya harus berasumsi bahwa itu adalah database Unspecified. Saya tidak dapat mengandalkan bahwa kalender lokal saya adalah kalender yang sama dengan aslinya.

  • Jika Anda harus selalu yakin akan momen tersebut, pastikan Anda mewakili waktu instan. Gunakan DateTimeOffsetuntuk menegakkannya, atau menggunakan UTC DateTimedengan konvensi.

  • Jika Anda perlu melacak waktu sesaat, tetapi Anda juga ingin tahu "Jam berapa pengguna pikir itu ada di kalender lokal mereka?" - maka Anda harus menggunakan a DateTimeOffset. Ini sangat penting untuk sistem ketepatan waktu, misalnya - baik untuk masalah teknis dan hukum.

  • Jika Anda perlu memodifikasi rekaman sebelumnya DateTimeOffset- Anda tidak memiliki informasi yang cukup di offset saja untuk memastikan bahwa offset baru masih relevan bagi pengguna. Anda juga harus menyimpan pengenal zona waktu (pikirkan - Saya perlu nama kamera itu sehingga saya dapat mengambil gambar baru meskipun posisinya telah berubah).

    Juga harus ditunjukkan bahwa Noda Time memiliki representasi yang disebut ZonedDateTimeuntuk ini, sedangkan perpustakaan kelas .Net tidak memiliki hal yang serupa. Anda harus menyimpan nilai DateTimeOffsetdan TimeZoneInfo.Idnilai.

  • Kadang-kadang, Anda ingin mewakili waktu kalender yang bersifat lokal untuk "siapa pun yang melihatnya". Misalnya, ketika mendefinisikan apa artinya hari ini . Hari ini selalu tengah malam hingga tengah malam, tetapi ini mewakili jumlah tumpang tindih yang hampir tak terbatas pada garis waktu sesaat. (Dalam praktiknya kami memiliki sejumlah zona waktu yang terbatas, tetapi Anda dapat mengimbangi hingga tanda centang). Jadi dalam situasi ini, pastikan Anda memahami cara membatasi "siapa yang bertanya?" pertanyaan ke zona waktu tunggal, atau berurusan dengan menerjemahkannya kembali ke waktu sesaat yang sesuai.

Berikut adalah beberapa bagian lain tentang hal DateTimeOffsetitu yang mendukung analogi ini, dan beberapa tips untuk menjaganya agar tetap lurus:

  • Jika Anda membandingkan dua DateTimeOffsetnilai, mereka pertama dinormalisasi ke nol offset sebelum membandingkan. Dengan kata lain, 2012-01-01T00:00:00+00:00dan 2012-01-01T02:00:00+02:00merujuk pada momen instan yang sama, dan karenanya setara.

  • Jika Anda melakukan setiap pengujian unit dan kebutuhan untuk menjadi tertentu dari offset, tes kedua yang DateTimeOffsetnilai, dan .Offsetproperti secara terpisah.

  • Ada konversi implisit satu arah yang dibangun ke dalam kerangka .Net yang memungkinkan Anda mengirimkan a DateTimeke DateTimeOffsetparameter atau variabel apa pun . Ketika melakukannya, yang .Kindpenting . Jika Anda melewati jenis UTC, itu akan dibawa dengan nol offset, tetapi jika Anda melewati salah satu .Localatau .Unspecified, itu akan dianggap sebagai lokal . Kerangka kerja ini pada dasarnya mengatakan, "Ya, Anda meminta saya untuk mengubah waktu kalender menjadi waktu instan, tetapi saya tidak tahu dari mana ini berasal, jadi saya hanya akan menggunakan kalender lokal." Ini adalah masalah besar jika Anda memuat yang tidak ditentukan DateTimepada komputer dengan zona waktu yang berbeda. (IMHO - yang seharusnya melempar pengecualian - tetapi tidak.)

Plug tak tahu malu:

Banyak orang telah berbagi dengan saya bahwa mereka menganggap analogi ini sangat berharga, jadi saya memasukkannya ke dalam kursus Pluralsight saya, Fundamental Date and Time . Anda akan menemukan langkah demi langkah langkah-langkah analogi kamera dalam modul kedua, "Konteks Konteks", dalam klip berjudul "Waktu Kalender vs. Waktu Instan".

Matt Johnson-Pint
sumber
4
@ ZackJannsen Jika Anda memiliki DateTimeOffsetdi C #, maka Anda harus bertahan DATETIMEOFFSETdi SQL Server. DATETIME2atau hanya DATETIME(tergantung pada kisaran yang diperlukan) baik untuk DateTimenilai reguler . Ya - Anda dapat menyelesaikan waktu lokal dari pasangan zona waktu + dto atau utc. Perbedaannya adalah - apakah Anda selalu ingin menghitung aturan dengan setiap tekad, atau Anda ingin menghitungnya? Dalam banyak kasus (kadang-kadang karena masalah hukum) DTO adalah pilihan yang lebih baik.
Matt Johnson-Pint
3
@ZackJannsen Untuk bagian kedua dari pertanyaan Anda, saya akan merekomendasikan melakukan sebanyak mungkin sisi server. Javascript tidak terlalu bagus untuk perhitungan zona waktu. Jika Anda harus melakukannya, gunakan salah satu perpustakaan ini . Namun sisi server yang terbaik. Jika Anda memiliki pertanyaan lain yang lebih terperinci, silakan mulai pertanyaan SO baru untuk mereka dan saya akan menjawab jika saya bisa. Terima kasih.
Matt Johnson-Pint
4
@ JoaoLeme - Itu tergantung dari mana Anda mendapatkannya. Anda benar bahwa jika Anda mengatakan DateTimeOffset.Nowpada server, Anda memang akan mendapatkan offset server. Intinya adalah bahwa DateTimeOffsetjenis dapat mempertahankan offset itu. Anda bisa dengan mudah melakukan itu pada klien, mengirimkannya ke server, dan kemudian server Anda akan tahu offset klien.
Matt Johnson-Pint
8
Sangat menyukai analogi kamera.
Sachin Kainth
2
Ya itu benar. Kecuali bahwa DTO disimpan sebagai pasangan (waktu lokal, offset), bukan pasangan (waktu utc, offset). Dengan kata lain, offset dari UTC sudah tercermin dalam waktu setempat. Untuk mengonversi kembali ke utc, balikkan tanda offset dan terapkan pada waktu setempat.
Matt Johnson-Pint
328

Dari Microsoft:

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

sumber: "Memilih Antara DateTime, DateTimeOffset, TimeSpan, dan TimeZoneInfo" , MSDN

Kami menggunakan DateTimeOffsethampir semua hal karena aplikasi kami berhubungan dengan titik waktu tertentu (misalnya ketika catatan dibuat / diperbarui). Sebagai catatan, kami juga menggunakan DATETIMEOFFSETSQL Server 2008.

Saya melihat DateTimeberguna ketika Anda ingin berurusan dengan kencan saja, hanya kali, atau berurusan dengan baik dalam arti umum. Misalnya, jika Anda memiliki alarm yang Anda ingin pergi setiap hari pukul 7 pagi, Anda bisa menyimpan bahwa dalam DateTimememanfaatkan DateTimeKinddari Unspecifiedkarena Anda ingin pergi pada 7:00 terlepas dari DST. Tetapi jika Anda ingin merepresentasikan riwayat kejadian alarm, Anda akan menggunakannya DateTimeOffset.

Berhati-hatilah saat menggunakan campuran DateTimeOffsetdan DateTimeterutama saat menetapkan dan membandingkan antara jenis. Selain itu, hanya bandingkan DateTimeinstance yang sama DateTimeKindkarena DateTimemengabaikan offset zona waktu saat membandingkan.

Tanah liat
sumber
146
Jawaban yang diterima terlalu panjang dan analoginya tegang, ini adalah jawaban IMO yang jauh lebih baik dan lebih ringkas.
nexus mengatakan
10
Saya hanya akan mengatakan bahwa saya suka jawaban ini juga, dan terangkat. Meskipun pada bagian terakhir - walaupun memastikannya Kindsama, perbandingan bisa saja salah. Jika kedua belah pihak memiliki DateTimeKind.UnspecifiedAnda tidak benar-benar tahu bahwa mereka berasal dari zona waktu yang sama. Jika kedua belah pihak DateTimeKind.Local, sebagian besar perbandingan akan baik-baik saja, tetapi Anda masih bisa memiliki kesalahan adalah satu sisi ambigu di zona waktu lokal. Benar-benar hanya DateTimeKind.Utcperbandingan yang sangat mudah, dan ya, DateTimeOffsetbiasanya lebih disukai. (Ceria!)
Matt Johnson-Pint
1
+1 Saya akan menambahkan ini: Tipe Data yang Anda pilih harus mencerminkan niat Anda. Jangan gunakan DateTimeOffset di mana-mana, hanya karena. Jika Offset penting untuk Perhitungan Anda dan Membaca-Dari / Bertahan-Ke DataBase, kemudian gunakan DateTimeOffset. Jika tidak masalah, maka gunakan DateTime, jadi Anda mengerti (hanya dengan melihat DataType) bahwa Offset seharusnya tidak ada kaitannya dan Times harus tetap relatif terhadap Lokalitas Server / Mesin Kode C # Anda berjalan.
MikeTeeVee
"Tapi jika kamu ingin merepresentasikan riwayat kejadian alarm, kamu akan menggunakan DateTimeOffset." Maukah Anda menjelaskan mengapa menurut Anda ini? Saya bisa mengisinya dengan membaca semua informasi di halaman ini, tetapi mungkin Anda bisa memberikan informasi itu dengan cara yang mudah dibaca juga. Ini jawaban yang sangat mudah dibaca.
Barrosy
77

DateTime hanya mampu menyimpan dua waktu berbeda, waktu lokal dan UTC. The Kind properti yang menunjukkan.

DateTimeOffset memperluas ini dengan bisa menyimpan waktu lokal dari mana saja di dunia. Itu juga menyimpan offset antara waktu setempat dan UTC. Perhatikan bagaimana DateTime tidak dapat melakukan ini kecuali Anda menambahkan anggota tambahan ke kelas Anda untuk menyimpan offset UTC itu. Atau hanya pernah bekerja dengan UTC. Yang dengan sendirinya adalah ide yang bagus btw.

Hans Passant
sumber
33

Ada beberapa tempat yang DateTimeOffsetmasuk akal. Salah satunya adalah ketika Anda berurusan dengan acara berulang dan waktu musim panas. Katakanlah saya ingin mengatur alarm untuk berbunyi jam 9 pagi setiap hari. Jika saya menggunakan aturan "store as UTC, display as time time", maka alarm akan berbunyi pada waktu yang berbeda ketika waktu musim panas berlaku.

Mungkin ada yang lain, tetapi contoh di atas sebenarnya adalah salah satu yang pernah saya temui sebelumnya (ini sebelum penambahan DateTimeOffsetBCL - solusi saya pada saat itu adalah secara eksplisit menyimpan waktu di zona waktu lokal, dan menyimpan informasi zona waktu di sampingnya: pada dasarnya apa yang DateTimeOffsetdilakukan secara internal).

Dean Harding
sumber
12
DateTimeOffset tidak memperbaiki masalah DST
JarrettV
2
Menggunakan kelas TimeZoneInfo memang membawa aturan untuk DST. jika Anda menggunakan .net 3.5 atau yang lebih baru maka gunakan kelas TimeZone atau TimeZoneInfo untuk menangani tanggal yang harus menangani Daylight Savings Time bersamaan dengan offset zona waktu.
Zack Jannsen
1
Ya contoh pengecualian (aplikasi alarm) yang bagus tetapi ketika waktu lebih penting daripada tanggal Anda harus benar-benar menyimpan yang terpisah dalam struktur data jadwal Anda untuk aplikasi, yaitu jenis kejadian = Harian dan waktu = 09:00. Intinya di sini adalah pengembang perlu mengetahui jenis tanggal apa yang mereka rekam, kalkulasi atau presentasikan kepada pengguna. Terutama aplikasi yang cenderung lebih global sekarang kita memiliki internet sebagai toko aplikasi standar dan besar untuk menulis perangkat lunak. Sebagai simpul samping saya juga ingin melihat Microsoft menambahkan struktur Tanggal dan Waktu yang terpisah.
Tony Wall
3
Meringkas komentar Jarrett dan Zack: Kedengarannya seperti DateTimeOffset saja tidak akan menangani masalah DST tetapi menggunakan DateTimeOffset dalam hubungannya dengan TimeZoneInfo akan menanganinya. Ini tidak berbeda dari DateTime di mana jenisnya adalah Utc. Dalam kedua kasus saya harus mengetahui zona waktu (bukan hanya offset) dari kalender yang saya proyeksikan saat ini. (Saya mungkin menyimpannya di profil pengguna atau mendapatkannya dari klien (misalnya Windows) jika memungkinkan). Kedengarannya benar?
Jeremy Cook
"Ada beberapa tempat di mana DateTimeOffset masuk akal." --- Boleh dibilang, itu lebih sering masuk akal daripada tidak.
Ronnie Overby
23

Perbedaan paling penting adalah bahwa DateTime tidak menyimpan informasi zona waktu, sedangkan DateTimeOffset tidak.

Meskipun DateTime membedakan antara UTC dan Lokal, sama sekali tidak ada offset zona waktu eksplisit yang terkait dengannya. Jika Anda melakukan serialisasi atau konversi apa pun, zona waktu server akan digunakan. Bahkan jika Anda secara manual membuat waktu lokal dengan menambahkan menit untuk mengimbangi waktu UTC, Anda masih bisa mendapatkan sedikit dalam langkah serialisasi, karena (karena kurangnya offset eksplisit di DateTime) itu akan menggunakan zona waktu server offset.

Misalnya, jika Anda membuat serialisasi nilai DateTime dengan Kind = Lokal menggunakan Json.Net dan format tanggal ISO, Anda akan mendapatkan string seperti 2015-08-05T07:00:00-04. Perhatikan bahwa bagian terakhir (-04) tidak ada hubungannya dengan DateTime Anda atau offset apa pun yang Anda gunakan untuk menghitungnya ... itu hanya murni zona waktu server offset.

Sementara itu, DateTimeOffset secara eksplisit menyertakan offset. Ini mungkin tidak termasuk nama zona waktu, tetapi setidaknya itu termasuk offset, dan jika Anda membuat serial, Anda akan mendapatkan offset yang dimasukkan secara eksplisit dalam nilai Anda alih-alih apa pun waktu lokal server yang terjadi.

Triynko
sumber
14
dengan semua jawaban di atas, saya bertanya-tanya mengapa tidak ada yang mau repot-repot menulis kalimat tunggal Anda yang merangkum semuanyaThe most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Korayem
9
DateTimeOffset TIDAK menyimpan informasi zona waktu. Dokumen MS berjudul "Memilih antara DateTime, DateTimeOffset, TimeSpan, dan TimeZoneInfo" menetapkan pernyataan ini: "Nilai DateTimeOffset tidak terikat ke zona waktu tertentu, tetapi dapat berasal dari berbagai zona waktu". Yang mengatakan, DateTimeOffset IS zona waktu AWARE, berisi offset dari UTC, yang membuat semua perbedaan dan itulah sebabnya MS merekomendasikan kelas default ketika berhadapan dengan pengembangan aplikasi yang berhubungan dengan info tanggal. Jika Anda benar-benar peduli pada zona waktu spesifik mana data berasal, Anda harus mempertahankannya secara terpisah
stonedauwg
1
Ya, tetapi seperti yang telah ditunjukkan di banyak tempat, + atau - jam tidak mengatakan apa-apa tentang zona waktu Anda dan pada akhirnya tidak berguna. Tergantung pada apa yang perlu Anda lakukan, Anda bisa menyimpan datetime sebagai Kind.Unspecified dan kemudian menyimpan id zona waktu dan saya pikir Anda sebenarnya lebih baik.
Arwin
13

Sepotong kode dari Microsoft ini menjelaskan semuanya:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
Mojtaba
sumber
9

Sebagian besar jawabannya baik, tetapi saya berpikir untuk menambahkan beberapa tautan MSDN untuk informasi lebih lanjut

dekdev
sumber
7

Perbedaan utama adalah bahwa DateTimeOffsetdapat digunakan bersamaan dengan TimeZoneInfountuk mengkonversi ke waktu lokal di zona waktu selain yang saat ini.

Ini berguna pada aplikasi server (misalnya ASP.NET) yang diakses oleh pengguna di zona waktu yang berbeda.

Joe
sumber
3
@Bugeo Bugeo benar, tetapi ada risiko. Anda dapat membandingkan dua DateTimes dengan terlebih dahulu memanggil "ToUniversalTime" pada masing-masing. Jika Anda memiliki tepat satu nilai dalam perbandingan yaitu DateTimeKind = Tidak ditentukan strategi Anda akan gagal. Potensi kegagalan ini merupakan alasan untuk mempertimbangkan DateTimeOffset dari DateTime ketika konversi ke waktu lokal diperlukan.
Zack Jannsen
Seperti di atas, saya pikir dalam skenario ini Anda lebih baik menyimpan TimeZoneId daripada menggunakan DateTimeOffset, yang pada akhirnya tidak berarti apa-apa.
Arwin
2

Satu-satunya sisi negatif dari DateTimeOffset yang saya lihat adalah Microsoft "lupa" (sesuai desain) untuk mendukungnya di kelas XmlSerializer mereka. Tetapi sejak itu telah ditambahkan ke kelas utilitas XmlConvert.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Saya katakan maju dan gunakan DateTimeOffset dan TimeZoneInfo karena semua manfaatnya, berhati-hatilah saat membuat entitas yang akan atau mungkin diserialisasi ke atau dari XML (semua objek bisnis).

Tony Wall
sumber