How do you convert between a DateTime and a Time object in Ruby?
-
1I'm not sure if this should be a seperate question, but how do you convert between a Date and a Time? – Andrew Grimm Jul 15 '10 at 04:31
-
10The accepted and highest-rated answers are no longer the most accurate under modern versions of Ruby. See the answers [by @theTinMan](http://stackoverflow.com/a/8511699/405017) and [by @PatrickMcKenzie](http://stackoverflow.com/a/280026/405017) below. – Phrogz Mar 07 '12 at 20:16
7 Answers
require 'time'
require 'date'
t = Time.now
d = DateTime.now
dd = DateTime.parse(t.to_s)
tt = Time.parse(d.to_s)

- 5,945
- 5
- 24
- 29
-
13+1 This may not be the most efficient in execution, but it works, it's concise, and it's very readable. – Walt Jones Jun 16 '09 at 22:20
-
6Unfortunately this only really works when dealing with local times. If you start with a DateTime or Time with a different timezone, the parse function will convert into local timezone. You basically lose the original timezone. – Bernard Aug 18 '10 at 14:20
-
6As of ruby 1.9.1, DateTime.parse does preserve timezone. (I do not have access to earlier versions.) Time.parse doesn't preserve timezone, because it represents the POSIX-standard time_t, which I believe is an integer difference from epoch. Any conversion to Time should have the same behaviour. – anshul Aug 19 '10 at 00:39
-
1You're right. DateTime.parse works in 1.9.1 but not Time.parse. In any case, it's less error prone (consistent) and likely faster to use DateTime.new(...) and Time.new(..). See my answer for sample code. – Bernard Aug 19 '10 at 01:08
-
@Alkaline, I am not sure I get what you mean. This method is consistent and error free. Do you have any good reasons for implying otherwise? – anshul Aug 20 '10 at 17:31
-
1Hi @anshul. I'm not implying I'm stating :-). Timezone info is not kept when using Time.parse(). It's easy to test. In your code above, simply replace d = DateTime.now with d = DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24)). tt will now show the date d converted into your local timezone. You can still do date arithmetics and all but the original tz info is lost. This info is a context for the date and it is often important. See here: http://stackoverflow.com/questions/279769/convert-to-from-datetime-and-time-in-ruby/3513247#3513247 – Bernard Aug 21 '10 at 02:28
-
@Alkaline: `Time` does not store time zone information and this is the intended behaviour. Thus, no method of converting a `DateTime` to `Time` will "preserve" timezone. – anshul Aug 23 '10 at 20:15
-
@asnhul. The Ruby 1.9 Time class does support timezone. I keep repeating the same thing but just check the answer I gave below. It has code that converts from DateTime to Time and back to DateTime while preserving the timezone info. The Ruby Time class is a wrapper for the C Standard Library time functions and those don't support timezone (it's stripped). That's true except that the timezone support is implemented in the ruby wrapper. Just check the time.rb file in the Ruby src distribution. Or, just run my sample code to convince yourself. – Bernard Aug 24 '10 at 15:00
-
-
I think it is much more preferable to use `t.to_datetime` than convert to string and thatn parse to datetime. – gorn Mar 25 '17 at 00:46
As an update to the state of the Ruby ecosystem, Date
, DateTime
and Time
now have methods to convert between the various classes. Using 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

- 158,662
- 42
- 215
- 303
-
1DateTime.to_time returns a 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` – Jesse Clark Feb 01 '13 at 21:22
-
Oops. Just realized that this is a Ruby on Rails issue not a Ruby issue: http://stackoverflow.com/questions/11277454/weird-ruby-behavior-in-datetime-to-time-conversion . They even had a bug filed against this method in the 2.x line and marked it "won't fix". Horrible decision IMHO. The Rails behavior totally breaks the underlying Ruby interface. – Jesse Clark Feb 01 '13 at 21:39
You'll need two slightly different conversions.
To convert from Time
to DateTime
you can amend the Time class as follows:
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
Similar adjustments to Date will let you convert DateTime
to 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
Note that you have to choose between local time and GM/UTC time.
Both the above code snippets are taken from O'Reilly's Ruby Cookbook. Their code reuse policy permits this.

- 19,053
- 14
- 65
- 60

- 26,244
- 11
- 57
- 60
-
5This will break on 1.9 where DateTime#sec_fraction returns the number of milliseconds in one second. For 1.9 you want to use: usec = dest.sec_fraction * 10**6 – dkubb Mar 14 '11 at 21:52
Unfortunately, the DateTime.to_time, Time.to_datetime
and Time.parse
functions don't retain the timezone info. Everything is converted to local timezone during conversion. Date arithmetics still work but you won't be able to display the dates with their original timezones. That context information is often important. For example, if I want to see transactions performed during business hours in New York I probably prefer to see them displayed in their original timezones, not my local timezone in Australia (which 12 hrs ahead of New York).
The conversion methods below do keep that tz info.
For Ruby 1.8, look at Gordon Wilson's answer. It's from the good old reliable Ruby Cookbook.
For Ruby 1.9, it's slightly easier.
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
This prints the following
2010-01-01T10:00:00-02:00
2010-01-01 10:00:00 -0200
2010-01-01T10:00:00-02:00
The full original DateTime info including timezone is kept.
-
2Time is complicated, but there's no excuse for not providing built-in conversion between different built-in time classes. You can throw a RangeException if you try to get a UNIX time_t for 4713 BC (though a BigNum negative value would be nicer), but at least provide a method for it. – Mark Reed Oct 25 '11 at 23:00
-
1`Time#to_datetime` appears to preserve tz for me: `Time.local(0).to_datetime.zone #=> "-07:00"; Time.gm(0).to_datetime.zone #=> "+00:00"` – Phrogz Mar 07 '12 at 20:07
-
@Phrogz UTC offset is not the same thing as a time zone. One is constant, the other can change at different times of year for daylight saving time. DateTime doesn't have a zone, it ignores DST. Time respects it, but only in the "local" (system environment) TZ. – Andrew Vit Feb 17 '13 at 04:19
-
Now this is resolved, you can just do `.to_time`: `DateTime.new(2010,01,01, 10,00,00, Rational(-2, 24)).to_time == Time.new(d.year, d.month, d.day, d.hour, d.min, d.sec, d.zone)` (`2010-01-01T10:00:00-02:00 / 2010-01-01 10:00:00 -0200`) – Adit Saxena Nov 29 '20 at 08:19
Improving on Gordon Wilson solution, here is my try:
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
You'll get the same time in UTC, loosing the timezone (unfortunately)
Also, if you have ruby 1.9, just try the to_time
method

- 3,887
- 4
- 36
- 44
You can use to_date
, e.g.
> Event.last.starts_at
=> Wed, 13 Jan 2021 16:49:36.292979000 CET +01:00
> Event.last.starts_at.to_date
=> Wed, 13 Jan 2021

- 7,749
- 4
- 38
- 57