Skip to content

Commit 09ab6a8

Browse files
committed
Add earliest and latest function
1 parent b0321f6 commit 09ab6a8

File tree

6 files changed

+261
-0
lines changed

6 files changed

+261
-0
lines changed

src/datetime/_compare.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {
2+
getConstructor,
3+
isInstant,
4+
isPlainDate,
5+
isPlainDateTime,
6+
isPlainMonthDay,
7+
isPlainTime,
8+
isPlainYearMonth,
9+
isZonedDateTime,
10+
} from "../type-utils.js";
11+
import type { ComparableDateTimeType, Temporal } from "../types.js";
12+
13+
export function compare(
14+
a: Temporal.Instant,
15+
b: Temporal.Instant,
16+
): Temporal.ComparisonResult;
17+
export function compare(
18+
a: Temporal.ZonedDateTime,
19+
b: Temporal.ZonedDateTime,
20+
): Temporal.ComparisonResult;
21+
export function compare(
22+
a: Temporal.PlainDate,
23+
b: Temporal.PlainDate,
24+
): Temporal.ComparisonResult;
25+
export function compare(
26+
a: Temporal.PlainTime,
27+
b: Temporal.PlainTime,
28+
): Temporal.ComparisonResult;
29+
export function compare(
30+
a: Temporal.PlainDateTime,
31+
b: Temporal.PlainDateTime,
32+
): Temporal.ComparisonResult;
33+
export function compare(
34+
a: Temporal.PlainYearMonth,
35+
b: Temporal.PlainYearMonth,
36+
): Temporal.ComparisonResult;
37+
export function compare(
38+
a: ComparableDateTimeType,
39+
b: ComparableDateTimeType,
40+
): Temporal.ComparisonResult;
41+
export function compare(a: ComparableDateTimeType, b: ComparableDateTimeType) {
42+
if (isInstant(a)) {
43+
if (!isInstant(b)) {
44+
throw new Error("Unmatched type");
45+
}
46+
return getConstructor(a).compare(a, b);
47+
}
48+
if (isZonedDateTime(a)) {
49+
if (!isZonedDateTime(b)) {
50+
throw new Error("Unmatched type");
51+
}
52+
return getConstructor(a).compare(a, b);
53+
}
54+
if (isPlainDate(a)) {
55+
if (!isPlainDate(b)) {
56+
throw new Error("Unmatched type");
57+
}
58+
return getConstructor(a).compare(a, b);
59+
}
60+
if (isPlainTime(a)) {
61+
if (!isPlainTime(b)) {
62+
throw new Error("Unmatched type");
63+
}
64+
return getConstructor(a).compare(a, b);
65+
}
66+
if (isPlainDateTime(a)) {
67+
if (!isPlainDateTime(b)) {
68+
throw new Error("Unmatched type");
69+
}
70+
return getConstructor(a).compare(a, b);
71+
}
72+
if (isPlainYearMonth(a)) {
73+
if (!isPlainYearMonth(b)) {
74+
throw new Error("Unmatched type");
75+
}
76+
return getConstructor(a).compare(a, b);
77+
}
78+
if (isPlainMonthDay(a) || isPlainMonthDay(b)) {
79+
throw new Error("Can't compare PlainMonthDay");
80+
}
81+
throw new Error("Unknown type");
82+
}

