Fork me on GitHub

Timezone

Compact, timezone aware time and date library for JavaScript, for use in Node.js and the browser, timezone is a database friendly, timezone aware replacement for the Date object that implements date parsing, date formatting, and date math.

The Timezone library uses the Olson timezone database, to create a database of timezone rules, one per contenent, in a compact JSON representation, so you can, with some confidence, determine the correct local time of any place in the world, since 1970. It replaces the Date object with POSIX time, milliseconds since the epoch, for a cross-platform, durable representation of time.

Why You Need a Timezone Library

If you've worked with dates in JavaScript, you've already run up against the limitations of the JavaScript Date object. You probably already know that the JavaScript Date object offers no way to parse dates, or format dates, nor does it have any way to add or subtract time from dates.

You might not be aware of that Date has no concept of local time. Local time is the time out there in the real world, the time on clock on the wall, offset by a timezone, adjusted for daylight savings time. It is the time according to the local government.

Local time is important if your program runs in more than one timezone. If your program runs on the public web, then it runs in more than one timezone.

The JavaScript Date object has a usless dribble of support for local time. The duplicate date field functions, some named getXyz, other named getUTCXyz, might lead you to believe that it local time support is in effect, but it is not. If you're formatting your dates by concatenating the Date.getXyz methods string, you're making a mistake.

// The quick and easy and wrong way to format a JavaScript date.
function brokenFormatDate (date) {
  return '' + (date.getMonth() +  1) + '/' +
               date.getDate() + '/' +
               date.getFullYear();
  }
}

When you create a Date object it will offset the field values by the timezone offset of the computer at the time of the Date object's creation. You cannot reset that offset.

Even if the Date value came from a server in your control, this offset is in effect when you create the date. If you are providing dates as strings, you might build a local date with the following date parser.

function brokenParseDate (string) {
  var parts = /(\d{2})\/\d{2}\/\d{4}/.exec(string);
  for (var i = 1; i < 4; i++) {
    parts[i] = parseInt(parts[i], 10);
  }
  parts[1]++;
  return new Date(parts[3], parts[1], parts[2]);
}

The getUTCXyz methods of the Date object give you values of the different date components in UTC, while the getXyz methods give you the UTC value offset by an arbitrary number of minutes. Yes, arbitrary, because the preferred timezone of user of your web application should be dictated by your web application, not by the settings of an arbitrary client computer.

Local time is more than a single arbitrary offset. Local time is entirely political. It is defined by borders and the whims of legislatures within those borders. They adjust for daylight savings time. Consider the muddle that is time in Indiana, and you'll see that a timezone is in no way scientific or standardized.

If your appliction works with dates in the past or the future, you need to know when and if daylight savings time adjusts, if the local government has chosen to apply daylight savings time at all. You need to know if the local government has decided to move to a different timezone all together, and when this timezone change took place.

Getting the number of minutes in a timestamp as an integer value is not terribly useful to an application developer.

Date as Object

When JavaScript was born, objects were all the rage. They were going to fix everything, for everybody, forever. With your best comic book guy voice, read the follow: A date, you see, is a collection of attributes, including the number of minutes of the date, and the day in year in which the day occoured. Using getters and setters we are able to alter the date by adjusting its various properties. Also, there is a timezone offset, but timezones are nasty political constructs that have no place in the, as Spock would say, logical world of computing.

We're rarely interested in a date as a component, composed of component parts. The number of minutes in a date as an integer is not often useful for application programming. Applications are going to use dates the way people use dates in the real world. They are not a collection of attributes, but a point on a timeline, interesting only relative to other points on that timeline.

Objects were all the rage, it is a wonder that we didn't have a regular expression factory pattern, and build regular expressions using getters and setters, instead of parsing regular expression patterns. That is what the date object is like. When you display the date on a page, you don't display as an exploded diagram.

Today's date is:

The major benefit of POSIX time over the Date object is that it is sortable. You can order POSIX time quickly, since POSIX time is simply an integer value.

The benefit of Timezone over Date object is that is operates on a timeline. If what you really want to do is get the number of minutes in a particular timestamp as an integer, Timezone will do that. But, if what you'd rather do is know the number of hours between a given timestamp and lunch the following Friday, Timezone will do that too.

