วิธีการแปลงเวลาประทับ unix (วินาทีนับตั้งแต่ยุค) ถึง Ruby DateTime?


คำตอบ:


354

DateTime.strptimeสามารถจัดการกับวินาทีตั้งแต่ยุค จำนวนต้องถูกแปลงเป็นสตริง:

require 'date'
DateTime.strptime("1318996912",'%s')

4
สิ่งนี้ไม่สามารถจัดการกับเสี้ยววินาทีได้
Dan Sandberg

45
มันจัดการกับ miliseconds ด้วย ' %Qtho
มินิจอห์น

3
หากต้องการติดตามคำตอบของ @ TheMiniJohn ดูเหมือนว่าเป็นสิ่งที่จำเป็นแทนTime DateTimeดังนั้นใช้Time.strptime("1318996912345",'%Q').to_fและคุณจะเห็นมิลลิวินาทีที่เก็บรักษาไว้ในขณะที่DateTime.strptime("1318996912345",'%Q').to_fไม่ได้รักษาไว้
skensell

625

ขออภัยช่วงเวลาสั้น ๆ ที่เกิดความล้มเหลวของการซิงค์ นี่คือคำตอบที่แท้จริง

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

ตัวอย่างสั้น ๆ (สิ่งนี้คำนึงถึงเขตเวลาของระบบปัจจุบัน):

$ date +%s
1318996912

$ irb

ruby-1.9.2-p180 :001 > require 'date'
 => true 

ruby-1.9.2-p180 :002 > Time.at(1318996912).to_datetime
 => #<DateTime: 2011-10-18T23:01:52-05:00 (13261609807/5400,-5/24,2299161)> 

การปรับปรุงเพิ่มเติม (สำหรับ UTC):

ruby-1.9.2-p180 :003 > Time.at(1318996912).utc.to_datetime
 => #<DateTime: 2011-10-19T04:01:52+00:00 (13261609807/5400,0/1,2299161)>

อัปเดตล่าสุด : ฉันเปรียบเทียบโซลูชันชั้นนำในชุดข้อความนี้ในขณะที่ทำงานกับบริการ HA เมื่อสองหรือสามสัปดาห์ที่ผ่านมาและรู้สึกประหลาดใจที่พบว่ามีTime.at(..)ประสิทธิภาพสูงกว่าDateTime.strptime(..)(อัปเดต: เพิ่มมาตรฐานเพิ่มเติม)

# ~ % ruby -v
#  => ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]

irb(main):038:0> Benchmark.measure do
irb(main):039:1*   ["1318996912", "1318496912"].each do |s|
irb(main):040:2*     DateTime.strptime(s, '%s')
irb(main):041:2>   end
irb(main):042:1> end

=> #<Benchmark ... @real=2.9e-05 ... @total=0.0>

irb(main):044:0> Benchmark.measure do
irb(main):045:1>   [1318996912, 1318496912].each do |i|
irb(main):046:2>     DateTime.strptime(i.to_s, '%s')
irb(main):047:2>   end
irb(main):048:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>

irb(main):050:0* Benchmark.measure do
irb(main):051:1*   ["1318996912", "1318496912"].each do |s|
irb(main):052:2*     Time.at(s.to_i).to_datetime
irb(main):053:2>   end
irb(main):054:1> end

=> #<Benchmark ... @real=1.5e-05 ... @total=0.0>

irb(main):056:0* Benchmark.measure do
irb(main):057:1*   [1318996912, 1318496912].each do |i|
irb(main):058:2*     Time.at(i).to_datetime
irb(main):059:2>   end
irb(main):060:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>

1
ขอบคุณ ... คำตอบต่อไปนี้ค่อนข้างสั้นกว่านี้ฉันพบ Time.at แต่พยายามหา DateTime ที่เทียบเท่ากัน
Tronathan

27
มันตลก แต่ Time.at () to_datetime ดูน่าพอใจกว่า DateTime.strptime () เพียงเพราะอ่านง่าย ... อย่างน้อยสำหรับฉันต่อไป
tybro0103

34
นี่ไม่เหมือนกับ anser ข้างต้น Time.at ถือว่าเป็นเขตเวลาปัจจุบันโดยที่ DateTime.strptime ใช้ UTC
Vitaly Babiy

5
มันไม่มากเกินไปน่าแปลกใจที่มีประสิทธิภาพเหนือกว่าTime.at DateTime.strptimeหลังต้องแยกสตริงซึ่งโดยทั่วไปจะช้ากว่าการรับจำนวนโดยตรง
กรงเล็บ

2
มาตรฐานของคุณไม่ได้ทดสอบอย่างแน่นอนDateTime.strptimeเพราะมันสร้างสายอักขระใหม่สองสายทุกการวนซ้ำซึ่งมีราคาแพงมาก มันไม่ได้เป็นเพียงการแยกสตริงเช่น @claw กล่าว
WattsInABox

67

การจัดการโซนเวลา

ฉันแค่ต้องการชี้แจงแม้ว่าสิ่งนี้ได้รับการแสดงความคิดเห็นเพื่อให้ผู้คนในอนาคตไม่พลาดความแตกต่างที่สำคัญมากนี้

