Bug 1278186 - Implement valueAsNumber and valueAsDate for <input type=week>. r=smaug,Waldo

This commit is contained in:
Jessica Jong 2016-09-29 01:08:00 +02:00
Родитель c89ca66d22
Коммит c35a0bf546
6 изменённых файлов: 506 добавлений и 57 удалений

Просмотреть файл

@ -215,9 +215,13 @@ const Decimal HTMLInputElement::kDefaultStep = Decimal(1);
const Decimal HTMLInputElement::kDefaultStepTime = Decimal(60);
const Decimal HTMLInputElement::kStepAny = Decimal(0);
const double HTMLInputElement::kMaximumYear = 275760;
const double HTMLInputElement::kMinimumYear = 1;
const double HTMLInputElement::kMaximumYear = 275760;
const double HTMLInputElement::kMaximumWeekInMaximumYear = 37;
const double HTMLInputElement::kMaximumDayInMaximumYear = 13;
const double HTMLInputElement::kMaximumMonthInMaximumYear = 9;
const double HTMLInputElement::kMaximumWeekInYear = 53;
const double HTMLInputElement::kMsPerDay = 24 * 60 * 60 * 1000;
#define NS_INPUT_ELEMENT_STATE_IID \
{ /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */ \
@ -1880,12 +1884,12 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
return false;
}
// Maximum valid month is 275760-09.
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
if (year == kMaximumYear && month > 9) {
// Maximum valid month is 275760-09.
if (year == kMaximumYear && month > kMaximumMonthInMaximumYear) {
return false;
}
@ -1893,6 +1897,26 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
aResultValue = Decimal(int32_t(months));
return true;
}
case NS_FORM_INPUT_WEEK:
{
uint32_t year, week;
if (!ParseWeek(aValue, &year, &week)) {
return false;
}
if (year < kMinimumYear || year > kMaximumYear) {
return false;
}
// Maximum week is 275760-W37, the week of 275760-09-13.
if (year == kMaximumYear && week > kMaximumWeekInMaximumYear) {
return false;
}
double days = DaysSinceEpochFromWeek(year, week);
aResultValue = Decimal::fromDouble(days * kMsPerDay);
return true;
}
default:
MOZ_ASSERT(false, "Unrecognized input type");
return false;
@ -2131,6 +2155,41 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue,
aResultString.AppendPrintf("%04.0f-%02.0f", year, month + 1);
return true;
}
case NS_FORM_INPUT_WEEK:
{
aValue = aValue.floor();
// Based on ISO 8601 date.
double year = JS::YearFromTime(aValue.toDouble());
double month = JS::MonthFromTime(aValue.toDouble());
double day = JS::DayFromTime(aValue.toDouble());
// Adding 1 since day starts from 0.
double dayInYear = JS::DayWithinYear(aValue.toDouble(), year) + 1;
// Adding 1 since month starts from 0.
uint32_t isoWeekday = DayOfWeek(year, month + 1, day, true);
// Target on Wednesday since ISO 8601 states that week 1 is the week
// with the first Thursday of that year.
uint32_t week = (dayInYear - isoWeekday + 10) / 7;
if (week < 1) {
year--;
if (year < 1) {
return false;
}
week = MaximumWeekInYear(year);
} else if (week > MaximumWeekInYear(year)) {
year++;
if (year > kMaximumYear ||
(year == kMaximumYear && week > kMaximumWeekInMaximumYear)) {
return false;
}
week = 1;
}
aResultString.AppendPrintf("%04.0f-W%02d", year, week);
return true;
}
default:
MOZ_ASSERT(false, "Unrecognized input type");
return false;
@ -2141,8 +2200,7 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue,
Nullable<Date>
HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
{
// TODO: this is temporary until bug 888316 is fixed.
if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_WEEK) {
if (!IsDateTimeInputType(mType)) {
return Nullable<Date>();
}
@ -2186,6 +2244,20 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, 1));
return Nullable<Date>(Date(time));
}
case NS_FORM_INPUT_WEEK:
{
uint32_t year, week;
nsAutoString value;
GetValueInternal(value);
if (!ParseWeek(value, &year, &week)) {
return Nullable<Date>();
}
double days = DaysSinceEpochFromWeek(year, week);
JS::ClippedTime time = JS::TimeClip(days * kMsPerDay);
return Nullable<Date>(Date(time));
}
}
MOZ_ASSERT(false, "Unrecognized input type");
@ -2196,8 +2268,7 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
void
HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
{
// TODO: this is temporary until bug 888316 is fixed.
if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_WEEK) {
if (!IsDateTimeInputType(mType)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -5096,24 +5167,31 @@ HTMLInputElement::IsLeapYear(uint32_t aYear) const
}
uint32_t
HTMLInputElement::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay) const
HTMLInputElement::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
bool isoWeek) const
{
// Tomohiko Sakamoto algorithm.
int monthTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
aYear -= aMonth < 3;
return (aYear + aYear / 4 - aYear / 100 + aYear / 400 +
monthTable[aMonth - 1] + aDay) % 7;
uint32_t day = (aYear + aYear / 4 - aYear / 100 + aYear / 400 +
monthTable[aMonth - 1] + aDay) % 7;
if (isoWeek) {
return ((day + 6) % 7) + 1;
}
return day;
}
uint32_t
HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const
{
int day = DayOfWeek(aYear, 1, 1); // January 1.
int day = DayOfWeek(aYear, 1, 1, true); // January 1.
// A year starting on Thursday or a leap year starting on Wednesday has 53
// weeks. All other years have 52 weeks.
return day == 4 || (day == 3 && IsLeapYear(aYear)) ? kMaximumWeekInYear
: kMaximumWeekInYear - 1;
return day == 4 || (day == 3 && IsLeapYear(aYear)) ?
kMaximumWeekInYear : kMaximumWeekInYear - 1;
}
bool
@ -5228,6 +5306,25 @@ bool HTMLInputElement::ParseDate(const nsAString& aValue,
*aDay > 0 && *aDay <= NumberOfDaysInMonth(*aMonth, *aYear);
}
double
HTMLInputElement::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const
{
double days = JS::DayFromYear(aYear) + (aWeek - 1) * 7;
uint32_t dayOneIsoWeekday = DayOfWeek(aYear, 1, 1, true);
// If day one of that year is on/before Thursday, we should subtract the
// days that belong to last year in our first week, otherwise, our first
// days belong to last year's last week, and we should add those days
// back.
if (dayOneIsoWeekday <= 4) {
days -= (dayOneIsoWeekday - 1);
} else {
days += (7 - dayOneIsoWeekday + 1);
}
return days;
}
uint32_t
HTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const
{
@ -5251,8 +5348,7 @@ HTMLInputElement::NumberOfDaysInMonth(uint32_t aMonth, uint32_t aYear) const
return 30;
}
return (aYear % 400 == 0 || (aYear % 100 != 0 && aYear % 4 == 0))
? 29 : 28;
return IsLeapYear(aYear) ? 29 : 28;
}
/* static */ bool

