Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to handle time zones with Javascript

I have an authenticated user with a given time zone, e.g. "Berlin, GMT+1". For the sake of this question let's say I have this in the global scope:

var timeZone = "Berlin";
var gmtDistance = 1;

What is the best solution to have all the date-related JS to behave accordingly, meaning that if I create a new Date object it will take the timezone into account.


I thought it'd be pretty straightforward, but I don't seem to find the perfect way to do this on Google/SO. I would privilege an answer that doesn't need any external library.

like image 390
marcgg Avatar asked Jun 04 '12 16:06

marcgg


People also ask

How does JavaScript handle different time zones?

The JavaScript getTimezoneOffset() method is used to find the timezone offset. It returns the timezone difference in minutes, between the UTC and the current local time. If the returned value is positive, local timezone is behind the UTC and if it is negative, the local timezone if ahead of UTC.

What timezone does JavaScript use?

JavaScript's internal representation uses the “universal” UTC time but by the time the date/time is displayed, it has probably been localized per the timezone settings on the user's computer.

How does JavaScript handle daylight time?

We can solve this problem in simple 5 steps. Obtain the current UTC time, by adding the local time zone offset to the local time. Obtain the destination city's UTC offset in hours, convert it to milliseconds and add it to UTC time. Convert final msec value into new Date string.


1 Answers

My preference is to store all dates on the server side using UTC time, and when I am handling data coming back via AJAX calls, to create a global handler which does some parsing.

The following example allows you to simply use:

app.ajax({
    url: '/My/Post/Url',
    data: {
        MyProperty: 'MyValue'
    },
    success: function (data, status, xhr) {
        // Do stuff here...
    },
    error: function (xhr, settings, error) {
        // Do stuff here...
    }
});

But, it pre-parses any returned values in the "success" function's "data" element by fixing dates for UTC time to the local timezone. Be aware - after doing this, if you further process the data, you will need to UN-fix it before sending back to the server, or you'll be posting it back up with the offset.

var app = window.app = $.extend(true, {}, app, {
    // Creating a namespace for my app which is safe across multiple files.
    ajax: function (options) {
        var defaultSettings = {
            type: 'POST',
            async: true
        };

        // Capture the settings.
        var settings = $.extend(true, {}, defaultSettings, options);

        // Install our general handlers;
        if (settings.success) {
            settings.success = function (data, textStatus, jqXHR) {
                app.OnPostSuccess(data, textStatus, jqXHR, options.success);
            }
        } else 
            settings.success = app.OnPostSuccess;

        if (settings.error) {
            settings.error = function (jqXHR, ajaxSettings, thrownError) {
                app.OnPostError(event, jqXHR, ajaxSettings, thrownError, options.error);
            }
        } else
            settings.error = app.OnPostError;

        $.ajax(settings);
    },
    OnPostSuccess: function (data, textStatus, jqXHR, fn_after) {
        // Do my generalized success handling here.

        // Fix Dates.
        var fixedData = app.FixDate(data);

        // Call any other handler that's been specified.
        if (typeof fn_after === 'function')
            fn_after(fixedData, textStatus, jqXHR);
    },
    OnPostError: function (jqXHR, ajaxSettings, thrownError, fn_after) {
        // Do my generalized error handling here.

        // Call any other handler that's been specified.
        if (typeof fn_after === 'function')
            fn_after(jqXHR, ajaxSettings, thrownError);
    },
    FixDate: function (obj) {
        var fixed = obj;

        if (typeof obj == 'string' && obj.indexOf('\/Date(') == 0) {
            // Microsoft date "/Date(12345678)/" - convert to real date.
            fixed = new Date(parseInt(fixed.substr(6, fixed.length - 8), 10));
        }

        if (typeof fixed === 'object') {
            if (fixed.getTimezoneOffset) {
                // If the value is a date, apply timezone correction.
                var now = new Date();
                var offset = now.getTimezoneOffset(); // # of minutes from GMT.
                fixed = new Date(fixed.getTime() + offset * 60000);
                // This updates the value based on the offset.
            } else {
                // Otherwise, update each of its properties.
                // This fixes objects with dates for properties, recursively.
                $.each(fixed, function (index, value) {
                    fixed[index] = app.FixDate(value);
                });
            }
        }

        return fixed;
    }
});

All of that set-up to get to this. If you now handle things like dates within OnPostSuccess, you can ensure that they are always in the right format - and always in the right timezone.

Whether or not you use the above AJAX methods, you can use the FixDate method as follows:

var MyObj = { 
    MyDate: "\/Date(12345678)\/"
};

console.log('Before: ', MyObj.MyDate);
MyObj = app.FixDate(MyObj);
console.log('After: ', MyObj.MyDate);

To see the example in action, check out the following jsFiddle:

http://jsfiddle.net/TroyAlford/TBNVV/

Note: this also includes the AJAX bits - but they aren't used in the example - only there for completeness.

like image 93
Troy Alford Avatar answered Nov 14 '22 21:11

Troy Alford