Hmm... Actually, do I want durations? Would that return object? Would it return an array?

For the most parts, applications use dates a timestamps, search for events within date ranges, or search for events before or after a certain date. We format dates for display, which takes timezone and locale into account. We parse date strings given by the user or by other systems.

Recording

What are you going to do? Display it? It would be nicer to have a date format specifier to display the whole date, rather than catenating a string with extremely verbose object method invocations to get each part, plus the pad method that needs to be rewritten every time.

What are you going to do? Increment it? If you want to move forward by minutes, you could add the minutes and then set the value again, but if the new value is greater than 60 then, you need to carry the minutes into the next hour.

To my mind, I could imagine building regular expressions in JavaScript using object composition. Wouldn't that be a nightmare? Dates are so common that they deserve their own language, like regular expressions. The Timezone library replaces the Date object in applications with a domain specific language, so that they feel more natural, like strings or regular expressions.

Dates feel unnatural because dates are represented as an object, as a box of parts, a set of cubbyholes with getters and setters for each component.

Like regular expressions, we turn on switches, so modifiers... Good old printf. We came back around to printf. As an example.

Make a slideshow and present. Use your silly examples there. Show a regular expression, then show it being bulit as an object using a factory pattern. Show an example of someone who is a slave to fashion, 80's fashion or some silly french fashion from the big crazy hair days.

You can get the integer value using a date format and the int modifier, which will be null if you screw up, because the format is a programmer supplied value.

Overview

Timezone is a timezone aware date library for JavaScript that

Timezone uses POSIX time, milliseconds since the epoch represented as a JavaScript Number. Timezone does not monkey patch the JavaScript Date object. Timezone replaces the JavaScript Date object with POSIX time.

Time Types

Timezone works with one of two types of date value,

Timezone uses POSIX time, milliseconds since the epoch in UTC, for a univeral representation of a point in time. If given a JavaScript Date object, Timezone will use the POSIX time value of the Date object.

eq(88927498237492734927, bicentenial = tz("1976-07-04"));
eq(98327943274923794329, tz(bicentenial, "+1 milli"));

You can also create a date from an array, if you've gathered fields from another means. Unlike the Date object, the first month of the year is 1, not 0.

eq(tz("7/4/76"), tz([ 1976, 7, 4 ]));
eq(tz("6/21/69 2:36"), tz([ 1969, 6, 21, 2, 36 ]));

If you need pass in a Date object, the value of Date.getTime() is used.

// Note that the Date object is problematic. If you create a date using the
// Date constructor, the timezone offset is applied at creation, so it is
// aribitrary and dependent on the timezone settings of the local machine. For
// our example here, we use Date.UTC to create a Date in a convoluted way.
var moonwalk = new Date(Date.UTC(1969, 6, 21, 2, 36));

// Now we have a Date object, let's use tz to format it.
eq("6/21/69 2:36", tz(moonwalk, "%-m/%-d/%Y %-H:%M"));

Working with POSIX Time

Timezone will return ether a POSIX time or a formatted date string if a format specifier is given as a parameter. It never generates JavaScript Date objects.

Timezone is timezone aware. It uses the same timezone names found in tzdata. The timezone support is created from the same text database usd to create the tzdata timezone files found on most UNIX systems.

Timezone uses local time for date math, but uses POSIX time for date comparisons. POSIX time is used for comparisons so we can compare points in time, and know that we're not comparing timestamps in different timezones.

While Timezone uses local time for date math, an application should use POSIX time for date comparisons. POSIX time is used for comparisons so we can compare points in time, and know that we're not comparing timestamps in different timezones.

When creating an application with Timezone, POSIX time is used for persistent storage and for date comparisons. We store our dates in POSIX time and convert them to local time when we format them for presentation.

We convert all our dates to POSIX time Times should be stored using POSIX time, since queries against a timestamp in a database is a comparison.

If you're using a database, and you want to use TIMESTAMP, then make sure that your database won't trip you up by adding the timezone offset of the server. MySQL is not timezone aware, so I set the timezone of my MySQL servers to UTC, because MySQL always adds the host machines current timezone offset, which is arbitrary information that is external to and not recorded anywhere within the database. That is, if you export your MySQL database, it doesn't record what timezone it was running in when the database was created.

