คุณแปลงระหว่างวัตถุ DateTime และ Time ใน Ruby ได้อย่างไร?
คุณแปลงระหว่างวัตถุ DateTime และ Time ใน Ruby ได้อย่างไร?
คำตอบ:
คุณจะต้องมีการแปลงสองรายการที่แตกต่างกันเล็กน้อย
ในการแปลงจาก Time
เป็น DateTime
คุณสามารถแก้ไขคลาสเวลาได้ดังนี้:
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
การปรับคล้ายกับวันที่จะช่วยให้คุณสามารถแปลงไป DateTime
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
โปรดทราบว่าคุณต้องเลือกระหว่างเวลาท้องถิ่นและเวลา GM / UTC
ทั้งโค้ดข้างต้นถูกนำมาจากโอเรลลีทับทิมตำรา นโยบายการใช้รหัสซ้ำของพวกเขาอนุญาตสิ่งนี้
require 'time'
require 'date'
t = Time.now
d = DateTime.now
dd = DateTime.parse(t.to_s)
tt = Time.parse(d.to_s)
ในฐานะที่เป็นอัปเดตสถานะของระบบนิเวศทับทิม, Date
, DateTime
และTime
ตอนนี้มีวิธีการที่จะแปลงระหว่างเรียนต่างๆ การใช้ 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
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
น่าเสียดายที่ฟังก์ชันDateTime.to_time, Time.to_datetime
และTime.parse
ฟังก์ชันไม่เก็บข้อมูลเขตเวลาไว้ ทุกอย่างจะถูกแปลงเป็นเขตเวลาท้องถิ่นในระหว่างการแปลง การคำนวณวันที่ยังคงใช้งานได้ แต่คุณจะไม่สามารถแสดงวันที่ด้วยเขตเวลาเดิมได้ ข้อมูลบริบทนั้นมักมีความสำคัญ ตัวอย่างเช่นหากฉันต้องการดูธุรกรรมที่ดำเนินการในช่วงเวลาทำการในนิวยอร์กฉันอาจต้องการเห็นการทำธุรกรรมดังกล่าวแสดงในเขตเวลาเดิมไม่ใช่เขตเวลาท้องถิ่นของฉันในออสเตรเลีย (ซึ่งก่อนนิวยอร์ก 12 ชั่วโมง)
วิธีการแปลงด้านล่างจะเก็บข้อมูล tz นั้นไว้
สำหรับ Ruby 1.8 ดูที่คำตอบกอร์ดอนวิลสัน มันมาจาก Ruby Cookbook เก่าแก่ที่น่าเชื่อถือ
สำหรับ Ruby 1.9 นั้นง่ายกว่าเล็กน้อย
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
สิ่งนี้จะพิมพ์สิ่งต่อไปนี้
2010-01-01T10:00:00-02:00
2010-01-01 10:00:00 -0200
2010-01-01T10:00:00-02:00
ข้อมูล DateTime ดั้งเดิมเต็มรูปแบบรวมถึงเขตเวลาจะถูกเก็บไว้
Time#to_datetime
ดูเหมือนจะรักษา tz สำหรับฉัน:Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"
การปรับปรุงโซลูชัน Gordon Wilson นี่คือความพยายามของฉัน:
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
คุณจะได้รับเวลาเดียวกันใน UTC โดยสูญเสียเขตเวลา (น่าเสียดาย)
นอกจากนี้หากคุณมีทับทิม 1.9 ให้ลองใช้to_time
วิธีนี้
ในขณะที่ทำการแปลงดังกล่าวเราควรคำนึงถึงพฤติกรรมของเขตเวลาในขณะที่แปลงจากวัตถุหนึ่งไปเป็นอีกวัตถุหนึ่ง ฉันพบบันทึกย่อและตัวอย่างที่ดีในโพสต์ stackoverflow นี้