Ruby'de DateTime ve Time'a / dan dönüştürme


132

Ruby'de bir DateTime ve bir Time nesnesi arasında nasıl dönüşüm gerçekleştirirsiniz?


1
Bunun ayrı bir soru olması gerekip gerekmediğinden emin değilim, ancak Tarih ve Saat arasında nasıl geçiş yaparsınız?
Andrew Grimm

8
Kabul edilen ve en yüksek puan alan yanıtlar, Ruby'nin modern sürümlerine göre artık en doğru yanıtlar değildir. Aşağıda @theTinMan ve @PatrickMcKenzie tarafından verilen yanıtlara bakın.
Phrogz

Yanıtlar:


50

Biraz farklı iki dönüşüme ihtiyacınız olacak.

Dan dönüştürmek için Time için DateTimeşöyle Zaman sınıfını düzeltebileceksiniz size:

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

Tarih benzer ayarlamalar dönüştürmek izin verir DateTime etmek Time .

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

Yerel saat ile GM / UTC saati arasında seçim yapmanız gerektiğini unutmayın.

Yukarıdaki kod parçalarının her ikisi de O'Reilly's Ruby Cookbook'tan alınmıştır . Kod yeniden kullanım politikası buna izin verir.


5
Bu 1.9'da sona erecek ve DateTime # sec_fraction bir saniyedeki milisaniye sayısını döndürür. 1.9 için kullanmak istediğiniz: usec = dest.sec_fraction * 10 ** 6
dkubb

185
require 'time'
require 'date'

t = Time.now
d = DateTime.now

dd = DateTime.parse(t.to_s)
tt = Time.parse(d.to_s)

13
+1 Bu, yürütmede en verimli olmayabilir, ancak işe yarıyor, özlü ve çok okunabilir.
Walt Jones

6
Ne yazık ki bu sadece yerel zamanlarla uğraşırken gerçekten işe yarıyor. Farklı bir saat dilimine sahip bir Tarih Saat veya Saat ile başlarsanız, ayrıştırma işlevi yerel saat dilimine dönüşür. Esasen orijinal saat dilimini kaybedersiniz.
Bernard

6
Ruby 1.9.1 itibariyle, DateTime.parse saat dilimini korur. (Daha önceki sürümlere erişimim yok.) Time.parse saat dilimini korumaz, çünkü epoch'tan tamsayı farkı olduğuna inandığım POSIX-standard time_t'yi temsil ediyor. Zamana yapılan herhangi bir dönüşüm aynı davranışa sahip olmalıdır.
anshul

1
Haklısın. DateTime.parse 1.9.1'de çalışır ancak Time.parse'da çalışmaz. Her durumda, hataya daha az meyillidir (tutarlıdır) ve DateTime.new (...) ve Time.new (..) 'i kullanmak muhtemelen daha hızlıdır. Örnek kod için cevabıma bakın.
Bernard

1
Merhaba @anshul. Ben ifade ettiğimi ima etmiyorum :-). Time.parse () kullanılırken saat dilimi bilgisi tutulmaz. Test etmesi kolay. Yukarıdaki kodunuzda, d = DateTime.now yerine d = DateTime.new (2010,01,01, 10,00,00, Rational (-2, 24)) yazın. tt şimdi yerel saat diliminize dönüştürülen d tarihini gösterecektir. Hala tarih aritmetiği yapabilirsiniz ve orijinal tz bilgisi dışındaki tüm bilgiler kaybolur. Bu bilgi, tarih için bir bağlamdır ve genellikle önemlidir. Buraya bakın: stackoverflow.com/questions/279769/…
Bernard

63

Yakut ekosistemin durumuna bir güncelleme olarak Date, DateTimeve Timeartık çeşitli sınıflar arasındaki dönüştürmek için yöntemler var. Ruby 1.9.2+ kullanımı:

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

1
DateTime.to_time bir DateTime döndürür ... 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
Jesse Clark

Hata. Bunun Ruby on Rails'in bir Ruby sorunu olmadığını anladım: stackoverflow.com/questions/11277454/… . Hatta 2.x satırında bu yönteme karşı bir hata bildirdiler ve bunu "düzeltilmeyecek" olarak işaretlediler. Korkunç karar IMHO. Rails davranışı, temeldeki Ruby arayüzünü tamamen bozar.
Jesse Clark

12

Maalesef DateTime.to_time, Time.to_datetimeve Time.parseişlevleri saat dilimi bilgilerini korumaz. Dönüştürme sırasında her şey yerel saat dilimine dönüştürülür. Tarih aritmetiği hala çalışıyor ancak tarihleri ​​orijinal saat dilimleriyle görüntüleyemeyeceksiniz. Bu bağlam bilgisi genellikle önemlidir. Örneğin, New York'ta iş saatlerinde gerçekleştirilen işlemleri görmek istiyorsam, muhtemelen bunların Avustralya'daki yerel saat dilimimde (New York'tan 12 saat ileride) değil, orijinal saat dilimlerinde görüntülenmesini tercih ederim.

Aşağıdaki dönüştürme yöntemleri bu tz bilgilerini saklar.

Ruby 1.8 için Gordon Wilson'ın cevabına bakın . Eski, güvenilir Ruby Yemek Kitabı'ndan.

Ruby 1.9 için biraz daha kolaydır.

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

Bu, aşağıdakileri yazdırır

2010-01-01T10:00:00-02:00
2010-01-01 10:00:00 -0200
2010-01-01T10:00:00-02:00

Saat dilimi dahil tam orijinal DateTime bilgisi saklanır.


2
Zaman karmaşıktır, ancak farklı yerleşik zaman sınıfları arasında yerleşik dönüşüm sağlamamanın bir mazereti yoktur. 4713 BC için bir UNIX time_t almaya çalışırsanız bir RangeException atabilirsiniz (ancak bir BigNum negatif değeri daha iyi olurdu), ancak en azından bunun için bir yöntem sağlayın.
Mark Reed

1
Time#to_datetimeTime.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
tz'yi

@Phrogz UTC uzaklığı saat dilimi ile aynı şey değildir. Biri sabittir, diğeri yaz saati uygulaması için yılın farklı zamanlarında değişebilir. DateTime'ın bir bölgesi yoktur, DST'yi yok sayar. Zaman buna saygı duyar, ancak yalnızca "yerel" (sistem ortamı) TZ'de.
Andrew Vit

1

Gordon Wilson çözümünü geliştirmek, işte benim denemem:

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

Saat dilimini kaybederek UTC'de aynı saati alacaksınız (maalesef)

Ayrıca, Ruby 1.9'a sahipseniz, to_timeyöntemi deneyin.


0

Bu tür dönüşümler yapılırken, bir nesneden diğerine dönüştürülürken saat dilimlerinin davranışı dikkate alınmalıdır. Bu stackoverflow gönderisinde bazı iyi notlar ve örnekler buldum .

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.