var moment = require('../../moment');

exports.zones = {
    setUp : function (done) {
        moment.locale('en');
        moment.createFromInputFallback = function () {
            throw new Error('input not handled by moment');
        };

        done();
    },

    tearDown : function (cb) {
        moment.locale('en');
        cb();
    },

    'set zone' : function (test) {
        var zone = moment();

        zone.zone(0);
        test.equal(zone.zone(), 0, 'should be able to set the zone to 0');

        zone.zone(60);
        test.equal(zone.zone(), 60, 'should be able to set the zone to 60');

        zone.zone(-60);
        test.equal(zone.zone(), -60, 'should be able to set the zone to -60');

        test.done();
    },

    'set zone shorthand' : function (test) {
        var zone = moment();

        zone.zone(1);
        test.equal(zone.zone(), 60, 'setting the zone to 1 should imply hours and convert to 60');

        zone.zone(-1);
        test.equal(zone.zone(), -60, 'setting the zone to -1 should imply hours and convert to -60');

        zone.zone(15);
        test.equal(zone.zone(), 900, 'setting the zone to 15 should imply hours and convert to 900');

        zone.zone(-15);
        test.equal(zone.zone(), -900, 'setting the zone to -15 should imply hours and convert to -900');

        zone.zone(16);
        test.equal(zone.zone(), 16, 'setting the zone to 16 should imply minutes');

        zone.zone(-16);
        test.equal(zone.zone(), -16, 'setting the zone to -16 should imply minutes');

        test.done();
    },

    'set zone with string' : function (test) {
        var zone = moment();

        zone.zone('+00:00');
        test.equal(zone.zone(), 0, 'set the zone with a timezone string');

        zone.zone('2013-03-07T07:00:00-08:00');
        test.equal(zone.zone(), 480, 'set the zone with a string that does not begin with the timezone');

        zone.zone('2013-03-07T07:00:00+0100');
        test.equal(zone.zone(), -60, 'set the zone with a string that uses the +0000 syntax');

        zone.zone('03-07-2013T07:00:00-08:00');
        test.equal(zone.zone(), 480, 'set the zone with a string with a non-ISO 8601 date');

        test.done();
    },

    'change hours when changing the zone' : function (test) {
        var zone = moment.utc([2000, 0, 1, 6]);

        zone.zone(0);
        test.equal(zone.hour(), 6, 'UTC 6AM should be 6AM at +0000');

        zone.zone(60);
        test.equal(zone.hour(), 5, 'UTC 6AM should be 5AM at -0100');

        zone.zone(-60);
        test.equal(zone.hour(), 7, 'UTC 6AM should be 7AM at +0100');

        test.done();
    },

    'change minutes when changing the zone' : function (test) {
        var zone = moment.utc([2000, 0, 1, 6, 31]);

        zone.zone(0);
        test.equal(zone.format('HH:mm'), '06:31', 'UTC 6:31AM should be 6:31AM at +0000');

        zone.zone(30);
        test.equal(zone.format('HH:mm'), '06:01', 'UTC 6:31AM should be 6:01AM at -0030');

        zone.zone(-30);
        test.equal(zone.format('HH:mm'), '07:01', 'UTC 6:31AM should be 7:01AM at +0030');

        zone.zone(1380);
        test.equal(zone.format('HH:mm'), '07:31', 'UTC 6:31AM should be 7:31AM at +1380');

        test.done();
    },

    'distance from the unix epoch' : function (test) {
        var zoneA = moment(),
            zoneB = moment(zoneA),
            zoneC = moment(zoneA),
            zoneD = moment(zoneA),
            zoneE = moment(zoneA);

        zoneB.utc();
        test.equal(+zoneA, +zoneB, 'moment should equal moment.utc');

        zoneC.zone(-60);
        test.equal(+zoneA, +zoneC, 'moment should equal moment.zone(-60)');

        zoneD.zone(480);
        test.equal(+zoneA, +zoneD, 'moment should equal moment.zone(480)');

        zoneE.zone(1000);
        test.equal(+zoneA, +zoneE, 'moment should equal moment.zone(1000)');

        test.done();
    },

    'update offset after changing any values' : function (test) {
        var oldOffset = moment.updateOffset,
            m = moment.utc([2000, 6, 1]);

        moment.updateOffset = function (mom, keepTime) {
            if (mom.__doChange) {
                if (+mom > 962409600000) {
                    mom.zone(120, keepTime);
                } else {
                    mom.zone(60, keepTime);
                }
            }
        };

        test.equal(m.format('ZZ'), '+0000', 'should be at +0000');
        test.equal(m.format('HH:mm'), '00:00', 'should start 12AM at +0000 timezone');

        m.__doChange = true;
        m.add(1, 'h');

        test.equal(m.format('ZZ'), '-0200', 'should be at -0200');
        test.equal(m.format('HH:mm'), '23:00', '1AM at +0000 should be 11PM at -0200 timezone');

        m.subtract(1, 'h');

        test.equal(m.format('ZZ'), '-0100', 'should be at -0100');
        test.equal(m.format('HH:mm'), '23:00', '12AM at +0000 should be 11PM at -0100 timezone');

        moment.updateOffset = oldOffset;

        test.done();
    },

    'getters and setters' : function (test) {
        var a = moment([2011, 5, 20]);

        test.equal(a.clone().zone(120).year(2012).year(), 2012, 'should get and set year correctly');
        test.equal(a.clone().zone(120).month(1).month(), 1, 'should get and set month correctly');
        test.equal(a.clone().zone(120).date(2).date(), 2, 'should get and set date correctly');
        test.equal(a.clone().zone(120).day(1).day(), 1, 'should get and set day correctly');
        test.equal(a.clone().zone(120).hour(1).hour(), 1, 'should get and set hour correctly');
        test.equal(a.clone().zone(120).minute(1).minute(), 1, 'should get and set minute correctly');

        test.done();
    },

    'getters' : function (test) {
        var a = moment.utc([2012, 0, 1, 0, 0, 0]);

        test.equal(a.clone().zone(120).year(),  2011, 'should get year correctly');
        test.equal(a.clone().zone(120).month(),   11, 'should get month correctly');
        test.equal(a.clone().zone(120).date(),    31, 'should get date correctly');
        test.equal(a.clone().zone(120).hour(),    22, 'should get hour correctly');
        test.equal(a.clone().zone(120).minute(),   0, 'should get minute correctly');

        test.equal(a.clone().zone(-120).year(),  2012, 'should get year correctly');
        test.equal(a.clone().zone(-120).month(),    0, 'should get month correctly');
        test.equal(a.clone().zone(-120).date(),     1, 'should get date correctly');
        test.equal(a.clone().zone(-120).hour(),     2, 'should get hour correctly');
        test.equal(a.clone().zone(-120).minute(),   0, 'should get minute correctly');

        test.equal(a.clone().zone(-90).year(),  2012, 'should get year correctly');
        test.equal(a.clone().zone(-90).month(),    0, 'should get month correctly');
        test.equal(a.clone().zone(-90).date(),     1, 'should get date correctly');
        test.equal(a.clone().zone(-90).hour(),     1, 'should get hour correctly');
        test.equal(a.clone().zone(-90).minute(),  30, 'should get minute correctly');

        test.done();
    },

    'from' : function (test) {
        var zoneA = moment(),
            zoneB = moment(zoneA).zone(720),
            zoneC = moment(zoneA).zone(360),
            zoneD = moment(zoneA).zone(-690),
            other = moment(zoneA).add(35, 'm');

        test.equal(zoneA.from(other), zoneB.from(other), 'moment#from should be the same in all zones');
        test.equal(zoneA.from(other), zoneC.from(other), 'moment#from should be the same in all zones');
        test.equal(zoneA.from(other), zoneD.from(other), 'moment#from should be the same in all zones');

        test.done();
    },

    'diff' : function (test) {
        var zoneA = moment(),
            zoneB = moment(zoneA).zone(720),
            zoneC = moment(zoneA).zone(360),
            zoneD = moment(zoneA).zone(-690),
            other = moment(zoneA).add(35, 'm');

        test.equal(zoneA.diff(other), zoneB.diff(other), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other), zoneC.diff(other), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other), zoneD.diff(other), 'moment#diff should be the same in all zones');

        test.equal(zoneA.diff(other, 'minute', true), zoneB.diff(other, 'minute', true), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other, 'minute', true), zoneC.diff(other, 'minute', true), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other, 'minute', true), zoneD.diff(other, 'minute', true), 'moment#diff should be the same in all zones');

        test.equal(zoneA.diff(other, 'hour', true), zoneB.diff(other, 'hour', true), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other, 'hour', true), zoneC.diff(other, 'hour', true), 'moment#diff should be the same in all zones');
        test.equal(zoneA.diff(other, 'hour', true), zoneD.diff(other, 'hour', true), 'moment#diff should be the same in all zones');

        test.done();
    },

    'unix offset and timestamp' : function (test) {
        var zoneA = moment(),
            zoneB = moment(zoneA).zone(720),
            zoneC = moment(zoneA).zone(360),
            zoneD = moment(zoneA).zone(-690);

        test.equal(zoneA.unix(), zoneB.unix(), 'moment#unix should be the same in all zones');
        test.equal(zoneA.unix(), zoneC.unix(), 'moment#unix should be the same in all zones');
        test.equal(zoneA.unix(), zoneD.unix(), 'moment#unix should be the same in all zones');

        test.equal(+zoneA, +zoneB, 'moment#valueOf should be the same in all zones');
        test.equal(+zoneA, +zoneC, 'moment#valueOf should be the same in all zones');
        test.equal(+zoneA, +zoneD, 'moment#valueOf should be the same in all zones');

        test.done();
    },

    'cloning' : function (test) {
        test.equal(moment().zone(120).clone().zone(),   120, 'explicit cloning should retain the zone');
        test.equal(moment().zone(-120).clone().zone(), -120, 'explicit cloning should retain the zone');
        test.equal(moment(moment().zone(120)).zone(),   120, 'implicit cloning should retain the zone');
        test.equal(moment(moment().zone(-120)).zone(), -120, 'implicit cloning should retain the zone');

        test.done();
    },

    'start of / end of' : function (test) {
        var a = moment.utc([2010, 1, 2, 0, 0, 0]).zone(450);

        test.equal(a.clone().startOf('day').hour(), 0, 'start of day should work on moments with a zone');
        test.equal(a.clone().startOf('day').minute(), 0, 'start of day should work on moments with a zone');
        test.equal(a.clone().startOf('hour').minute(), 0, 'start of hour should work on moments with a zone');

        test.equal(a.clone().endOf('day').hour(), 23, 'end of day should work on moments with a zone');
        test.equal(a.clone().endOf('day').minute(), 59, 'end of day should work on moments with a zone');
        test.equal(a.clone().endOf('hour').minute(), 59, 'end of hour should work on moments with a zone');

        test.done();
    },

    'reset zone with moment#utc' : function (test) {
        var a = moment.utc([2012]).zone(480);

        test.equal(a.clone().hour(),      16, 'different zone should have different hour');
        test.equal(a.clone().utc().hour(), 0, 'calling moment#utc should reset the offset');

        test.done();
    },

    'reset zone with moment#local' : function (test) {
        var a = moment([2012]).zone(480);

        test.equal(a.clone().local().hour(), 0, 'calling moment#local should reset the offset');

        test.done();
    },

    'toDate' : function (test) {
        var zoneA = new Date(),
            zoneB = moment(zoneA).zone(720).toDate(),
            zoneC = moment(zoneA).zone(360).toDate(),
            zoneD = moment(zoneA).zone(-690).toDate();

        test.equal(+zoneA, +zoneB, 'moment#toDate should output a date with the right unix timestamp');
        test.equal(+zoneA, +zoneC, 'moment#toDate should output a date with the right unix timestamp');
        test.equal(+zoneA, +zoneD, 'moment#toDate should output a date with the right unix timestamp');

        test.done();
    },

    'same / before / after' : function (test) {
        var zoneA = moment().utc(),
            zoneB = moment(zoneA).zone(120),
            zoneC = moment(zoneA).zone(-120);

        test.ok(zoneA.isSame(zoneB), 'two moments with different offsets should be the same');
        test.ok(zoneA.isSame(zoneC), 'two moments with different offsets should be the same');

        test.ok(zoneA.isSame(zoneB, 'hour'), 'two moments with different offsets should be the same hour');
        test.ok(zoneA.isSame(zoneC, 'hour'), 'two moments with different offsets should be the same hour');

        zoneA.add(1, 'hour');

        test.ok(zoneA.isAfter(zoneB), 'isAfter should work with two moments with different offsets');
        test.ok(zoneA.isAfter(zoneC), 'isAfter should work with two moments with different offsets');

        test.ok(zoneA.isAfter(zoneB, 'hour'), 'isAfter:hour should work with two moments with different offsets');
        test.ok(zoneA.isAfter(zoneC, 'hour'), 'isAfter:hour should work with two moments with different offsets');

        zoneA.subtract(2, 'hour');

        test.ok(zoneA.isBefore(zoneB), 'isBefore should work with two moments with different offsets');
        test.ok(zoneA.isBefore(zoneC), 'isBefore should work with two moments with different offsets');

        test.ok(zoneA.isBefore(zoneB, 'hour'), 'isBefore:hour should work with two moments with different offsets');
        test.ok(zoneA.isBefore(zoneC, 'hour'), 'isBefore:hour should work with two moments with different offsets');

        test.done();
    },

    'add / subtract over dst' : function (test) {
        var oldOffset = moment.updateOffset,
            m = moment.utc([2000, 2, 31, 3]);

        moment.updateOffset = function (mom, keepTime) {
            if (mom.clone().utc().month() > 2) {
                mom.zone(-60, keepTime);
            } else {
                mom.zone(0, keepTime);
            }
        };

        test.equal(m.hour(), 3, 'should start at 00:00');

        m.add(24, 'hour');

        test.equal(m.hour(), 4, 'adding 24 hours should disregard dst');

        m.subtract(24, 'hour');

        test.equal(m.hour(), 3, 'subtracting 24 hours should disregard dst');

        m.add(1, 'day');

        test.equal(m.hour(), 3, 'adding 1 day should have the same hour');

        m.subtract(1, 'day');

        test.equal(m.hour(), 3, 'subtracting 1 day should have the same hour');

        m.add(1, 'month');

        test.equal(m.hour(), 3, 'adding 1 month should have the same hour');

        m.subtract(1, 'month');

        test.equal(m.hour(), 3, 'subtracting 1 month should have the same hour');

        moment.updateOffset = oldOffset;

        test.done();
    },

    'isDST' : function (test) {
        var oldOffset = moment.updateOffset;

        moment.updateOffset = function (mom, keepTime) {
            if (mom.month() > 2 && mom.month() < 9) {
                mom.zone(-60, keepTime);
            } else {
                mom.zone(0, keepTime);
            }
        };

        test.ok(!moment().month(0).isDST(),  'Jan should not be summer dst');
        test.ok(moment().month(6).isDST(),   'Jul should be summer dst');
        test.ok(!moment().month(11).isDST(), 'Dec should not be summer dst');

        moment.updateOffset = function (mom) {
            if (mom.month() > 2 && mom.month() < 9) {
                mom.zone(0);
            } else {
                mom.zone(-60);
            }
        };

        test.ok(moment().month(0).isDST(),  'Jan should be winter dst');
        test.ok(!moment().month(6).isDST(), 'Jul should not be winter dst');
        test.ok(moment().month(11).isDST(), 'Dec should be winter dst');

        moment.updateOffset = oldOffset;

        test.done();
    },

    'zone names' : function (test) {
        test.expect(8);

        test.equal(moment().zoneAbbr(),   '', 'Local zone abbr should be empty');
        test.equal(moment().format('z'),  '', 'Local zone formatted abbr should be empty');
        test.equal(moment().zoneName(),   '', 'Local zone name should be empty');
        test.equal(moment().format('zz'), '', 'Local zone formatted name should be empty');

        test.equal(moment.utc().zoneAbbr(),   'UTC', 'UTC zone abbr should be UTC');
        test.equal(moment.utc().format('z'),  'UTC', 'UTC zone formatted abbr should be UTC');
        test.equal(moment.utc().zoneName(),   'Coordinated Universal Time', 'UTC zone abbr should be Coordinated Universal Time');
        test.equal(moment.utc().format('zz'), 'Coordinated Universal Time', 'UTC zone formatted abbr should be Coordinated Universal Time');

        test.done();
    },

    'hours alignment with UTC' : function (test) {
        test.expect(4);

        test.equals(moment().zone(120).hasAlignedHourOffset(), true);
        test.equals(moment().zone(-180).hasAlignedHourOffset(), true);
        test.equals(moment().zone(90).hasAlignedHourOffset(), false);
        test.equals(moment().zone(-90).hasAlignedHourOffset(), false);

        test.done();
    },

    'hours alignment with other zone' : function (test) {
        test.expect(16);

        var m = moment().zone(120);

        test.equals(m.hasAlignedHourOffset(moment().zone(180)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(-180)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(90)), false);
        test.equals(m.hasAlignedHourOffset(moment().zone(-90)), false);

        m = moment().zone(90);

        test.equals(m.hasAlignedHourOffset(moment().zone(180)), false);
        test.equals(m.hasAlignedHourOffset(moment().zone(-180)), false);
        test.equals(m.hasAlignedHourOffset(moment().zone(30)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(-30)), true);

        m = moment().zone(-60);

        test.equals(m.hasAlignedHourOffset(moment().zone(180)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(-180)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(90)), false);
        test.equals(m.hasAlignedHourOffset(moment().zone(-90)), false);

        m = moment().zone(25);

        test.equals(m.hasAlignedHourOffset(moment().zone(-35)), true);
        test.equals(m.hasAlignedHourOffset(moment().zone(85)), true);

        test.equals(m.hasAlignedHourOffset(moment().zone(35)), false);
        test.equals(m.hasAlignedHourOffset(moment().zone(-85)), false);

        test.done();
    },

    'parse zone' : function (test) {
        test.expect(2);
        var m = moment('2013-01-01T00:00:00-13:00').parseZone();
        test.equal(m.zone(), 13 * 60);
        test.equal(m.hours(), 0);
        test.done();
    },

    'parse zone static' : function (test) {
        test.expect(2);
        var m = moment.parseZone('2013-01-01T00:00:00-13:00');
        test.equal(m.zone(), 13 * 60);
        test.equal(m.hours(), 0);
        test.done();
    },

    'parse zone with more arguments' : function (test) {
        var m;
        test.expect(3);

        m = moment.parseZone('2013 01 01 05 -13:00', 'YYYY MM DD HH ZZ');
        test.equal(m.format(), '2013-01-01T05:00:00-13:00', 'accept input and format');
        m = moment.parseZone('2013-01-01-13:00', 'YYYY MM DD ZZ', true);
        test.equal(m.isValid(), false, 'accept input, format and strict flag');
        m = moment.parseZone('2013-01-01-13:00', ['DD MM YYYY ZZ', 'YYYY MM DD ZZ']);
        test.equal(m.format(), '2013-01-01T00:00:00-13:00', 'accept input and array of formats');

        test.done();
    },

    'parse zone with a timezone from the format string' : function (test) {
        test.expect(1);

        var m = moment('11-12-2013 -0400 +1100', 'DD-MM-YYYY ZZ #####').parseZone();

        test.equal(m.zone(), 4 * 60);
        test.done();
    },

    'parse zone without a timezone included in the format string' : function (test) {
        test.expect(1);

        var m = moment('11-12-2013 -0400 +1100', 'DD-MM-YYYY').parseZone();

        test.equal(m.zone(), -11 * 60);
        test.done();
    },

    'timezone format' : function (test) {
        test.equal(moment().zone(-60).format('ZZ'), '+0100', '-60 -> +0100');
        test.equal(moment().zone(-90).format('ZZ'), '+0130', '-90 -> +0130');
        test.equal(moment().zone(-120).format('ZZ'), '+0200', '-120 -> +0200');

        test.equal(moment().zone(+60).format('ZZ'), '-0100', '+60 -> -0100');
        test.equal(moment().zone(+90).format('ZZ'), '-0130', '+90 -> -0130');
        test.equal(moment().zone(+120).format('ZZ'), '-0200', '+120 -> -0200');
        test.done();
    }
};
