Ruby'de DateTime ve Time arasındaki fark


219

Ruby'deki sınıflar DateTimeve Timesınıflar arasındaki fark nedir ve hangi faktörleri seçmemize neden olur?


Dokümanlar, hangisinin ne zaman kullanılacağını açıklayan bir bölüme sahiptir.
x-yuri

Yanıtlar:


177

Ruby'nin yeni sürümleri (2.0+) iki sınıf arasında gerçekten önemli farklılıklar göstermiyor. Bazı kütüphaneler tarihi nedenlerden ötürü birini veya diğerini kullanacaktır, ancak yeni kodların endişelenmesi gerekmez. Tutarlılık için bir tane seçmek muhtemelen en iyisidir, bu yüzden kütüphanelerinizin beklediği şeyleri deneyin. Örneğin, ActiveRecord DateTime'ı tercih eder.

Ruby 1.9'dan önceki sürümlerde ve birçok sistemde Zaman, 1 Ocak 1970 UTC'den bu yana geçen saniye sayısını tanımlayan 32 bit işaretli bir değer olarak temsil edilir, POSIX standart time_tdeğerinin etrafındaki ince bir sarıcıdır ve sınırlıdır:

Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901

Ruby'nin daha yeni sürümleri hata üretmeden daha büyük değerleri işleyebilir.

DateTime, yıl, ay, gün, saat, dakika ve saniyenin ayrı ayrı depolandığı takvim tabanlı bir yaklaşımdır. Bu, SQL standardı DATETIME alanlarının etrafına sarıcı görevi gören bir Ruby on Rails yapısıdır. Bunlar keyfi tarihler içerir ve ifade aralığı tipik olarak çok büyük olduğu için neredeyse her noktayı temsil edebilir.

DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000

Bu nedenle DateTime'ın Aristotle'dan blog gönderilerini işleyebileceğine dair güven verici.

Birini seçerken, farklar şimdi biraz özneldir. Tarihsel olarak DateTime, bir takvim biçiminde manipüle etmek için daha iyi seçenekler sunmuştur, ancak bu yöntemlerin çoğu, en azından Rails ortamında da Zamana taşınmıştır.


6
Yani her zaman DateTime kullanmalıyım?
Tom Lehman

4
Tarihlerle çalışıyorsanız, DateTime kullanın. Zaman, günün geçerli saati veya yakın gelecekte 10. dakika gibi noktaları temsil etmek için uygundur. Her ikisinin de ortak noktası var, ancak belirtildiği gibi DateTime çok daha geniş bir değer aralığını temsil edebilir.
tadman

3
Bence Ruby bir taşma ile karşılaştığında 32 bit Fixnum'dan keyfi uzunluktaki Bignum'a geçiyor. Bu aralığın dışındaki sayılar harici uygulamalar tarafından desteklenmeyebilir. Evet, 2038'de, hepimiz uygun bir 64-bit zaman formatı üzerinde anlaşana kadar vidalandık. Jüri hala dışarıda.
tadman

28
Bu cevap 1.9.2'den önce gelir. Time'ın posix sınırlamaları hakkında söylediği her şeyi göz ardı edin ve Time ve DateTime API'lerine göre seçiminizi yapın.
Ben Nagy

8
Bu cevap modası geçmiş değil mi? Şimdi Time veya Rail'in ActiveSupport :: WithTimeZone kullanılması tavsiye edilir. Artık DateTime kullanmanıza gerek yok. Geriye dönük uyumluluk için vardır.
Donato

103

