const assert = require('assert');
const dates = require('ringo/utils/dates');

// list of years taken from http://en.wikipedia.org/wiki/List_of_leap_years
exports.testIsLeapYear_DaysInFebruary_DaysInYear_DaysInMonth = function () {
    const leapYears = [
        1896, 1904, 1908, 1912, 1916, 1920,
        1924, 1928, 1932, 1936, 1940, 1944,
        1948, 1952, 1956, 1960, 1964, 1968,
        1972, 1976, 1980, 1984, 1988, 1992,
        1996, 2000, 2004, 2008, 2012, 2016,
        2020, 2024, 2028, 2032, 2036, 2040,
        2400, 2800
    ],
    noLeapYears = [
        1700, 1800, 1900, 2001, 2002, 2003,
        2005, 2006, 2007, 2100, 2200, 2300,
        2301, 2500, 2600, 2700, 2900, 3000,
        4003, 4005, 4007, 4009, 4011, 4317
    ];

    leapYears.forEach(function(year) {
        const d = new Date(year, 1, 1);
        assert.isTrue(dates.isLeapYear(d), "Leap Year " + year);
        assert.equal(dates.daysInYear(d), 366, "Leap Year " + year);
        assert.equal(dates.daysInFebruary(d), 29, "Leap Year " + year);
        assert.equal(dates.daysInMonth(new Date(year, 1, 1)), 29, "Leap Year " + year);
        assert.isTrue(dates.checkDate(year, 1, 29), "Leap Year " + year);
    });

    noLeapYears.forEach(function(year) {
        const d = new Date(year, 0, 1);
        assert.isFalse(dates.isLeapYear(d), "No Leap Year " + year);
        assert.equal(dates.daysInYear(d), 365, "No Leap Year " + year);
        assert.equal(dates.daysInFebruary(d), 28, "No Leap Year " + year);
        assert.equal(dates.daysInMonth(new Date(year, 1, 1)), 28, "No Leap Year " + year);
        assert.isFalse(dates.checkDate(year, 1, 29), "No Leap Year " + year);
    });
};

exports.testAdd = function () {
    // invalid units
    assert.throws(function() {
        dates.add(new Date(), 1, "yeard")
    });
    assert.throws(function() {
        dates.add(new Date(), 1, "")
    });


    let d = new Date(Date.UTC(2010, 10, 10, 10, 10, 10, 10)); // Wed Nov 10 2010 10:10:10 GMT+0100 (MEZ)

    assert.equal(d.getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));

    const addOne = {
        "millisecond":  Date.UTC(2010, 10, 10, 10, 10, 10, 11),
        "second":       Date.UTC(2010, 10, 10, 10, 10, 11, 10),
        "minute":       Date.UTC(2010, 10, 10, 10, 11, 10, 10),
        "hour":         Date.UTC(2010, 10, 10, 11, 10, 10, 10),
        "day":          Date.UTC(2010, 10, 11, 10, 10, 10, 10),
        "year":         Date.UTC(2011, 10, 10, 10, 10, 10, 10)
    };

    for (let tUnit in addOne) {
        assert.equal((dates.add(d, 1, tUnit)).getTime(), new Date(addOne[tUnit]).getTime(), tUnit);
    }

    // To avoid time zone and daylight saving time problems, month and quarter are tested with full circles
    assert.equal((dates.add(d, 12, "month")).getTime(), new Date(Date.UTC(2011, 10, 10, 10, 10, 10, 10)).getTime());
    assert.equal((dates.add(d, 12, "months")).getTime(), new Date(Date.UTC(2011, 10, 10, 10, 10, 10, 10)).getTime());
    assert.equal((dates.add(d, 4, "quarter")).getTime(), new Date(Date.UTC(2011, 10, 10, 10, 10, 10, 10)).getTime());
    assert.equal((dates.add(d, 4, "quarters")).getTime(), new Date(Date.UTC(2011, 10, 10, 10, 10, 10, 10)).getTime());

    // Add nothing
    assert.equal(dates.add(d, 0, 'millisecond').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'milliseconds').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'second').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'seconds').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'minute').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'minutes').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'hour').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'hours').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));

    assert.equal(dates.add(d, 0, 'day').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'days').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0).getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));

    assert.equal(dates.add(d, 0, 'week').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'weeks').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'month').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'months').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'quarter').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'quarters').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'year').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));
    assert.equal(dates.add(d, 0, 'years').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));

    // Remove 1
    let removeOne = {
        "millisecond":  Date.UTC(2010, 10, 10, 10, 10, 10, 9),
        "second":       Date.UTC(2010, 10, 10, 10, 10, 9, 10),
        "minute":       Date.UTC(2010, 10, 10, 10, 9, 10, 10),
        "hour":         Date.UTC(2010, 10, 10, 9, 10, 10, 10),
        "day":          Date.UTC(2010, 10, 9, 10, 10, 10, 10),
        "year":         Date.UTC(2009, 10, 10, 10, 10, 10, 10)
    };

    for (let tUnit in removeOne) {
        assert.equal((dates.add(d, -1, tUnit)).getTime(), new Date(removeOne[tUnit]).getTime(), tUnit);
    }

    // plural form
    removeOne = {
        "milliseconds":  Date.UTC(2010, 10, 10, 10, 10, 10, 9),
        "seconds":       Date.UTC(2010, 10, 10, 10, 10, 9, 10),
        "minutes":       Date.UTC(2010, 10, 10, 10, 9, 10, 10),
        "hours":         Date.UTC(2010, 10, 10, 9, 10, 10, 10),
        "days":          Date.UTC(2010, 10, 9, 10, 10, 10, 10),
        "years":         Date.UTC(2009, 10, 10, 10, 10, 10, 10)
    };

    for (let tUnit in removeOne) {
        assert.equal((dates.add(d, -1, tUnit)).getTime(), new Date(removeOne[tUnit]).getTime(), tUnit);
    }

    // Remove 13 hours
    let fullHourDate = new Date(Date.UTC(2010, 10, 10, 23, 10, 10, 10)); // Wed Nov 10 2010 23:10:10 GMT+0100 (MEZ)
    assert.equal(dates.add(fullHourDate, -13, 'hour').getTime(), Date.UTC(2010, 10, 10, 10, 10, 10, 10));

    // Add 13 hours
    fullHourDate = new Date(Date.UTC(2010, 10, 10, 0, 10, 10, 10)); // Wed Nov 10 2010 00:10:10 GMT+0100 (MEZ)
    assert.equal(dates.add(fullHourDate, 13, 'hour').getTime(), Date.UTC(2010, 10, 10, 13, 10, 10, 10));

    // Remove 48 hours
    assert.equal(dates.add(d, -48, 'hour').getTime(), Date.UTC(2010, 10, 8, 10, 10, 10, 10));

    // Add 48 hours
    assert.equal(dates.add(d, 48, 'hour').getTime(), Date.UTC(2010, 10, 12, 10, 10, 10, 10));

    // Add 61 hours
    assert.equal(dates.add(d, 61, 'hour').getTime(), Date.UTC(2010, 10, 12, 23, 10, 10, 10));

    // Remove 61 hours
    assert.equal(dates.add(d, -61, 'hour').getTime(), Date.UTC(2010, 10, 7, 21, 10, 10, 10));

    // To avoid time zone and daylight saving time problems, month and quarter are tested with full circles
    assert.equal((dates.add(d, -12, "month")).getTime(), new Date(Date.UTC(2009, 10, 10, 10, 10, 10, 10)).getTime());
    assert.equal((dates.add(d, -4, "quarter")).getTime(), new Date(Date.UTC(2009, 10, 10, 10, 10, 10, 10)).getTime());

    // Use time zone and daylight saving time "save" calculations for week
    d = new Date(Date.UTC(2010, 11, 31, 0, 0, 0, 0))
    assert.equal((dates.add(d, -1, "week")).getTime(), new Date(Date.UTC(2010, 11, 24, 0, 0, 0, 0)).getTime());

    d = new Date(Date.UTC(2010, 11, 24, 0, 0, 0, 0))
    assert.equal((dates.add(d, 1, "week")).getTime(), new Date(Date.UTC(2010, 11, 31, 0, 0, 0, 0)).getTime());
};