DateTime.strptime("1318996912",'%s') # => Wed, 19 Oct 2011 04:01:52 +0000

แสดงค่าส่งคืนใน UTC และต้องใช้เวลาวินาทีในการเป็นสตริงและส่งออกวัตถุเวลา UTC ในขณะที่

Time.at(1318996912) # => 2011-10-19 00:01:52 -0400

แสดงค่าส่งคืนในเขตเวลา LOCAL โดยปกติต้องใช้อาร์กิวเมนต์ FixNum แต่วัตถุ Time เองยังคงอยู่ใน UTC แม้ว่าการแสดงจะไม่

ดังนั้นแม้ว่าฉันจะส่งจำนวนเต็มเท่ากันไปยังทั้งสองวิธี แต่ดูเหมือนว่าฉันจะได้ผลลัพธ์ที่แตกต่างกันสองอย่างเนื่องจากวิธี#to_sการทำงานของคลาส อย่างไรก็ตามเนื่องจาก @Eero ต้องเตือนฉันสองครั้งถึง:

Time.at(1318996912) == DateTime.strptime("1318996912",'%s') # => true

การเปรียบเทียบความเท่าเทียมกันระหว่างค่าที่ส่งคืนสองค่ายังคงเป็นจริง อีกครั้งนี้เป็นเพราะค่านั้นเป็นพื้นเดียวกัน (แม้ว่าชั้นเรียนที่แตกต่างกัน#==วิธีการดูแลเรื่องนี้สำหรับคุณ) แต่#to_sวิธีการพิมพ์สตริงที่แตกต่างกันอย่างมาก แม้ว่าถ้าเราดูที่สตริงเราสามารถเห็นพวกเขาเป็นจริงในเวลาเดียวกันเพียงพิมพ์ในเขตเวลาที่แตกต่างกัน

ชี้แจงวิธีการโต้แย้ง

เอกสารยังระบุด้วยว่า "หากมีการกำหนดอาร์กิวเมนต์ตัวเลขผลลัพธ์จะเป็นเวลาท้องถิ่น" ซึ่งสมเหตุสมผล แต่ทำให้ฉันสับสนเล็กน้อยเพราะพวกเขาไม่ได้ให้ตัวอย่างของการโต้แย้งที่ไม่ใช่จำนวนเต็มในเอกสาร ดังนั้นสำหรับตัวอย่างอาร์กิวเมนต์ที่ไม่ใช่จำนวนเต็ม:

Time.at("1318996912")
TypeError: can't convert String into an exact number

คุณไม่สามารถใช้อาร์กิวเมนต์ String ได้ แต่คุณสามารถใช้อาร์กิวเมนต์ Time ลงในTime.atนั้นและจะส่งคืนผลลัพธ์ในเขตเวลาของอาร์กิวเมนต์:

Time.at(Time.new(2007,11,1,15,25,0, "+09:00"))
=> 2007-11-01 15:25:00 +0900

มาตรฐาน

หลังจากการสนทนากับ @AdamEberlin กับคำตอบของเขาฉันตัดสินใจที่จะเผยแพร่มาตรฐานที่เปลี่ยนแปลงเล็กน้อยเพื่อให้ทุกอย่างเท่าที่เป็นไปได้ นอกจากนี้ฉันไม่เคยต้องการที่จะสร้างสิ่งเหล่านี้อีกครั้งดังนั้นที่นี่จึงเป็นสถานที่ที่ดีเช่นเดียวกับที่จะช่วยพวกเขา

Time.at (int) .to_datetime ~ 2.8x เร็วขึ้น

09:10:58-watsw018:~$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
09:11:00-watsw018:~$ irb
irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> require 'date'
=> true
irb(main):003:0>
irb(main):004:0* format = '%s'
=> "%s"
irb(main):005:0> times = ['1318996912', '1318496913']
=> ["1318996912", "1318496913"]
irb(main):006:0> int_times = times.map(&:to_i)
=> [1318996912, 1318496913]
irb(main):007:0>
irb(main):008:0* datetime_from_strptime = DateTime.strptime(times.first, format)
=> #<DateTime: 2011-10-19T04:01:52+00:00 ((2455854j,14512s,0n),+0s,2299161j)>
irb(main):009:0> datetime_from_time = Time.at(int_times.first).to_datetime
=> #<DateTime: 2011-10-19T00:01:52-04:00 ((2455854j,14512s,0n),-14400s,2299161j)>
irb(main):010:0>
irb(main):011:0* datetime_from_strptime === datetime_from_time
=> true
irb(main):012:0>
irb(main):013:0* Benchmark.measure do
irb(main):014:1*   100_000.times {
irb(main):015:2*     times.each do |i|
irb(main):016:3*       DateTime.strptime(i, format)
irb(main):017:3>     end
irb(main):018:2>   }
irb(main):019:1> end
=> #<Benchmark::Tms:0x00007fbdc18f0d28 @label="", @real=0.8680500000045868, @cstime=0.0, @cutime=0.0, @stime=0.009999999999999998, @utime=0.86, @total=0.87>
irb(main):020:0>
irb(main):021:0* Benchmark.measure do
irb(main):022:1*   100_000.times {
irb(main):023:2*     int_times.each do |i|
irb(main):024:3*       Time.at(i).to_datetime
irb(main):025:3>     end
irb(main):026:2>   }
irb(main):027:1> end
=> #<Benchmark::Tms:0x00007fbdc3108be0 @label="", @real=0.33059399999910966, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.32000000000000006, @total=0.32000000000000006>

**** แก้ไขให้ไม่สมบูรณ์และไม่ถูกต้องในทุกด้าน ****

**** เพิ่มมาตรฐาน ****


3
ดูเหมือนว่าเป็นไปได้และฉันได้ทำการอัปเดตแล้ว (ไม่สามารถยกเลิกได้ตอนนี้) แต่เมื่อตรวจสอบการอ้างสิทธิ์ของคุณเกี่ยวกับ UTC นั้นไม่เป็นความจริง วัตถุ DateTime / Time ที่ได้จะเป็น UTC กับ local ใช่ แต่ timestamp ดั้งเดิมถูกตีความว่าเป็น UTC ในทั้งสองกรณี! ดังนั้นช่วงเวลาที่เท่าเทียมกันโดยไม่คำนึงถึงวิธีการ ลองTime.at(1318996912) == DateTime.strptime("1318996912",'%s')ในเขตเวลาที่ไม่ใช่ UTC แล้วคุณจะเห็น!
Eero

4
ฉันขอโทษ แต่สิ่งที่คุณแก้ไขยังผิด! :-) เรียกใช้Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endเพื่อตรวจสอบว่าเวลาเท่ากันไม่มีการระบุเวลา LOCAL และในทั้งสองกรณี Unix timestamp ถูกตีความว่าอยู่ใน UTC Time.at นำเสนอวัตถุเวลาที่เกิดขึ้นในเขตเวลาท้องถิ่นและDateTime.strptime นำเสนอวัตถุ DateTime ที่เกิดขึ้นใน UTC แต่ไม่คำนึงถึงการนำเสนอที่เท่ากันเนื่องจากเป็นช่วงเวลาที่เท่ากัน
Eero