[Temmuz 2018'i düzenleyin]

Aşağıdakilerin tümü Ruby 2.5.1'de hala geçerlidir. Gönderen referans belgeleri :

DateTime herhangi bir artık saniye dikkate almaz, yaz saati kurallarını izlemez.

Bu konuda daha önce belirtilmeyen şey, aşağıdakilerin birkaç avantajından biridir DateTime: takvim reformlarının farkında olmasına Timerağmen:

[…] Ruby'nin Time sınıfı proleptik bir Gregoryen takvimi uygular ve takvim reformu kavramı yoktur […].

Referans belgeleri Time, yalnızca yakın geçmiş, şimdiki veya gelecekteki tarihler / saatler ile ilgilenirken ve yalnızca DateTimeörneğin Shakespeare'in doğum gününün doğru bir şekilde dönüştürülmesi gerektiğinde kullanılması önerisiyle sona erer : (vurgu eklendi)

Peki Ruby'de DateTime'ı ne zaman ve Time'ı ne zaman kullanmalısınız? Uygulamanız muhtemelen güncel tarihler ve saatler ile uğraştığından, Kesinlikle Zaman'ı kullanmak isteyeceksiniz. Ancak, tarihsel bir bağlamda tarihler ve saatler ile uğraşmak gerekirse DateTime […] kullanmak isteyeceksiniz . Zaman dilimleriyle de uğraşmanız gerekiyorsa, o zaman şansın en iyisi - muhtemelen yerel güneş zamanlarıyla uğraşacağınızı unutmayın, çünkü demiryollarının tanıtımı Standart Zaman ihtiyacını gerektirmiyordu. ve nihayetinde zaman dilimleri.

[/ Temmuz 2018'i düzenleyin]

Ruby 2.0'dan itibaren, diğer cevaplardaki bilgilerin çoğu güncel değil.

Özellikle, Timeşimdi pratik olarak bağlanmamıştır. Epoch'tan 63 bitten daha fazla veya daha az olabilir:

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

Daha büyük değerler kullanmanın tek sonucu, Integers kullanıldığında ( Bignums ( Integeraralık dışındaki değerler ) veya Rationals (nanosaniye izlendiğinde) vs. daha iyi olan performans olmalıdır :

Ruby 1.9.2'den bu yana, Time uygulaması işaretli bir 63 bit tam sayı, Bignum veya Rational kullanır. Tam sayı, 1823-11-12 ila 2116-02-20'yi temsil edebilen Epoch'tan beri bir dizi nanosaniyedir. Bignum veya Rational kullanıldığında (1823'ten önce, 2116'dan sonra, nanosaniyenin altında), Zaman, tamsayı kullanıldığında olduğu gibi daha yavaş çalışır. ( http://www.ruby-doc.org/core-2.1.0/Time.html )

Başka bir deyişle, anladığım kadarıyla, DateTimeartık daha geniş bir potansiyel değer aralığını kapsamıyorTime .

Ek olarak, daha önce bahsedilmeyen iki kısıtlama DateTimemuhtemelen belirtilmelidir:

DateTime herhangi bir sıçrama süresini dikkate almaz, yaz saati kurallarını izlemez. ( http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime )

İlk olarak, DateTimeartık saniye kavramı yoktur:

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

Yukarıdaki örneğin birlikte çalışabilmesi Timeiçin işletim sisteminin artık saniyeleri desteklemesi ve saat dilimi bilgilerinin TZ=right/UTC irb( örneğin birçok Unix sisteminde) doğru ayarlanması gerekir .

İkincisi, DateTimezaman dilimleri hakkında çok sınırlı bir anlayışa sahiptir ve özellikle gün ışığından yararlanma kavramı yoktur . Saat dilimlerini basitçe UTC + X ofsetleri olarak işler:

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>

Bu, zamanların DST olarak girilip daha sonra DST olmayan bir zaman dilimine dönüştürüldüğünde sorunlara neden olabilir ve doğru ofsetleri DateTimekendi dışında tutmaz (birçok işletim sistemi bunu zaten sizin için halledebilir).

Genel olarak, günümüzde Timeçoğu uygulama için daha iyi bir seçim olduğunu söyleyebilirim .

Ayrıca, eklemeyle ilgili önemli bir farka dikkat edin: Bir Time nesnesine sayı eklediğinizde, bu sayı saniye olarak sayılır, ancak bir DateTime öğesine sayı eklediğinizde gün olarak sayılır.


Bu 2018'de hala geçerli mi?
Qqwy

2
Ruby 2.5.1'de hala geçerli. ruby-doc.org/stdlib-2.5.1/libdoc/date/rdoc/DateTime.html : "DateTime artık bir saniye dikkate almıyor, yaz saati kurallarını izlemiyor ." Ancak, Gregoryen öncesi takvim tarihleriyle / saatleriyle uğraşmanız gerektiğinde DateTime'ın avantajları olduğunu unutmayın. Bu daha önce burada bahsedilmedi, ancak referans belgelerinde belgelendi: "[…] Ruby'nin Time sınıfı prolepik bir Gregoryen takvimi uygular ve takvim reformu kavramı yoktur […]." Daha fazla ayrıntı sağlamak için cevabımı düzenleyeceğim.
Niels Ganser

Timeartık bir saniye saniye kavramına sahip değil, bu yüzden farklı değil DateTime. Örneklerinizi nerede çalıştırdığınızı bilmiyorum, ancak Time.new(2012,6,30,23,59,60,0)2.0'dan 2.7'ye kadar farklı Ruby sürümlerinde denedim ve her zaman aldım 2012-07-01 00:00:00 +0000.
michau

@michau TimeArtık saniyeleri destekleyip desteklemeyeceği işletim sistemi ve saat dilimi yapılandırmanıza bağlıdır. Örneğin: TZ=right/UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-06-30 23:59:60 +0000oysa TZ=UTC ruby -e 'p Time.new(2012,6,30,23,59,60,0)'=> 2012-07-01 00:00:00 +0000.
Niels Ganser

@NielsGanser Harika, teşekkürler! Bu noktayı açıklığa kavuşturmak için bir düzenleme önerdim.
michau

44

"Fark nedir?" Cevabının Ruby standart kütüphanelerinde bu soruya verilen talihsiz ortak cevaplardan biri olduğunu düşünüyorum: iki sınıf / lib farklı zamanlarda farklı insanlar tarafından farklı şekilde yaratıldı. Java gibi bir şeyin dikkatlice planlanmış gelişimine kıyasla Ruby'nin evriminin topluluk doğasının talihsiz sonuçlarından biridir. Geliştiriciler yeni işlevsellik istiyorlar, ancak mevcut API'lara basmak istemiyorlar, bu yüzden sadece yeni bir sınıf oluşturuyorlar - son kullanıcı için bu ikisinin var olması için açık bir neden yok.

Bu genel olarak yazılım kütüphaneleri için geçerlidir: çoğu zaman bazı kodların veya API'lerin mantıksal olmaktan ziyade tarihsel olduğu ortaya çıkar.

Günaha DateTime ile başlamaktır çünkü daha genel görünür. Tarih ... ve Saat, değil mi? Yanlış. Zaman da tarihleri ​​daha iyi yapar ve aslında DateTime'ın yapamadığı zaman dilimlerini ayrıştırabilir. Ayrıca daha iyi performans gösterir.

Zaman'ı her yerde kullandım.

Güvende olmak için, DateTime argümanlarının Timey API'larıma geçirilmesine ve dönüştürülmesine izin verme eğilimindeyim. Ayrıca her ikisi de ilgilendiğim yöntem olduğunu biliyorum, ben de kabul ediyorum, bu yöntem gibi ben XML (XMLTV dosyaları için) kez dönüştürmek için yazdım

# 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

8
Ayrıca, Time.new ve DateTime.new, saat dilimi ile farklı şekilde ilgilenmektedir. Öyle, GMT + 7 olduğum Time.new(2011, 11, 1, 10, 30)üretir 2011-11-01 10:30:00 +0700iken DateTime.new(2011, 11, 1, 10, 30)üretir Tue, 01 Nov 2011 10:30:00 +0000.
Phễng Nguyễn

21
Hepimizin bildiği gibi, Java'nın dikkatlice planlanan gelişimi, yalnızca basit, mantıksal API'lerle sonuçlandı.
pje

@ PhươngNguyễn: Bunu oylama yapabilmek için lütfen cevap olarak ekler misiniz? Bu, DateTime Üzerinden Zaman'ı seçmeye karar vermemin sebebidir.
13'te

@Senseful Üzgünüm, mesajınızı şimdiye kadar almadım. İnsanlar zaten yorumuma bazı oylar veriyor, bu yüzden burada harika olduğunu düşünüyorum.
Phễng Nguyễn

@ PhươngNguyễn Merhaba, zaman dilimlerindeki bu farkın nasıl / neden olduğu hakkında herhangi bir fikriniz var mı? ofseti nereden alıyor?
Joel_Blum

10

ActiveSupport uzantılarını kullandığınızı varsayarsak , farklı saat dilimlerinde bir günün başlangıcını / bitişini hesaplamak ve yapmak gibi şeyleri DateTime ile yapmak daha kolay buldum .

Benim durumumda, günün sonunu kullanıcının dize olarak aldığım yerel saatine göre (keyfi olarak) bir saat diliminde hesaplamam gerekiyordu, örneğin "2012-10-10 10:10 +0300"

DateTime ile bu kadar basit

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

Şimdi Time ile aynı şekilde deneyelim:

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.

Aslında, Zaman ayrıştırmadan önce saat dilimini bilmelidir (ayrıca not edin Time.zone.parse, değil 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

Yani, bu durumda DateTime ile gitmek kesinlikle daha kolay.


1
Bunu üretimde şimdi kullanmak, tekniğin bir dezavantajı var mı?
Alex Moore-Niemi

1
DateTime yaz saati uygulamasını dikkate almaz. Böylece zamandan tasarruf konusunda doğru çalışmaz. DateTime.parse('2014-03-30 01:00:00 +0100').end_of_dayüretir Sun, 30 Mar 2014 23:59:59 +0100, ancak Time.zone = 'CET'; Time.zone.parse('2014-03-30 01:00:00').end_of_dayüretir Sun, 30 Mar 2014 23:59:59 CEST +02:00(CET = + 01: 00, CEST = + 02: 00 - ofsetin değiştiğine dikkat edin). Ancak bunu yapmak için, kullanıcının saat dilimi hakkında daha fazla bilgiye ihtiyacınız vardır (sadece ofset değil, aynı zamanda zaman tasarrufu kullanıp kullanmadığı da).
Petr '' Bubák '' Šedivý

1
Açık Time.zone.parseolanı belirtiyor olabilir, ancak farklı bölgelerle zamanları ayrıştırırken çok yararlıdır - sizi hangi bölgeyi kullanmanız gerektiğini düşünmeye zorlar. Bazen, Time.find_zone daha da iyi çalışır.
Pruswan

1
@ Petr''Bubák''Šedivý DateTime, verdiğin ofseti ayrıştırdı, +0100. TimeBir ofset vermediniz , ancak bir saat dilimi ("CET" bir ofseti tanımlamıyor, bir saat diliminin adı). Zaman dilimleri yıl boyunca farklı ofsetlere sahip olabilir, ancak bir ofset bir ofsettir ve her zaman aynıdır.
Mecki

4

Özel örneklemelerle zaman dilimlerini nasıl farklı şekilde ele aldıklarını düşünün:

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

Bu, zaman aralıkları vb. Oluştururken zor olabilir.


Sadece sade yakut ile gerçekleşiyor gibi görünüyor (yani Rails yok)
vemv

1

Bazı durumlarda davranış çok farklı görünüyor:

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"


Bu ilginç bir gözlem, ama neden olduğunu açıklamak gerekir. Date(şaşırtıcı olmayan bir şekilde) yalnızca tarihleri ​​ayrıştırır ve a Datedönüştürüldüğünde Time, saat olarak her zaman yerel saat diliminde gece yarısını kullanır. Zaman dilimlerinin genellikle (örneğin gün ışığından yararlanma saati açısından) tarafından daha doğru bir şekilde ele alındığı göz önüne alındığında, şaşırtıcı olan BST'yi anlamayan gerçek arasındaki fark Timeve TimeDatekaynaklardan kaynaklanmaktadır . Bu durumda, yalnızca tüm dizeyi doğru şekilde ayrıştırır. TimeTimeDateTime
michau

1

Niels Ganser'ın cevabına ek olarak , bu argümanı da düşünebilirsiniz:

Ruby Stil Kılavuzu'nun bu konuda oldukça açık bir şekilde ifade ettiğini unutmayın :

DateTime yok

Tarihsel takvim reformunu hesaba katmanız gerekmedikçe DateTime'ı kullanmayın ve eğer yaparsanız, niyetlerinizi açıkça belirtmek için başlangıç ​​argümanını açıkça belirtin.

# 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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.