James A. Rosen
James A. Rosen

Reputation: 65242

How can I serialize Ruby's TZInfo data?

I have a Rails application that, like all Rails applications, uses Ruby's TZInfo library for time-zone information. This library uses Olson-style information, but it the implementation doesn't actually parse Olson files. The definitions are in Ruby.

I want to make sure that my server and the clients are using the same time-zone data so users don't experience any surprises. Specifically, we patch the TZInfo data in Ruby much faster than new releases of the gem. Thus, I've considered and rejected the following:

  1. Using a JavaScript library that has the time-zone information built-in. The Ruby and JavaScript libraries' data will diverge.
  2. Exposing the contents of /usr/share/zoneinfo/* from my API. The Ruby and zoneinfo data will diverge.

That leaves me with two options:

  1. Rewrite or patch TZInfo to actually parse files from /usr/share/zoneinfo/*
  2. Come up with a way to serialize TZInfo's TimeZone object into JavaScript, JSON, YAML, or another useful format

It's not sufficient to simply tell the client the current time zone offset since the client needs to generate timestamps for historical (and future) dates.

Upvotes: 2

Views: 467

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241563

Use a library on both the server and the client that implement the same version of the IANA time zone database. The current version (as I am writing this) is 2013c, and can be found in it's original form here.

On the server side, use the TZInfo library for ruby. It has two gems, the tzinfo gem, and the tzinfo-data gem.

If you look at the tzinfo-data docs, you will see that there is a Version property that matches the IANA version. So tzinfo-data 2013.3 shows IANA version 2013c, which is also shown in the docs on this page.

You mentioned in comments that the data was hard-coded. This isn't exactly true. It's not hard-coded, it's code-generated. When you see the ruby files with the "hard-coded" time zone data, they were actually generated using the original IANA source files. There is a custom parser that performs this operation, so that every time a new version of the IANA time zone database is released, a corresponding update to tzinfo-data can be generated and published.

On the client side, you can use any of several different libraries. Most will do the exact same thing - starting with the same IANA sources, and code-generating a file that makes sense for the web. Usually, this is a JSON file.

Let's look at one of the libraries as an example - Walltime-js.

We can see on github that they have linked to the IANA/Olson tzdb sources on github. We can make sure we're using the exact same 2013c source data by checking out the precise version from git.

  • Look at the commit history for the tzdb.
  • Find that on April 19, 2013, there's a comment indicating release 2013c.
  • Verify that the release of 2013c at IANA was April 20, 2013.
  • So we know that the commit id for 2013c is f599ad15ce.
  • (Yes, this would be easier if they used git tags, but they don't for some reason).

Ultimately, we end up code-generating the walltime-data.js file, by following their build instructions, making one minor change to ensure we have the exact same 2013c source data. The new build looks like this:

git submodule init && git submodule update
git submodule foreach 'git checkout f599ad15ce'
cake data

Now we have a walltime-data.js file that is built from TZDB 2013c. This will go down to the client and be used by walltime.js.

We also have the tzinfo-data gem for 2013c that will sit on the server and be used by tzinfo in Ruby.

So the only data that needs to transmit between them is the id of the time zone, such as America/Los_Angeles. Each library will use their own copy of the data and their own implementation, but you can trust that they are referring to the same thing.

The only thing that could make them behave differently is if there was a bug in the way that they interpret the data, either in their parser or at runtime. Such bugs should be raised to the author's attention. But the only way you could possibly avoid them is to run the exact same code and data in both places - which would mean using Node.js on the server instead of Ruby.

Upvotes: 3

Related Questions