Skip to content

Commit a1acfad

Browse files
committed
Add expiresAt, registeredAt, updatedAt, and expiresInDays date helper functions
1 parent 945bb45 commit a1acfad

3 files changed

Lines changed: 95 additions & 0 deletions

File tree

src/dates.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Dates } from "./types.js";
2+
3+
/**
4+
* Parse the registered date into a Date object, or null if absent or unparseable.
5+
*/
6+
export function registeredAt(dates: Dates): Date | null {
7+
return parseISO(dates.registered);
8+
}
9+
10+
/**
11+
* Parse the expiry date into a Date object, or null if absent or unparseable.
12+
*/
13+
export function expiresAt(dates: Dates): Date | null {
14+
return parseISO(dates.expires);
15+
}
16+
17+
/**
18+
* Parse the updated date into a Date object, or null if absent or unparseable.
19+
*/
20+
export function updatedAt(dates: Dates): Date | null {
21+
return parseISO(dates.updated);
22+
}
23+
24+
/**
25+
* Days until expiration, or null if no expiry date is available.
26+
*
27+
* Returns a negative number if the domain has already expired.
28+
*/
29+
export function expiresInDays(dates: Dates): number | null {
30+
const exp = expiresAt(dates);
31+
if (exp === null) {
32+
return null;
33+
}
34+
return Math.floor((exp.getTime() - Date.now()) / 86_400_000);
35+
}
36+
37+
function parseISO(value: string | null): Date | null {
38+
if (value === null) {
39+
return null;
40+
}
41+
const d = new Date(value);
42+
return isNaN(d.getTime()) ? null : d;
43+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ export type {
3232
Remark,
3333
} from "./types.js";
3434

35+
export { expiresAt, expiresInDays, registeredAt, updatedAt } from "./dates.js";
36+
3537
export { VERSION } from "./version.js";

tests/dates.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, expect, it } from "vitest";
2+
import { expiresAt, expiresInDays, registeredAt, updatedAt } from "../src/dates.js";
3+
import type { Dates } from "../src/types.js";
4+
5+
describe("date helpers", () => {
6+
const dates: Dates = {
7+
registered: "2020-01-01T00:00:00Z",
8+
expires: "2028-09-14T04:00:00Z",
9+
updated: "2024-02-20T10:16:08Z",
10+
};
11+
12+
it("parses registered date", () => {
13+
const d = registeredAt(dates);
14+
expect(d).toBeInstanceOf(Date);
15+
expect(d!.getUTCFullYear()).toBe(2020);
16+
});
17+
18+
it("parses expires date", () => {
19+
const d = expiresAt(dates);
20+
expect(d).toBeInstanceOf(Date);
21+
expect(d!.getUTCFullYear()).toBe(2028);
22+
});
23+
24+
it("parses updated date", () => {
25+
const d = updatedAt(dates);
26+
expect(d).toBeInstanceOf(Date);
27+
expect(d!.getUTCFullYear()).toBe(2024);
28+
});
29+
30+
it("calculates days until expiry", () => {
31+
const days = expiresInDays(dates);
32+
expect(days).toBeTypeOf("number");
33+
expect(days!).toBeGreaterThan(0);
34+
});
35+
36+
it("returns null for null fields", () => {
37+
const nullDates: Dates = { registered: null, expires: null, updated: null };
38+
expect(registeredAt(nullDates)).toBeNull();
39+
expect(expiresAt(nullDates)).toBeNull();
40+
expect(updatedAt(nullDates)).toBeNull();
41+
expect(expiresInDays(nullDates)).toBeNull();
42+
});
43+
44+
it("returns null for invalid date strings", () => {
45+
const badDates: Dates = { registered: "not-a-date", expires: "garbage", updated: null };
46+
expect(registeredAt(badDates)).toBeNull();
47+
expect(expiresAt(badDates)).toBeNull();
48+
expect(expiresInDays(badDates)).toBeNull();
49+
});
50+
});

0 commit comments

Comments
 (0)