Skip to content

Commit 5632f50

Browse files
feat: added UTC getter / setter methods
1 parent 07b9fb6 commit 5632f50

File tree

7 files changed

+5225
-577
lines changed

7 files changed

+5225
-577
lines changed

std/assembly/date.ts

Lines changed: 140 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import {
2-
UTC as Date_UTC,
3-
now as Date_now
4-
} from "./bindings/Date";
1+
import { E_VALUEOUTOFRANGE } from "util/error";
2+
import { UTC as Date_UTC, now as Date_now } from "./bindings/Date";
53

64
export class Date {
7-
85
@inline static UTC(
96
year: i32,
107
month: i32 = 0,
@@ -14,25 +11,157 @@ export class Date {
1411
second: i32 = 0,
1512
millisecond: i64 = 0
1613
): i64 {
17-
return <i64>Date_UTC(year, month, day, hour, minute, second, <f64>millisecond);
14+
return <i64>(
15+
Date_UTC(year, month, day, hour, minute, second, <f64>millisecond)
16+
);
1817
}
1918

2019
@inline static now(): i64 {
2120
return <i64>Date_now();
2221
}
2322

24-
private value: i64;
23+
private epochMillis: i64;
2524

26-
constructor(value: i64) {
27-
this.value = value;
25+
constructor(epochMillis: i64) {
26+
this.epochMillis = epochMillis;
2827
}
2928

3029
getTime(): i64 {
31-
return this.value;
30+
return this.epochMillis;
3231
}
3332

3433
setTime(value: i64): i64 {
35-
this.value = value;
34+
this.epochMillis = value;
3635
return value;
3736
}
37+
38+
getUTCFullYear(): i32 {
39+
return ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY)).year;
40+
}
41+
42+
getUTCMonth(): i32 {
43+
return ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY)).month - 1;
44+
}
45+
46+
getUTCDate(): i32 {
47+
return ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY)).day;
48+
}
49+
50+
getUTCHours(): i32 {
51+
return i32(this.epochMillis % MILLIS_PER_DAY) / MILLIS_PER_HOUR;
52+
}
53+
54+
getUTCMinutes(): i32 {
55+
return i32(this.epochMillis % MILLIS_PER_HOUR) / MILLIS_PER_MINUTE;
56+
}
57+
58+
getUTCSeconds(): i32 {
59+
return i32(this.epochMillis % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND;
60+
}
61+
62+
getUTCMilliseconds(): i32 {
63+
return i32(this.epochMillis % MILLIS_PER_SECOND);
64+
}
65+
66+
setUTCMilliseconds(value: i32): void {
67+
this.epochMillis += value - this.getUTCMilliseconds();
68+
}
69+
70+
setUTCSeconds(value: i32): void {
71+
throwIfNotInRange(value, 0, 59);
72+
this.epochMillis += (value - this.getUTCSeconds()) * MILLIS_PER_SECOND;
73+
}
74+
75+
setUTCMinutes(value: i32): void {
76+
throwIfNotInRange(value, 0, 59);
77+
this.epochMillis += (value - this.getUTCMinutes()) * MILLIS_PER_MINUTE;
78+
}
79+
80+
setUTCHours(value: i32): void {
81+
throwIfNotInRange(value, 0, 23);
82+
this.epochMillis += (value - this.getUTCHours()) * MILLIS_PER_HOUR;
83+
}
84+
85+
setUTCDate(value: i32): void {
86+
const ymd = ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY));
87+
throwIfNotInRange(value, 1, lastDayOfMonth(ymd.year, ymd.month));
88+
const mills = this.epochMillis % MILLIS_PER_DAY;
89+
this.epochMillis =
90+
i64(daysSinceEpoch(ymd.year, ymd.month, value)) * MILLIS_PER_DAY + mills;
91+
}
92+
93+
setUTCMonth(value: i32): void {
94+
throwIfNotInRange(value, 1, 12);
95+
const ymd = ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY));
96+
const mills = this.epochMillis % MILLIS_PER_DAY;
97+
this.epochMillis =
98+
i64(daysSinceEpoch(ymd.year, value + 1, ymd.day)) * MILLIS_PER_DAY +
99+
mills;
100+
}
101+
102+
setUTCFullYear(value: i32): void {
103+
const ymd = ymdFromEpochDays(i32(this.epochMillis / MILLIS_PER_DAY));
104+
const mills = this.epochMillis % MILLIS_PER_DAY;
105+
this.epochMillis =
106+
i64(daysSinceEpoch(value, ymd.month, ymd.day)) * MILLIS_PER_DAY + mills;
107+
}
108+
}
109+
110+
function throwIfNotInRange(value: i32, lower: i32, upper: i32): void {
111+
if (value < lower || value > upper) throw new RangeError(E_VALUEOUTOFRANGE);
112+
}
113+
114+
const MILLIS_PER_DAY = 1_000 * 60 * 60 * 24;
115+
const MILLIS_PER_HOUR = 1_000 * 60 * 60;
116+
const MILLIS_PER_MINUTE = 1_000 * 60;
117+
const MILLIS_PER_SECOND = 1_000;
118+
119+
class YMD {
120+
year: i32;
121+
month: i32;
122+
day: i32;
123+
}
124+
125+
// http://howardhinnant.github.io/date_algorithms.html#is_leap
126+
function isLeap(y: i32): bool {
127+
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
128+
}
129+
130+
// http://howardhinnant.github.io/date_algorithms.html#last_day_of_month_common_year
131+
function lastDayOfMonthNonLeapYear(m: i32): i32 {
132+
const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
133+
return days[m - 1];
134+
}
135+
136+
// http://howardhinnant.github.io/date_algorithms.html#last_day_of_month
137+
function lastDayOfMonth(y: i32, m: i32): i32 {
138+
return m != 2 || !isLeap(y) ? lastDayOfMonthNonLeapYear(m) : 29;
139+
}
140+
141+
// see: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
142+
function ymdFromEpochDays(z: i32): YMD {
143+
z += 719468;
144+
const era = (z >= 0 ? z : z - 146096) / 146097;
145+
const doe = z - era * 146097; // [0, 146096]
146+
const yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
147+
const y = yoe + era * 400;
148+
const doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
149+
const mp = (5 * doy + 2) / 153; // [0, 11]
150+
const d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
151+
const m = mp + (mp < 10 ? 3 : -9); // [1, 12]
152+
return {
153+
year: y + (m <= 2 ? 1 : 0),
154+
month: m,
155+
day: d,
156+
};
157+
}
158+
159+
// http://howardhinnant.github.io/date_algorithms.html#days_from_civil
160+
function daysSinceEpoch(y: i32, m: i32, d: i32): i32 {
161+
y -= m <= 2 ? 1 : 0;
162+
const era = (y >= 0 ? y : y - 399) / 400;
163+
const yoe = y - era * 400; // [0, 399]
164+
const doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
165+
const doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
166+
return era * 146097 + doe - 719468;
38167
}

