Apa perbedaan antara DateTime
dan Time
kelas di Ruby dan faktor-faktor apa yang menyebabkan saya memilih satu atau yang lain?
Apa perbedaan antara DateTime
dan Time
kelas di Ruby dan faktor-faktor apa yang menyebabkan saya memilih satu atau yang lain?
Jawaban:
Versi Ruby yang lebih baru (2.0+) tidak benar-benar memiliki perbedaan yang signifikan antara kedua kelas. Beberapa perpustakaan akan menggunakan satu atau yang lain karena alasan historis, tetapi kode baru tidak perlu diperhatikan. Memilih satu untuk konsistensi mungkin yang terbaik, jadi cobalah dan sesuaikan dengan apa yang perpustakaan Anda harapkan. Misalnya, ActiveRecord lebih suka DateTime.
Dalam versi sebelum Ruby 1.9 dan pada banyak sistem, Time direpresentasikan sebagai nilai yang ditandatangani 32-bit yang menggambarkan jumlah detik sejak 1 Januari 1970 UTC, pembungkus tipis di sekitar time_t
nilai standar POSIX , dan terikat:
Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901
Versi Ruby yang lebih baru dapat menangani nilai yang lebih besar tanpa menghasilkan kesalahan.
DateTime adalah pendekatan berbasis kalender di mana tahun, bulan, hari, jam, menit dan detik disimpan secara terpisah. Ini adalah konstruksi Ruby on Rails yang berfungsi sebagai pembungkus di sekitar bidang DATETIME standar SQL. Ini berisi tanggal yang berubah-ubah dan dapat mewakili hampir semua titik waktu karena rentang ekspresi biasanya sangat besar.
DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000
Jadi meyakinkan bahwa DateTime dapat menangani posting blog dari Aristoteles.
Saat memilih satu, perbedaannya agak subyektif sekarang. Secara historis DateTime telah menyediakan opsi yang lebih baik untuk memanipulasinya dengan cara kalender, tetapi banyak dari metode ini telah dipindahkan ke Time juga, setidaknya dalam lingkungan Rails.
[Edit Juli 2018]
Semua hal di bawah ini masih berlaku di Ruby 2.5.1. Dari dokumentasi referensi :
DateTime tidak mempertimbangkan detik kabisat, tidak melacak aturan waktu musim panas.
Apa yang belum dicatat di utas ini sebelumnya adalah salah satu dari sedikit keuntungan DateTime
: ia menyadari reformasi kalender sedangkan Time
tidak:
[...] Kelas Waktu Ruby mengimplementasikan kalender Gregorian yang subur dan tidak memiliki konsep reformasi kalender [...].
Dokumentasi referensi diakhiri dengan rekomendasi untuk digunakan Time
ketika secara eksklusif berurusan dengan tanggal / waktu dekat-masa lalu, saat ini atau masa depan dan hanya digunakan DateTime
ketika, misalnya, ulang tahun Shakespeare perlu dikonversi secara akurat: (penekanan ditambahkan)
Jadi kapan Anda harus menggunakan DateTime di Ruby dan kapan Anda harus menggunakan Waktu? Hampir pasti Anda ingin menggunakan Waktu karena aplikasi Anda mungkin berurusan dengan tanggal dan waktu saat ini. Namun, jika Anda harus berurusan dengan tanggal dan waktu dalam konteks historis, Anda ingin menggunakan DateTime [...]. Jika Anda juga harus berurusan dengan zona waktu maka yang terbaik adalah keberuntungan - ingatlah bahwa Anda mungkin akan berhadapan dengan waktu matahari lokal, karena baru pada abad ke-19 pengenalan kereta api mengharuskan perlunya Waktu Standar. dan akhirnya zona waktu.
[/ Edit Juli 2018]
Pada ruby 2.0, sebagian besar informasi dalam jawaban lain sudah usang.
Secara khusus, Time
sekarang praktis tidak terikat. Itu bisa lebih atau kurang dari 63 bit dari Epoch:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> Time.at(2**62-1).utc # within Integer range
=> 146138514283-06-19 07:44:38 UTC
irb(main):003:0> Time.at(2**128).utc # outside of Integer range
=> 10783118943836478994022445751222-08-06 08:03:51 UTC
irb(main):004:0> Time.at(-2**128).utc # outside of Integer range
=> -10783118943836478994022445747283-05-28 15:55:44 UTC
Satu-satunya konsekuensi dari menggunakan nilai yang lebih besar adalah kinerja, yang lebih baik ketika Integer
s digunakan (vs Bignum
s (nilai di luar Integer
kisaran) atau Rational
s (ketika nanodetik dilacak)):
Sejak Ruby 1.9.2, implementasi waktu menggunakan integer 63 bit yang ditandatangani, Bignum atau Rasional. Bilangan bulat adalah sejumlah nanodetik sejak Zaman yang dapat mewakili 1823-11-12 hingga 2116-02-20. Ketika Bignum atau Rasional digunakan (sebelum 1823, setelah 2116, di bawah nanosecond), Waktu bekerja lebih lambat seperti ketika integer digunakan. ( http://www.ruby-doc.org/core-2.1.0/Time.html )
Dengan kata lain, sejauh yang saya mengerti, DateTime
tidak lagi mencakup rentang nilai potensial yang lebih luas daripadaTime
.
Selain itu, dua pembatasan yang sebelumnya tidak disebutkan DateTime
mungkin harus dicatat:
DateTime tidak mempertimbangkan lompatan detik, tidak melacak aturan waktu musim panas. ( http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime )
Pertama, DateTime
tidak memiliki konsep lompatan detik:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.new(2012,6,30,23,59,60,0)
=> 2012-06-30 23:59:60 +0000
irb(main):004:0> dt = t.to_datetime; dt.to_s
=> "2012-06-30T23:59:59+00:00"
irb(main):005:0> t == dt.to_time
=> false
irb(main):006:0> t.to_i
=> 1341100824
irb(main):007:0> dt.to_time.to_i
=> 1341100823
Agar contoh di atas dapat bekerja Time
, OS perlu mendukung detik kabisat dan informasi zona waktu harus ditetapkan dengan benar, misalnya melalui TZ=right/UTC irb
(pada banyak sistem Unix).
Kedua, DateTime
memiliki pemahaman yang sangat terbatas tentang zona waktu dan khususnya tidak memiliki konsep penghematan siang hari . Ini cukup menangani zona waktu sesederhana offset UTC + X:
irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.local(2012,7,1)
=> 2012-07-01 00:00:00 +0200
irb(main):004:0> t.zone
=> "CEST"
irb(main):005:0> t.dst?
=> true
irb(main):006:0> dt = t.to_datetime; dt.to_s
=> "2012-07-01T00:00:00+02:00"
irb(main):007:0> dt.zone
=> "+02:00"
irb(main):008:0> dt.dst?
NoMethodError: undefined method `dst?' for #<DateTime:0x007f34ea6c3cb8>
Ini dapat menyebabkan masalah ketika waktu dimasukkan sebagai DST dan kemudian dikonversi menjadi zona waktu non-DST tanpa melacak offset yang benar di luar dari DateTime
dirinya sendiri (banyak sistem operasi yang sebenarnya sudah menangani ini untuk Anda).
Secara keseluruhan, saya akan mengatakan bahwa saat ini Time
adalah pilihan yang lebih baik untuk sebagian besar aplikasi.
Perhatikan juga perbedaan penting pada penambahan: ketika Anda menambahkan angka ke objek Waktu, itu dihitung dalam hitungan detik, tetapi ketika Anda menambahkan nomor ke DateTime, itu dihitung dalam beberapa hari.
Time
tidak memiliki konsep detik kabisat, jadi tidak berbeda dari DateTime
. Saya tidak tahu di mana Anda menjalankan contoh Anda, tetapi saya mencoba Time.new(2012,6,30,23,59,60,0)
dalam versi Ruby yang berbeda, dari 2.0 hingga 2.7, dan selalu berhasil 2012-07-01 00:00:00 +0000
.
Time
mendukung lompatan detik atau tidak tergantung pada konfigurasi OS & zona waktu Anda. Misalnya: TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-06-30 23:59:60 +0000
sedangkan TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'
=> 2012-07-01 00:00:00 +0000
.
Saya pikir jawaban untuk "apa bedanya" adalah salah satu jawaban umum yang disayangkan untuk pertanyaan ini di perpustakaan standar Ruby: dua kelas / lib dibuat secara berbeda oleh orang yang berbeda di waktu yang berbeda. Ini adalah salah satu konsekuensi malang dari sifat komunitas evolusi Ruby dibandingkan dengan pengembangan yang direncanakan dengan hati-hati seperti Jawa. Pengembang menginginkan fungsionalitas baru tetapi tidak ingin menginjak API yang ada sehingga mereka hanya membuat kelas baru - bagi pengguna akhir tidak ada alasan yang jelas untuk keduanya ada.
Ini berlaku untuk pustaka perangkat lunak secara umum: sering kali alasan beberapa kode atau API adalah cara itu ternyata lebih bersifat historis daripada logis.
Godaan akan dimulai dengan DateTime karena tampaknya lebih umum. Tanggal ... dan Waktu, kan? Salah. Waktu juga menentukan tanggal dengan lebih baik, dan pada kenyataannya dapat mem-parsing zona waktu di mana DateTime tidak bisa. Berkinerja lebih baik.
Saya akhirnya menggunakan Waktu di mana-mana.
Agar lebih aman, saya cenderung membiarkan argumen DateTime diteruskan ke API Timey saya, dan entah itu dikonversi. Juga jika saya tahu bahwa keduanya memiliki metode yang saya tertarik saya juga menerima, seperti metode ini saya menulis untuk mengkonversi kali ke XML (untuk file XMLTV)
# Will take a date time as a string or as a Time or DateTime object and
# format it appropriately for xmtlv.
# For example, the 22nd of August, 2006 at 20 past midnight in the British Summertime
# timezone (i.e. GMT plus one hour for DST) gives: "20060822002000 +0100"
def self.format_date_time(date_time)
if (date_time.respond_to?(:rfc822)) then
return format_time(date_time)
else
time = Time.parse(date_time.to_s)
return format_time(time)
end
end
# Note must use a Time, not a String, nor a DateTime, nor Date.
# see format_date_time for the more general version
def self.format_time(time)
# The timezone feature of DateTime doesn't work with parsed times for some reason
# and the timezone of Time is verbose like "GMT Daylight Saving Time", so the only
# way I've discovered of getting the timezone in the form "+0100" is to use
# Time.rfc822 and look at the last five chars
return "#{time.strftime( '%Y%m%d%H%M%S' )} #{time.rfc822[-5..-1]}"
end
Time.new(2011, 11, 1, 10, 30)
menghasilkan 2011-11-01 10:30:00 +0700
sekaligus DateTime.new(2011, 11, 1, 10, 30)
menghasilkan Tue, 01 Nov 2011 10:30:00 +0000
.
Saya menemukan hal-hal seperti parsing dan menghitung awal / akhir hari di zona waktu yang berbeda lebih mudah dilakukan dengan DateTime, dengan asumsi Anda menggunakan ekstensi ActiveSupport .
Dalam kasus saya, saya perlu menghitung akhir hari di zona waktu pengguna (sewenang-wenang) berdasarkan waktu lokal pengguna yang saya terima sebagai string, misalnya "2012-10-10 10:10 +0300"
Dengan DateTime sesederhana itu
irb(main):034:0> DateTime.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 +0300
# it preserved the timezone +0300
Sekarang mari kita coba dengan cara yang sama dengan Time:
irb(main):035:0> Time.parse('2012-10-10 10:10 +0300').end_of_day
=> 2012-10-10 23:59:59 +0000
# the timezone got changed to the server's default UTC (+0000),
# which is not what we want to see here.
Sebenarnya, Waktu perlu mengetahui zona waktu sebelum parsing (perhatikan juga Time.zone.parse
, bukan Time.parse
):
irb(main):044:0> Time.zone = 'EET'
=> "EET"
irb(main):045:0> Time.zone.parse('2012-10-10 10:10 +0300').end_of_day
=> Wed, 10 Oct 2012 23:59:59 EEST +03:00
Jadi, dalam hal ini pasti lebih mudah menggunakan DateTime.
DateTime.parse('2014-03-30 01:00:00 +0100').end_of_day
menghasilkan Sun, 30 Mar 2014 23:59:59 +0100
, tetapi Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_day
menghasilkan Sun, 30 Mar 2014 23:59:59 CEST +02:00
(CET = + 01: 00, CEST = + 02: 00 - perhatikan offset berubah). Tetapi untuk melakukan ini, Anda memerlukan lebih banyak informasi tentang zona waktu pengguna (tidak hanya ofset tetapi juga apakah menghemat waktu digunakan).
Time.zone.parse
ini sangat berguna ketika parsing kali dengan zona yang berbeda - memaksa Anda untuk berpikir tentang yang zona Anda harus menggunakan. Terkadang, Time.find_zone bekerja lebih baik.
DateTime
mengurai offset yang Anda berikan, yaitu +0100. Anda tidak memberikan Time
offset, tetapi zona waktu ("CET" tidak menggambarkan offset, itu adalah nama zona waktu). Zona waktu dapat memiliki offset yang berbeda sepanjang tahun, tetapi offset adalah offset dan selalu sama.
Pertimbangkan bagaimana mereka menangani zona waktu secara berbeda dengan instantiasi khusus:
irb(main):001:0> Time.new(2016,9,1)
=> 2016-09-01 00:00:00 -0400
irb(main):002:0> DateTime.new(2016,9,1)
=> Thu, 01 Sep 2016 00:00:00 +0000
irb(main):003:0> Time.new(2016,9,1).to_i
=> 1472702400
irb(main):004:0> DateTime.new(2016,9,1).to_i
=> 1472688000
Ini bisa rumit ketika membuat rentang waktu, dll.
Tampaknya dalam beberapa kasus perilaku ini sangat berbeda:
Time.parse("Ends from 28 Jun 2018 12:00 BST").utc.to_s
"2018-06-28 09:00:00 UTC"
Date.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
"2018-06-27 21:00:00 UTC"
DateTime.parse("Ends from 28 Jun 2018 12:00 BST").to_time.utc.to_s
"2018-06-28 11:00:00 UTC"
Date
(Tidak mengherankan) hanya mengurai tanggal, dan ketika Date
dikonversi menjadi Time
, ia selalu menggunakan tengah malam di zona waktu lokal sebagai waktu. Perbedaan antara Time
dan TimeDate
berasal dari fakta yang Time
tidak memahami BST, yang mengejutkan, mengingat bahwa zona waktu biasanya ditangani dengan lebih benar Time
(misalnya berkaitan dengan waktu musim panas, misalnya). Jadi dalam hal ini, hanya DateTime
mem-parsing seluruh string dengan benar.
Selain jawaban Niels Ganser, Anda dapat mempertimbangkan argumen ini:
Perhatikan bahwa The Ruby Style Guide dengan jelas menyatakan posisi tentang hal ini:
Tanpa DateTime
Jangan gunakan DateTime kecuali Anda perlu memperhitungkan reformasi kalender historis - dan jika Anda melakukannya, tentukan secara eksplisit argumen awal untuk secara jelas menyatakan niat Anda.
# bad - uses DateTime for current time DateTime.now # good - uses Time for current time Time.now # bad - uses DateTime for modern date DateTime.iso8601('2016-06-29') # good - uses Date for modern date Date.iso8601('2016-06-29') # good - uses DateTime with start argument for historical date DateTime.iso8601('1751-04-23', Date::ENGLAND)