src/datetime/earliest.test.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Temporal } from "@js-temporal/polyfill";
2+
import { expect, test } from "vitest";
3+
4+
import { earliest } from "./earliest.js";
5+
6+
test("earliest() with Instant", () => {
7+
const target = [1700000000, 1720000000, 1600000000].map((t) =>
8+
Temporal.Instant.fromEpochSeconds(t),
9+
);
10+
expect(earliest(target)).toBe(target[2]);
11+
});
12+
test("earliest() with ZonedDateTime", () => {
13+
const target = [
14+
"2024-01-01T09:00:00+09:00[Asia/Tokyo]",
15+
"2024-01-01T03:00:00+01:00[Europe/Paris]",
16+
"2024-01-01T00:00:00-05:00[America/Toronto]",
17+
].map((t) => Temporal.ZonedDateTime.from(t));
18+
expect(earliest(target)).toBe(target[0]);
19+
});
20+
test("earliest() with PlainDate", () => {
21+
const target = ["2024-01-01[u-ca=hebrew]", "2024-01-02", "2023-12-23"].map(
22+
(t) => Temporal.PlainDate.from(t),
23+
);
24+
expect(earliest(target)).toBe(target[2]);
25+
});
26+
test("earliest() with PlainTime", () => {
27+
const target = ["03:00:00", "06:00:00", "23:45:00"].map((t) =>
28+
Temporal.PlainTime.from(t),
29+
);
30+
expect(earliest(target)).toBe(target[0]);
31+
});
32+
test("earliest() with PlainDateTime", () => {
33+
const target = [
34+
"2024-01-01T09:00:00+09:00[Asia/Tokyo]",
35+
"2024-01-01T03:00:00+01:00[Europe/Paris]",
36+
"2024-01-01T00:00:00-05:00[America/Toronto]",
37+
].map((t) => Temporal.PlainDateTime.from(t));
38+
expect(earliest(target)).toBe(target[2]);
39+
});
40+
test("PlainYearMonth", () => {
41+
const target = ["2023-12", "2024-01", "2023-11"].map((t) =>
42+
Temporal.PlainYearMonth.from(t),
43+
);
44+
expect(earliest(target)).toBe(target[2]);
45+
});
46+
test("PlainMonthDay", () => {
47+
expect(() => {
48+
// @ts-expect-error
49+
earliest([Temporal.PlainMonthDay.from("12-03")]);
50+
}).toThrow();
51+
});

src/datetime/earliest.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { isPlainMonthDay } from "../type-utils.js";
2+
import type { Temporal } from "../types.js";
3+
import { compare } from "./_compare.js";
4+
5+
/**
6+
* Returns the earliest of the given datetime objects.
7+
* @param dateTimes array of datetime objects
8+
* @returns the earliest of the datetime objects
9+
*/
10+
export function earliest(dateTimes: Temporal.Instant[]): Temporal.Instant;
11+
export function earliest(
12+
dateTimes: Temporal.ZonedDateTime[],
13+
): Temporal.ZonedDateTime;
14+
export function earliest(dateTimes: Temporal.PlainDate[]): Temporal.PlainDate;
15+
export function earliest(dateTimes: Temporal.PlainTime[]): Temporal.PlainTime;
16+
export function earliest(
17+
dateTimes: Temporal.PlainDateTime[],
18+
): Temporal.PlainDateTime;
19+
export function earliest(
20+
dateTimes: Temporal.PlainYearMonth[],
21+
): Temporal.PlainYearMonth;
22+
export function earliest(
23+
dateTimes:
24+
| Temporal.Instant[]
25+
| Temporal.ZonedDateTime[]
26+
| Temporal.PlainDate[]
27+
| Temporal.PlainTime[]
28+
| Temporal.PlainDateTime[]
29+
| Temporal.PlainYearMonth[],
30+
) {
31+
if (dateTimes.some(isPlainMonthDay)) {
32+
throw new Error("Can't compare PlainMonthDay");
33+
}
34+
return dateTimes.reduce((a, b) => (compare(a, b) === 1 ? b : a));
35+
}

