Ruby'deki sınıflar DateTimeve Timesınıflar arasındaki fark nedir ve hangi faktörleri seçmemize neden olur?
Ruby'deki sınıflar DateTimeve Timesınıflar arasındaki fark nedir ve hangi faktörleri seçmemize neden olur?
Yanıtlar:
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.
[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.
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.
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.
"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
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.
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.
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).
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.
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.
Ö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.
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"
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
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)