Thus, if we want to allow the user to move appointments forward by a number of hours on their calendar, which would be presented in their local time, Timezone will add the hours and then adjust the time if we enter or leave daylight savings time.

Timezone can perform date math in both POSIX time and local time. When adding or subtracting by hours, minutes, seconds, and milliseconds, Timezone will adjust for daylight savings time.

An an example, let's say the you're writing a calendar application for a travel web site, so that frequent travelers can choose from fights that do not conflict with their scheduled appointments. To use the feature, the customers sync their calendar with the reservation system using any iCalendar compatable calendar. Now when they search for flights, a flight that interferes with an appointment is flagged, so the user can see what appointments need to be rescheduled to take that flight.

If you are a New Yorker who wants to check to see if you're going to miss any conference calls if you take a particular flight from Vilinus to San Francisco, you need check to see if the dates in your Eastern Time calendar fit between an Easter Eurpoean Time departure and a Pacific Time arrival.

Obviously, this query needs to adjust these different times to a common timezone, which is UTC for POSIX time. The application needs to display the appointment dates, and the arrival and departure date in their with respective time zones offsets.

These days should all be converted to a common timezone to check for conficts, then the various times should be presented according to the local time of the event.

It works with the strategy of using POSIX time as the persistent representation of a timestamp, and converting that universal time to wall clock time for presentation.

POSIX time is the notion of the passage of time common to all POSIX compient UNIX systems. It is milliseconds since the epoch in UTC. This represents a specific point in time.

The milliseconds since the epoch in UTC value is an absolute value that is not affected by the politics of timezones and daylight savings time. It is considered a stable representation of a point in time, that is suitable for storage in a database.

Strings are used to represent wall clock time. If a time needs to be presented to a user,

You can use the tz function to create a date format for display, one that cannot be in turn parsed by the tz function. That's fine.

Usage

Timezone exports a single funtion to keep from polluting the namespace of the client application. This single method accepts parameters similar to UNIX date.

The first parameter is always a date, either as milliseconds since the epoch as UTC or a date string to parse.

Other arguments can appear in any order.

You can pass these in any order after the initial date parameter, the tz function knows what you mean.

Only one timezone, locale or format can be applied to a date, so if a value is repeated for one of these parameter types, the first value is used, subsequent values are ignored.

The offsets parameter can be specified multiple times. The offsets are applied to the date in the order in which they are passed to the tz function.

The return value is always either milliseconds since the epoch as UTC or a date string if the tz function was passed a format specifier.

Rationale

While other languages extend the JavaScript date object to

The API is really a domain specific language. The parameters can be passed in any order because the different parameter types have an unambiguous meaning.

The Date object takes POSIX time and exposes the component values, which is somewhat useful, but not not often what you need. You don't really don't often need the number seconds in a timestamp as an integer value, you need to parse, format, offset and compare dates. The POSIX time representation is perfect for comparision. Formatting is easiest to express with a format pattern.

Even if it were timezone aware, the Date object is not a particularly useful representation of

Date Math

If we land on a time missing due to the start daylight savings time, we continue in the direction we were going, adding an additional day if we are doing addition, subtracting an additional day if we are doing subtraction. We then go back by 24 hours. This gives us the same counter intuitive times as Java's Calendar.

Just In Time Time

Always adjust your dates just in time. Store your dates in UTC. Convert them when they are displayed. Record events using UTC on the server, not the client. You cannot trust the client time, you do not know if the clock is set correctly, you can't keep the user from adjusting it, even you try to account for skew.

UTC timestamps will always indicate a particular second since the epoch.

If you are doing math in hours, minutes, seconds or milliseconds, this will reflect the UTC time.

If you are day math, you may land on a daylight savings time shift. If this is the case, then the last day is treated as 24 hours. Otherwise, if are on a day at 6:00 PM standard time, and go back six months to the same day, different month, in daylight savings time, the time will still be 6:00 PM. (Use real Detroit, Michigan examples.)

tz

The namespace.

tz.tz()

One function to rule them all and in the darkness bind them.

Why You Need a Timezone Library

Recording

Overview

Time Types

Working with POSIX Time

Usage

Rationale

Date Math

Just In Time Time

tz

tz

Docco

timezone.coffee