src/datetime/latest.test.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Temporal } from "@js-temporal/polyfill";
2+
import { expect, test } from "vitest";
3+
4+
import { latest } from "./latest.js";
5+
6+
test("latest() with Instant", () => {
7+
const target = [1700000000, 1720000000, 1600000000].map((t) =>
8+
Temporal.Instant.fromEpochSeconds(t),
9+
);
10+
expect(latest(target)).toBe(target[1]);
11+
});
12+
test("latest() with ZonedDateTime", () => {
13+
const target = [
14+
"2024-01-01T09:00:00+09:00[Asia/Tokyo]",
15+
"2024-01-01T03:00:00+01:00[Europe/Paris]",
16+
"2024-01-01T00:00:00-05:00[America/Toronto]",
17+
].map((t) => Temporal.ZonedDateTime.from(t));
18+
expect(latest(target)).toBe(target[2]);
19+
});
20+
test("latest() with PlainDate", () => {
21+
const target = ["2024-01-01[u-ca=hebrew]", "2024-01-02", "2023-12-23"].map(
22+
(t) => Temporal.PlainDate.from(t),
23+
);
24+
expect(latest(target)).toBe(target[1]);
25+
});
26+
test("latest() with PlainTime", () => {
27+
const target = ["03:00:00", "06:00:00", "23:45:00"].map((t) =>
28+
Temporal.PlainTime.from(t),
29+
);
30+
expect(latest(target)).toBe(target[2]);
31+
});
32+
test("latest() with PlainDateTime", () => {
33+
const target = [
34+
"2024-01-01T09:00:00+09:00[Asia/Tokyo]",
35+
"2024-01-01T03:00:00+01:00[Europe/Paris]",
36+
"2024-01-01T00:00:00-05:00[America/Toronto]",
37+
].map((t) => Temporal.PlainDateTime.from(t));
38+
expect(latest(target)).toBe(target[0]);
39+
});
40+
test("PlainYearMonth", () => {
41+
const target = ["2023-12", "2024-01", "2023-11"].map((t) =>
42+
Temporal.PlainYearMonth.from(t),
43+
);
44+
expect(latest(target)).toBe(target[1]);
45+
});
46+
test("PlainMonthDay", () => {
47+
expect(() => {
48+
// @ts-expect-error
49+
latest([Temporal.PlainMonthDay.from("12-03")]);
50+
}).toThrow();
51+
});

src/datetime/latest.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { isPlainMonthDay } from "../type-utils.js";
2+
import type { Temporal } from "../types.js";
3+
import { compare } from "./_compare.js";
4+
5+
/**
6+
* Returns the latest of the given datetime objects.
7+
* @param dateTimes array of datetime objects
8+
* @returns the latest of the datetime objects
9+
*/
10+
export function latest(dateTimes: Temporal.Instant[]): Temporal.Instant;
11+
export function latest(
12+
dateTimes: Temporal.ZonedDateTime[],
13+
): Temporal.ZonedDateTime;
14+
export function latest(dateTimes: Temporal.PlainDate[]): Temporal.PlainDate;
15+
export function latest(dateTimes: Temporal.PlainTime[]): Temporal.PlainTime;
16+
export function latest(
17+
dateTimes: Temporal.PlainDateTime[],
18+
): Temporal.PlainDateTime;
19+
export function latest(
20+
dateTimes: Temporal.PlainYearMonth[],
21+
): Temporal.PlainYearMonth;
22+
export function latest(
23+
dateTimes:
24+
| Temporal.Instant[]
25+
| Temporal.ZonedDateTime[]
26+
| Temporal.PlainDate[]
27+
| Temporal.PlainTime[]
28+
| Temporal.PlainDateTime[]
29+
| Temporal.PlainYearMonth[],
30+
) {
31+
if (dateTimes.some(isPlainMonthDay)) {
32+
throw new Error("Can't compare PlainMonthDay");
33+
}
34+
return dateTimes.reduce((a, b) => (compare(a, b) === -1 ? b : a));
35+
}

src/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ export type DateTimeType =
1111
| Temporal.PlainYearMonth
1212
| Temporal.PlainMonthDay;
1313
export type TemporalType = DateTimeType | Temporal.Duration;
14+
export type ComparableDateTimeType =
15+
| Temporal.Instant
16+
| Temporal.ZonedDateTime
17+
| Temporal.PlainDate
18+
| Temporal.PlainTime
19+
| Temporal.PlainDateTime
20+
| Temporal.PlainYearMonth;
1421
export type ComparableTemporalType =
1522
| Temporal.Instant
1623
| Temporal.ZonedDateTime

0 commit comments

Comments
 (0)