Time is an illusion but TimeZones are not.

Recently I have been working on handling timezones on a client project. Front end is AngularJs 1.3 and backend api is Rails api.

It is a very common scenario. User chooses what timezone the data should be displayed in. Api saves and returns all the dates in UTC format.

There are also a lot many client side validations comparing saved time with local time (in all permutation and combinations possible).

Tools used:

  1. Javascript Date object:
    new Date();

    While working with timezones, it is always good to know that Javascript Date object is always in local time zone. There is no direct option to set the timezone. You can change time by adding/subtracting timezone offset.

  2. moment.js: moment is a javascript library which provides pretty options to format the moment date object which is not possible with javascript Date object.
  3. moment-timezone.js: This library allows you to convert moment time object in different timezones.
    moment.tz("2015-12-23 20:30:00", "US/Central")

 

My approach:

  • Keep all the times in local timezone, whatever that may be .. the browser figures that out and by default all the times are in local timezone.
  • All the comparison of different time objects should be done when all the objects are in same time zone and preferably in local time zone.
  • When submitting the form, change the time zone of the time objects from local timezone to desired timezone. Will explain in more detail later.
  • When doing a get request of the resource(i.e the edit page of lets say business profile), change all the time objects to time in local timezone.

Reasons for the approach:

There are 2 main things that I am doing. First I am changing all time objects to time objects in desired timezone. And second, I am converting all the time objects in local timezone.

These are 2 different things.

In first case, I am doing this:

make_double_digit = function(num) {
  if(num < 10) {
    return ("0" + String(num));
  }else{
    return String(num);
  }
}

var start_time = new Date();
var year = String(start_time.getFullYear());
var month = make_double_digit(start_time.getMonth() + 1);
var day = make_double_digit(start_time.getDate());
var time_hour = make_double_digit(start_time.getHours());
var time_min = make_double_digit(start_time.getMinutes());
var time_sec = make_double_digit(start_time.getSeconds());

var new_date_format = [year, month, day].join('-') + " " + [time_hour, time_min, time_seconds].join(':');

return moment.tz(new_date_format, zone_name);

For ex: let start_time be 2015-dec-21 8:30:00 IST. So I am extracting year, month etc  from this date object and asking moment to return moment object for this string “2015-12-21 08:30:00” in US/Central timezone. It is not converting start_time to US/Central timezone but rather building time object in US/Central timezone.

In second case, I am doing this:

var timezoned_start_date = moment(vm.info.start_date).tz('US/Central');

var start_date = new Date();
start_date.setYear(timezoned_start_date.format('YYYY'));
start_date.setMonth(timezoned_start_date.format('MM') - 1);
start_date.setDate(timezoned_start_date.format('DD'));
start_date.setHours(timezoned_start_date.format('hh'));
start_date.setMinutes(timezoned_start_date.format('mm'));
start_date.setSeconds(timezoned_start_date.format('ss'));

I am converting the time object obtained in response which is in UTC format to US/Central time zone. and then building a javascript Date object from it.

Now start_date object is in local time zone and we can use this to compare it with Date objects as needed.

Conclusion:

Dealing with timezones is not very straight forward. And this is not the only way to do it. A lot of people(read StackOverflow) suggested that I convert timezones by adding/subtracting timezone offsets. I found this to be more easy to manage in future and for other team mates to understand.

Advertisements

2 thoughts on “Time is an illusion but TimeZones are not.

  1. Sorry to rain on your parade, but there are several errors in the code you posted.

    In your first case, consider:

    var a = moment();
    var b = moment(a).tz(‘US/Central’);
    var c = moment(b).add(a.utcOffset() – b.utcOffset(), ‘minutes’);

    a is the current time, displayed in the local time zone
    b is the current time, displayed in the named US/Central time zone
    c is not the current time at all, but has the same wall-time value in the named US/Central time zone that matches the local wall-time value.

    Your code produces c, but in a very round-about way. (You don’t need the Date object at all. However, I think you are probably expecting b.

    In your second case, consider:

    var start_date = “2015-12-21 08:30:00”;
    var x = moment(start_date);
    var y = moment.tz(start_date, ‘US/Central’);
    var z = moment(start_date).tz(‘US/Central’).add(y.utcOffset() – x.utcOffset(), ‘minutes’)

    Then recognize that you can call .toDate() on x, y, or z to get a Date object.

    x.toDate() will represent the local time at 8:30.
    y.toDate() will represent 8:30 in US/Central, converted to the local time.
    z.toDate() will represent 8:30 in local time, converted to US/Central, then adjusted back to local time.

    Your code produces z, but again in a very round-about way. You don’t need the Date object at all, and again, I think you are expecting y. (Additionally, you are using setYear instead of setFullYear, but that’s besides the point.)

    Moment and moment-timezone are great libraries, but you have to use them carefully. Any time you see yourself reaching for the Date object, you’re probably doing it wrong unless you absolutely have to pass the results to an API that expects a Date object. And then, you really need to be careful because the Date object cannot be tricked into representing the wall-time of another time zone. Moment can do that, but only when you keep it in a moment and use it’s own parsing and formatting routines. As soon as you go back to Date objects, you lose that ability.

    Lastly, When using a library like moment, don’t try to break out values into separate string parts. There’s no good reason for that.

    Good luck, and I hope this was helpful.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s