mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 04:17:31 +09:00
Bug 1310078 - Implement valueAsNumber and valueAsDate for <input type=datetime-local>
This commit is contained in:
parent
5381ebe25c
commit
344ea39e20
6 changed files with 351 additions and 26 deletions
|
|
@ -1918,6 +1918,22 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
|
||||||
aResultValue = Decimal::fromDouble(days * kMsPerDay);
|
aResultValue = Decimal::fromDouble(days * kMsPerDay);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case NS_FORM_INPUT_DATETIME_LOCAL:
|
||||||
|
{
|
||||||
|
uint32_t year, month, day, timeInMs;
|
||||||
|
if (!ParseDateTimeLocal(aValue, &year, &month, &day, &timeInMs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day,
|
||||||
|
timeInMs));
|
||||||
|
if (!time.isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aResultValue = Decimal::fromDouble(time.toDouble());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT(false, "Unrecognized input type");
|
MOZ_ASSERT(false, "Unrecognized input type");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2107,21 +2123,17 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue,
|
||||||
}
|
}
|
||||||
case NS_FORM_INPUT_TIME:
|
case NS_FORM_INPUT_TIME:
|
||||||
{
|
{
|
||||||
|
aValue = aValue.floor();
|
||||||
// Per spec, we need to truncate |aValue| and we should only represent
|
// Per spec, we need to truncate |aValue| and we should only represent
|
||||||
// times inside a day [00:00, 24:00[, which means that we should do a
|
// times inside a day [00:00, 24:00[, which means that we should do a
|
||||||
// modulo on |aValue| using the number of milliseconds in a day (86400000).
|
// modulo on |aValue| using the number of milliseconds in a day (86400000).
|
||||||
uint32_t value = NS_floorModulo(aValue.floor(), Decimal(86400000)).toDouble();
|
uint32_t value =
|
||||||
|
NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble();
|
||||||
|
|
||||||
uint16_t milliseconds = value % 1000;
|
uint16_t milliseconds, seconds, minutes, hours;
|
||||||
value /= 1000;
|
if (!GetTimeFromMs(value, &hours, &minutes, &seconds, &milliseconds)) {
|
||||||
|
return false;
|
||||||
uint8_t seconds = value % 60;
|
}
|
||||||
value /= 60;
|
|
||||||
|
|
||||||
uint8_t minutes = value % 60;
|
|
||||||
value /= 60;
|
|
||||||
|
|
||||||
uint8_t hours = value;
|
|
||||||
|
|
||||||
if (milliseconds != 0) {
|
if (milliseconds != 0) {
|
||||||
aResultString.AppendPrintf("%02d:%02d:%02d.%03d",
|
aResultString.AppendPrintf("%02d:%02d:%02d.%03d",
|
||||||
|
|
@ -2189,6 +2201,42 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
aResultString.AppendPrintf("%04.0f-W%02d", year, week);
|
aResultString.AppendPrintf("%04.0f-W%02d", year, week);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case NS_FORM_INPUT_DATETIME_LOCAL:
|
||||||
|
{
|
||||||
|
aValue = aValue.floor();
|
||||||
|
|
||||||
|
uint32_t timeValue =
|
||||||
|
NS_floorModulo(aValue, Decimal::fromDouble(kMsPerDay)).toDouble();
|
||||||
|
|
||||||
|
uint16_t milliseconds, seconds, minutes, hours;
|
||||||
|
if (!GetTimeFromMs(timeValue,
|
||||||
|
&hours, &minutes, &seconds, &milliseconds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double year = JS::YearFromTime(aValue.toDouble());
|
||||||
|
double month = JS::MonthFromTime(aValue.toDouble());
|
||||||
|
double day = JS::DayFromTime(aValue.toDouble());
|
||||||
|
|
||||||
|
if (IsNaN(year) || IsNaN(month) || IsNaN(day)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (milliseconds != 0) {
|
||||||
|
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d.%03d",
|
||||||
|
year, month + 1, day, hours, minutes,
|
||||||
|
seconds, milliseconds);
|
||||||
|
} else if (seconds != 0) {
|
||||||
|
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d:%02d",
|
||||||
|
year, month + 1, day, hours, minutes,
|
||||||
|
seconds);
|
||||||
|
} else {
|
||||||
|
aResultString.AppendPrintf("%04.0f-%02.0f-%02.0fT%02d:%02d",
|
||||||
|
year, month + 1, day, hours, minutes);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -2201,8 +2249,7 @@ HTMLInputElement::ConvertNumberToString(Decimal aValue,
|
||||||
Nullable<Date>
|
Nullable<Date>
|
||||||
HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
|
HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
// TODO: this is temporary until bug 888331 is fixed.
|
if (!IsDateTimeInputType(mType)) {
|
||||||
if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
|
|
||||||
return Nullable<Date>();
|
return Nullable<Date>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2260,6 +2307,19 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
|
||||||
|
|
||||||
return Nullable<Date>(Date(time));
|
return Nullable<Date>(Date(time));
|
||||||
}
|
}
|
||||||
|
case NS_FORM_INPUT_DATETIME_LOCAL:
|
||||||
|
{
|
||||||
|
uint32_t year, month, day, timeInMs;
|
||||||
|
nsAutoString value;
|
||||||
|
GetNonFileValueInternal(value);
|
||||||
|
if (!ParseDateTimeLocal(value, &year, &month, &day, &timeInMs)) {
|
||||||
|
return Nullable<Date>();
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day,
|
||||||
|
timeInMs));
|
||||||
|
return Nullable<Date>(Date(time));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(false, "Unrecognized input type");
|
MOZ_ASSERT(false, "Unrecognized input type");
|
||||||
|
|
@ -2270,8 +2330,7 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
|
||||||
void
|
void
|
||||||
HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
|
HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
// TODO: this is temporary until bug 888331 is fixed.
|
if (!IsDateTimeInputType(mType)) {
|
||||||
if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
|
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -5384,6 +5443,29 @@ HTMLInputElement::MaximumWeekInYear(uint32_t aYear) const
|
||||||
kMaximumWeekInYear : kMaximumWeekInYear - 1;
|
kMaximumWeekInYear : kMaximumWeekInYear - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
HTMLInputElement::GetTimeFromMs(double aValue, uint16_t* aHours,
|
||||||
|
uint16_t* aMinutes, uint16_t* aSeconds,
|
||||||
|
uint16_t* aMilliseconds) const {
|
||||||
|
MOZ_ASSERT(aValue >= 0 && aValue < kMsPerDay,
|
||||||
|
"aValue must be milliseconds within a day!");
|
||||||
|
|
||||||
|
uint32_t value = floor(aValue);
|
||||||
|
|
||||||
|
*aMilliseconds = value % 1000;
|
||||||
|
value /= 1000;
|
||||||
|
|
||||||
|
*aSeconds = value % 60;
|
||||||
|
value /= 60;
|
||||||
|
|
||||||
|
*aMinutes = value % 60;
|
||||||
|
value /= 60;
|
||||||
|
|
||||||
|
*aHours = value;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HTMLInputElement::IsValidWeek(const nsAString& aValue) const
|
HTMLInputElement::IsValidWeek(const nsAString& aValue) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1075,11 +1075,7 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Returns if valueAsNumber attribute applies for the current type.
|
* Returns if valueAsNumber attribute applies for the current type.
|
||||||
*/
|
*/
|
||||||
bool DoesValueAsNumberApply() const
|
bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); }
|
||||||
{
|
|
||||||
// TODO: this is temporary until bug 888331 is fixed.
|
|
||||||
return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if autocomplete attribute applies for the current type.
|
* Returns if autocomplete attribute applies for the current type.
|
||||||
|
|
@ -1287,6 +1283,7 @@ protected:
|
||||||
* https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string
|
* https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string
|
||||||
*/
|
*/
|
||||||
void NormalizeDateTimeLocal(nsAString& aValue) const;
|
void NormalizeDateTimeLocal(nsAString& aValue) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This methods returns the number of days since epoch for a given year and
|
* This methods returns the number of days since epoch for a given year and
|
||||||
* week.
|
* week.
|
||||||
|
|
@ -1317,6 +1314,13 @@ protected:
|
||||||
*/
|
*/
|
||||||
uint32_t MaximumWeekInYear(uint32_t aYear) const;
|
uint32_t MaximumWeekInYear(uint32_t aYear) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts aValue (milliseconds within a day) to hours, minutes,
|
||||||
|
* seconds and milliseconds.
|
||||||
|
*/
|
||||||
|
bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes,
|
||||||
|
uint16_t* aSeconds, uint16_t* aMilliseconds) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This methods returns true if it's a leap year.
|
* This methods returns true if it's a leap year.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,7 @@ var validTypes =
|
||||||
["color", false],
|
["color", false],
|
||||||
["month", true],
|
["month", true],
|
||||||
["week", true],
|
["week", true],
|
||||||
// TODO: temporary set to false until bug 888331 is fixed.
|
["datetime-local", true],
|
||||||
["datetime-local", false],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function checkAvailability()
|
function checkAvailability()
|
||||||
|
|
@ -622,6 +621,107 @@ function checkWeekSet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkDatetimeLocalGet()
|
||||||
|
{
|
||||||
|
var validData =
|
||||||
|
[
|
||||||
|
// Simple cases.
|
||||||
|
[ "2016-12-27T10:30", Date.UTC(2016, 11, 27, 10, 30, 0) ],
|
||||||
|
[ "2016-12-27T10:30:40", Date.UTC(2016, 11, 27, 10, 30, 40) ],
|
||||||
|
[ "2016-12-27T10:30:40.567", Date.UTC(2016, 11, 27, 10, 30, 40, 567) ],
|
||||||
|
[ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ],
|
||||||
|
[ "1970-01-01T00:00", 0 ],
|
||||||
|
// Leap years.
|
||||||
|
[ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ],
|
||||||
|
[ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ],
|
||||||
|
[ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ],
|
||||||
|
[ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ],
|
||||||
|
[ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ],
|
||||||
|
// Maximum valid datetime-local (limited by the ecma date object range).
|
||||||
|
[ "275760-09-13T00:00", 8640000000000000 ],
|
||||||
|
// Minimum valid datetime-local (limited by the input element minimum valid value).
|
||||||
|
[ "0001-01-01T00:00", -62135596800000 ],
|
||||||
|
];
|
||||||
|
|
||||||
|
var invalidData =
|
||||||
|
[
|
||||||
|
[ "invaliddateime-local" ],
|
||||||
|
[ "0000-01-01T00:00" ],
|
||||||
|
[ "2016-12-25T00:00Z" ],
|
||||||
|
[ "2015-02-29T12:34" ],
|
||||||
|
[ "1-1-1T12:00" ],
|
||||||
|
[ "" ],
|
||||||
|
// This datetime-local 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-09-13T12:00", true ],
|
||||||
|
];
|
||||||
|
|
||||||
|
element.type = "datetime-local";
|
||||||
|
for (let data of validData) {
|
||||||
|
element.value = data[0];
|
||||||
|
is(element.valueAsDate.valueOf(), data[1],
|
||||||
|
"valueAsDate should return the " +
|
||||||
|
"valid date object representing this datetime-local");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 datetime-local");
|
||||||
|
} else {
|
||||||
|
is(element.valueAsDate, null,
|
||||||
|
"valueAsDate should return null " +
|
||||||
|
"when the element value is not a valid datetime-local");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDatetimeLocalSet()
|
||||||
|
{
|
||||||
|
var testData =
|
||||||
|
[
|
||||||
|
// Simple cases.
|
||||||
|
[ Date.UTC(2016, 11, 27, 10, 30, 0), "2016-12-27T10:30" ],
|
||||||
|
[ Date.UTC(2016, 11, 27, 10, 30, 30), "2016-12-27T10:30:30" ],
|
||||||
|
[ Date.UTC(1999, 11, 31, 23, 59, 59), "1999-12-31T23:59:59" ],
|
||||||
|
[ Date.UTC(1999, 11, 31, 23, 59, 59, 999), "1999-12-31T23:59:59.999" ],
|
||||||
|
[ Date.UTC(123456, 7, 8, 9, 10), "123456-08-08T09:10" ],
|
||||||
|
[ 0, "1970-01-01T00:00" ],
|
||||||
|
// Maximum valid datetime-local (limited by the ecma date object range).
|
||||||
|
[ 8640000000000000, "275760-09-13T00:00" ],
|
||||||
|
// Minimum valid datetime-local (limited by the input element minimum valid value).
|
||||||
|
[ -62135596800000, "0001-01-01T00:00" ],
|
||||||
|
// Leap years.
|
||||||
|
[ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ],
|
||||||
|
[ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ],
|
||||||
|
[ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ],
|
||||||
|
[ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ],
|
||||||
|
[ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ],
|
||||||
|
// "Values must be truncated to valid datetime-local"
|
||||||
|
[ 123.123456789123, "1970-01-01T00:00:00.123" ],
|
||||||
|
[ 1e-1, "1970-01-01T00:00" ],
|
||||||
|
[ -1.1, "1969-12-31T23:59:59.999" ],
|
||||||
|
[ -345600000, "1969-12-28T00:00" ],
|
||||||
|
// Negative years, this is out of range for the input element,
|
||||||
|
// the corresponding datetime-local string is the empty string
|
||||||
|
[ -62135596800001, "" ],
|
||||||
|
];
|
||||||
|
|
||||||
|
element.type = "datetime-local";
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
checkAvailability();
|
checkAvailability();
|
||||||
checkGarbageValues();
|
checkGarbageValues();
|
||||||
checkWithBustedPrototype();
|
checkWithBustedPrototype();
|
||||||
|
|
@ -642,6 +742,10 @@ checkMonthSet();
|
||||||
checkWeekGet();
|
checkWeekGet();
|
||||||
checkWeekSet();
|
checkWeekSet();
|
||||||
|
|
||||||
|
// Test <input type='datetime-local'>.
|
||||||
|
checkDatetimeLocalGet();
|
||||||
|
checkDatetimeLocalSet();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,7 @@ function checkAvailability()
|
||||||
["color", false],
|
["color", false],
|
||||||
["month", true],
|
["month", true],
|
||||||
["week", true],
|
["week", true],
|
||||||
// TODO: temporary set to false until bug 888331 is fixed.
|
["datetime-local", true],
|
||||||
["datetime-local", false],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
var element = document.createElement('input');
|
var element = document.createElement('input');
|
||||||
|
|
@ -612,7 +611,6 @@ function checkWeekGet()
|
||||||
var element = document.createElement('input');
|
var element = document.createElement('input');
|
||||||
element.type = "week";
|
element.type = "week";
|
||||||
for (let data of validData) {
|
for (let data of validData) {
|
||||||
dump("Test: " + data[0]);
|
|
||||||
element.value = data[0];
|
element.value = data[0];
|
||||||
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
|
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
|
||||||
"integer value representing this week");
|
"integer value representing this week");
|
||||||
|
|
@ -698,7 +696,120 @@ function checkWeekSet()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
element.valueAsNumber = data[0];
|
element.valueAsNumber = data[0];
|
||||||
is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDatetimeLocalGet() {
|
||||||
|
var validData =
|
||||||
|
[
|
||||||
|
// Simple cases.
|
||||||
|
[ "2016-12-20T09:58", Date.UTC(2016, 11, 20, 9, 58) ],
|
||||||
|
[ "2016-12-20T09:58:30", Date.UTC(2016, 11, 20, 9, 58, 30) ],
|
||||||
|
[ "2016-12-20T09:58:30.123", Date.UTC(2016, 11, 20, 9, 58, 30, 123) ],
|
||||||
|
[ "2017-01-01T10:00", Date.UTC(2017, 0, 1, 10, 0, 0) ],
|
||||||
|
[ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ],
|
||||||
|
[ "1970-01-01T00:00", 0 ],
|
||||||
|
// Leap years.
|
||||||
|
[ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ],
|
||||||
|
[ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ],
|
||||||
|
[ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ],
|
||||||
|
[ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ],
|
||||||
|
[ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ],
|
||||||
|
// Maximum valid datetime-local (limited by the ecma date object range).
|
||||||
|
[ "275760-09-13T00:00", 8640000000000000 ],
|
||||||
|
// Minimum valid datetime-local (limited by the input element minimum valid value).
|
||||||
|
[ "0001-01-01T00:00", -62135596800000 ],
|
||||||
|
];
|
||||||
|
|
||||||
|
var invalidData =
|
||||||
|
[
|
||||||
|
"invaliddatetime-local",
|
||||||
|
"0000-01-01T00:00",
|
||||||
|
"2016-12-25T00:00Z",
|
||||||
|
"2015-02-29T12:34",
|
||||||
|
"1-1-1T12:00",
|
||||||
|
// Out of range.
|
||||||
|
"275760-09-13T12:00",
|
||||||
|
];
|
||||||
|
|
||||||
|
var element = document.createElement('input');
|
||||||
|
element.type = "datetime-local";
|
||||||
|
for (let data of validData) {
|
||||||
|
element.value = data[0];
|
||||||
|
is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
|
||||||
|
"integer value representing this datetime-local");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let data of invalidData) {
|
||||||
|
element.value = data;
|
||||||
|
ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
|
||||||
|
"when the element value is not a valid datetime-local");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDatetimeLocalSet()
|
||||||
|
{
|
||||||
|
var testData =
|
||||||
|
[
|
||||||
|
// Simple cases.
|
||||||
|
[ Date.UTC(2016, 11, 20, 9, 58, 0), "2016-12-20T09:58", ],
|
||||||
|
[ Date.UTC(2016, 11, 20, 9, 58, 30), "2016-12-20T09:58:30" ],
|
||||||
|
[ Date.UTC(2016, 11, 20, 9, 58, 30, 123), "2016-12-20T09:58:30.123" ],
|
||||||
|
[ Date.UTC(2017, 0, 1, 10, 0, 0), "2017-01-01T10:00" ],
|
||||||
|
[ Date.UTC(1969, 11, 31, 12, 0, 0), "1969-12-31T12:00" ],
|
||||||
|
[ 0, "1970-01-01T00:00" ],
|
||||||
|
// Maximum valid week (limited by the ecma date object range).
|
||||||
|
[ 8640000000000000, "275760-09-13T00:00" ],
|
||||||
|
// Minimum valid datetime-local (limited by the input element minimum valid value).
|
||||||
|
[ -62135596800000, "0001-01-01T00:00" ],
|
||||||
|
// Leap years.
|
||||||
|
[ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ],
|
||||||
|
[ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ],
|
||||||
|
[ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ],
|
||||||
|
[ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ],
|
||||||
|
[ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ],
|
||||||
|
// "Values must be truncated to valid datetime-local"
|
||||||
|
[ 0.3, "1970-01-01T00:00" ],
|
||||||
|
[ 1e-1, "1970-01-01T00:00" ],
|
||||||
|
[ -1 , "1969-12-31T23:59:59.999" ],
|
||||||
|
[ -345600000, "1969-12-28T00:00" ],
|
||||||
|
// Invalid numbers.
|
||||||
|
// Those are implicitly converted to numbers
|
||||||
|
[ "", "1970-01-01T00:00" ],
|
||||||
|
[ true, "1970-01-01T00:00:00.001" ],
|
||||||
|
[ false, "1970-01-01T00:00" ],
|
||||||
|
[ null, "1970-01-01T00:00" ],
|
||||||
|
// Those are converted to NaN, the corresponding week string is the empty string
|
||||||
|
[ "invaliddatetime-local", "" ],
|
||||||
|
[ NaN, "" ],
|
||||||
|
[ undefined, "" ],
|
||||||
|
// Infinity will keep the current value and throw (so we need to set a current value).
|
||||||
|
[ Date.UTC(2016, 11, 27, 15, 10, 0), "2016-12-27T15:10" ],
|
||||||
|
[ Infinity, "2016-12-27T15:10", true ],
|
||||||
|
[ -Infinity, "2016-12-27T15:10", true ],
|
||||||
|
];
|
||||||
|
|
||||||
|
var element = document.createElement('input');
|
||||||
|
element.type = "datetime-local";
|
||||||
|
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) {
|
} catch(e) {
|
||||||
caught = true;
|
caught = true;
|
||||||
}
|
}
|
||||||
|
|
@ -738,6 +849,10 @@ checkMonthSet();
|
||||||
checkWeekGet();
|
checkWeekGet();
|
||||||
checkWeekSet();
|
checkWeekSet();
|
||||||
|
|
||||||
|
// <input type='datetime-local'> test
|
||||||
|
checkDatetimeLocalGet();
|
||||||
|
checkDatetimeLocalSet();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,14 @@ NewDateObject(JSContext* cx, ClippedTime time);
|
||||||
JS_PUBLIC_API(double)
|
JS_PUBLIC_API(double)
|
||||||
MakeDate(double year, unsigned month, unsigned day);
|
MakeDate(double year, unsigned month, unsigned day);
|
||||||
|
|
||||||
|
// Year is a year, month is 0-11, day is 1-based, and time is in milliseconds.
|
||||||
|
// The return value is a number of milliseconds since the epoch.
|
||||||
|
//
|
||||||
|
// Consistent with the MakeDate algorithm defined in ECMAScript, this value is
|
||||||
|
// *not* clipped! Use JS::TimeClip if you need a clipped date.
|
||||||
|
JS_PUBLIC_API(double)
|
||||||
|
MakeDate(double year, unsigned month, unsigned day, double time);
|
||||||
|
|
||||||
// Takes an integer number of milliseconds since the epoch and returns the
|
// Takes an integer number of milliseconds since the epoch and returns the
|
||||||
// year. Can return NaN, and will do so if NaN is passed in.
|
// year. Can return NaN, and will do so if NaN is passed in.
|
||||||
JS_PUBLIC_API(double)
|
JS_PUBLIC_API(double)
|
||||||
|
|
|
||||||
|
|
@ -354,9 +354,21 @@ MakeDate(double day, double time)
|
||||||
JS_PUBLIC_API(double)
|
JS_PUBLIC_API(double)
|
||||||
JS::MakeDate(double year, unsigned month, unsigned day)
|
JS::MakeDate(double year, unsigned month, unsigned day)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(month <= 11);
|
||||||
|
MOZ_ASSERT(day >= 1 && day <= 31);
|
||||||
|
|
||||||
return ::MakeDate(MakeDay(year, month, day), 0);
|
return ::MakeDate(MakeDay(year, month, day), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_PUBLIC_API(double)
|
||||||
|
JS::MakeDate(double year, unsigned month, unsigned day, double time)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(month <= 11);
|
||||||
|
MOZ_ASSERT(day >= 1 && day <= 31);
|
||||||
|
|
||||||
|
return ::MakeDate(MakeDay(year, month, day), time);
|
||||||
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(double)
|
JS_PUBLIC_API(double)
|
||||||
JS::YearFromTime(double time)
|
JS::YearFromTime(double time)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue