Skip to content

Commit b90059e

Browse files
committed
Add fromRfc7231 function
1 parent 1d1b8b4 commit b90059e

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- `startOfYear`, `startOfMonth`, `startOfDay`, `startOfHour`, `startOfMinute`, `startOfSecond`, `endOfYear`, `endOfMonth`, `endOfDay`, `endOfHour`, `endOfMinute`, `endOfSecond`, `fromRfc2822` functions
12+
- `startOfYear`, `startOfMonth`, `startOfDay`, `startOfHour`, `startOfMinute`, `startOfSecond`, `endOfYear`, `endOfMonth`, `endOfDay`, `endOfHour`, `endOfMinute`, `endOfSecond`, `fromRfc2822`, `fromRfc7231` functions
1313

1414
### Changed
1515

src/datetime/fromRfc7231.test.ts

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Temporal } from "@js-temporal/polyfill";
2+
import { expect, test } from "vitest";
3+
4+
import { fromRfc7231 } from "./fromRfc7231.js";
5+
6+
test("Instant", () => {
7+
expect(
8+
fromRfc7231("Fri, 07 Jun 2024 01:23:45 GMT", Temporal.Instant),
9+
).toEqual(Temporal.Instant.from("2024-06-07T01:23:45Z"));
10+
});
11+
12+
test("PlainDateTime", () => {
13+
expect(
14+
fromRfc7231("Fri, 07 Jun 2024 01:23:45 GMT", Temporal.PlainDateTime),
15+
).toEqual(Temporal.PlainDateTime.from("2024-06-07T01:23:45"));
16+
});
17+
18+
test("ZonedDateTime", () => {
19+
expect(
20+
fromRfc7231("Fri, 07 Jun 2024 01:23:45 GMT", Temporal.ZonedDateTime),
21+
).toEqual(Temporal.ZonedDateTime.from("2024-06-07T01:23:45+00:00[UTC]"));
22+
});
23+
24+
test("wrong format", () => {
25+
expect(() => {
26+
fromRfc7231("Fri, 7 Jun 2024 01:23:45 GMT", Temporal.Instant);
27+
}).toThrowError();
28+
});
29+
30+
test("wrong day of week", () => {
31+
expect(() => {
32+
fromRfc7231("Tue, 07 Jun 2024 01:23:45 GMT", Temporal.Instant);
33+
}).toThrowError(/Tue/);
34+
expect(() => {
35+
fromRfc7231("Tue, 07 Jun 2024 01:23:45 GMT", Temporal.PlainDateTime);
36+
}).toThrowError(/Tue/);
37+
expect(() => {
38+
fromRfc7231("Tue, 07 Jun 2024 01:23:45 GMT", Temporal.ZonedDateTime);
39+
}).toThrowError(/Tue/);
40+
});
41+
42+
test("invalid day of week", () => {
43+
expect(() => {
44+
fromRfc7231("Mot, 07 Jun 2024 01:23:45 GMT", Temporal.Instant);
45+
}).toThrowError(/Mot/);
46+
expect(() => {
47+
fromRfc7231("Mot, 07 Jun 2024 01:23:45 GMT", Temporal.PlainDateTime);
48+
}).toThrowError(/Mot/);
49+
expect(() => {
50+
fromRfc7231("Mot, 07 Jun 2024 01:23:45 GMT", Temporal.ZonedDateTime);
51+
}).toThrowError(/Mot/);
52+
});
53+
54+
test("invalid month", () => {
55+
expect(() => {
56+
fromRfc7231("Mon, 07 Jut 2024 01:23:45 GMT", Temporal.Instant);
57+
}).toThrowError(/Jut/);
58+
expect(() => {
59+
fromRfc7231("Mon, 07 Jut 2024 01:23:45 GMT", Temporal.PlainDateTime);
60+
}).toThrowError(/Jut/);
61+
expect(() => {
62+
fromRfc7231("Mon, 07 Jut 2024 01:23:45 GMT", Temporal.ZonedDateTime);
63+
}).toThrowError(/Jut/);
64+
});