exports.testBefore_After_Compare = function () {
    let a = new Date(2010, 0, 2), b = new Date(2010, 0, 1);

    assert.isFalse(dates.before(a, b));
    assert.isTrue(dates.after(a, b));
    assert.equal(dates.compare(a, b), 1);

    a = new Date(2010, 0, 1);
    b = new Date(2010, 0, 2);
    assert.isTrue(dates.before(a, b));
    assert.isFalse(dates.after(a, b));
    assert.equal(dates.compare(a, b), -1);

    a = new Date(2010, 0, 1);
    b = new Date(2010, 0, 1);
    assert.isFalse(dates.before(a, b));
    assert.isFalse(dates.after(a, b));
    assert.equal(dates.compare(a, b), 0);

    a = new Date(2008, 1, 29);
    b = new Date(2008, 1, 29);
    assert.isFalse(dates.before(a, b));
    assert.isFalse(dates.after(a, b));
    assert.equal(dates.compare(a, b), 0);

    a = new Date(2000, 1, 29);
    b = new Date(2008, 1, 29);
    assert.isTrue(dates.before(a, b));
    assert.isFalse(dates.after(a, b));
    assert.equal(dates.compare(a, b), -1);
};

exports.testFirstDayOfWeek = function () {
    assert.equal(dates.firstDayOfWeek(java.util.Locale.GERMANY), 2);
    assert.equal(dates.firstDayOfWeek(java.util.Locale.US), 1);
};

exports.testQuarterInYear = function() {
    assert.equal(dates.quarterInYear(new Date(2010, 0, 1)), 1);
    assert.equal(dates.quarterInYear(new Date(2010, 3, 1)), 2);
    assert.equal(dates.quarterInYear(new Date(2010, 6, 1)), 3);
    assert.equal(dates.quarterInYear(new Date(2010, 9, 1)), 4);

    assert.equal(dates.quarterInYear(new Date(2010, 1, 1)), 1);
    assert.equal(dates.quarterInYear(new Date(2010, 4, 1)), 2);
    assert.equal(dates.quarterInYear(new Date(2010, 7, 1)), 3);
    assert.equal(dates.quarterInYear(new Date(2010, 10, 1)), 4);

    assert.equal(dates.quarterInYear(new Date(2010, 2, 1)), 1);
    assert.equal(dates.quarterInYear(new Date(2010, 5, 1)), 2);
    assert.equal(dates.quarterInYear(new Date(2010, 8, 1)), 3);
    assert.equal(dates.quarterInYear(new Date(2010, 11, 1)), 4);

    assert.equal(dates.quarterInYear(new Date(2010, 2, 31)), 1);
    assert.equal(dates.quarterInYear(new Date(2010, 5, 30)), 2);
    assert.equal(dates.quarterInYear(new Date(2010, 8, 30)), 3);
    assert.equal(dates.quarterInYear(new Date(2010, 11, 31)), 4);
}

