Nie jestem pewien, czy to powinno być osobne pytanie, ale jak przekonwertować datę i godzinę?
Andrew Grimm,
8
Zaakceptowane i najwyżej ocenione odpowiedzi nie są już najdokładniejsze w nowoczesnych wersjach Rubiego. Zobacz odpowiedzi @theTinMan i @PatrickMcKenzie poniżej.
Phrogz
Odpowiedzi:
51
Będziesz potrzebować dwóch nieco różnych konwersji.
Aby przekonwertować z Time na DateTime, możesz zmienić klasę czasu w następujący sposób:
require'date'classTimedefto_datetime# Convert seconds + microseconds into a fractional number of seconds
seconds = sec + Rational(usec, 10**6)
# Convert a UTC offset measured in minutes to one measured in a# fraction of a day.
offset = Rational(utc_offset, 60 * 60 * 24)
DateTime.new(year, month, day, hour, min, seconds, offset)
endend
Podobne korekty do daty umożliwiają konwersję DateTime do formatu Time .
classDatedefto_gm_time
to_time(new_offset, :gm)
enddefto_local_time
to_time(new_offset(DateTime.now.offset-offset), :local)
end
private
defto_time(dest, method)#Convert a fraction of a day to a number of microseconds
usec = (dest.sec_fraction * 60 * 60 * 24 * (10**6)).to_i
Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min,
dest.sec, usec)
endend
Pamiętaj, że musisz wybrać między czasem lokalnym a czasem GM / UTC.
Oba powyższe fragmenty kodu pochodzą z Ruby Cookbook O'Reilly . Na to zezwala ich polityka ponownego wykorzystywania kodu .
Spowoduje to przerwanie w 1,9, gdzie DateTime # sec_fraction zwraca liczbę milisekund w ciągu jednej sekundy. Dla 1.9, którego chcesz użyć: usec = dest.sec_fraction * 10 ** 6
dkubb
186
require'time'require'date'
t = Time.now
d = DateTime.now
dd = DateTime.parse(t.to_s)
tt = Time.parse(d.to_s)
+1 Może nie jest to najbardziej wydajne w wykonaniu, ale działa, jest zwięzłe i bardzo czytelne.
Walt Jones,
6
Niestety to naprawdę działa tylko w przypadku lokalnych czasów. Jeśli zaczniesz od DateTime lub Time z inną strefą czasową, funkcja analizy zostanie przekonwertowana na lokalną strefę czasową. Zasadniczo tracisz pierwotną strefę czasową.
Bernard
6
Począwszy od ruby 1.9.1, DateTime.parse zachowuje strefę czasową. (Nie mam dostępu do wcześniejszych wersji.) Time.parse nie zachowuje strefy czasowej, ponieważ reprezentuje standard POSIX time_t, co moim zdaniem jest całkowitą różnicą w stosunku do epoki. Każda konwersja na Time powinna mieć takie samo zachowanie.
anshul
1
Masz rację. DateTime.parse działa w wersji 1.9.1, ale nie Time.parse. W każdym razie mniej podatne na błędy (spójne) i prawdopodobnie szybsze jest użycie DateTime.new (...) i Time.new (..). Zobacz moją odpowiedź na przykładowy kod.
Bernard
1
Cześć @anshul. Nie sugeruję, że stwierdzam :-). Informacje o strefie czasowej nie są przechowywane podczas korzystania z Time.parse (). To łatwe do przetestowania. W powyższym kodzie po prostu zamień d = DateTime.now na d = DateTime.new (2010,01,01, 10,00,00, Rational (-2, 24)). tt pokaże teraz datę d przekonwertowaną na lokalną strefę czasową. Nadal możesz wykonywać arytmetykę na datach i wszystkie oprócz oryginalnych informacji tz zostaną utracone. Ta informacja jest kontekstem dla daty i często jest ważna. Zobacz tutaj: stackoverflow.com/questions/279769/...
Bernard
63
Jako aktualizacja stanu ekosystemu Ruby Date, DateTimea Timeteraz mamy metody do konwersji między różnymi klasami. Korzystanie z Ruby 1.9.2+:
Ups. Właśnie zdałem sobie sprawę, że jest to problem Ruby on Rails, a nie problem Rubiego: stackoverflow.com/questions/11277454/… . Mieli nawet zgłoszony błąd dotyczący tej metody w wierszu 2.x i oznaczyli go „nie da się naprawić”. Straszna decyzja IMHO. Zachowanie Railsów całkowicie łamie podstawowy interfejs Rubiego.
Jesse Clark
12
Niestety DateTime.to_time, Time.to_datetimeiTime.parse funkcje nie zachowują informacje strefy czasowej. Podczas konwersji wszystko jest konwertowane na lokalną strefę czasową. Arytmetyka dat nadal działa, ale nie będzie można wyświetlać dat w ich oryginalnych strefach czasowych. Ta informacja kontekstowa jest często ważna. Na przykład, jeśli chcę zobaczyć transakcje wykonane w godzinach pracy w Nowym Jorku, prawdopodobnie wolę, aby były wyświetlane w ich oryginalnych strefach czasowych, a nie w mojej lokalnej strefie czasowej w Australii (12 godzin przed Nowym Jorkiem).
Poniższe metody konwersji zachowują te informacje tz.
W przypadku Ruby 1.8 spójrz na odpowiedź Gordona Wilsona . Pochodzi ze starej, dobrej, niezawodnej książki kucharskiej Ruby.
W przypadku Rubiego 1.9 jest to nieco łatwiejsze.
require'date'# Create a date in some foreign time zone (middle of the Atlantic)
d = DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24))
puts d
# Convert DateTime to Time, keeping the original timezone
t = Time.new(d.year, d.month, d.day, d.hour, d.min, d.sec, d.zone)
puts t
# Convert Time to DateTime, keeping the original timezone
d = DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec, Rational(t.gmt_offset / 3600, 24))
puts d
Czas jest skomplikowany, ale nie ma wymówki, by nie zapewniać wbudowanej konwersji między różnymi wbudowanymi klasami czasu. Możesz zgłosić RangeException, jeśli spróbujesz uzyskać UNIX time_t dla 4713 BC (chociaż ujemna wartość BigNum byłaby ładniejsza), ale przynajmniej zapewnij odpowiednią metodę.
Mark Reed
1
Time#to_datetimewydaje się, że zachowuje tz dla mnie:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Phrogz
@Phrogz Offset UTC to nie to samo, co strefa czasowa. Jedna jest stała, druga może zmieniać się o różnych porach roku na czas letni. DateTime nie ma strefy, ignoruje czas letni. Czas to szanuje, ale tylko w „lokalnym” (środowisku systemowym) TZ.
Andrew Vit
1
Ulepszając rozwiązanie Gordona Wilsona, oto moja próba:
defto_time#Convert a fraction of a day to a number of microseconds
usec = (sec_fraction * 60 * 60 * 24 * (10**6)).to_i
t = Time.gm(year, month, day, hour, min, sec, usec)
t - offset.abs.div(SECONDS_IN_DAY)
end
Otrzymasz ten sam czas w UTC, tracąc strefę czasową (niestety)
Ponadto, jeśli masz Ruby 1.9, po prostu wypróbuj tę to_timemetodę
Dokonując takich konwersji należy wziąć pod uwagę zachowanie stref czasowych podczas konwersji z jednego obiektu do drugiego. Znalazłem kilka dobrych notatek i przykładów w tym poście dotyczącym stackoverflow .
Odpowiedzi:
Będziesz potrzebować dwóch nieco różnych konwersji.
Aby przekonwertować z
Time
naDateTime
, możesz zmienić klasę czasu w następujący sposób:require 'date' class Time def to_datetime # Convert seconds + microseconds into a fractional number of seconds seconds = sec + Rational(usec, 10**6) # Convert a UTC offset measured in minutes to one measured in a # fraction of a day. offset = Rational(utc_offset, 60 * 60 * 24) DateTime.new(year, month, day, hour, min, seconds, offset) end end
Podobne korekty do daty umożliwiają konwersję
DateTime
do formatuTime
.class Date def to_gm_time to_time(new_offset, :gm) end def to_local_time to_time(new_offset(DateTime.now.offset-offset), :local) end private def to_time(dest, method) #Convert a fraction of a day to a number of microseconds usec = (dest.sec_fraction * 60 * 60 * 24 * (10**6)).to_i Time.send(method, dest.year, dest.month, dest.day, dest.hour, dest.min, dest.sec, usec) end end
Pamiętaj, że musisz wybrać między czasem lokalnym a czasem GM / UTC.
Oba powyższe fragmenty kodu pochodzą z Ruby Cookbook O'Reilly . Na to zezwala ich polityka ponownego wykorzystywania kodu .
źródło
require 'time' require 'date' t = Time.now d = DateTime.now dd = DateTime.parse(t.to_s) tt = Time.parse(d.to_s)
źródło
Jako aktualizacja stanu ekosystemu Ruby
Date
,DateTime
aTime
teraz mamy metody do konwersji między różnymi klasami. Korzystanie z Ruby 1.9.2+:pry [1] pry(main)> ts = 'Jan 1, 2000 12:01:01' => "Jan 1, 2000 12:01:01" [2] pry(main)> require 'time' => true [3] pry(main)> require 'date' => true [4] pry(main)> ds = Date.parse(ts) => #<Date: 2000-01-01 (4903089/2,0,2299161)> [5] pry(main)> ds.to_date => #<Date: 2000-01-01 (4903089/2,0,2299161)> [6] pry(main)> ds.to_datetime => #<DateTime: 2000-01-01T00:00:00+00:00 (4903089/2,0,2299161)> [7] pry(main)> ds.to_time => 2000-01-01 00:00:00 -0700 [8] pry(main)> ds.to_time.class => Time [9] pry(main)> ds.to_datetime.class => DateTime [10] pry(main)> ts = Time.parse(ts) => 2000-01-01 12:01:01 -0700 [11] pry(main)> ts.class => Time [12] pry(main)> ts.to_date => #<Date: 2000-01-01 (4903089/2,0,2299161)> [13] pry(main)> ts.to_date.class => Date [14] pry(main)> ts.to_datetime => #<DateTime: 2000-01-01T12:01:01-07:00 (211813513261/86400,-7/24,2299161)> [15] pry(main)> ts.to_datetime.class => DateTime
źródło
1.9.3p327 :007 > ts = '2000-01-01 12:01:01 -0700' => "2000-01-01 12:01:01 -0700" 1.9.3p327 :009 > dt = ts.to_datetime => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :010 > dt.to_time => Sat, 01 Jan 2000 12:01:01 -0700 1.9.3p327 :011 > dt.to_time.class => DateTime
Niestety
DateTime.to_time, Time.to_datetime
iTime.parse
funkcje nie zachowują informacje strefy czasowej. Podczas konwersji wszystko jest konwertowane na lokalną strefę czasową. Arytmetyka dat nadal działa, ale nie będzie można wyświetlać dat w ich oryginalnych strefach czasowych. Ta informacja kontekstowa jest często ważna. Na przykład, jeśli chcę zobaczyć transakcje wykonane w godzinach pracy w Nowym Jorku, prawdopodobnie wolę, aby były wyświetlane w ich oryginalnych strefach czasowych, a nie w mojej lokalnej strefie czasowej w Australii (12 godzin przed Nowym Jorkiem).Poniższe metody konwersji zachowują te informacje tz.
W przypadku Ruby 1.8 spójrz na odpowiedź Gordona Wilsona . Pochodzi ze starej, dobrej, niezawodnej książki kucharskiej Ruby.
W przypadku Rubiego 1.9 jest to nieco łatwiejsze.
require 'date' # Create a date in some foreign time zone (middle of the Atlantic) d = DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24)) puts d # Convert DateTime to Time, keeping the original timezone t = Time.new(d.year, d.month, d.day, d.hour, d.min, d.sec, d.zone) puts t # Convert Time to DateTime, keeping the original timezone d = DateTime.new(t.year, t.month, t.day, t.hour, t.min, t.sec, Rational(t.gmt_offset / 3600, 24)) puts d
Spowoduje to wydrukowanie następującego
2010-01-01T10:00:00-02:00 2010-01-01 10:00:00 -0200 2010-01-01T10:00:00-02:00
Zachowywane są pełne oryginalne informacje dotyczące daty i godziny, w tym strefa czasowa.
źródło
Time#to_datetime
wydaje się, że zachowuje tz dla mnie:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
Ulepszając rozwiązanie Gordona Wilsona, oto moja próba:
def to_time #Convert a fraction of a day to a number of microseconds usec = (sec_fraction * 60 * 60 * 24 * (10**6)).to_i t = Time.gm(year, month, day, hour, min, sec, usec) t - offset.abs.div(SECONDS_IN_DAY) end
Otrzymasz ten sam czas w UTC, tracąc strefę czasową (niestety)
Ponadto, jeśli masz Ruby 1.9, po prostu wypróbuj tę
to_time
metodęźródło
Dokonując takich konwersji należy wziąć pod uwagę zachowanie stref czasowych podczas konwersji z jednego obiektu do drugiego. Znalazłem kilka dobrych notatek i przykładów w tym poście dotyczącym stackoverflow .
źródło