Understanding Time, DateTime, and timezones in Rails

Ask questions Research chat →

https://michaelcho.me/article/understanding-time-datetime-and-timezones-in-rails · scraped

rails ruby

Attachments

Scraped Content

— 995 words · 2026-02-14 03:23:48 UTC ·

Excerpt

← All Articles Understanding Time, DateTime, and timezones in Rails I am sitting here in Singapore, where it is currently 1pm on 6th March (my laptop's system time is also set to this time).I'm working for a client with their Rails app setup in Palo Alto, where it is currently 9pm on 5th March.Their application is processing some data from London, where it is currently 5am on 6th March. So... how do we handle time (and time-like) variables in this app? This is a long article, so you can also skip straight to the summary. 3 timezones - system time, application time, selected time For this article, we are concerned with 3 different "times":LocationLabelCurrent TimeSingaporeMy time / System time1pm, 6th MarchLondonSelected time5am, 6th MarchPalo AltoApplication time9pm, 5th March Some notes to remember: System time is set in your OS. You can set this to be different to your physical location, but for today let's assume system time is the same as the time at my location.Application time is
← All Articles Understanding Time, DateTime, and timezones in Rails I am sitting here in Singapore, where it is currently 1pm on 6th March (my laptop's system time is also set to this time).I'm working for a client with their Rails app setup in Palo Alto, where it is currently 9pm on 5th March.Their application is processing some data from London, where it is currently 5am on 6th March. So... how do we handle time (and time-like) variables in this app? This is a long article, so you can also skip straight to the summary. 3 timezones - system time, application time, selected time For this article, we are concerned with 3 different "times":LocationLabelCurrent TimeSingaporeMy time / System time1pm, 6th MarchLondonSelected time5am, 6th MarchPalo AltoApplication time9pm, 5th March Some notes to remember: System time is set in your OS. You can set this to be different to your physical location, but for today let's assume system time is the same as the time at my location.Application time is the time in which your Rails application is running. This can be set either from a line in your application.rb like config.time_zone = "Pacific Time (US & Canada)" or manually setting via Time.zone = "Pacific Time (US & Canada)".Selected time is just the particular data point we are considering at the moment.  In this example, London time is in the GMT timezone which makes it easier since it is also UTC (+00:00 offset). However this is not always the case. Differences between GMT and UTC Although both of these are used interchangeably in casual language, they are technically different. GMT is a timezone used in several locations.A location can switch into / out of using GMT at different times in the year. For example, London is in BST (British Summer Time) with daylight savings for some part of the year, and GMT for the rest.UTC is not a timezone, it is a universal standard for keeping time based on atomic time. However, for most purposes we can treat these as interchangeable since the GMT timezone is on a +00:00 offset to UTC. ie there is no hourly offset.More background info on GMT vs UTC and different timezones. Manipulating Time and DateTime objects in Rails Here's what happens:CommandReturnsNotesTime.nowTime 2015-03-06 13:00:00 +0800System timezone (SG)Time.now.in_time_zone Time.currentActiveSupport::TimeWithZone Thu, 05 Mar 2015 21:00:00 PST -08:00Application timezone (CA)Time.now.in_time_zone(nil)Time 2015-03-06 13:00:00 +0800System timezone Careful! A nil timezone defaults to system time and returns a Time object. Time.now.in_time_zone and Time.now.in_time_zone(nil) are different!Time.now.in_time_zone("London")ActiveSupport::TimeWithZone Fri, 06 Mar 2015 05:00:00 GMT +00:00Selected timezone (London)Time.now.in_time_zone("UTC")ActiveSupport::TimeWithZone Fri, 06 Mar 2015 05:00:00 UTC +00:00Selected timezone (UTC)Time.now.utcTime 2015-03-06 05:00:00 UTCSelected timezone (UTC) Careful!Time.now.in_time_zone("UTC") and Time.now.utc are equivalent but different classes.DateTime.nowDateTime Fri, 06 Mar 2015 13:00:00 +0800System timezone (SG)DateTime.now.in_time_zoneActiveSupport::TimeWithZone Thu, 05 Mar 2015 21:00:00 PST -08:00Application timezone (CA)DateTime.now.in_time_zone("UTC")ActiveSupport::TimeWithZone Fri, 06 Mar 2015 05:00:00 UTC +00:00Selected timezone (UTC)DateTime.now.utcDateTime Fri, 06 Mar 2015 05:00:00 +0000Selected timezone (UTC) Careful!DateTime.now.in_time_zone("UTC") and DateTime.now.utc are equivalent but different classes.  Converting times to strings You can format any Time, DateTime, or ActiveSupport::TimeWithZone object into a string using a list of format characters.VariableCommandReturnsTimet = Time.now t.strftime("%Y-%m-%d %H:%M")"2015-03-06 13:00"(System time, 1pm in SG)ActiveSupport::TimeWithZonet = Time.now.in_time_zone("London") t.strftime("%Y-%m-%d %H:%M")"2015-03-06 05:00" (Selected time, 5am in London))DateTimet = DateTime.now t.strftime("%Y-%m-%d %H:%M")"2015-03-06 13:00"(System time, 1pm in SG)  Parsing times from strings (no timezone info) Assuming these variables: t = "2015-11-06T13:00:00" Note no timezone...format = "%Y-%m-%dT%H:%M:%S" CommandReturnsNotesTime.strptime(t, format) Time.parse(sample_time)Time 2015-03-06 13:00:00 +0800Has system timezone info, but not converted.Time.zone.parse(t)ActiveSupport::TimeWithZone Fri, 06 Mar 2015 13:00:00 PST -08:00Has application timezone info, but not converted.DateTime.strptime(t, format) DateTime.parse(sample_time)DateTime Fri, 06 Mar 2015 13:00:00 +0000Careful! The numbers show system time (ie 1pm in SG), but this DateTime object is actually in UTC time.t.in_time_zoneActiveSupport::TimeWithZone Fri, 06 Mar 2015 13:00:00 PST -08:00Careful! The numbers show system time (ie 1pm in SG), but this ActiveSupport::TimeWithZone object is actually in application time (ie CA).t.in_time_zone(nil)Time Fri, 06 Mar 2015 13:00:00 +0800Careful! The numbers show system time (ie 1pm in SG), but this Time object is actually in system time (ie SG).t.in_time_zone("London")ActiveSupport::TimeWithZone Fri, 06 Mar 2015 13:00:00 GMT +00:00Careful! The numbers show system time (ie 1pm in SG), but this ActiveSupport::TimeWithZone object is actually in selected time (ie London).  Parsing times from strings (with timezone info) Assuming these variables: t = "2015-11-06T13:00:00 +00:00" (ie London)format = "%Y-%m-%dT%H:%M:%S" CommandReturnsNotesTime.strptime(t, format) Time.parse(sample_time)Time 2015-11-06 13:00:00 +0800Has system timezone info, but not converted. Careful! If you don’t include timezone format in your format variable, the timezone does not get parsed.Time.zone.parse(t)ActiveSupport::TimeWithZone Fri, 06 Mar 2015 05:00:00 PST -08:00Has application timezone info, and also converted.DateTime.strptime(t, format)DateTime Fri, 06 Mar 2015 13:00:00 +0000Has selected timezone info, but not converted. Careful! If you don’t include timezone format in your format variable, the timezone does not get parsed.DateTime.parse(t)DateTime Fri, 06 Mar 2015 13:00:00 +0000Has selected timezone info, and also converted.t.in_time_zoneActiveSupport::TimeWithZone Fri, 06 Mar 2015 05:00:00 PST -08:00Has application timezone info, and also converted.  Other time-related helpers CommandReturnsNotes2.hours.agoActiveSupport::TimeWithZone Thu, 05 Mar 2015 19:00:00 PSTApplication timezoneDate.currentDate Thu, 05 Mar 2015Application timezoneDate.todayDate Fri, 06 Mar 2015System timezone There's also the use_zone method: Time.now # => System time, e.g. 2015-03-06 13:00:00 +0800 Time.zone.now # => Application time, e.g. Thu, 05 Mar 2015 21:00:00 PST -08:00 Time.use_zone("London") do p Time.now # => still System time, e.g. 2015-03-06 13:00:00 +0800 p Time.zone.now # => London time, e.g. Fri, 06 Mar 2015 05:00:00 GMT +00:00 end Summary After this long list of different commands, here's what I keep in mind: Time.now or Time.now.utc returns Time objects in system time and UTC respectively, e.g. 2015-11-06 05:28:28 UTCDateTime.now or DateTime.now.utc returns DateTime objects in system time and UTC respectively, e.g. 2015-11-06 05:28:28 UTCAny use of in_time_zone or zone returns ActiveSupport::TimeWithZone objects in application time, and will be the same whether it is called by a Time or DateTime object. e.g. Fri, 06 Nov 2015 05:25:03 GMT +00:00Time.current is equivalent to Time.now.in_time_zone in application time.in_time_zone and current use application timezone. Date.today and Time.now uses system timezone.Time.now.in_time_zone and Time.now.in_time_zone(nil) are different! A nil timezone defaults to system time and returns a Time object. Not passing a parameter gives application time and returns ActiveSupport::TimeWithZone object.Get list of timezones from ActiveSupport::TimeZone.zones_map

Visibility

Visible to everyone

Reading Status

Related Bookmarks

My Note


Saved!

Annotations

Export as Markdown
+ Annotate selection

Add Annotation