2009-08-11 12 views

Odpowiedz

161

Nowsze wersje Ruby (2.0+) tak naprawdę nie różnią się znacząco między dwiema klasami. Niektóre biblioteki będą używać jednego z nich z powodów historycznych, ale nowy kod niekoniecznie musi być zaniepokojony. Wybór jednego dla spójności jest prawdopodobnie najlepszy, spróbuj więc dopasować to, czego oczekują twoje biblioteki. Na przykład ActiveRecord preferuje DateTime.

W wersjach wcześniejszych niż Ruby 1.9 i wielu systemach Czas jest reprezentowany jako 32-bitowa wartość ze znakiem, opisująca liczbę sekund od 1 stycznia 1970 UTC, cienkie opakowanie o wartości zgodnej ze standardem POSIX time_t i ograniczone :

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

Nowsze wersje Ruby są w stanie obsłużyć większe wartości bez tworzenia błędów.

DateTime to podejście oparte na kalendarzach, w którym rok, miesiąc, dzień, godzina, minuta i sekunda są przechowywane indywidualnie. Jest to konstrukcja Ruby on Rails, która służy jako opakowanie dla standardowych pól DATETIME języka SQL. Zawierają one arbitralne daty i mogą reprezentować prawie każdy punkt w czasie, ponieważ zakres ekspresji jest zazwyczaj bardzo duży.

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

Zapewnia to, że DateTime może obsłużyć posty na blogu od Arystotelesa.

Wybierając jeden, różnice są teraz nieco subiektywne. Historycznie DateTime zapewniało lepsze opcje manipulowania nim w kalendarzu, ale wiele z tych metod przeniesiono również do czasu, przynajmniej w środowisku Rails.

+5

Dlatego należy zawsze używać DateTime? –

+4

Jeśli pracujesz z datami, użyłbym DateTime. Czas jest wygodny do przedstawiania rzeczy takich jak aktualna pora dnia lub punktów w niedalekiej przyszłości, takich jak 10.minutes.from_now. Oba mają ze sobą wiele wspólnego, choć jak wspomniano, DateTime może reprezentować znacznie szerszy zakres wartości. – tadman

+0

> Czas jest wygodny do przedstawiania rzeczy takich jak aktualna godzina lub punktów w bliskiej przyszłości, takich jak 10.minutes.from_now. Dlaczego w tym celu czas jest wygodniejszy niż DateTime? –

42

Myślę, że odpowiedź na pytanie "jaka jest różnica" jest jedną z niefortunnych, wspólnych odpowiedzi na to pytanie w standardowych bibliotekach Ruby: dwie klasy/biblioteki zostały stworzone w różny sposób przez różnych ludzi w różnym czasie. Jest to jedna z niefortunnych konsekwencji społecznościowej ewolucji Rubiego w porównaniu ze starannie zaplanowanym rozwojem czegoś takiego jak Java. Deweloperzy oczekują nowej funkcjonalności, ale nie chcą wchodzić na istniejące interfejsy API, więc tworzą nową klasę - dla użytkownika końcowego nie ma żadnego oczywistego powodu, dla którego obie strony mogłyby istnieć.

Dotyczy to ogólnie bibliotek oprogramowania: często powodem tego, że jakiś kod lub interfejs API jest taki, jak się okazuje, jest raczej historyczny niż logiczny.

Kuszenie zaczyna się od DateTime, ponieważ wydaje się bardziej ogólne. Data ... i czas, prawda? Źle. Czas również robi daty lepiej, i faktycznie może parsować strefy czasowe, gdzie DateTime nie może. Działa również lepiej.

Skończyło się na używaniu czasu wszędzie.