std/assembly/index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,22 @@ declare class Date {
17161716
getTime(): i64;
17171717
/** Sets the UTC timestamp of this date in milliseconds. */
17181718
setTime(value: i64): i64;
1719+
1720+
getUTCFullYear(): i32;
1721+
getUTCMonth(): i32;
1722+
getUTCDate(): i32;
1723+
getUTCHours(): i32;
1724+
getUTCMinutes(): i32;
1725+
getUTCSeconds(): i32;
1726+
getUTCMilliseconds(): i32;
1727+
1728+
setUTCFullYear(value: i32): void;
1729+
setUTCMonth(value: i32): void;
1730+
setUTCDate(value: i32): void;
1731+
setUTCHours(value: i32): void;
1732+
setUTCMinutes(value: i32): void;
1733+
setUTCSeconds(value: i32): void;
1734+
setUTCMilliseconds(value: i32): void;
17191735
}
17201736

17211737
/** Class for representing a runtime error. Base class of all errors. */

std/assembly/util/error.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
// Common error messages for use accross the standard library. Keeping error messages compact
1+
// Common error messages for use across the standard library. Keeping error messages compact
22
// and reusing them where possible ensures minimal static data in binaries.
33

44
// @ts-ignore: decorator
55
@lazy @inline
66
export const E_INDEXOUTOFRANGE: string = "Index out of range";
77

8+
// @ts-ignore: decorator
9+
@lazy @inline
10+
export const E_VALUEOUTOFRANGE: string = "Value out of range";
11+
812
// @ts-ignore: decorator
913
@lazy @inline
1014
export const E_INVALIDLENGTH: string = "Invalid length";

tests/compiler/std/date.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"asc_flags": [
3+
"--explicitStart"
34
]
45
}

0 commit comments

Comments
 (0)