src/datetime/fromRfc7231.ts

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
isInstantConstructor,
3+
isZonedDateTimeConstructor,
4+
} from "../type-utils.js";
5+
import type { Temporal } from "../types.js";
6+
import { formatExactTimeIso } from "./_formatExactTimeIso.js";
7+
import { getDayOfWeekFromYmd } from "./_getDayOfWeekFromYmd.js";
8+
import { getDayOfWeekNumberFromAbbreviation } from "./_getDayOfWeekNumberFromAbbreviation.js";
9+
import { getMonthNumberFromAbbreviation } from "./_getMonthNumberFromAbbreviation.js";
10+
11+
const regex =
12+
/^([A-Za-z]{3}), (\d\d) ([A-Za-z]{3}) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/;
13+
14+
function parse(date: string) {
15+
const result = regex.exec(date);
16+
if (result === null) {
17+
throw new Error("Invalid format");
18+
}
19+
const [, dayOfWeek, day, monthName, year, hour, minute, second] = result;
20+
if (
21+
dayOfWeek === undefined ||
22+
day === undefined ||
23+
monthName === undefined ||
24+
year === undefined ||
25+
hour === undefined ||
26+
minute === undefined ||
27+
second === undefined
28+
) {
29+
throw new Error("something wrong");
30+
}
31+
const y = parseInt(year);
32+
const m = getMonthNumberFromAbbreviation(monthName);
33+
const d = parseInt(day);
34+
const weekNum = getDayOfWeekNumberFromAbbreviation(dayOfWeek);
35+
if (getDayOfWeekFromYmd(y, m, d) !== weekNum) {
36+
throw new Error(`Wrong day of week: ${dayOfWeek}`);
37+
}
38+
return [y, m, d, parseInt(hour), parseInt(minute), parseInt(second)] as const;
39+
}
40+
41+
/**
42+
* Creates Temporal object from datetime string in RFC 7231's format (HTTP date format)
43+
* such as `Mon, 01 Jan 2024 01:23:45 GMT`.
44+
* This function doesn't support obsoleted formats.
45+
*
46+
* @param date datetime string in RFC 7231's format
47+
* @param TemporalClass Temporal class (such as `Temporal.PlainDateTime` or `Temporal.Instant`) which will be returned
48+
* @returns an instance of Temporal class specified in `TemporalClass` argument
49+
*/
50+
export function fromRfc7231<
51+
TemporalClassType extends
52+
| typeof Temporal.Instant
53+
| typeof Temporal.ZonedDateTime
54+
| typeof Temporal.PlainDateTime,
55+
>(
56+
date: string,
57+
TemporalClass: TemporalClassType,
58+
): InstanceType<TemporalClassType> {
59+
const result = parse(date);
60+
const [year, month, day, hour, minute, second] = result;
61+
if (isInstantConstructor(TemporalClass)) {
62+
return TemporalClass.from(
63+
formatExactTimeIso(year, month, day, hour, minute, second, 0, "Z"),
64+
) as InstanceType<TemporalClassType>;
65+
}
66+
if (isZonedDateTimeConstructor(TemporalClass)) {
67+
return TemporalClass.from({
68+
year,
69+
month,
70+
day,
71+
hour,
72+
minute,
73+
second,
74+
calendarId: "iso8601",
75+
timeZone: "UTC",
76+
}) as InstanceType<TemporalClassType>;
77+
}
78+
return TemporalClass.from({
79+
year,
80+
month,
81+
day,
82+
hour,
83+
minute,
84+
second,
85+
calendarId: "iso8601",
86+
}) as InstanceType<TemporalClassType>;
87+
}

src/datetime/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export {
2020
type FormatWithoutLocaleOptions,
2121
} from "./formatWithoutLocale.js";
2222
export { fromRfc2822 } from "./fromRfc2822.js";
23+
export { fromRfc7231 } from "./fromRfc7231.js";
2324
export { isAfter } from "./isAfter.js";
2425
export { isBefore } from "./isBefore.js";
2526
export { isWithinInterval } from "./isWithinInterval.js";

0 commit comments

Comments
 (0)