Skip to content

Instantly share code, notes, and snippets.

@etipton
Last active February 15, 2016 10:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save etipton/f4dd251ccdf7cd7be8a2 to your computer and use it in GitHub Desktop.
Save etipton/f4dd251ccdf7cd7be8a2 to your computer and use it in GitHub Desktop.
Rails TZs

So... dealing with dates and timezones is always tricky in ruby (and, well, in software in general) --

Ruby Date objects don't have a concept of timezones, but Rails adds the concept of TimeWithZone for dealing with Time objects.

It's important also to note that timestamps are stored in the db without a timezone, and it's up to the application layer to apply the timezone.

In RenterUp, we allow timezones for billing purposes to be set at the property level. Most apps have a per-user timezone. We don't have that yet (but we should eventually add it).

Anyway, for billing purposes, currently we set all due dates to a second before midnight (23:59:59) on the bill's due date. If you look at the overridden Bill#due_by method, you can see where we apply the property's timezone to the timestamp.

A couple of example of how this might play out:

Bill for Property 1:

  • Timezone: "Pacific Time (US & Canada)"
  • Next bill due by date: January 1, 2016
  • bills.due_by value in database: 2016-01-01 07:59:59
  • Ruby value: > bill.due_by => Thu, 01 Jan 2015 23:59:59 PST -08:00

Bill for Property 2:

  • Timezone: "Central Time (US & Canada)"
  • Next bill due by date: January 1, 2016
  • bills.due_by value in database: 2016-01-01 05:59:59
  • Ruby value: > bill.due_by => Thu, 01 Jan 2015 23:59:59 CST -06:00

... it can get tricky.

Anyway, for this particular line of code, I wanted to be sure that it's doing the equivalent of "Date.today" in the same timezone that the due_by date is in. However, since Date objects don't take into account timezones, I had to build a Time object, then use Time#to_date.

Here's some code showing how the old way was wrong for some edge cases, but works properly now (at the time I'm writing this, it's Dec 24th here but Dec 25th in Perth, Australia):

> Time.use_zone("Perth") { Date.today } => Thu, 24 Dec 2015

  • If Date respected timezones, this would actually be Friday, Dec 25. As it is, this is using my computer's time to get the date.
> Time.use_zone("Perth") { Time.current.to_date } => Fri, 25 Dec 2015
> Time.current.in_time_zone("Perth").to_date => Fri, 25 Dec 2015
  • These are equivalent
  • In Rails, Time objects can be made to respect timezones

One last thing to be aware of: Time.current is the same as Time.now.in_time_zone(Time.zone) and if you DON'T use in_time_zone (i.e. you just use Time.now, the TimeWithZone stuff won't be utilized and it will just use your computer's time).

Time.zone is a global value that can temporarily be overridden (within a block) via Time#use_zone() as shown above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment