This blog is part of our Ruby 2.4 series.

In Ruby, DateTime#to_time and Time#to_time methods can be used to return a Time object.

In Ruby 2.3, these methods convert time into system timezone offset instead of preserving timezone offset of the receiver.

Ruby 2.3

> datetime = DateTime.strptime('2017-05-16 10:15:30 +09:00', '%Y-%m-%d %H:%M:%S %Z')
 #=> #<DateTime: 2017-05-16T10:15:30+09:00 ((2457890j,4530s,0n),+32400s,2299161j)>
> datetime.to_time
 #=> 2017-05-16 06:45:30 +0530

> time = Time.new(2017, 5, 16, 10, 15, 30, '+09:00')
 #=> 2017-05-16 10:15:30 +0900
> time.to_time
 #=> 2017-05-16 06:45:30 +0530

As you can see, DateTime#to_time and Time#to_time methods return time in system timezone offset +0530.

Ruby 2.4 fixed DateTime#to_time and Time#to_time.

Now, DateTime#to_time and Time#to_time preserve receiver’s timezone offset info.

Ruby 2.4

> datetime = DateTime.strptime('2017-05-16 10:15:30 +09:00', '%Y-%m-%d %H:%M:%S %Z')
 #=> #<DateTime: 2017-05-16T10:15:30+09:00 ((2457890j,4530s,0n),+32400s,2299161j)>
> datetime.to_time
 #=> 2017-05-16 10:15:30 +0900

> time = Time.new(2017, 5, 16, 10, 15, 30, '+09:00')
 #=> 2017-05-16 10:15:30 +0900
> time.to_time
 #=> 2017-05-16 10:15:30 +0900

Since this is a breaking change for Rails application upgrading to ruby 2.4, Rails 4.2.8 built a compatibility layer by adding a config option. ActiveSupport.to_time_preserves_timezone was added to control how to_time handles timezone offsets.

Here is an example of how application behaves when to_time_preserves_timezone is set to false.

> ActiveSupport.to_time_preserves_timezone = false

> datetime = DateTime.strptime('2017-05-16 10:15:30 +09:00', '%Y-%m-%d %H:%M:%S %Z')
 #=> Tue, 16 May 2017 10:15:30 +0900
> datetime.to_time
 #=> 2017-05-16 06:45:30 +0530

> time = Time.new(2017, 5, 16, 10, 15, 30, '+09:00')
 #=> 2017-05-16 10:15:30 +0900
> time.to_time
 #=> 2017-05-16 06:45:30 +0530

Here is an example of how application behaves when to_time_preserves_timezone is set to true.

> ActiveSupport.to_time_preserves_timezone = true

> datetime = DateTime.strptime('2017-05-16 10:15:30 +09:00', '%Y-%m-%d %H:%M:%S %Z')
 #=> Tue, 16 May 2017 10:15:30 +0900
> datetime.to_time
 #=> 2017-05-16 10:15:30 +0900

> time = Time.new(2017, 5, 16, 10, 15, 30, '+09:00')
 #=> 2017-05-16 10:15:30 +0900
> time.to_time
 #=> 2017-05-16 10:15:30 +0900