I need to convert a Date object into a TimeWithZone object representing the beginning of that day in a given time zone.
The following approach works, but seems too c
I strongly recommend against any solution that converts the date to a time using to_datetime
or to_time
because those methods are unaware of the zone, and tacking in_time_zone
onto the result, as some answers suggest, won't retroactively fix the mistake. Also, don't try to build your own daylight saving time math using UTC offsets. You're bound to get it wrong, and you're doing work unnecessarily.
Use the TimeZone itself which has this logic built in.
Given a zone and a date, you can get a TimeWithZone for the beginning of the day like this:
time = zone.local(date.year, date.month, date.day)
If you want a specific time of day other than the beginning, you can pass the hour, minute, and second as the 4th, 5th, and 6th arguments to #local
.
If zone
is actually your system's local time zone (Time.zone
), then ActiveSupport will let you shorten the above to this:
time = date.to_time_in_current_zone
All of the above handle daylight saving time correctly. Let's verify that by looking at the UTC offsets for two times, one that's outside DST and one that's within DST:
irb(main):009:0> zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
=> (GMT-05:00) Eastern Time (US & Canada)
irb(main):010:0> t1 = zone.local(2013, 1, 1)
=> Tue, 01 Jan 2013 00:00:00 EST -05:00
irb(main):011:0> t2 = zone.local(2013, 5, 1)
=> Wed, 01 May 2013 00:00:00 EDT -04:00
irb(main):012:0> t1.utc_offset
=> -18000
irb(main):013:0> t2.utc_offset
=> -14400