Dla bezpieczeństwa jednak pozwalam na przekazywanie argumentów DateTime do moich interfejsów API Timey i albo konwertuję. Także, jeśli wiem, że obie mają metodę Jestem zainteresowany Akceptuję albo, jak tej metody napisałem dla razy konwersji do formatu XML (dla plików 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 
+8

Ponadto, Time.new i DateTime.new mają do czynienia ze strefą czasową w różny sposób. Jestem na GMT + 7, więc 'Time.nowy (2011, 11, 1, 10, 30) 'produkuje' 2011-11-01 10:30:00 + 0700' podczas gdy 'DateTime.new (2011, 11, 1, 10, 30)' tworzy 'Wt, 01 Lis 2011 10:30:00 + 0000 ". –

+19

I jak wszyscy wiemy, starannie zaplanowany rozwój Javy zaowocował wyłącznie prostymi, logicznymi interfejsami API. – pje

+0

@ PhươngNguyễn: Czy dodasz to jako odpowiedź, abym mógł ją głosować w górę? Właśnie z tego powodu zdecydowałem się wybrać Time over DateTime. – Senseful

66

Nieaktualny! Zobacz poniżej ...

Różnice w wydajności nie można wystarczająco podkreślić ...Czas to C i DateTime jest Ruby:

>> Benchmark.bm do |bm| 
?> bm.report('DateTime:') do 
?>  n1 = DateTime.now 
>>  n2 = DateTime.now 
>>  1_000_000.times{ n1 < n2 } 
>> end 
>> bm.report('Time: ') do 
?>  n1 = Time.now 
>>  n2 = Time.now 
>>  1_000_000.times{ n1 < n2 } 
>> end 
>> end 
     user  system  total  real 
DateTime: 4.980000 0.020000 5.000000 ( 5.063963) 
Time:  0.330000 0.000000 0.330000 ( 0.335913) 

Update (2/2012):

Jak już wspomniano w komentarzu, 1.9.3 znacznie zwiększyła DateTime wydajność:

 user  system  total  real 
DateTime: 0.330000 0.000000 0.330000 ( 0.333869) 
Time:  0.300000 0.000000 0.300000 ( 0.306444) 
+23

Aktualizacja: Ruby 1.9.3 powinien przynieść szybsze 'DateTime' zaimplementowane w C: http://www.rubyinside.com/ruby-1-9-3-preview-1-released-5229.html, więc ta odpowiedź z nadzieją może nie być istotnym dłużej. –

+2

Uruchomiłem twój kod w Ruby 2.0.0-p247, a moje wyniki są bardzo różne. Z DateTime jest znacznie szybszy (5 razy) 'DateTime: 0.520000 0.010000 0.530000 (0.522596) | Czas: 2.800000 0.000000 2.800000 (2.814311) ' – Leito

+0

Powtarzam to samo na 32-bitowym Ruby 2.0.0p247' DateTime: 0.200000 0.010000 0.210000 (0.200798) | Czas: 0.180000 0.000000 0.180000 (0.184122) ' – Kokizzu

9

Znalazłem takie rzeczy jak parsowanie i obliczanie początku/końca dnia w różnych strefach czasowych są łatwiejsze do zrobienia z DateTime, zakładając, że korzystasz z rozszerzeń ActiveSupport.

W moim przypadku musiałem obliczyć koniec dnia w strefie czasowej użytkownika (dowolna) w oparciu o czas lokalny użytkownika, który otrzymałem jako ciąg znaków, np. "10.10.2012 10:10 +0300"

Z DateTime jest to tak proste jak

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 

Teraz spróbujmy go w ten sam sposób z Czasu:

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. 

Właściwie potrzebuje czasu znać strefę czasową przed parsowania (również pamiętać, że to Time.zone.parse nie 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 

S o, w tym przypadku zdecydowanie łatwiej jest iść z DateTime.

+0

Używając tego w produkcji teraz, są czy są jakieś wady techniki? –

+0

DateTime nie uwzględnia czasu letniego. Tak więc przy oszczędzaniu czasu nie będzie działać poprawnie. 'DateTime.parse ('2014-03-30 01:00:00 +0100'). End_of_day' produkuje' Sun, 30 Mar 2014 23:59:59 + 0100', ale 'Time.zone = 'CET'; Time.zone.parse ('2014-03-30 01:00:00'). End_of_day' produkuje 'Sun, 30 Mar 2014 23:59:59 CEST +02: 00' (CET = + 01: 00, CEST = +02: 00 - zauważ, że przesunięcie zostało zmienione). Ale aby to zrobić, potrzebujesz więcej informacji o strefie czasowej użytkownika (nie tylko przesunięcie, ale także o tym, czy oszczędzasz czas). –

+0

Może to być oczywiste, ale 'Time.zone.parse' jest * bardzo * użyteczne podczas analizowania czasu z różnymi strefami - zmusza do zastanowienia się, która strefa powinna być używana. Czasami funkcja Time.find_zone działa jeszcze lepiej. – prusswan

75

Począwszy od ruby ​​2.0, większość z wyżej wymienionych informacji jest nieaktualna.

W szczególności Time jest obecnie praktycznie niezwiązany. Może ona być wyższa lub nawet mniej niż 63 bitów ze Epoki:

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 

jedynie wskutek użycia większej wartości należy wydajności, co jest lepsze, gdy stosuje się Integer s (w porównaniu Bignum S (wartości poza Integer zakresów) lub Rational s (przy nanosekund są śledzone)):

od Ruby 1.9.2 wdrożenie czas używa podpisany 63 bitową liczbę całkowitą Bignum lub racjonalny. Liczba całkowita jest liczbą nanosekund od epoki, która może reprezentować 1823-11-12 do 2116-02-20. Gdy używana jest metoda Bignum lub Rational (przed 1823, po 2116, pod nanosekundą), czas działa wolniej, niż gdy używana jest liczba całkowita. (http://www.ruby-doc.org/core-2.1.0/Time.html)

Innymi słowy, o ile mi zrozumieć, DateTime nie obejmuje szerszy zakres potencjalnych wartości niż Time.

Ponadto dwa wcześniej niewymienione ograniczenia DateTime należy prawdopodobnie zauważył:

DateTime nie uznaje żadnych leapseconds, nie zapisuje żadnych zasad okresie letnim. (http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)

Najpierw DateTime ma koncepcji sekundy przestępne:

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_i 
=> 1341100823 

drugie, DateTime ma bardzo ograniczoną wiedzę o strefach czasowych, w szczególności ma koncepcji światła dziennego oszczędności. To dość dużo obsługuje stref czasowych jako proste UTC + X offsety:

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> 

Może to powodować problemy, gdy czasy są wprowadzane jako DST, a następnie przekształcony w non-DST strefy czasowej bez śledzenie odpowiednich przesunięć poza DateTime sama (wiele systemów operacyjnych może już to zająć).

Ogólnie rzecz biorąc, chciałbym powiedzieć, że w dzisiejszych czasach Time jest lepszym wyborem dla większości aplikacji.

Należy również zwrócić uwagę na ważną różnicę w zakresie dodawania: dodawanie liczby do obiektu czasu jest liczone w sekundach, ale po dodaniu numeru do obiektu DateTime jest ono liczone w dniach.

4

Zastanów się, jak poradzić sobie z niestandardowymi strefy czasowe inaczej dawałaby:

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 

To może być trudne podczas tworzenia zakresów czasowych itp

+0

Wydaje się, że tylko z prostym rubinem (tzn. Bez szyn) – vemv

Powiązane problemy