Skip to content

Commit 1cf37f5

Browse files
committed
Breaking: removes YYYY and YYYYMM formats. All valid dates here are now valid Temporal dates (not necessarily the other way though) and runs all tests through Temporal polyfill. Also fixes bug with error reporting on fractional hours and minutes.
1 parent a2afb4c commit 1cf37f5

File tree

6 files changed

+652
-547
lines changed

6 files changed

+652
-547
lines changed

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"license": "MIT",
2121
"devDependencies": {
2222
"luxon": "^3.7.1",
23+
"temporal-polyfill": "^0.3.0",
2324
"vitest": "^3.2.4"
2425
},
2526
"author": "Zach Leatherman <[email protected]> (https://zachleat.com/)",

parse.js

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
// Times are parsed as UTC if no offset is specified
2-
3-
class IsoDateParts {
2+
export class IsoDateParts {
43
static DEFAULT_TIMEZONE_OFFSET = {
54
hours: 0,
65
minutes: 0,
76
};
87

9-
static VAGUE_DATE_REGEX = /^\d{5,7}$/;
10-
static PARTIAL_DATE_REGEX = /^(\d{4})(?:-([01]\d)(?:-([0-3]\d))?)?$/;
11-
static FULL_DATE_REGEX = /^(\d{4})-?([01]\d)-?([0-3]\d)$/; // dashes optional when 8 digits
12-
13-
static VAGUE_DATETIME_REGEX = /^(\d{4})-?([01]\d)-?([0-3]\d)T(\d{1}|\d{3}|\d{5})(?:[\.\,](\d+))?([+-]\d{2}:?\d{2}|Z)?$/;
14-
static DATETIME_REGEX = /^(\d{4})-?([01]\d)-?([0-3]\d)T([0-2]\d)(?::?([0-5]\d))?(?::?([0-5]\d))?(?:[\.\,](\d+))?(Z|[+-][0-2]\d(?::?[0-5]\d)?)?$/;
8+
static FULL_DATE_REGEX = /^(\d{4})-?([01]\d)-?([0-3]\d)$/;
9+
static DATETIME_REGEX = /^(\d{4})-?([01]\d)-?([0-3]\d)T([0-2]\d(?:[\.\,]\d+)?)(?::?([0-5]\d(?:[\.\,]\d+)?)(?::?([0-5]\d))?(?:[\.\,](\d+))?)?(Z|[+-][0-2]\d(?::?[0-5]\d)?)?$/;
10+
static TIMEZONE_REGEX = /^([+-]\d{2})(?::?(\d{2}))?$/;
11+
static IS_FRACTIONAL_REGEX = /^\d+[\.\,]\d+$/;
1512

16-
// this format is cursed, burn it with fire https://en.wikipedia.org/wiki/ISO_week_date
17-
static DATE_WEEK_REGEX = /^(\d{4})-W\d{2}-?\d{0,1}/; // unsupported
18-
static YEARDAY_REGEX = /^(\d{4})-?\d{3}$/; // unsupported
19-
static TIMEZONE_REGEX = /^([+-]\d{2})(?::?(\d{2}))?$/; // unsupported
13+
// Unsupported formats https://en.wikipedia.org/wiki/ISO_week_date
14+
static DATE_WEEK_REGEX = /^(\d{4})-W\d{2}-?\d{0,1}/;
15+
static YEARDAY_REGEX = /^(\d{4})-?\d{3}$/;
2016

2117
static getTimezoneOffset(offset = "") {
2218
if(offset === "Z") {
@@ -51,35 +47,28 @@ class IsoDateParts {
5147

5248
static getParts(str) {
5349
if(str.match(this.DATE_WEEK_REGEX) || str.match(this.YEARDAY_REGEX)) {
54-
throw new Error(`Unsupported date format (dropped syntax): ${str}`);
55-
}
56-
57-
if(str.match(this.VAGUE_DATE_REGEX)) {
58-
throw new Error(`Unsupported date format (vague date): ${str}`);
50+
throw new Error(`Unsupported date format (unsupported syntax): ${str}`);
5951
}
6052

61-
let dateMatch = str.match(this.PARTIAL_DATE_REGEX) || str.match(this.FULL_DATE_REGEX);
53+
let dateMatch = str.match(this.FULL_DATE_REGEX);
6254
if(dateMatch) {
6355
return this.getByDateTime(dateMatch);
6456
}
6557

66-
let vagueDatetime = str.match(this.VAGUE_DATETIME_REGEX);
67-
if(vagueDatetime) {
68-
throw new Error(`Unsupported date format (vague datetime): ${str}`);
69-
}
70-
7158
let dateTimeMatch = str.match(this.DATETIME_REGEX);
7259
if(dateTimeMatch) {
60+
if(dateTimeMatch[4]?.match(this.IS_FRACTIONAL_REGEX) || dateTimeMatch[5]?.match(this.IS_FRACTIONAL_REGEX)) {
61+
throw new Error(`Unsupported date format (fractional hours or minutes): ${str}`);
62+
}
63+
7364
return this.getByDateTime(dateTimeMatch);
7465
}
7566

76-
throw new Error(`Unsupported date format (unknown): ${str}`);
67+
throw new Error(`Unsupported date format: ${str}`);
7768
}
7869
}
7970

80-
class IsoDate {
81-
#source;
82-
71+
export class IsoDate {
8372
static parse(str) {
8473
let parts = IsoDateParts.getParts(str);
8574
if(parts) {
@@ -102,14 +91,6 @@ class IsoDate {
10291
return [this.year, this.month, this.day, this.hours, this.minutes, this.seconds, this.milliseconds];
10392
}
10493

105-
set source(val) {
106-
this.#source = val;
107-
}
108-
109-
get source() {
110-
return this.#source;
111-
}
112-
11394
checkParts() {
11495
// months: 0-indexed, 0–11 are valid
11596
if(this.month < 0 || this.month > 11) {
@@ -123,4 +104,6 @@ class IsoDate {
123104
}
124105
}
125106

126-
export { IsoDate };
107+
export function parse(str) {
108+
return IsoDate.parse(str);
109+
}

0 commit comments

Comments
 (0)