คำสั่งในขณะที่Time.at(1318996912) # => 2011-10-19 00:01:52 -0400แสดงค่าส่งคืนในเขตเวลา LOCAL ดูเหมือนจะไม่ถูกต้อง ... คุณช่วยยืนยันได้ไหม? ฉันเชื่อว่าข้อความของคุณจะเป็นจริงถ้าคุณใช้Time.zone.at(1318996912)
BigRon

ใช่ว่าดูเหมือนจะถูกต้อง เครื่องท้องถิ่นของฉันถูกตั้งค่าเป็น EST และเวลาจะแสดงใน EST
WattsInABox

คุณสามารถให้ตัวอย่างที่กรณีนี้ไม่ใช่ @BigRon ได้หรือไม่? โซนเวลารุ่นทับทิม ฯลฯ ไม่ทำงานในลักษณะนี้อย่างไร
WattsInABox

10

หนึ่งคำสั่งเพื่อแปลงเวลาวันที่ให้เป็นรูปแบบ Unix จากนั้นเป็นสตริง

    DateTime.strptime(Time.now.utc.to_i.to_s,'%s').strftime("%d %m %y")

    Time.now.utc.to_i #Converts time from Unix format
    DateTime.strptime(Time.now.utc.to_i.to_s,'%s') #Converts date and time from unix format to DateTime

ในที่สุด strftime จะใช้ในการจัดรูปแบบวันที่

ตัวอย่าง:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"

สิ่งหนึ่งที่ควรทราบก็คือรูปแบบยุคที่ไม่ได้มีเขตเวลาเพื่อผูกมัด UTC ก่อนที่จะผูกมัด to_i Time.now.utc.to_iไม่ได้อยู่ในความจำเป็น
เทรเวอร์ McCasland

1

สิ่งนี้จะบอกวันที่ของจำนวนวินาทีในอนาคตจากช่วงเวลาที่คุณเรียกใช้รหัส

time = Time.new + 1000000000 #date in 1 billion seconds

ทำให้ (เวลา)

ตามเวลาปัจจุบันฉันตอบคำถามที่พิมพ์047-05-14 05:16:16 +0000(1 พันล้านวินาทีในอนาคต)

หรือถ้าคุณต้องการนับพันล้านวินาทีจากเวลาใดเวลาหนึ่งมันอยู่ในรูปแบบ Time.mktime(year, month,date,hours,minutes)

time = Time.mktime(1987,8,18,6,45) + 1000000000

ทำให้ ("ฉันจะมีอายุ 1 พันล้านวินาทีเมื่อ:" + เวลา)


0

หากคุณต้องการเพียงแค่วันที่คุณสามารถทำDate.strptime(invoice.date.to_s, '%s')ที่invoice.dateมาในรูปแบบของแล้วแปลงเป็นFixnumString


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.