Converting Between Time and DateTime Objects


You're working with both DateTime and Time objects, created from Ruby's two standard date/time libraries. You can't mix these objects in comparisons, iterations, or date arithmetic because they're incompatible. You want to convert all the objects into one form or another so that you can treat them all the same way.


To convert a Time object to a DateTime, you'll need some code like this:

	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), month, day, hour, min, seconds, offset)

	time =, 6, 4, 10, 30, 22, 4010)
	# => Sun Jun 04 10:30:22 UTC 2000
	# => "2000-06-04T10:30:22Z"

Converting a DateTime to a Time is similar; you just need to decide whether you want the Time object to use local time or GMT. This code adds the conversion method to Date, the superclass of DateTime, so it will work on both Date and DateTime objects.

	class Date
	 def to_gm_time
	 to_time(new_offset, :gm)

	 def to_local_time
	 to_time(new_offset(, :local)

	 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.hour, dest.min,
	 dest.sec, usec)

	(datetime =, 10, 1, 22, 16, Rational(41,2))).to_s
	# => "1990-10-01T22:16:20Z"
	# => Mon Oct 01 22:16:20 UTC 1990
	# => Mon Oct 01 17:16:20 EDT 1990



Ruby's two ways of representing dates and times don't coexist very well. But since neither can be a total substitute for the other, you'll probably use them both during your Ruby career. The conversion methods let you get around incompatibilities by simply converting one type to the other:

	time < datetime
	# ArgumentError: comparison of Time with DateTime failed
	time.to_datetime < datetime
	# => false
	time < datetime.to_gm_time
	# => false

	time - datetime
	# TypeError: can't convert DateTime into Float
	(time.to_datetime - datetime).to_f
	# => 3533.50973962975 # Measured in days
	time - datetime.to_gm_time
	# => 305295241.50401 # Measured in seconds

The methods defined above are reversible: you can convert back and forth between Date and DateTime objects without losing accuracy.

	time # => Sun Jun 04 10:30:22 UTC 2000
	time.usec # => 4010'

	time.to_datetime.to_gm_time # => Sun Jun 04 10:30:22 UTC 2000
	time.to_datetime.to_gm_time.usec # => 4010

	datetime.to_s # => "1990-10-01T22:16:20Z"
	datetime.to_gm_time.to_datetime.to_s # => "1990-10-01T22:16:20Z"

Once you can convert between Time and DateTime objects, it's simple to write code that normalizes a mixed array, so that all its elements end up being of the same type. This method tries to turn a mixed array into an array containing only Time objects. If it encounters a date that won't fit within the constraints of the Time class, it starts over and converts the array into an array of DateTime objects instead (thus losing anyinformation about Daylight Saving Time):

	def normalize_time_types(array)
	 # Don't do anything if all the objects are already of the same type.
	 first_class = array[0].class
	 first_class = first_class.super if first_class == DateTime
	 return unless array.detect { |x| !x.is_a?(first_class) }

	 normalized = array.collect do |t|
	 if t.is_a?(Date)
	 rescue ArgumentError # Time out of range; convert to DateTimes instead.
	 convert_to = DateTime

	 unless normalized
	 normalized = array.collect { |t| t.is_a?( 
Time) ? t.to_datetime : t }
	 return normalized

When all objects in a mixed array can be represented as either Time or DateTime objects, this method makes them all Time objects:

	mixed_array = [,]
	# => [Sat Mar 18 22:17:10 EST 2006,
	# #]
	# => [Sat Mar 18 22:17:10 EST 2006, Sun Mar 19 03:17:10 EST 2006]

If one of the DateTime objects can't be represented as a Time, normalize_time_types turns all the objects into DateTime instances. This code is run on a system with a 32-bit time counter:

	mixed_array << DateTime.civil(1776, 7, 4)
	normalize_time_types(mixed_array).collect { |x| x.to_s }
	# => ["2006-03-18T22:17:10-0500", "2006-03-18T22:17:10-0500",
	# => "1776-07-04T00:00:00Z"]


See Also

  • Recipe 3.1, "Finding Today's Date"



Date and Time



Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming


Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration

Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: