
function dateToComponentString(dt) {
    return [
               dt.getUTCFullYear(),
               dt.getUTCMonth(),
               dt.getUTCDate(),
               dt.getUTCHours(),
               dt.getUTCMinutes(),
               dt.getUTCSeconds(),
               dt.getUTCMilliseconds()
           ].join(' ');
}

function utcDiffAtTime(dt) {
    var d1;

    // This is not exactly accurate but good enough for testing:
    // take the UTC components and use them as local ones and see
    // how much difference that makes.
    d1 = new Date(dt.getUTCFullYear(),
                  dt.getUTCMonth(),
                  dt.getUTCDate(),
                  dt.getUTCHours(),
                  dt.getUTCMinutes(),
                  dt.getUTCSeconds(),
                  dt.getUTCMilliseconds());

    // Positive offset = local time is ahead of UTC (e.g. EET is
    // +7200e3 or +10800e3
    return dt.getTime() - d1.getTime();
}

/*===
constructor as function
no side effects
constructor components
valueOf 1
valueOf 2
valueOf 3
valueOf 4
valueOf 5
valueOf 6
valueOf 7
2013 1 2 3 4 5 6
constructor time value
valueOf 8
12345 1 2 3 4 5 6
component setters
valueOf 1
valueOf 1
valueOf 1
valueOf 2
valueOf 1
valueOf 2
valueOf 1
valueOf 2
valueOf 3
valueOf 1
valueOf 2
valueOf 3
valueOf 1
valueOf 2
valueOf 3
valueOf 4
valueOf 1
valueOf 2
valueOf 3
valueOf 4
valueOf 1
valueOf 1
valueOf 1
valueOf 2
valueOf 1
valueOf 2
valueOf 1
valueOf 2
valueOf 3
valueOf 1
valueOf 2
valueOf 3
valueOf 1
===*/

/* Side effects and evaluation order of coercions */

function coercionSideEffectsTest() {
    var d;
    var obj1 = { toString: function() { print('toString 1'); return 'toString 1'; },
                 valueOf: function() { print('valueOf 1'); return 2013; } };
    var obj2 = { toString: function() { print('toString 2'); return 'toString 2'; },
                 valueOf: function() { print('valueOf 2'); return 1; } };
    var obj3 = { toString: function() { print('toString 3'); return 'toString 3'; },
                 valueOf: function() { print('valueOf 3'); return 2; } };
    var obj4 = { toString: function() { print('toString 4'); return 'toString 4'; },
                 valueOf: function() { print('valueOf 4'); return 3; } };
    var obj5 = { toString: function() { print('toString 5'); return 'toString 5'; },
                 valueOf: function() { print('valueOf 5'); return 4; } };
    var obj6 = { toString: function() { print('toString 6'); return 'toString 6'; },
                 valueOf: function() { print('valueOf 6'); return 5; } };
    var obj7 = { toString: function() { print('toString 7'); return 'toString 7'; },
                 valueOf: function() { print('valueOf 7'); return 6; } };
    var obj8 = { toString: function() { print('toString 8'); return 'toString 8'; },
                 valueOf: function() { print('valueOf 8'); return 327406158245006; } };
                 // Date.UTC(12345,1,2,3,4,5,6) -> above return value
 
    function toUtcAndPrint(dt) {
        var utcDiff = utcDiffAtTime(dt);
        dt = new Date(dt.getTime() + utcDiff);  // Now UTC time
        print(dateToComponentString(dt));
    }

    // Date constructor, called as a function (ignore all arguments)
    print('constructor as function');
    d = Date(obj1, obj2, obj3, obj4, obj5, obj6, obj7);
    print('no side effects');

    // Date constructor, component call
    print('constructor components');
    d = new Date(obj1, obj2, obj3, obj4, obj5, obj6, obj7);
    toUtcAndPrint(d);

    // Date constructor, time value call
    print('constructor time value');
    d = new Date(obj8);
    print(dateToComponentString(d));

    // year setters will convert internal NaN timevalue to +0, but
    // otherwise proceed normally
    print('component setters');
    d = new Date(NaN);
    d.setUTCMilliseconds(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setMilliseconds(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCSeconds(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setSeconds(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCMinutes(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setMinutes(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCHours(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setHours(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCDate(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setDate(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCMonth(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setMonth(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setUTCFullYear(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setFullYear(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);
    d.setYear(obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8);

    // test-bi-date-tojson-generic covers toJSON() coercion order
}

try {
    coercionSideEffectsTest();
} catch (e) {
    print(e.name);
}

/*===
this check
valueOf
TypeError
===*/

function coercionThisValueCheck() {
    // The 'this' check should happen before anything else is coerced.
    // This is not tested exhaustively now.
    print('this check')
    d = new Date(NaN);
    d.setUTCSeconds.call(d,
                         { valueOf: function() { print('valueOf'); return 123; } });
    d.setUTCSeconds.call('foo',
                         { valueOf: function() { print('valueOf'); return 123; } });
}

try {
    coercionThisValueCheck();
} catch (e) {
    print(e.name);
}

