Showing with 100 additions and 16 deletions.
  1. +14 −0 src/date/is-leap-year.js
  2. +14 −0 src/date/last-day-of-month.js
  3. +20 −15 src/date/parse.js
  4. +52 −1 test/unit/date/parse.js
@@ -0,0 +1,14 @@
define(function() {

/**
* isLeapYear( year )
*
* @year [Number]
*
* Returns an indication whether the specified year is a leap year.
*/
return function( year ) {
return new Date(year, 1, 29).getMonth() === 1;
};

});
@@ -0,0 +1,14 @@
define(function() {

/**
* lastDayOfMonth( date )
*
* @date [Date]
*
* Return the last day of the given date's month
*/
return function( date ) {
return new Date( date.getFullYear(), date.getMonth() + 1, 0).getDate();
};

});
@@ -1,12 +1,13 @@
define([
"./is-leap-year",
"./last-day-of-month",
"./pattern-re",
"./start-of",
"../common/create-error/unsupported-feature",
"../util/date/set-date",
"../util/date/set-month",
"../util/out-of-range"
], function( datePatternRe, dateStartOf, createErrorUnsupportedFeature, dateSetDate,
dateSetMonth, outOfRange ) {
], function( dateIsLeapYear, dateLastDayOfMonth, datePatternRe, dateStartOf,
createErrorUnsupportedFeature, dateSetMonth, outOfRange ) {

/**
* parse( value, tokens, properties )
@@ -20,7 +21,7 @@ define([
* ref: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
*/
return function( value, tokens, properties ) {
var amPm, era, hour, hour12, timezoneOffset, valid,
var amPm, day, era, hour, hour12, timezoneOffset, totalDays, valid,
YEAR = 0,
MONTH = 1,
DAY = 2,
@@ -112,21 +113,12 @@ return function( value, tokens, properties ) {

// Day
case "d":
value = token.value;
if ( outOfRange( value, 1, 31 ) ) {
return false;
}
dateSetDate( date, value );
day = token.value;
truncateAt.push( DAY );
break;

case "D":
value = token.value;
if ( outOfRange( value, 1, 366 ) ) {
return false;
}
date.setMonth(0);
date.setDate( value );
totalDays = token.value;
truncateAt.push( DAY );
break;

@@ -249,6 +241,19 @@ return function( value, tokens, properties ) {
date.setFullYear( date.getFullYear() * -1 + 1 );
}

if ( day !== undefined ) {
if ( outOfRange( day, 1, dateLastDayOfMonth( date ) ) ) {
return null;
}
date.setDate( day );
} else if ( totalDays !== undefined ) {
if ( outOfRange( totalDays, 1, dateIsLeapYear( date.getFullYear() ) ? 366 : 365 ) ) {
return null;
}
date.setMonth(0);
date.setDate( totalDays );
}

if ( hour12 && amPm === "pm" ) {
date.setHours( date.getHours() + 12 );
}
@@ -16,7 +16,7 @@ define([
], function( Cldr, parse, parseProperties, startOf, tokenizer, numberTokenizerProperties,
enCaGregorian, enNumbers, likelySubtags, timeData, weekData ) {

var cldr, date1, date2, midnight;
var cldr, date1, date2, FakeDate, midnight;

function assertParse( assert, stringDate, pattern, cldr, date ) {
var tokenizerProperties, tokens;
@@ -43,6 +43,36 @@ function assertParseTimezone( assert, stringDate, pattern, cldr, timezoneOffset
"` pattern `" + pattern + "`" );
}

FakeDate = (function( Date ) {
function FakeDate() {
var date;
if ( arguments.length === 0 ) {
return FakeDate.today;
}
if ( arguments.length === 1 ) {
date = new Date( arguments[ 0 ] );
} else if ( arguments.length === 2 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ] );
} else if ( arguments.length === 3 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ] );
} else if ( arguments.length === 4 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ], arguments[ 3 ] );
} else if ( arguments.length === 5 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ], arguments[ 3 ], arguments[ 4 ] );
} else if ( arguments.length === 6 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ], arguments[ 3 ], arguments[ 4 ], arguments[ 5 ] );
} else if ( arguments.length === 7 ) {
date = new Date( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ], arguments[ 3 ], arguments[ 4 ], arguments[ 5 ], arguments[ 6 ] );
}

/* jshint proto:true */
date.__proto__ = FakeDate.prototype;
return date;
}
FakeDate.prototype = FakeDate.today = new Date();
return FakeDate;
})( Date );

// Simple number parser for this test purposes.
function simpleParseNumber( value ) {
return +value;
@@ -165,10 +195,31 @@ QUnit.test( "should parse month (MMMMM|LLLLL)", function( assert ) {
*/

QUnit.test( "should parse day (d) with no padding", function( assert ) {
var OrigDate;

date1 = new Date();
date1.setDate( 2 );
date1 = startOf( date1, "day" );
assertParse( assert, "2", "d", cldr, date1 );

/* globals Date:true */
// Test #323 - Day parsing must use the correct day range given its corresponding month/year.
OrigDate = Date;
Date = FakeDate;

date1 = new Date( 2014, 1, 28 );
date1 = startOf( date1, "day" );
FakeDate.today = new Date( 2014, 1 );
assertParse( assert, "29", "d", cldr, null );
assertParse( assert, "28", "d", cldr, date1 );

date2 = new Date( 2016, 1, 29 );
date2 = startOf( date2, "day" );
FakeDate.today = new Date( 2016, 1 );
assertParse( assert, "30", "d", cldr, null );
assertParse( assert, "29", "d", cldr, date2 );

Date = OrigDate;
});

QUnit.test( "should parse day (dd) with padding", function( assert ) {