/**
 * This script gives you the zone info key representing your device's time zone setting.
 *
 * @name jsTimezoneDetect
 * @version 1.0.5
 * @author Jon Nylander
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * For usage and examples, visit:
 * http://pellepim.bitbucket.org/jstz/
 *
 * Copyright (c) Jon Nylander
 */

/*jslint undef: true */
/*global console, exports*/

(function (root) {
    /**
     * Namespace to hold all the code for timezone detection.
     */
    var jstz = (function () {
        'use strict';
        var HEMISPHERE_SOUTH = 's',

            /**
             * Gets the offset in minutes from UTC for a certain date.
             * @param {Date} date
             * @returns {Number}
             */
            get_date_offset = function (date) {
                var offset = -date.getTimezoneOffset();
                return (offset !== null ? offset : 0);
            },

            get_date = function (year, month, date) {
                var d = new Date();
                if (year !== undefined) {
                    d.setFullYear(year);
                }
                d.setMonth(month);
                d.setDate(date);
                return d;
            },

            get_january_offset = function (year) {
                return get_date_offset(get_date(year, 0, 2));
            },

            get_june_offset = function (year) {
                return get_date_offset(get_date(year, 5, 2));
            },

            /**
             * Private method.
             * Checks whether a given date is in daylight saving time.
             * If the date supplied is after august, we assume that we're checking
             * for southern hemisphere DST.
             * @param {Date} date
             * @returns {Boolean}
             */
            date_is_dst = function (date) {
                var is_southern = date.getMonth() > 7,
                    base_offset = is_southern ? get_june_offset(date.getFullYear()) :
                        get_january_offset(date.getFullYear()),
                    date_offset = get_date_offset(date),
                    is_west = base_offset < 0,
                    dst_offset = base_offset - date_offset;

                if (!is_west && !is_southern) {
                    return dst_offset < 0;
                }

                return dst_offset !== 0;
            },

            /**
             * This function does some basic calculations to create information about
             * the user's timezone. It uses REFERENCE_YEAR as a solid year for which
             * the script has been tested rather than depend on the year set by the
             * client device.
             *
             * Returns a key that can be used to do lookups in jstz.olson.timezones.
             * eg: "720,1,2".
             *
             * @returns {String}
             */

            lookup_key = function () {
                var january_offset = get_january_offset(),
                    june_offset = get_june_offset(),
                    diff = january_offset - june_offset;

                if (diff < 0) {
                    return january_offset + ",1";
                } else if (diff > 0) {
                    return june_offset + ",1," + HEMISPHERE_SOUTH;
                }

                return january_offset + ",0";
            },

            /**
             * Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
             *
             * Returns a primitive object on the format:
             * {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
             *
             * @returns Object
             */
            determine = function () {
                var key = lookup_key();
                return new jstz.TimeZone(jstz.olson.timezones[key]);
            },

            /**
             * This object contains information on when daylight savings starts for
             * different timezones.
             *
             * The list is short for a reason. Often we do not have to be very specific
             * to single out the correct timezone. But when we do, this list comes in
             * handy.
             *
             * Each value is a date denoting when daylight savings starts for that timezone.
             */
            dst_start_for = function (tz_name) {

                var ru_pre_dst_change = new Date(2010, 6, 15, 1, 0, 0, 0), // In 2010 Russia had DST, this allows us to detect Russia :)
                    dst_starts = {
                        'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0),
                        'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0),
                        'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0),
                        'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0),
                        'America/Asuncion': new Date(2012, 9, 7, 3, 0, 0, 0),
                        'America/Santiago': new Date(2012, 9, 3, 3, 0, 0, 0),
                        'America/Campo_Grande': new Date(2012, 9, 21, 5, 0, 0, 0),
                        'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0),
                        'America/Sao_Paulo': new Date(2011, 9, 16, 5, 0, 0, 0),
                        'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0),
                        'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0),
                        'America/Havana': new Date(2012, 2, 10, 2, 0, 0, 0),
                        'America/New_York': new Date(2012, 2, 10, 7, 0, 0, 0),
                        'Europe/Helsinki': new Date(2013, 2, 31, 5, 0, 0, 0),
                        'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0),
                        'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0),
                        'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0),
                        'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0),
                        'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0),
                        'Europe/Moscow': ru_pre_dst_change,
                        'Asia/Amman': new Date(2013, 2, 29, 1, 0, 0, 0),
                        'Asia/Beirut': new Date(2013, 2, 31, 2, 0, 0, 0),
                        'Asia/Damascus': new Date(2013, 3, 6, 2, 0, 0, 0),
                        'Asia/Jerusalem': new Date(2013, 2, 29, 5, 0, 0, 0),
                        'Asia/Yekaterinburg': ru_pre_dst_change,
                        'Asia/Omsk': ru_pre_dst_change,
                        'Asia/Krasnoyarsk': ru_pre_dst_change,
                        'Asia/Irkutsk': ru_pre_dst_change,
                        'Asia/Yakutsk': ru_pre_dst_change,
                        'Asia/Vladivostok': ru_pre_dst_change,
                        'Asia/Baku': new Date(2013, 2, 31, 4, 0, 0),
                        'Asia/Yerevan': new Date(2013, 2, 31, 3, 0, 0),
                        'Asia/Kamchatka': ru_pre_dst_change,
                        'Asia/Gaza': new Date(2010, 2, 27, 4, 0, 0),
                        'Africa/Cairo': new Date(2010, 4, 1, 3, 0, 0),
                        'Europe/Minsk': ru_pre_dst_change,
                        'Pacific/Apia': new Date(2010, 10, 1, 1, 0, 0, 0),
                        'Pacific/Fiji': new Date(2010, 11, 1, 0, 0, 0),
                        'Australia/Perth': new Date(2008, 10, 1, 1, 0, 0, 0)
                    };

                return dst_starts[tz_name];
            };

        return {
            determine: determine,
            date_is_dst: date_is_dst,
            dst_start_for: dst_start_for
        };
    }());

    /**
     * Simple object to perform ambiguity check and to return name of time zone.
     */
    jstz.TimeZone = function (tz_name) {
        'use strict';
        /**
         * The keys in this object are timezones that we know may be ambiguous after
         * a preliminary scan through the olson_tz object.
         *
         * The array of timezones to compare must be in the order that daylight savings
         * starts for the regions.
         */
        var AMBIGUITIES = {
                'America/Denver': ['America/Denver', 'America/Mazatlan'],
                'America/Chicago': ['America/Chicago', 'America/Mexico_City'],
                'America/Santiago': ['America/Santiago', 'America/Asuncion', 'America/Campo_Grande'],
                'America/Montevideo': ['America/Montevideo', 'America/Sao_Paulo'],
                'Asia/Beirut': ['Asia/Amman', 'Asia/Jerusalem', 'Asia/Beirut', 'Europe/Helsinki', 'Asia/Damascus'],
                'Pacific/Auckland': ['Pacific/Auckland', 'Pacific/Fiji'],
                'America/Los_Angeles': ['America/Los_Angeles', 'America/Santa_Isabel'],
                'America/New_York': ['America/Havana', 'America/New_York'],
                'America/Halifax': ['America/Goose_Bay', 'America/Halifax'],
                'America/Godthab': ['America/Miquelon', 'America/Godthab'],
                'Asia/Dubai': ['Europe/Moscow'],
                'Asia/Dhaka': ['Asia/Yekaterinburg'],
                'Asia/Jakarta': ['Asia/Omsk'],
                'Asia/Shanghai': ['Asia/Krasnoyarsk', 'Australia/Perth'],
                'Asia/Tokyo': ['Asia/Irkutsk'],
                'Australia/Brisbane': ['Asia/Yakutsk'],
                'Pacific/Noumea': ['Asia/Vladivostok'],
                'Pacific/Tarawa': ['Asia/Kamchatka', 'Pacific/Fiji'],
                'Pacific/Tongatapu': ['Pacific/Apia'],
                'Asia/Baghdad': ['Europe/Minsk'],
                'Asia/Baku': ['Asia/Yerevan', 'Asia/Baku'],
                'Africa/Johannesburg': ['Asia/Gaza', 'Africa/Cairo']
            },

            timezone_name = tz_name,

            /**
             * Checks if a timezone has possible ambiguities. I.e timezones that are similar.
             *
             * For example, if the preliminary scan determines that we're in America/Denver.
             * We double check here that we're really there and not in America/Mazatlan.
             *
             * This is done by checking known dates for when daylight savings start for different
             * timezones during 2010 and 2011.
             */
            ambiguity_check = function () {
                var ambiguity_list = AMBIGUITIES[timezone_name],
                    length = ambiguity_list.length,
                    i = 0,
                    tz = ambiguity_list[0];

                for (; i < length; i += 1) {
                    tz = ambiguity_list[i];

                    if (jstz.date_is_dst(jstz.dst_start_for(tz))) {
                        timezone_name = tz;
                        return;
                    }
                }
            },

            /**
             * Checks if it is possible that the timezone is ambiguous.
             */
            is_ambiguous = function () {
                return typeof (AMBIGUITIES[timezone_name]) !== 'undefined';
            };

        if (is_ambiguous()) {
            ambiguity_check();
        }

        return {
            name: function () {
                return timezone_name;
            }
        };
    };

    jstz.olson = {};

    /*
     * The keys in this dictionary are comma separated as such:
     *
     * First the offset compared to UTC time in minutes.
     *
     * Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
     * does.
     *
     * Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
     * only interesting for timezones with DST.
     *
     * The mapped arrays is used for constructing the jstz.TimeZone object from within
     * jstz.determine_timezone();
     */
    jstz.olson.timezones = {
        '-720,0': 'Pacific/Majuro',
        '-660,0': 'Pacific/Pago_Pago',
        '-600,1': 'America/Adak',
        '-600,0': 'Pacific/Honolulu',
        '-570,0': 'Pacific/Marquesas',
        '-540,0': 'Pacific/Gambier',
        '-540,1': 'America/Anchorage',
        '-480,1': 'America/Los_Angeles',
        '-480,0': 'Pacific/Pitcairn',
        '-420,0': 'America/Phoenix',
        '-420,1': 'America/Denver',
        '-360,0': 'America/Guatemala',
        '-360,1': 'America/Chicago',
        '-360,1,s': 'Pacific/Easter',
        '-300,0': 'America/Bogota',
        '-300,1': 'America/New_York',
        '-270,0': 'America/Caracas',
        '-240,1': 'America/Halifax',
        '-240,0': 'America/Santo_Domingo',
        '-240,1,s': 'America/Santiago',
        '-210,1': 'America/St_Johns',
        '-180,1': 'America/Godthab',
        '-180,0': 'America/Argentina/Buenos_Aires',
        '-180,1,s': 'America/Montevideo',
        '-120,0': 'America/Noronha',
        '-120,1': 'America/Noronha',
        '-60,1': 'Atlantic/Azores',
        '-60,0': 'Atlantic/Cape_Verde',
        '0,0': 'UTC',
        '0,1': 'Europe/London',
        '60,1': 'Europe/Berlin',
        '60,0': 'Africa/Lagos',
        '60,1,s': 'Africa/Windhoek',
        '120,1': 'Asia/Beirut',
        '120,0': 'Africa/Johannesburg',
        '180,0': 'Asia/Baghdad',
        '180,1': 'Europe/Moscow',
        '210,1': 'Asia/Tehran',
        '240,0': 'Asia/Dubai',
        '240,1': 'Asia/Baku',
        '270,0': 'Asia/Kabul',
        '300,1': 'Asia/Yekaterinburg',
        '300,0': 'Asia/Karachi',
        '330,0': 'Asia/Kolkata',
        '345,0': 'Asia/Kathmandu',
        '360,0': 'Asia/Dhaka',
        '360,1': 'Asia/Omsk',
        '390,0': 'Asia/Rangoon',
        '420,1': 'Asia/Krasnoyarsk',
        '420,0': 'Asia/Jakarta',
        '480,0': 'Asia/Shanghai',
        '480,1': 'Asia/Irkutsk',
        '525,0': 'Australia/Eucla',
        '525,1,s': 'Australia/Eucla',
        '540,1': 'Asia/Yakutsk',
        '540,0': 'Asia/Tokyo',
        '570,0': 'Australia/Darwin',
        '570,1,s': 'Australia/Adelaide',
        '600,0': 'Australia/Brisbane',
        '600,1': 'Asia/Vladivostok',
        '600,1,s': 'Australia/Sydney',
        '630,1,s': 'Australia/Lord_Howe',
        '660,1': 'Asia/Kamchatka',
        '660,0': 'Pacific/Noumea',
        '690,0': 'Pacific/Norfolk',
        '720,1,s': 'Pacific/Auckland',
        '720,0': 'Pacific/Tarawa',
        '765,1,s': 'Pacific/Chatham',
        '780,0': 'Pacific/Tongatapu',
        '780,1,s': 'Pacific/Apia',
        '840,0': 'Pacific/Kiritimati'
    };

    if (typeof exports !== 'undefined') {
        exports.jstz = jstz;
    } else {
        root.jstz = jstz;
    }
})(this);