Просмотреть файл

@ -1049,11 +1049,7 @@ protected:
/**
* Returns if valueAsNumber attribute applies for the current type.
*/
bool DoesValueAsNumberApply() const
{
// TODO: this is temporary until bug 888316 is fixed.
return DoesMinMaxApply() && mType != NS_FORM_INPUT_WEEK;
}
bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); }
/**
* Returns if autocomplete attribute applies for the current type.
@ -1231,6 +1227,12 @@ protected:
uint32_t* aMonth,
uint32_t* aDay) const;
/**
* This methods returns the number of days since epoch for a given year and
* week.
*/
double DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const;
/**
* This methods returns the number of days in a given month, for a given year.
*/
@ -1243,9 +1245,11 @@ protected:
int32_t MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const;
/**
* This methods returns the day of the week given a date, note that 0 = Sunday.
* This methods returns the day of the week given a date. If @isoWeek is true,
* 7=Sunday, otherwise, 0=Sunday.
*/
uint32_t DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay) const;
uint32_t DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
bool isoWeek) const;
/**
* This methods returns the maximum number of week in a given year, the
@ -1490,12 +1494,21 @@ protected:
// Float value returned by GetStep() when the step attribute is set to 'any'.
static const Decimal kStepAny;
// Maximum year limited by ECMAScript date object range, year <= 275760.
static const double kMaximumYear;
// Minimum year limited by HTML standard, year >= 1.
static const double kMinimumYear;
// Maximum year limited by ECMAScript date object range, year <= 275760.
static const double kMaximumYear;
// Maximum valid week is 275760-W37.
static const double kMaximumWeekInMaximumYear;
// Maximum valid day is 275760-09-13.
static const double kMaximumDayInMaximumYear;
// Maximum valid month is 275760-09.
static const double kMaximumMonthInMaximumYear;
// Long years in a ISO calendar have 53 weeks in them.
static const double kMaximumWeekInYear;
// Milliseconds in a day.
static const double kMsPerDay;
/**
* The type of this input (<input type=...>) as an integer.

Просмотреть файл

@ -46,8 +46,7 @@ var validTypes =
["time", true],
["color", false],
["month", true],
// TODO: temporary set to false until bug 888316 is fixed.
["week", false],
["week", true],
];
var todoTypes =
@ -59,7 +58,7 @@ var todoTypes =
function checkAvailability()
{
for (data of validTypes) {
for (let data of validTypes) {
var exceptionCatched = false;
element.type = data[0];
try {
@ -80,7 +79,7 @@ function checkAvailability()
" availability is not correct");
}
for (data of todoTypes) {
for (let data of todoTypes) {
var exceptionCatched = false;
element.type = data[0];
try {
@ -104,7 +103,7 @@ function checkAvailability()
function checkGarbageValues()
{
for (type of validTypes) {
for (let type of validTypes) {
if (!type[1]) {
continue;
}
@ -129,7 +128,7 @@ function checkGarbageValues()
"foobar", 42, {}, function() { return 42; }, function() { return Date(); }
];
for (value of illegalValues) {
for (let value of illegalValues) {
try {
var caught = false;
element.valueAsDate = value;
@ -177,14 +176,14 @@ function checkDateGet()
];
element.type = "date";
for (data of validData) {
for (let data of validData) {
element.value = data[0];
is(element.valueAsDate.valueOf(), data[1],
"valueAsDate should return the " +
"valid date object representing this date");
}
for (data of invalidData) {
for (let data of invalidData) {
element.value = data[0];
if (data[1]) {
is(String(element.valueAsDate), "Invalid Date",
@ -225,7 +224,7 @@ function checkDateSet()
];
element.type = "date";
for (data of testData) {
for (let data of testData) {
element.valueAsDate = new Date(data[0]);
is(element.value, data[1], "valueAsDate should set the value to "
+ data[1]);
@ -276,7 +275,7 @@ function checkTimeGet()
var element = document.createElement('input');
element.type = 'time';
for (test of tests) {
for (let test of tests) {
element.value = test.value;
if (test.result === null) {
is(element.valueAsDate, null, "element.valueAsDate should return null");
@ -319,7 +318,7 @@ function checkTimeSet()
var element = document.createElement('input');
element.type = 'time';
for (test of tests) {
for (let test of tests) {
element.valueAsDate = new Date(test.value);
is(element.value, test.result,
"element.value should have been changed by setting valueAsDate");
@ -328,7 +327,7 @@ function checkTimeSet()
function checkWithBustedPrototype()
{
for (type of validTypes) {
for (let type of validTypes) {
if (!type[1]) {
continue;
}
@ -443,14 +442,14 @@ function checkMonthGet()
];
element.type = "month";
for (data of validData) {
for (let data of validData) {
element.value = data[0];
is(element.valueAsDate.valueOf(), data[1],
"valueAsDate should return the " +
"valid date object representing this month");
}
for (data of invalidData) {
for (let data of invalidData) {
element.value = data[0];
if (data[1]) {
is(String(element.valueAsDate), "Invalid Date",
@ -490,7 +489,156 @@ function checkMonthSet()
];
element.type = "month";
for (data of testData) {
for (let data of testData) {
element.valueAsDate = new Date(data[0]);
is(element.value, data[1], "valueAsDate should set the value to "
+ data[1]);
element.valueAsDate = new testFrame.Date(data[0]);
is(element.value, data[1], "valueAsDate with other-global date should " +
"set the value to " + data[1]);
}
}
function checkWeekGet()
{
var validData =
[
// Common years starting on different days of week.
[ "2007-W01", Date.UTC(2007, 0, 1) ], // Mon
[ "2013-W01", Date.UTC(2012, 11, 31) ], // Tue
[ "2014-W01", Date.UTC(2013, 11, 30) ], // Wed
[ "2015-W01", Date.UTC(2014, 11, 29) ], // Thu
[ "2010-W01", Date.UTC(2010, 0, 4) ], // Fri
[ "2011-W01", Date.UTC(2011, 0, 3) ], // Sat
[ "2017-W01", Date.UTC(2017, 0, 2) ], // Sun
// Common years ending on different days of week.
[ "2007-W52", Date.UTC(2007, 11, 24) ], // Mon
[ "2013-W52", Date.UTC(2013, 11, 23) ], // Tue
[ "2014-W52", Date.UTC(2014, 11, 22) ], // Wed
[ "2015-W53", Date.UTC(2015, 11, 28) ], // Thu
[ "2010-W52", Date.UTC(2010, 11, 27) ], // Fri
[ "2011-W52", Date.UTC(2011, 11, 26) ], // Sat
[ "2017-W52", Date.UTC(2017, 11, 25) ], // Sun
// Leap years starting on different days of week.
[ "1996-W01", Date.UTC(1996, 0, 1) ], // Mon
[ "2008-W01", Date.UTC(2007, 11, 31) ], // Tue
[ "2020-W01", Date.UTC(2019, 11, 30) ], // Wed
[ "2004-W01", Date.UTC(2003, 11, 29) ], // Thu
[ "2016-W01", Date.UTC(2016, 0, 4) ], // Fri
[ "2000-W01", Date.UTC(2000, 0, 3) ], // Sat
[ "2012-W01", Date.UTC(2012, 0, 2) ], // Sun
// Leap years ending on different days of week.
[ "2012-W52", Date.UTC(2012, 11, 24) ], // Mon
[ "2024-W52", Date.UTC(2024, 11, 23) ], // Tue
[ "1980-W52", Date.UTC(1980, 11, 22) ], // Wed
[ "1992-W53", Date.UTC(1992, 11, 28) ], // Thu
[ "2004-W53", Date.UTC(2004, 11, 27) ], // Fri
[ "1988-W52", Date.UTC(1988, 11, 26) ], // Sat
[ "2000-W52", Date.UTC(2000, 11, 25) ], // Sun
// Other normal cases.
[ "2016-W36", 1473033600000 ],
[ "1969-W52", -864000000 ],
[ "1970-W01", -259200000 ],
[ "275760-W37", 8639999568000000 ],
];
var invalidData =
[
[ "invalidweek" ],
[ "0000-W01" ],
[ "2016-W00" ],
[ "123-W01" ],
[ "2016-W53" ],
[ "" ],
// This week is valid for the input element, but is out of
// the date object range. In this case, on getting valueAsDate,
// a Date object will be created, but it will have a NaN internal value,
// and will return the string "Invalid Date".
[ "275760-W38", true ],
];
element.type = "week";
for (let data of validData) {
element.value = data[0];
is(element.valueAsDate.valueOf(), data[1],
"valueAsDate should return the " +
"valid date object representing this week");
}
for (let data of invalidData) {
element.value = data[0];
if (data[1]) {
is(String(element.valueAsDate), "Invalid Date",
"valueAsDate should return an invalid Date object " +
"when the element value is not a valid week");
} else {
is(element.valueAsDate, null,
"valueAsDate should return null " +
"when the element value is not a valid week");
}
}
}
function checkWeekSet()
{
var testData =
[
// Common years starting on different days of week.
[ Date.UTC(2007, 0, 1), "2007-W01" ], // Mon
[ Date.UTC(2013, 0, 1), "2013-W01" ], // Tue
[ Date.UTC(2014, 0, 1), "2014-W01" ], // Wed
[ Date.UTC(2015, 0, 1), "2015-W01" ], // Thu
[ Date.UTC(2010, 0, 1), "2009-W53" ], // Fri
[ Date.UTC(2011, 0, 1), "2010-W52" ], // Sat
[ Date.UTC(2017, 0, 1), "2016-W52" ], // Sun
// Common years ending on different days of week.
[ Date.UTC(2007, 11, 31), "2008-W01" ], // Mon
[ Date.UTC(2013, 11, 31), "2014-W01" ], // Tue
[ Date.UTC(2014, 11, 31), "2015-W01" ], // Wed
[ Date.UTC(2015, 11, 31), "2015-W53" ], // Thu
[ Date.UTC(2010, 11, 31), "2010-W52" ], // Fri
[ Date.UTC(2011, 11, 31), "2011-W52" ], // Sat
[ Date.UTC(2017, 11, 31), "2017-W52" ], // Sun
// Leap years starting on different days of week.
[ Date.UTC(1996, 0, 1), "1996-W01" ], // Mon
[ Date.UTC(2008, 0, 1), "2008-W01" ], // Tue
[ Date.UTC(2020, 0, 1), "2020-W01" ], // Wed
[ Date.UTC(2004, 0, 1), "2004-W01" ], // Thu
[ Date.UTC(2016, 0, 1), "2015-W53" ], // Fri
[ Date.UTC(2000, 0, 1), "1999-W52" ], // Sat
[ Date.UTC(2012, 0, 1), "2011-W52" ], // Sun
// Leap years ending on different days of week.
[ Date.UTC(2012, 11, 31), "2013-W01" ], // Mon
[ Date.UTC(2024, 11, 31), "2025-W01" ], // Tue
[ Date.UTC(1980, 11, 31), "1981-W01" ], // Wed
[ Date.UTC(1992, 11, 31), "1992-W53" ], // Thu
[ Date.UTC(2004, 11, 31), "2004-W53" ], // Fri
[ Date.UTC(1988, 11, 31), "1988-W52" ], // Sat
[ Date.UTC(2000, 11, 31), "2000-W52" ], // Sun
// Other normal cases.
[ Date.UTC(2016, 8, 9), "2016-W36" ],
[ Date.UTC(2010, 0, 3), "2009-W53" ],
[ Date.UTC(2010, 0, 4), "2010-W01" ],
[ Date.UTC(2010, 0, 10), "2010-W01" ],
[ Date.UTC(2010, 0, 11), "2010-W02" ],
[ 0, "1970-W01" ],
// Maximum valid month (limited by the ecma date object range).
[ 8640000000000000, "275760-W37" ],
// Minimum valid month (limited by the input element minimum valid value).
[ -62135596800000 , "0001-W01" ],
// "Values must be truncated to valid week"
[ 42.1234, "1970-W01" ],
[ 123.123456789123, "1970-W01" ],
[ 1e-1, "1970-W01" ],
[ -1.1, "1970-W01" ],
[ -345600000, "1969-W52" ],
// Negative years, this is out of range for the input element,
// the corresponding week string is the empty string
[ -62135596800001, "" ],
];
element.type = "week";
for (let data of testData) {
element.valueAsDate = new Date(data[0]);
is(element.value, data[1], "valueAsDate should set the value to "
+ data[1]);
@ -516,7 +664,12 @@ checkTimeSet();
checkMonthGet();
checkMonthSet();
// Test <input type='week'>.
checkWeekGet();
checkWeekSet();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -46,7 +46,7 @@ function checkAvailability()
["color", false],
["month", true],
// TODO: temporary set to false until bug 888316 is fixed.
["week", false],
["week", true],
];
var todoList =
@ -57,7 +57,7 @@ function checkAvailability()
var element = document.createElement('input');
for (data of testData) {
for (let data of testData) {
var exceptionCatched = false;
element.type = data[0];
try {
@ -78,7 +78,7 @@ function checkAvailability()
" availability is not correct");
}
for (data of todoList) {
for (let data of todoList) {
var exceptionCatched = false;
element.type = data[0];
try {
@ -122,7 +122,7 @@ function checkNumberGet()
var element = document.createElement('input');
element.type = "number";
for (data of testData) {
for (let data of testData) {
element.value = data[0];
// Given that NaN != NaN, we have to use null when the expected value is NaN.
@ -162,7 +162,7 @@ function checkNumberSet()
var element = document.createElement('input');
element.type = "number";
for (data of testData) {
for (let data of testData) {
var caught = false;
try {
element.valueAsNumber = data[0];
@ -213,7 +213,7 @@ function checkRangeGet()
element.setAttribute("min", min); // avoids out of range sanitization
element.setAttribute("max", max);
element.setAttribute("step", "any"); // avoids step mismatch sanitization
for (data of testData) {
for (let data of testData) {
element.value = data[0];
// Given that NaN != NaN, we have to use null when the expected value is NaN.
@ -253,7 +253,7 @@ function checkRangeSet()
element.setAttribute("min", min); // avoids out of range sanitization
element.setAttribute("max", max);
element.setAttribute("step", "any"); // avoids step mismatch sanitization
for (data of testData) {
for (let data of testData) {
var caught = false;
try {
element.valueAsNumber = data[0];
@ -305,13 +305,13 @@ function checkDateGet()
var element = document.createElement('input');
element.type = "date";
for (data of validData) {
for (let data of validData) {
element.value = data[0];
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
"timestamp representing this date");
}
for (data of invalidData) {
for (let data of invalidData) {
element.value = data;
ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
"when the element value is not a valid date");
@ -361,7 +361,7 @@ function checkDateSet()
var element = document.createElement('input');
element.type = "date";
for (data of testData) {
for (let data of testData) {
var caught = false;
try {
@ -372,7 +372,7 @@ function checkDateSet()
}
if (data[2]) {
ok(caught, "valueAsNumber should have trhown");
ok(caught, "valueAsNumber should have thrown");
is(element.value, data[1], "the value should not have changed");
} else {
ok(!caught, "valueAsNumber should not have thrown");
@ -422,7 +422,7 @@ function checkTimeGet()
var element = document.createElement('input');
element.type = 'time';
for (test of tests) {
for (let test of tests) {
element.value = test.value;
if (isNaN(test.result)) {
ok(isNaN(element.valueAsNumber),
@ -469,7 +469,7 @@ function checkTimeSet()
var element = document.createElement('input');
element.type = 'time';
for (test of tests) {
for (let test of tests) {
try {
var caught = false;
element.valueAsNumber = test.value;
@ -488,7 +488,6 @@ function checkTimeSet()
function checkMonthGet()
{
dump("checkMonthGet\n");
var validData =
[
[ "2016-07", 558 ],
@ -511,13 +510,13 @@ function checkMonthGet()
var element = document.createElement('input');
element.type = "month";
for (data of validData) {
for (let data of validData) {
element.value = data[0];
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
"integer value representing this month");
}
for (data of invalidData) {
for (let data of invalidData) {
element.value = data;
ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
"when the element value is not a valid month");
@ -564,7 +563,7 @@ function checkMonthSet()
var element = document.createElement('input');
element.type = "month";
for (data of testData) {
for (let data of testData) {
var caught = false;
try {
@ -575,7 +574,163 @@ function checkMonthSet()
}
if (data[2]) {
ok(caught, "valueAsNumber should have trhown");
ok(caught, "valueAsNumber should have thrown");
is(element.value, data[1], "the value should not have changed");
} else {
ok(!caught, "valueAsNumber should not have thrown");
}
}
}
function checkWeekGet()
{
var validData =
[
// Common years starting on different days of week.
[ "2007-W01", Date.UTC(2007, 0, 1) ], // Mon
[ "2013-W01", Date.UTC(2012, 11, 31) ], // Tue
[ "2014-W01", Date.UTC(2013, 11, 30) ], // Wed
[ "2015-W01", Date.UTC(2014, 11, 29) ], // Thu
[ "2010-W01", Date.UTC(2010, 0, 4) ], // Fri
[ "2011-W01", Date.UTC(2011, 0, 3) ], // Sat
[ "2017-W01", Date.UTC(2017, 0, 2) ], // Sun
// Common years ending on different days of week.
[ "2007-W52", Date.UTC(2007, 11, 24) ], // Mon
[ "2013-W52", Date.UTC(2013, 11, 23) ], // Tue
[ "2014-W52", Date.UTC(2014, 11, 22) ], // Wed
[ "2015-W53", Date.UTC(2015, 11, 28) ], // Thu
[ "2010-W52", Date.UTC(2010, 11, 27) ], // Fri
[ "2011-W52", Date.UTC(2011, 11, 26) ], // Sat
[ "2017-W52", Date.UTC(2017, 11, 25) ], // Sun
// Leap years starting on different days of week.
[ "1996-W01", Date.UTC(1996, 0, 1) ], // Mon
[ "2008-W01", Date.UTC(2007, 11, 31) ], // Tue
[ "2020-W01", Date.UTC(2019, 11, 30) ], // Wed
[ "2004-W01", Date.UTC(2003, 11, 29) ], // Thu
[ "2016-W01", Date.UTC(2016, 0, 4) ], // Fri
[ "2000-W01", Date.UTC(2000, 0, 3) ], // Sat
[ "2012-W01", Date.UTC(2012, 0, 2) ], // Sun
// Leap years ending on different days of week.
[ "2012-W52", Date.UTC(2012, 11, 24) ], // Mon
[ "2024-W52", Date.UTC(2024, 11, 23) ], // Tue
[ "1980-W52", Date.UTC(1980, 11, 22) ], // Wed
[ "1992-W53", Date.UTC(1992, 11, 28) ], // Thu
[ "2004-W53", Date.UTC(2004, 11, 27) ], // Fri
[ "1988-W52", Date.UTC(1988, 11, 26) ], // Sat
[ "2000-W52", Date.UTC(2000, 11, 25) ], // Sun
// Other normal cases.
[ "2015-W53", Date.UTC(2015, 11, 28) ],
[ "2016-W36", Date.UTC(2016, 8, 5) ],
[ "1970-W01", Date.UTC(1969, 11, 29) ],
[ "275760-W37", Date.UTC(275760, 8, 8) ],
];
var invalidData =
[
"invalidweek",
"0000-W01",
"2016-W00",
"2016-W53",
// Out of range.
"275760-W38",
];
var element = document.createElement('input');
element.type = "week";
for (let data of validData) {
dump("Test: " + data[0]);
element.value = data[0];
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
"integer value representing this week");
}
for (let data of invalidData) {
element.value = data;
ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
"when the element value is not a valid week");
}
}
function checkWeekSet()
{
var testData =
[
// Common years starting on different days of week.
[ Date.UTC(2007, 0, 1), "2007-W01" ], // Mon
[ Date.UTC(2013, 0, 1), "2013-W01" ], // Tue
[ Date.UTC(2014, 0, 1), "2014-W01" ], // Wed
[ Date.UTC(2015, 0, 1), "2015-W01" ], // Thu
[ Date.UTC(2010, 0, 1), "2009-W53" ], // Fri
[ Date.UTC(2011, 0, 1), "2010-W52" ], // Sat
[ Date.UTC(2017, 0, 1), "2016-W52" ], // Sun
// Common years ending on different days of week.
[ Date.UTC(2007, 11, 31), "2008-W01" ], // Mon
[ Date.UTC(2013, 11, 31), "2014-W01" ], // Tue
[ Date.UTC(2014, 11, 31), "2015-W01" ], // Wed
[ Date.UTC(2015, 11, 31), "2015-W53" ], // Thu
[ Date.UTC(2010, 11, 31), "2010-W52" ], // Fri
[ Date.UTC(2011, 11, 31), "2011-W52" ], // Sat
[ Date.UTC(2017, 11, 31), "2017-W52" ], // Sun
// Leap years starting on different days of week.
[ Date.UTC(1996, 0, 1), "1996-W01" ], // Mon
[ Date.UTC(2008, 0, 1), "2008-W01" ], // Tue
[ Date.UTC(2020, 0, 1), "2020-W01" ], // Wed
[ Date.UTC(2004, 0, 1), "2004-W01" ], // Thu
[ Date.UTC(2016, 0, 1), "2015-W53" ], // Fri
[ Date.UTC(2000, 0, 1), "1999-W52" ], // Sat
[ Date.UTC(2012, 0, 1), "2011-W52" ], // Sun
// Leap years ending on different days of week.
[ Date.UTC(2012, 11, 31), "2013-W01" ], // Mon
[ Date.UTC(2024, 11, 31), "2025-W01" ], // Tue
[ Date.UTC(1980, 11, 31), "1981-W01" ], // Wed
[ Date.UTC(1992, 11, 31), "1992-W53" ], // Thu
[ Date.UTC(2004, 11, 31), "2004-W53" ], // Fri
[ Date.UTC(1988, 11, 31), "1988-W52" ], // Sat
[ Date.UTC(2000, 11, 31), "2000-W52" ], // Sun
// Other normal cases.
[ Date.UTC(2008, 8, 26), "2008-W39" ],
[ Date.UTC(2016, 0, 4), "2016-W01" ],
[ Date.UTC(2016, 0, 10), "2016-W01" ],
[ Date.UTC(2016, 0, 11), "2016-W02" ],
// Maximum valid week (limited by the ecma date object range).
[ 8640000000000000, "275760-W37" ],
// Minimum valid week (limited by the input element minimum valid value)
[ -62135596800000, "0001-W01" ],
// "Values must be truncated to valid weeks"
[ 0.3, "1970-W01" ],
[ 1e-1, "1970-W01" ],
[ -1.1, "1970-W01" ],
[ -345600000, "1969-W52" ],
// Invalid numbers.
// Those are implicitly converted to numbers
[ "", "1970-W01" ],
[ true, "1970-W01" ],
[ false, "1970-W01" ],
[ null, "1970-W01" ],
// Those are converted to NaN, the corresponding week string is the empty string
[ "invalidweek", "" ],
[ NaN, "" ],
[ undefined, "" ],
// Infinity will keep the current value and throw (so we need to set a current value).
[ Date.UTC(2016, 8, 8), "2016-W36" ],
[ Infinity, "2016-W36", true ],
[ -Infinity, "2016-W36", true ],
];
var element = document.createElement('input');
element.type = "week";
for (let data of testData) {
var caught = false;
try {
element.valueAsNumber = data[0];
is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
} catch(e) {
caught = true;
}
if (data[2]) {
ok(caught, "valueAsNumber should have thrown");
is(element.value, data[1], "the value should not have changed");
} else {
ok(!caught, "valueAsNumber should not have thrown");
@ -605,6 +760,10 @@ checkTimeSet();
checkMonthGet();
checkMonthSet();
// <input type='week'> test
checkWeekGet();
checkWeekSet();
</script>
</pre>
</body>

Просмотреть файл

@ -149,6 +149,22 @@ MonthFromTime(double time);
JS_PUBLIC_API(double)
DayFromTime(double time);
// Takes an integer year and returns the number of days from epoch to the given
// year.
// NOTE: The calculation performed by this function is literally that given in
// the ECMAScript specification. Nonfinite years, years containing fractional
// components, and years outside ECMAScript's date range are not handled with
// any particular intelligence. Garbage in, garbage out.
JS_PUBLIC_API(double)
DayFromYear(double year);
// Takes an integer number of milliseconds since the epoch and an integer year,
// returns the number of days in that year. If |time| is nonfinite, returns NaN.
// Otherwise |time| *must* correspond to a time within the valid year |year|.
// This should usually be ensured by computing |year| as |JS::DayFromYear(time)|.
JS_PUBLIC_API(double)
DayWithinYear(double time, double year);
} // namespace JS
#endif /* js_Date_h */

Просмотреть файл

@ -375,6 +375,18 @@ JS::DayFromTime(double time)
return DateFromTime(time);
}
JS_PUBLIC_API(double)
JS::DayFromYear(double year)
{
return ::DayFromYear(year);
}
JS_PUBLIC_API(double)
JS::DayWithinYear(double time, double year)
{
return ::DayWithinYear(time, year);
}
/*
* Find a year for which any given date will fall on the same weekday.
*