exports.testQuarterInFiscalYear = function() {
    // UK tax year
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 0, 6), new Date(1990, 3, 6)), 4);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 3, 6), new Date(1990, 3, 6)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 6, 6), new Date(1990, 3, 6)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 9, 6), new Date(1990, 3, 6)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 3, 5, 23, 59, 59), new Date(1990, 3, 6)), 4);

    // Farmers (grassland calendar starts with 1st May)
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 3, 30), new Date(1990, 4, 1)), 4);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 4, 1),  new Date(1990, 4, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 6, 31), new Date(1990, 4, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 7, 1),  new Date(1990, 4, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 9, 31), new Date(1990, 4, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 10, 1), new Date(1990, 4, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2011, 0, 31), new Date(1990, 4, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2011, 1, 1),  new Date(1990, 4, 1)), 4);
    assert.equal(dates.quarterInFiscalYear(new Date(2011, 3, 30), new Date(1990, 4, 1)), 4);

    // With standard year starting on 01/01
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 0, 1), new Date(1970, 0, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 3, 1), new Date(1970, 0, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 6, 1), new Date(1970, 0, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 9, 1), new Date(1970, 0, 1)), 4);

    assert.equal(dates.quarterInFiscalYear(new Date(2010, 1, 1), new Date(1970, 0, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 4, 1), new Date(1970, 0, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 7, 1), new Date(1970, 0, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 10, 1), new Date(1970, 0, 1)), 4);

    assert.equal(dates.quarterInFiscalYear(new Date(2010, 2, 1), new Date(1970, 0, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 5, 1), new Date(1970, 0, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 8, 1), new Date(1970, 0, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 11, 1), new Date(1970, 0, 1)), 4);

    assert.equal(dates.quarterInFiscalYear(new Date(2010, 2, 31), new Date(1970, 0, 1)), 1);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 5, 30), new Date(1970, 0, 1)), 2);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 8, 30), new Date(1970, 0, 1)), 3);
    assert.equal(dates.quarterInFiscalYear(new Date(2010, 11, 31), new Date(1970, 0, 1)), 4);
};

