LibJS: Basic implementation of most of Date's constructor arguments

The constructor with a string argument isn't implemented yet, but
this implements the other variants.

The timestamp constructor doens't handle negative timestamps correctly.

Out-of-bound and invalid arguments aren't handled correctly.
This commit is contained in:
Nico Weber 2020-08-20 10:41:10 -04:00 committed by Andreas Kling
parent 45827cace9
commit d4d9222eea
Notes: sideshowbarker 2024-07-19 03:23:33 +09:00
2 changed files with 83 additions and 6 deletions

View file

@ -60,13 +60,48 @@ Value DateConstructor::call(Interpreter& interpreter)
return js_string(interpreter, static_cast<Date&>(date.as_object()).string());
}
Value DateConstructor::construct(Interpreter&, Function&)
Value DateConstructor::construct(Interpreter& interpreter, Function&)
{
// TODO: Support args
struct timeval tv;
gettimeofday(&tv, nullptr);
auto datetime = Core::DateTime::now();
auto milliseconds = static_cast<u16>(tv.tv_usec / 1000);
if (interpreter.argument_count() == 0) {
struct timeval tv;
gettimeofday(&tv, nullptr);
auto datetime = Core::DateTime::now();
auto milliseconds = static_cast<u16>(tv.tv_usec / 1000);
return Date::create(global_object(), datetime, milliseconds);
}
if (interpreter.argument_count() == 1 && interpreter.argument(0).is_string()) {
// FIXME: Parse simplified ISO8601-like string, like Date.parse() will do.
struct timeval tv;
gettimeofday(&tv, nullptr);
auto datetime = Core::DateTime::now();
auto milliseconds = static_cast<u16>(tv.tv_usec / 1000);
return Date::create(global_object(), datetime, milliseconds);
}
if (interpreter.argument_count() == 1) {
// A timestamp since the epoch, in UTC.
// FIXME: Date() probably should use a double as internal representation, so that NaN arguments and larger offsets are handled correctly.
// FIXME: DateTime::from_timestamp() seems to not support negative offsets.
double value = interpreter.argument(0).to_double(interpreter);
auto datetime = Core::DateTime::from_timestamp(static_cast<time_t>(value / 1000));
auto milliseconds = static_cast<u16>(fmod(value, 1000));
return Date::create(global_object(), datetime, milliseconds);
}
// A date/time in components, in local time.
// FIXME: This doesn't construct an "Invalid Date" object if one of the parameters is NaN.
// FIXME: This doesn't range-check args and convert months > 12 to year increments etc.
auto arg_or = [&interpreter](size_t i, i32 fallback) { return interpreter.argument_count() > i ? interpreter.argument(i).to_i32(interpreter) : fallback; };
int year = interpreter.argument(0).to_i32(interpreter);
int month_index = interpreter.argument(1).to_i32(interpreter);
int day = arg_or(2, 1);
int hours = arg_or(3, 0);
int minutes = arg_or(4, 0);
int seconds = arg_or(5, 0);
int milliseconds = arg_or(6, 0);
if (year >= 0 && year <= 99)
year += 1900;
int month = month_index + 1;
auto datetime = Core::DateTime::create(year, month, day, hours, minutes, seconds);
return Date::create(global_object(), datetime, milliseconds);
}

View file

@ -3,3 +3,45 @@ test("basic functionality", () => {
expect(Date.name === "Date");
expect(Date.prototype).not.toHaveProperty("length");
});
test("timestamp constructor", () => {
// The timestamp constructor takes a timestamp in milliseconds since the start of the epoch, in UTC.
// 50 days and 1234 milliseconds after the start of the epoch.
// Most Date methods return values in local time, but since timezone offsets are less than 17 days,
// these checks will pass in all timezones.
let timestamp = 50 * 24 * 60 * 60 * 1000 + 1234;
let date = new Date(timestamp);
expect(date.getTime()).toBe(timestamp); // getTime() returns the timestamp in UTC.
expect(date.getMilliseconds()).toBe(234);
expect(date.getSeconds()).toBe(1);
expect(date.getFullYear()).toBe(1970);
expect(date.getMonth()).toBe(1); // Feb
});
test("tuple constructor", () => {
// The tuple constructor takes a date in local time.
expect(new Date(2019, 11).getFullYear()).toBe(2019);
expect(new Date(2019, 11).getMonth()).toBe(11);
expect(new Date(2019, 11).getDate()).toBe(1); // getDay() returns day of week, getDate() returnsn day in month
expect(new Date(2019, 11).getHours()).toBe(0);
expect(new Date(2019, 11).getMinutes()).toBe(0);
expect(new Date(2019, 11).getSeconds()).toBe(0);
expect(new Date(2019, 11).getMilliseconds()).toBe(0);
let date = new Date(2019, 11, 15, 9, 16, 14, 123); // Note: Month is 0-based.
expect(date.getFullYear()).toBe(2019);
expect(date.getMonth()).toBe(11);
expect(date.getDate()).toBe(15);
expect(date.getHours()).toBe(9);
expect(date.getMinutes()).toBe(16);
expect(date.getSeconds()).toBe(14);
expect(date.getMilliseconds()).toBe(123);
// getTime() returns a time stamp in UTC, but we can at least check it's in the right interval, which will be true independent of the local timezone if the range is big enough.
let timestamp_lower_bound = 1575072000000; // 2019-12-01T00:00:00Z
let timestamp_upper_bound = 1577750400000; // 2019-12-31T00:00:00Z
expect(date.getTime()).toBeGreaterThan(timestamp_lower_bound);
expect(date.getTime()).toBeLessThan(timestamp_upper_bound);
});