summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2020-08-20 10:41:10 -0400
committerAndreas Kling <kling@serenityos.org>2020-08-20 20:53:43 +0200
commitd4d9222eea2dff296c841504b4f3f9769b86a49f (patch)
treeef670facac10481e2fa66404b7cc803a83d132a4 /Libraries
parent45827cace92f749d7a729f7fc535e66bacc97a4b (diff)
downloadserenity-d4d9222eea2dff296c841504b4f3f9769b86a49f.zip
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.
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibJS/Runtime/DateConstructor.cpp47
-rw-r--r--Libraries/LibJS/Tests/builtins/Date/Date.js42
2 files changed, 83 insertions, 6 deletions
diff --git a/Libraries/LibJS/Runtime/DateConstructor.cpp b/Libraries/LibJS/Runtime/DateConstructor.cpp
index a0783b0eb7..d66c0fa93b 100644
--- a/Libraries/LibJS/Runtime/DateConstructor.cpp
+++ b/Libraries/LibJS/Runtime/DateConstructor.cpp
@@ -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);
}
diff --git a/Libraries/LibJS/Tests/builtins/Date/Date.js b/Libraries/LibJS/Tests/builtins/Date/Date.js
index 3934fb5142..c7a2669675 100644
--- a/Libraries/LibJS/Tests/builtins/Date/Date.js
+++ b/Libraries/LibJS/Tests/builtins/Date/Date.js
@@ -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);
+});