exports.testDiff = function() {
    let a = new Date(2010, 0, 1),
    b = new Date(2010, 0, 2);

    assert.throws(function() {
        dates.diff(a, b, "yeard")
    });
    assert.throws(function() {
        dates.diff(a, b, "")
    });

    assert.equal(dates.diff(a, b, "year"), 0);
    assert.equal(dates.diff(a, b, "quarter"), 0);
    assert.equal(dates.diff(a, b, "month"), 0);
    assert.equal(dates.diff(a, b, "week"), 0);
    assert.equal(dates.diff(a, b), 1);
    assert.equal(dates.diff(a, b, "day"), 1);
    assert.equal(dates.diff(a, b, "hour"), 24);
    assert.equal(dates.diff(a, b, "minute"), 1440);
    assert.equal(dates.diff(a, b, "second"), 86400);
    assert.equal(dates.diff(a, b, "millisecond"), 86400000);
    assert.equal(dates.diff(a, b, "years"), 0);
    assert.equal(dates.diff(a, b, "quarters"), 0);
    assert.equal(dates.diff(a, b, "months"), 0);
    assert.equal(dates.diff(a, b, "weeks"), 0);
    assert.equal(dates.diff(a, b, "days"), 1);
    assert.equal(dates.diff(a, b, "hours"), 24);
    assert.equal(dates.diff(a, b, "minutes"), 1440);
    assert.equal(dates.diff(a, b, "seconds"), 86400);
    assert.equal(dates.diff(a, b, "milliseconds"), 86400000);
    assert.deepEqual(dates.diff(a, b, "mixed"), {
        "days": 1,
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "milliseconds": 0
    });

    // normal year
    b = new Date(2009, 0, 1);
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 4);
    assert.equal(dates.diff(a, b, "month"), 12);
    assert.equal(dates.diff(a, b, "week"), 52);
    assert.equal(dates.diff(a, b, "day"), 365);
    assert.equal(dates.diff(a, b, "hour"), 8760);
    assert.equal(dates.diff(a, b, "minute"), 525600);
    assert.equal(dates.diff(a, b, "second"), 31536000);
    assert.equal(dates.diff(a, b, "millisecond"), 31536000000);
    assert.deepEqual(dates.diff(a, b, "mixed"), {
        "days": 365,
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "milliseconds": 0
    });

    // leap year
    a = new Date(2012, 0, 1);
    b = new Date(2013, 0, 1);
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 4);
    assert.equal(dates.diff(a, b, "month"), 12);
    assert.equal(dates.diff(a, b, "week"), 52);
    assert.equal(dates.diff(a, b, "day"), 366);
    assert.equal(dates.diff(a, b, "hour"), 8784);
    assert.equal(dates.diff(a, b, "minute"), 527040);
    assert.equal(dates.diff(a, b, "second"), 31622400);
    assert.equal(dates.diff(a, b, "millisecond"), 31622400000);
    assert.deepEqual(dates.diff(a, b, "mixed"), {
        "days": 366,
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "milliseconds": 0
    });

    // one minute
    a = new Date(2012, 0, 1, 0, 1);
    b = new Date(2012, 0, 1, 0, 2);
    assert.equal(dates.diff(a, b, "year"), 0);
    assert.equal(dates.diff(a, b, "quarter"), 0);
    assert.equal(dates.diff(a, b, "month"), 0);
    assert.equal(dates.diff(a, b, "week"), 0);
    assert.equal(dates.diff(a, b, "day"), 0);
    assert.equal(dates.diff(a, b, "hour"), 0);
    assert.equal(dates.diff(a, b, "minute"), 1);
    assert.equal(dates.diff(a, b, "second"), 60);
    assert.equal(dates.diff(a, b, "millisecond"), 60000);
    assert.deepEqual(dates.diff(a, b, "mixed"), {
        "days": 0,
        "hours": 0,
        "minutes": 1,
        "seconds": 0,
        "milliseconds": 0
    });

    // one millisecond
    a = new Date(1234567890);
    b = new Date(1234567891);
    assert.equal(dates.diff(a, b, "year"), 0);
    assert.equal(dates.diff(a, b, "quarter"), 0);
    assert.equal(dates.diff(a, b, "month"), 0);
    assert.equal(dates.diff(a, b, "week"), 0);
    assert.equal(dates.diff(a, b, "day"), 0);
    assert.equal(dates.diff(a, b, "hour"), 0);
    assert.equal(dates.diff(a, b, "minute"), 0);
    assert.equal(dates.diff(a, b, "second"), 0);
    assert.equal(dates.diff(a, b, "millisecond"), 1);
    assert.equal(dates.diff(a, b, "years"), 0);
    assert.equal(dates.diff(a, b, "quarters"), 0);
    assert.equal(dates.diff(a, b, "months"), 0);
    assert.equal(dates.diff(a, b, "weeks"), 0);
    assert.equal(dates.diff(a, b, "days"), 0);
    assert.equal(dates.diff(a, b, "hours"), 0);
    assert.equal(dates.diff(a, b, "minutes"), 0);
    assert.equal(dates.diff(a, b, "seconds"), 0);
    assert.equal(dates.diff(a, b, "milliseconds"), 1);
    assert.deepEqual(dates.diff(a, b, "mixed"), {
        "days": 0,
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "milliseconds": 1
    });

    // 366 + 31 days = 397 days
    a = new Date(2012, 0, 1);
    b = new Date(2013, 1, 1);
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 4);
    assert.equal(dates.diff(a, b, "month"), 13);
    assert.equal(dates.diff(a, b, "week"), 56);
    assert.equal(dates.diff(a, b, "day"), 397);

    // no difference in quarters
    a = new Date(2013, 11, 1, 12, 0, 0); // Q4/2013
    b = new Date(2013, 9, 1, 12, 0, 0);  // Q4/2013
    assert.equal(dates.diff(a, b, "year"), 0);
    assert.equal(dates.diff(b, a, "year"), 0);
    assert.equal(dates.diff(a, b, "quarter"), 0);
    assert.equal(dates.diff(b, a, "quarter"), 0);

    // 3 quarter difference, same years
    a = new Date(2013, 2, 30); // Q1/2013
    b = new Date(2013, 9, 2);  // Q4/2013
    assert.equal(dates.diff(a, b, "year"), 0);
    assert.equal(dates.diff(b, a, "year"), 0);
    assert.equal(dates.diff(a, b, "quarter"), 3);
    assert.equal(dates.diff(b, a, "quarter"), 3);

    // 3 quarter difference, different years
    a = new Date(2013, 8, 30); // Q3/2013
    b = new Date(2014, 5, 30); // Q2/2013
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(b, a, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 3);
    assert.equal(dates.diff(b, a, "quarter"), 3);

    // 4 quarter difference, different years
    a = new Date(2012, 0, 1); // Q4/2012
    b = new Date(2013, 0, 1);  // Q1/2013
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(b, a, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 4);
    assert.equal(dates.diff(b, a, "quarter"), 4);

    // 1 quarter difference, different years
    a = new Date(2012, 11, 1); // Q4/2012
    b = new Date(2013, 0, 1);  // Q1/2013
    assert.equal(dates.diff(a, b, "year"), 1);
    assert.equal(dates.diff(b, a, "year"), 1);
    assert.equal(dates.diff(a, b, "quarter"), 1);
    assert.equal(dates.diff(b, a, "quarter"), 1);
};

exports.testOverlapping = function() {
    let aStart = new Date(2010, 0, 1),
    aEnd = new Date(2010, 0, 10),
    bStart = new Date(2010, 0, 2),
    bEnd = new Date(2010, 0, 3);

    // A  |-------|
    // B    |--|
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A    |-------|
    // B |----|
    bStart = new Date(2009, 0, 1);
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A    |-----|
    // B |-----------|
    bStart = new Date(2009, 0, 1);
    bEnd   = new Date(2010, 0, 11);
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A |-------|
    // B   |--------|
    bStart = new Date(2010, 0, 2);
    bEnd   = new Date(2010, 0, 11);
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A |----|
    // B      |----|
    bStart = new Date(2010, 0, 10);
    bEnd   = new Date(2010, 0, 13);
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A       |----|
    // B  |----|
    bStart = new Date(2009, 0, 1);
    bEnd   = new Date(2010, 0, 1);
    assert.isTrue(dates.overlapping(aStart, aEnd, bStart, bEnd));

    // A |----|
    // B        |----|
    bStart = new Date(2010, 0, 11);
    bEnd   = new Date(2010, 0, 13);
    assert.isFalse(dates.overlapping(aStart, aEnd, bStart, bEnd));
};

