Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion tests/unit/datepicker/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ QUnit.test( "keystrokes", function( assert ) {
} );

QUnit.test( "mouse", function( assert ) {
assert.expect( 15 );
assert.expect( 16 );
var inl,
inp = testHelper.init( "#inp" ),
dp = $( "#ui-datepicker-div" ),
Expand All @@ -481,6 +481,13 @@ QUnit.test( "mouse", function( assert ) {
$( "button.ui-datepicker-close", dp ).simulate( "click", {} );
testHelper.equalsDate( assert, inp.datepicker( "getDate" ), new Date( 2008, 2 - 1, 4 ),
"Mouse click - abandoned" );
inp.datepicker( "hide" );

inp.val( "02/24/0999" ).datepicker( "show" );
$( ".ui-datepicker-calendar tbody a:contains(15)", dp ).simulate( "click", {} );
inp.datepicker( "show" );
testHelper.equalsDate( assert, inp.datepicker( "getDate" ), new Date( 999, 2 - 1, 15 ), "Mouse click inline - preset year 999" );
inp.datepicker( "hide" );

// Current/previous/next
inp.val( "02/04/2008" ).datepicker( "option", { showButtonPanel: true } ).datepicker( "show" );
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/datepicker/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ define( [

return $.extend( helper, {
addMonths: function( date, offset ) {
var maxDay = 32 - new Date( date.getFullYear(), date.getMonth() + offset, 32 ).getDate();
var maxDay = 32 - $.datepicker._createDate( date.getFullYear(), date.getMonth() + offset, 32 ).getDate();
date.setDate( Math.min( date.getDate(), maxDay ) );
date.setMonth( date.getMonth() + offset );
return date;
Expand All @@ -19,8 +19,8 @@ return $.extend( helper, {
assert.ok( false, message + " - missing date" );
return;
}
d1 = new Date( d1.getFullYear(), d1.getMonth(), d1.getDate() );
d2 = new Date( d2.getFullYear(), d2.getMonth(), d2.getDate() );
d1 = $.datepicker._createDate( d1.getFullYear(), d1.getMonth(), d1.getDate() );
d2 = $.datepicker._createDate( d2.getFullYear(), d2.getMonth(), d2.getDate() );
assert.equal( d1.toString(), d2.toString(), message );
},

Expand Down
61 changes: 60 additions & 1 deletion tests/unit/datepicker/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,7 @@ QUnit.test( "Ticket #7244: date parser does not fail when too many numbers are p
} );

QUnit.test( "formatDate", function( assert ) {
assert.expect( 16 );
assert.expect( 24 );
testHelper.init( "#inp" );
var gmtDate, fr, settings;
assert.equal( $.datepicker.formatDate( "d m y", new Date( 2001, 2 - 1, 3 ) ),
Expand Down Expand Up @@ -1164,6 +1164,17 @@ QUnit.test( "formatDate", function( assert ) {
assert.equal( $.datepicker.formatDate( "'jour' d 'de' MM (''DD''), yy",
new Date( 2001, 4 - 1, 9 ), settings ), "jour 9 de avril ('lundi'), 2001",
"Format date 'jour' d 'de' MM (''DD''), yy with settings" );

assert.equal( $.datepicker.formatDate( "yy-mm-dd", $.datepicker._createDate( 0, 0, 1 ) ), "0000-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", $.datepicker._createDate( 12, 0, 1 ) ), "0012-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", $.datepicker._createDate( 99, 0, 1 ) ), "0099-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", new Date( 100, 0, 1 ) ), "0100-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", new Date( 999, 0, 1 ) ), "0999-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", new Date( 1000, 0, 1 ) ), "1000-01-01" );

// -1 and 100000 will not be parsed correctly, but can be selected
assert.equal( $.datepicker.formatDate( "yy-mm-dd", new Date( -1, 0, 1 ) ), "-1-01-01" );
assert.equal( $.datepicker.formatDate( "yy-mm-dd", new Date( 10000, 0, 1 ) ), "10000-01-01" );
} );

// TODO: Fix this test so it isn't mysteriously flaky in Browserstack on certain OS/Browser combos
Expand Down Expand Up @@ -1260,4 +1271,52 @@ QUnit.test( "Ticket #15284: escaping text parameters", function( assert ) {
} );
} );

QUnit.test( "Ticket #7098: Broken handling of four digit years before year 100", function( assert ) {
assert.expect( 123 );

var year = null,
someDate = null,
parsedHDate = null,
inp = testHelper.init( "#inp", {
changeMonth: true,
changeYear: true,
yearRange: "0:2100"
} );

someDate = $.datepicker._createDate( 0, 1, 29 );
assert.equal( someDate.getFullYear(), 0, "creates Date object for 0000-02-29" );
assert.equal( someDate.getMonth(), 1, "creates Date object for 0000-02-29" );
assert.equal( someDate.getDate(), 29, "creates Date object for 0000-02-29" );

for ( year = 0; year < 100; year++ ) { // loop 100
parsedHDate = $.datepicker.parseDate( "yy-m-d", ( year < 10 ? "000" : "00" ) + year + "-10-23", { shortYearCutoff: 0 } );
someDate = new Date( year, 2 - 1, 3 );
someDate.setFullYear( year );
assert.equal(
someDate.getFullYear(), parsedHDate.getFullYear(),
"parseDate returns date object with the correct year"
);
}

for ( year = 1; year < 100; year += 10 ) { // loop 10
someDate = new Date( year, 2, 3 );
someDate.setFullYear( year );
inp.datepicker( "option", "defaultDate", someDate );
inp.datepicker( "show" );
$( "td[data-handler='selectDay'] a" ).first().click();
inp.datepicker( "show" );
assert.equal(
$( ".ui-datepicker-year option:selected" ).val(),
someDate.getFullYear(),
"Selected year stays the same as the default"
);
inp.datepicker( "hide" );
assert.ok(
inp.val().indexOf( someDate.getFullYear() ) !== -1,
"inp val has the default/selected year"
);
inp.datepicker( "setDate", null );
}
} );

} );
77 changes: 51 additions & 26 deletions ui/widgets/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,8 @@ $.extend( Datepicker.prototype, {
day = -1,
doy = -1,
literal = false,
length = -1,
isFromFullYear = false,
date,

// Check whether a format character is doubled
Expand All @@ -1242,7 +1244,8 @@ $.extend( Datepicker.prototype, {
if ( !num ) {
throw "Missing number at position " + iValue;
}
iValue += num[ 0 ].length;
length = num[ 0 ].length;
iValue += length;
return parseInt( num[ 0 ], 10 );
},

Expand Down Expand Up @@ -1304,18 +1307,21 @@ $.extend( Datepicker.prototype, {
break;
case "y":
year = getNumber( "y" );
isFromFullYear = length === 4;
break;
case "@":
date = new Date( getNumber( "@" ) );
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
isFromFullYear = true;
break;
case "!":
date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
year = date.getFullYear();
month = date.getMonth() + 1;
day = date.getDate();
isFromFullYear = true;
break;
case "'":
if ( lookAhead( "'" ) ) {
Expand All @@ -1339,7 +1345,7 @@ $.extend( Datepicker.prototype, {

if ( year === -1 ) {
year = new Date().getFullYear();
} else if ( year < 100 ) {
} else if ( year < 100 && !isFromFullYear ) {
year += new Date().getFullYear() - new Date().getFullYear() % 100 +
( year <= shortYearCutoff ? 0 : -100 );
}
Expand All @@ -1357,7 +1363,7 @@ $.extend( Datepicker.prototype, {
} while ( true );
}

date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
date = this._daylightSavingAdjust( this._createDate( year, month - 1, day ) );
if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
throw "Invalid date"; // E.g. 31/02/00
}
Expand Down Expand Up @@ -1419,6 +1425,7 @@ $.extend( Datepicker.prototype, {
dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
year,

// Check whether a format character is doubled
lookAhead = function( match ) {
Expand Down Expand Up @@ -1465,7 +1472,7 @@ $.extend( Datepicker.prototype, {
break;
case "o":
output += formatNumber( "o",
Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
Math.round( ( this._createDate( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - this._createDate( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
break;
case "m":
output += formatNumber( "m", date.getMonth() + 1, 2 );
Expand All @@ -1474,8 +1481,10 @@ $.extend( Datepicker.prototype, {
output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
break;
case "y":
output += ( lookAhead( "y" ) ? date.getFullYear() :
( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
year = date.getFullYear();
output += ( lookAhead( "y" ) ?
( year >= 0 && year < 1000 ? ( year < 10 ? "000" : year < 100 ? "00" : "0" ) : "" ) + year :
( year % 100 < 10 ? "0" : "" ) + year % 100 );
break;
case "@":
output += date.getTime();
Expand Down Expand Up @@ -1622,7 +1631,7 @@ $.extend( Datepicker.prototype, {
}
matches = pattern.exec( offset );
}
return new Date( year, month, day );
return $.datepicker._createDate( year, month, day );
},
newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );
Expand Down Expand Up @@ -1674,9 +1683,9 @@ $.extend( Datepicker.prototype, {
/* Retrieve the date(s) directly. */
_getDate: function( inst ) {
var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
this._daylightSavingAdjust( new Date(
inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
return startDate;
this._daylightSavingAdjust( this._createDate(
inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
return startDate;
},

/* Attach the onxxx handlers. These are declared statically so
Expand Down Expand Up @@ -1726,7 +1735,7 @@ $.extend( Datepicker.prototype, {
printDate, dRow, tbody, daySettings, otherMonth, unselectable,
tempDate = new Date(),
today = this._daylightSavingAdjust(
new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
this._createDate( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
isRTL = this._get( inst, "isRTL" ),
showButtonPanel = this._get( inst, "showButtonPanel" ),
hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
Expand All @@ -1736,7 +1745,7 @@ $.extend( Datepicker.prototype, {
stepMonths = this._get( inst, "stepMonths" ),
isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
this._createDate( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
minDate = this._getMinMaxDate( inst, "min" ),
maxDate = this._getMinMaxDate( inst, "max" ),
drawMonth = inst.drawMonth - showCurrentAtPos,
Expand All @@ -1747,10 +1756,12 @@ $.extend( Datepicker.prototype, {
drawYear--;
}
if ( maxDate ) {
maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
maxDraw = this._daylightSavingAdjust( this._createDate(
maxDate.getFullYear(),
maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1,
maxDate.getDate() ) );
maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
while ( this._daylightSavingAdjust( this._createDate( drawYear, drawMonth, 1 ) ) > maxDraw ) {
drawMonth--;
if ( drawMonth < 0 ) {
drawMonth = 11;
Expand All @@ -1763,7 +1774,7 @@ $.extend( Datepicker.prototype, {

prevText = this._get( inst, "prevText" );
prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
this._daylightSavingAdjust( this._createDate( drawYear, drawMonth - stepMonths, 1 ) ),
this._getFormatConfig( inst ) ) );

if ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ) {
Expand Down Expand Up @@ -1798,7 +1809,7 @@ $.extend( Datepicker.prototype, {

nextText = this._get( inst, "nextText" );
nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
this._daylightSavingAdjust( this._createDate( drawYear, drawMonth + stepMonths, 1 ) ),
this._getFormatConfig( inst ) ) );

if ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ) {
Expand Down Expand Up @@ -1883,7 +1894,7 @@ $.extend( Datepicker.prototype, {
group = "";
this.maxRows = 4;
for ( col = 0; col < numMonths[ 1 ]; col++ ) {
selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
selectedDate = this._daylightSavingAdjust( this._createDate( drawYear, drawMonth, inst.selectedDay ) );
cornerClass = " ui-corner-all";
calender = "";
if ( isMultiMonth ) {
Expand Down Expand Up @@ -1921,7 +1932,7 @@ $.extend( Datepicker.prototype, {
curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
this.maxRows = numRows;
printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
printDate = this._daylightSavingAdjust( this._createDate( drawYear, drawMonth, 1 - leadDays ) );
for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
calender += "<tr>";
tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
Expand Down Expand Up @@ -2055,7 +2066,7 @@ $.extend( Datepicker.prototype, {
var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
date = this._restrictMinMax( inst, this._daylightSavingAdjust( this._createDate( year, month, day ) ) );

inst.selectedDay = date.getDate();
inst.drawMonth = inst.selectedMonth = date.getMonth();
Expand Down Expand Up @@ -2095,19 +2106,21 @@ $.extend( Datepicker.prototype, {

/* Find the number of days in a given month. */
_getDaysInMonth: function( year, month ) {
return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
return 32 - this._daylightSavingAdjust( this._createDate( year, month, 32 ) ).getDate();
},

/* Find the day of the week of the first of a month. */
_getFirstDayOfMonth: function( year, month ) {
return new Date( year, month, 1 ).getDay();
return this._createDate( year, month, 1 ).getDay();
},

/* Determines if we should allow a "next/prev" month display change. */
_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
var numMonths = this._getNumberOfMonths( inst ),
date = this._daylightSavingAdjust( new Date( curYear,
curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
date = this._daylightSavingAdjust( this._createDate(
curYear,
curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ),
1 ) );

if ( offset < 0 ) {
date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
Expand Down Expand Up @@ -2160,9 +2173,21 @@ $.extend( Datepicker.prototype, {
inst.currentYear = inst.selectedYear;
}
var date = ( day ? ( typeof day === "object" ? day :
this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
this._daylightSavingAdjust( this._createDate( year, month, day ) ) ) :
this._daylightSavingAdjust( this._createDate( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
},

/** Create a date object with the correct year for years 0 through 99 */
_createDate: function( year, month, day ) {
var dateObject;
if ( year >= 0 && year < 100 ) {
dateObject = new Date( 2000, month, day );
dateObject.setFullYear( year );
} else {
dateObject = new Date( year, month, day );
}
return dateObject;
}
} );

Expand Down