exports.testInPeriod = function() {
    const pStart = new Date(2010, 0, 10),
    pEnd = new Date(2010, 0, 20);

    //  Period   [--------]
    //  Date     ^
    assert.isTrue(dates.inPeriod(new Date(2010, 0, 10), pStart, pEnd, false, false));

    //  Period   (--------]
    //  Date     ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 10), pStart, pEnd, true, false));

    //  Period   [--------]
    //  Date              ^
    assert.isTrue(dates.inPeriod(new Date(2010, 0, 20), pStart, pEnd, false, false));

    //  Period   [--------)
    //  Date              ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 20), pStart, pEnd, false, true));

    //  Period   [--------]
    //  Date         ^
    assert.isTrue(dates.inPeriod(new Date(2010, 0, 15), pStart, pEnd, false, false));

    //  Period   (--------)
    //  Date         ^
    assert.isTrue(dates.inPeriod(new Date(2010, 0, 15), pStart, pEnd, true, true));

    //  Period   [--------]
    //  Date                 ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 22), pStart, pEnd, false, false));

    //  Period     [--------]
    //  Date    ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 5), pStart, pEnd, false, false));

    //  Period   (--------)
    //  Date                 ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 22), pStart, pEnd, true, true));

    //  Period     (--------)
    //  Date    ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 5), pStart, pEnd, true, true));

    //  Period   (--------]
    //  Date                 ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 22), pStart, pEnd, true, false));

    //  Period     [--------)
    //  Date    ^
    assert.isFalse(dates.inPeriod(new Date(2010, 0, 5), pStart, pEnd, false, true));
};

exports.testResetTime = function() {
    const d = new Date(2010, 0, 1, 20, 20, 20);
    assert.equal(dates.resetTime(d).getFullYear(), 2010);
    assert.equal(dates.resetTime(d).getMonth(), 0);
    assert.equal(dates.resetTime(d).getDate(), 1);
    assert.equal(dates.resetTime(d).getHours(), 0);
    assert.equal(dates.resetTime(d).getMinutes(), 0);
    assert.equal(dates.resetTime(d).getSeconds(), 0);
};

exports.testResetDate = function() {
    const d = new Date(2010, 0, 1, 20, 20, 20);
    assert.equal(dates.resetDate(d).getFullYear(), 1970);
    assert.equal(dates.resetDate(d).getMonth(), 0);
    assert.equal(dates.resetDate(d).getDate(), 1);
    assert.equal(dates.resetDate(d).getHours(), 20);
    assert.equal(dates.resetDate(d).getMinutes(), 20);
    assert.equal(dates.resetDate(d).getSeconds(), 20);
};

exports.testSecondsOfDay = function() {
    assert.equal(dates.secondOfDay(new Date(1970, 0, 1, 0, 0, 0)), 0);
    assert.equal(dates.secondOfDay(new Date(1970, 0, 1, 0, 1, 0)), 60);
    assert.equal(dates.secondOfDay(new Date(1970, 0, 1, 1, 0, 0)), 3600);
    assert.equal(dates.secondOfDay(new Date(1970, 0, 2, 0, 0, 0)), 0);
};

exports.testDayOfYear = function() {
    assert.equal(dates.dayOfYear(new Date(1970, 0, 1, 0, 0, 0)), 1);
    assert.equal(dates.dayOfYear(new Date(1970, 0, 1, 0, 1, 0)), 1);
    assert.equal(dates.dayOfYear(new Date(1970, 0, 1, 1, 0, 0)), 1);
    assert.equal(dates.dayOfYear(new Date(1970, 0, 2, 0, 0, 0)), 2);
    assert.equal(dates.dayOfYear(new Date(2010, 11, 31, 0, 0, 0)), 365);
    // leap year
    assert.equal(dates.dayOfYear(new Date(2000, 1, 29, 0, 0, 0)), 60);
    assert.equal(dates.dayOfYear(new Date(2000, 11, 31, 0, 0, 0)), 366);
};

exports.testWeekOfMonth = function() {
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 1), java.util.Locale.GERMANY), 0);
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 31), java.util.Locale.GERMANY), 5);
    assert.equal(dates.weekOfMonth(new Date(2011, 1, 1), java.util.Locale.GERMANY), 1);
    assert.equal(dates.weekOfMonth(new Date(2011, 1, 28), java.util.Locale.GERMANY), 5);
    assert.equal(dates.weekOfMonth(new Date(2010, 4, 31), java.util.Locale.GERMANY), 5);

    // Additional check for different locales
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 1), java.util.Locale.US), 1);
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 31), java.util.Locale.US), 6);
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 1), java.util.Locale.US), 1);
    assert.equal(dates.weekOfMonth(new Date(2011, 0, 31), java.util.Locale.US), 6);
};

exports.testWeekOfYear = function() {
    assert.equal(dates.weekOfYear(new Date(2010, 0, 1), java.util.Locale.GERMANY), 53);
    assert.equal(dates.weekOfYear(new Date(2010, 0, 4), java.util.Locale.GERMANY), 1);
    assert.equal(dates.weekOfYear(new Date(2010, 11, 31), java.util.Locale.GERMANY), 52);

    assert.equal(dates.weekOfYear(new Date(2011, 0, 1), java.util.Locale.GERMANY), 52);
    assert.equal(dates.weekOfYear(new Date(2011, 0, 3), java.util.Locale.GERMANY), 1);
    assert.equal(dates.weekOfYear(new Date(2011, 11, 31), java.util.Locale.GERMANY), 52);

    // Additional check for different locales
    assert.equal(dates.weekOfYear(new Date(2012, 0, 1), java.util.Locale.US), 1);
    assert.equal(dates.weekOfYear(new Date(2012, 0, 1), java.util.Locale.GERMANY), 52);
};

exports.testYearInCentury = function() {
    assert.equal(dates.yearInCentury(new Date(1000, 0, 1)), 0);
    assert.equal(dates.yearInCentury(new Date(1900, 0, 1)), 0);
    assert.equal(dates.yearInCentury(new Date(2000, 0, 1)), 0);
    assert.equal(dates.yearInCentury(new Date(2010, 0, 1)), 10);
    assert.equal(dates.yearInCentury(new Date(2022, 0, 1)), 22);
};

exports.testDaysInMonth = function() {
    assert.equal(dates.daysInMonth(new Date(2010, 0, 1)), 31); // Jan
    assert.equal(dates.daysInMonth(new Date(2010, 1, 1)), 28); // Feb
    assert.equal(dates.daysInMonth(new Date(2010, 2, 1)), 31); // Mar
    assert.equal(dates.daysInMonth(new Date(2010, 3, 1)), 30); // Apr
    assert.equal(dates.daysInMonth(new Date(2010, 4, 1)), 31); // May
    assert.equal(dates.daysInMonth(new Date(2010, 5, 1)), 30); // Jun
    assert.equal(dates.daysInMonth(new Date(2010, 6, 1)), 31); // Jul
    assert.equal(dates.daysInMonth(new Date(2010, 7, 1)), 31); // Aug
    assert.equal(dates.daysInMonth(new Date(2010, 8, 1)), 30); // Sep
    assert.equal(dates.daysInMonth(new Date(2010, 9, 1)), 31); // Oct
    assert.equal(dates.daysInMonth(new Date(2010, 10, 1)), 30); // Nov
    assert.equal(dates.daysInMonth(new Date(2010, 11, 1)), 31); // Dec

    // Leap Year
    assert.equal(dates.daysInMonth(new Date(2008, 1, 1)), 29); // Feb
};

exports.testFromUTCDate = function() {
    let d = dates.fromUTCDate(1970, 0, 1, 0, 0, 0);
    assert.equal(d.getUTCFullYear(), 1970);
    assert.equal(d.getUTCMonth(), 0);
    assert.equal(d.getUTCDate(), 1);
    assert.equal(d.getUTCHours(), 0);
    assert.equal(d.getUTCMinutes(), 0);
    assert.equal(d.getUTCSeconds(), 0);
    assert.equal(d.getTime(), 0);

    d = dates.fromUTCDate(1970, 0, 1, 0, 0, 0, 910);
    assert.equal(d.getUTCFullYear(), 1970);
    assert.equal(d.getUTCMonth(), 0);
    assert.equal(d.getUTCDate(), 1);
    assert.equal(d.getUTCHours(), 0);
    assert.equal(d.getUTCMinutes(), 0);
    assert.equal(d.getUTCSeconds(), 0);
    assert.equal(d.getTime(), 910);

    d = dates.fromUTCDate(1970, 0, 1, 0, 0, 10, 910);
    assert.equal(d.getUTCFullYear(), 1970);
    assert.equal(d.getUTCMonth(), 0);
    assert.equal(d.getUTCDate(), 1);
    assert.equal(d.getUTCHours(), 0);
    assert.equal(d.getUTCMinutes(), 0);
    assert.equal(d.getUTCSeconds(), 10);
    assert.equal(d.getTime(), 10910);

    d = dates.fromUTCDate(1970, 0, 1, 0, 11, 10, 910);
    assert.equal(d.getUTCFullYear(), 1970);
    assert.equal(d.getUTCMonth(), 0);
    assert.equal(d.getUTCDate(), 1);
    assert.equal(d.getUTCHours(), 0);
    assert.equal(d.getUTCMinutes(), 11);
    assert.equal(d.getUTCSeconds(), 10);
    assert.equal(d.getTime(), (11 * 60000) + 10910);

    d = dates.fromUTCDate(1970, 0, 1, 12, 11, 10, 910);
    assert.equal(d.getUTCFullYear(), 1970);
    assert.equal(d.getUTCMonth(), 0);
    assert.equal(d.getUTCDate(), 1);
    assert.equal(d.getUTCHours(), 12);
    assert.equal(d.getUTCMinutes(), 11);
    assert.equal(d.getUTCSeconds(), 10);
    assert.equal(d.getTime(), (12 * 60 * 60000) + (11 * 60000) + 10910);
};

exports.testCheckDate = function() {
    assert.isTrue(dates.checkDate(2008, 1, 28));
    assert.isTrue(dates.checkDate(2008, 1, 29));

    assert.isTrue(dates.checkDate(2010, 0, 31));
    assert.isTrue(dates.checkDate(2010, 1, 28));
    assert.isTrue(dates.checkDate(2010, 2, 31));
    assert.isTrue(dates.checkDate(2010, 3, 30));
    assert.isTrue(dates.checkDate(2010, 4, 31));
    assert.isTrue(dates.checkDate(2010, 5, 30));
    assert.isTrue(dates.checkDate(2010, 6, 31));
    assert.isTrue(dates.checkDate(2010, 7, 31));
    assert.isTrue(dates.checkDate(2010, 8, 30));
    assert.isTrue(dates.checkDate(2010, 9, 31));
    assert.isTrue(dates.checkDate(2010, 10, 30));
    assert.isTrue(dates.checkDate(2010, 11, 31));

    assert.isFalse(dates.checkDate(2010, 1, 29));
    assert.isFalse(dates.checkDate("a", "b", "c"));
    assert.isFalse(dates.checkDate(2010, "b", "c"));
    assert.isFalse(dates.checkDate("a", 2, "c"));
    assert.isFalse(dates.checkDate("a", "b", 2));
};

exports.testParse = function() {
    // Check for same time: http://www.w3.org/TR/NOTE-datetime
    assert.strictEqual(dates.parse("1994-11-05T08:15:30-05:00").getTime(), dates.parse("1994-11-05T13:15:30Z").getTime());

    assert.strictEqual((dates.parse("2009-02-13T23:31:30Z")).getTime(), 1234567890000); // GMT: Fri, 13 Feb 2009 23:31:30 GMT
    assert.strictEqual((dates.parse("2009-02-13T23:31:30+00:00")).getTime(), 1234567890000); // GMT: Fri, 13 Feb 2009 23:31:30 GMT
    assert.strictEqual((dates.parse("2010-01-01T00:00+01:00")).getTime(), 1262300400000);
    assert.strictEqual((dates.parse("2010-10-26T00:00+02:00")).getTime(), 1288044000000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00+02:00")).getTime(), 1288044000000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.0+02:00")).getTime(), 1288044000000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.00+02:00")).getTime(), 1288044000000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.000+02:00")).getTime(), 1288044000000);

    // UTC
    assert.strictEqual((dates.parse("2010-10-26T00:00Z")).getTime(), 1288051200000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00Z")).getTime(), 1288051200000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.0Z")).getTime(), 1288051200000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.00Z")).getTime(), 1288051200000);
    assert.strictEqual((dates.parse("2010-10-26T00:00:00.000Z")).getTime(), 1288051200000);

    // NaN and not instance of Date
    assert.isNaN(dates.parse("asdf"));
    assert.isFalse(dates.parse("asdf") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-"));
    assert.isNaN(dates.parse("2010-", "yyyy-MM-dd"));
    assert.isFalse(dates.parse("2010-") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-99"));
    assert.isNaN(dates.parse("2010-99", "yyyy-MM-dd"));
    assert.isFalse(dates.parse("2010-99") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-01-99"));
    assert.isNotNaN(dates.parse("2010-01-99", "yyyy-MM-dd"));
    assert.isNotNaN(dates.parse("2010-01-99", "yyyy-MM-dd", null, null, true));
    assert.isNaN(dates.parse("2010-01-99", "yyyy-MM-dd", null, null, false));
    assert.isFalse(dates.parse("2010-01-99") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-01-01T24:59Z"));
    assert.isFalse(dates.parse("2010-01-01T24:59Z") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-01-01T25:00Z"));
    assert.isFalse(dates.parse("2010-01-01T25:00Z") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-01-01TT25:00Z"));
    assert.isFalse(dates.parse("2010-01-01TT25:00Z") instanceof Date, "parse should return NaN and not an invalid Date");
    assert.isNaN(dates.parse("2010-01-01T23:00-25:00"));
    assert.isFalse(dates.parse("2010-01-01T23:00-25:00") instanceof Date, "parse should return NaN and not an invalid Date");

    // java date format tests
    assert.strictEqual(dates.parse("2016-06-29T12:11:10.001", "yyyy-MM-dd'T'HH:mm:ss.SSS").getTime(), (new Date(2016,5,29,12,11,10,1)).getTime());
    assert.strictEqual(dates.parse("2016-06-29T12:11:10.001", "yyyy-MM-dd'T'HH:mm:ss.SSS", "en", "UTC").getTime(), 1467202270001);
    assert.strictEqual(dates.parse("2016-06-29T12:11:10.001-01:00", "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", "en", "UTC").getTime(), 1467205870001);
    assert.strictEqual(dates.parse("29. Juni 2016", "dd. MMM yyyy", "de", "UTC").getTime(), 1467158400000);

    // this might be a JDK bug?! Time zone given, but not in pattern --> should return NaN, but doesn't
    assert.isNotNaN(dates.parse("2016-06-30T12:11:10.001-24:00", "yyyy-MM-dd'T'HH:mm:ss.SSS", "en", "UTC", false));

    // Check for not NaN
    // FIXME no exact checks because of local time...
    assert.isNotNaN(dates.parse("2010-01-01T01:01").getTime());
    assert.isNotNaN(dates.parse("2010-01-01T01:01:01").getTime());
    assert.isNotNaN(dates.parse("2010-01-01T01:01:01.001").getTime());

    // cases map datestrings to objects with corresponding UTC date properties
    const cases = {
        "2000": {
            year: 2000,
            month: 0,
            date: 1
        },
        "2005-10": {
            year: 2005,
            month: 9,
            date: 1
        },
        "1971-07-23": {
            year: 1971,
            month: 6,
            date: 23
        },
        "1801-11-20T04:30:15Z": {
            year: 1801,
            month: 10,
            date: 20,
            hour: 4,
            minutes: 30,
            seconds: 15
        },
        "1989-06-15T18:30:15.91Z": {
            year: 1989,
            month: 5,
            date: 15,
            hour: 18,
            minutes: 30,
            seconds: 15,
            milliseconds: 910
        },
        "1989-06-15T18:30:15.9105Z": {
            year: 1989,
            month: 5,
            date: 15,
            hour: 18,
            minutes: 30,
            seconds: 15,
            milliseconds: 911
        },
        "2010-01-01T00:00:00+01:00": { // zero hour
            year: 2009,
            month: 11,
            date: 31,
            hour: 23,
            minutes: 0,
            seconds: 0
        },
        "2010-01-01T24:00Z": { // 24:00
            year: 2010,
            month: 0,
            date: 2,
            hour: 0,
            minutes: 0,
            seconds: 0
        },
        "2010-01-01T00:00+01:00": { // no seconds (lenient)
            year: 2009,
            month: 11,
            date: 31,
            hour: 23,
            minutes: 0,
            seconds: 0
        },
        "2010-08-06T15:21:25-06": { // MDT
            year: 2010,
            month: 7,
            date: 6,
            hour: 21,
            minutes: 21,
            seconds: 25
        },
        "2010-08-07T06:21:25+9": { // JSP
            year: 2010,
            month: 7,
            date: 6,
            hour: 21,
            minutes: 21,
            seconds: 25
        },
        "2010-08-07T02:51:25+05:30": { // IST
            year: 2010,
            month: 7,
            date: 6,
            hour: 21,
            minutes: 21,
            seconds: 25
        },
        "T18:30:15.91Z": {
            hour: 18,
            minutes: 30,
            seconds: 15,
            milliseconds: 910
        },
        "T21:51:25Z": {
            hour: 21,
            minutes: 51,
            seconds: 25
        },
        "T02:51:25+05:30": { // IST
            hour: 21,
            minutes: 21,
            seconds: 25
        },
        "T2:51:25.1234-7": { // lenient
            hour: 9,
            minutes: 51,
            seconds: 25,
            milliseconds: 123
        }
    };

    let o, got, exp;
    for (let str in cases) {
        o = cases[str];
        got = dates.parse(str);
        exp = new Date(Date.UTC(o.year || 0, o.month || 0, o.date || 1, o.hour || 0, o.minutes || 0, o.seconds || 0, o.milliseconds || 0));
        if ("year" in o) {
            assert.strictEqual(got.getUTCFullYear(), exp.getUTCFullYear(), str + ": correct UTCFullYear");
            assert.strictEqual(got.getUTCMonth(), exp.getUTCMonth(), str + ": correct UTCMonth");
            assert.strictEqual(got.getUTCDate(), exp.getUTCDate(), str + ": correct UTCDate");
        }
        assert.strictEqual(got.getUTCHours(), exp.getUTCHours(), str + ": correct UTCHours");
        assert.strictEqual(got.getUTCMinutes(), exp.getUTCMinutes(), str + ": correct UTCMinutes");
        assert.strictEqual(got.getUTCSeconds(), exp.getUTCSeconds(), str + ": correct UTCSeconds");
        assert.strictEqual(got.getUTCMilliseconds(), exp.getUTCMilliseconds(), str + ": correct UTCMilliseconds");
    }
};

exports.testToISOString = function() {
    let d = new Date(Date.UTC(2010, 0, 2, 2, 3, 4, 5));
    assert.strictEqual(dates.toISOString(d, false, false), "2010-01-02");
    assert.strictEqual(dates.toISOString(d, true, false, false), "2010-01-02T02:03Z");
    assert.strictEqual(dates.toISOString(d, true, false, true, false), "2010-01-02T02:03:04Z");
    assert.strictEqual(dates.toISOString(d, true, false, true, true), "2010-01-02T02:03:04.005Z");

    d = new Date(Date.UTC(2010, 0, 2, 12, 0, 0, 0));
    assert.strictEqual(dates.toISOString(d, true, false, true, false), "2010-01-02T12:00:00Z");
    assert.strictEqual(dates.toISOString(d, true, false, true, true), "2010-01-02T12:00:00.000Z");

    d = new Date(Date.UTC(2010, 0, 2, 12, 1, 2, 3));
    assert.strictEqual(dates.toISOString(d, true, false, true, false), "2010-01-02T12:01:02Z");
    assert.strictEqual(dates.toISOString(d, true, false, true, true), "2010-01-02T12:01:02.003Z");

    d = new Date(Date.UTC(2010, 0, 2, 12, 1, 2, 99));
    assert.strictEqual(dates.toISOString(d, true, false, true, false), "2010-01-02T12:01:02Z");
    assert.strictEqual(dates.toISOString(d, true, false, true), "2010-01-02T12:01:02.099Z");
    assert.strictEqual(dates.toISOString(d, true, false, true, true), "2010-01-02T12:01:02.099Z");

    // Test for local time using current time to prevent nasty timzone/dst jumps
    d = new Date();
    assert.strictEqual(dates.toISOString(d), d.toISOString());

    let sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
    let formatted = sdf.format(d);
    assert.strictEqual(dates.toISOString(d, true, true, true, false), formatted.substr(0,22) + ":" + formatted.substr(-2));

    sdf = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    formatted = sdf.format(d);
    assert.strictEqual(dates.toISOString(d, true, true, true, true), formatted.substr(0,26) + ":" + formatted.substr(-2));

    // default should be UTC and not local time
    d = new Date(Date.UTC(2010, 0, 2, 2, 3, 4, 5));
    assert.strictEqual(dates.toISOString(d), "2010-01-02T02:03:04.005Z")
};

exports.testJavaTime = function() {
    const d = new Date(Date.UTC(2010, 0, 2, 12, 0, 0, 0));
    const formatter = java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
    const instant = dates.toInstant(d);

    const dt = java.time.ZonedDateTime.ofInstant(instant, java.time.ZoneId.of("+02:30"));
    assert.strictEqual(dt.format(formatter), "2010-01-02T14:30:00+02:30");

    assert.strictEqual(dates.toOffsetDateTime(new Date()).getOffset().getTotalSeconds() / -60, (new Date()).getTimezoneOffset());

    const odtNow = dates.toOffsetDateTime(new Date());
    const zdtNow = java.time.OffsetDateTime.now();
    assert.strictEqual(odtNow.getOffset().getTotalSeconds(), zdtNow.getOffset().getTotalSeconds());
};

if (require.main === module) {
    require('system').exit(require("test").run(module.id));
}
