Skip to content

Commit

Permalink
parse anything that looks like a date
Browse files Browse the repository at this point in the history
  • Loading branch information
david-crespo committed Feb 6, 2025
1 parent b843d30 commit 14446f5
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 30 deletions.
12 changes: 9 additions & 3 deletions oxide-api/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const isObjectOrArray = (o: unknown) =>
export const mapObj =
(
kf: (k: string) => string,
vf: (k: string | undefined, v: unknown) => unknown = (k, v) => v,
vf: (k: string | undefined, v: unknown) => unknown = (_k, v) => v,
) =>
(o: unknown): unknown => {
if (!isObjectOrArray(o)) return o;
Expand All @@ -45,8 +45,14 @@ export const mapObj =
return newObj;
};

export const parseIfDate = (k: string | undefined, v: unknown) => {
if (typeof v === "string" && (k?.startsWith("time_") || k === "timestamp")) {
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?Z$/;

/**
* Use regex to recognize a likely date and then attempt to parse it. Original
* value is passed through if either step fails.
*/
export const parseIfDate = (_k: string | undefined, v: unknown) => {
if (typeof v === "string" && isoDateRegex.test(v)) {
const d = new Date(v);
if (isNaN(d.getTime())) return v;
return d;
Expand Down
4 changes: 2 additions & 2 deletions oxide-openapi-gen-ts/src/client/static/http-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ describe("handleResponse", () => {
});

it("parses dates and converts to camel case", async () => {
const resp = json({ time_created: "2022-05-01" });
const resp = json({ time_created: "2022-05-01T02:03:04Z" });
const { response, ...rest } = await handleResponse(resp);
expect(rest).toMatchObject({
type: "success",
data: {
timeCreated: new Date(Date.UTC(2022, 4, 1)),
timeCreated: new Date(Date.UTC(2022, 4, 1, 2, 3, 4)),
},
});
expect(response.headers.get("Content-Type")).toBe("application/json");
Expand Down
34 changes: 12 additions & 22 deletions oxide-openapi-gen-ts/src/client/static/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test("isObjectOrArray", () => {
describe("mapObj", () => {
const fn = mapObj(
(k) => k + "_",
(k, v) => (typeof v === "number" ? v * 2 : v)
(_k, v) => (typeof v === "number" ? v * 2 : v)
);

it("leaves non-objects alone", () => {
Expand Down Expand Up @@ -78,34 +78,24 @@ test("processResponseBody", () => {
});

describe("parseIfDate", () => {
it("passes through non-date values", () => {
expect(parseIfDate("abc", 123)).toEqual(123);
expect(parseIfDate("abc", "def")).toEqual("def");
it.each([
123,
"def",
// missing 0 ↓ here
"2021-05-03T05:3:05Z",
])("passes through non-date value %s", (v) => {
expect(parseIfDate("abc", v)).toEqual(v);
expect(parseIfDate("time_created", v)).toEqual(v);
});

const timestamp = 1643092429315;
const dateStr = new Date(timestamp).toISOString();
it("parses values that pass the regex for dates", () => {
const timestamp = 1643092429315;
const dateStr = new Date(timestamp).toISOString();

it("doesn't parse dates if key doesn't start with time_", () => {
expect(parseIfDate("abc", dateStr)).toEqual(dateStr);
});

it("parses dates if key starts with time_", () => {
const value = parseIfDate("time_whatever", dateStr);
expect(value).toBeInstanceOf(Date);
expect((value as Date).getTime()).toEqual(timestamp);
});

it("parses dates if key = 'timestamp'", () => {
const value = parseIfDate("timestamp", dateStr);
expect(value).toBeInstanceOf(Date);
expect((value as Date).getTime()).toEqual(timestamp);
});

it("passes through values that fail to parse as dates", () => {
const value = parseIfDate("time_whatever", "blah");
expect(value).toEqual("blah");
});
});

test("snakeify", () => {
Expand Down
12 changes: 9 additions & 3 deletions oxide-openapi-gen-ts/src/client/static/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const isObjectOrArray = (o: unknown) =>
export const mapObj =
(
kf: (k: string) => string,
vf: (k: string | undefined, v: unknown) => unknown = (k, v) => v
vf: (k: string | undefined, v: unknown) => unknown = (_k, v) => v
) =>
(o: unknown): unknown => {
if (!isObjectOrArray(o)) return o;
Expand All @@ -45,8 +45,14 @@ export const mapObj =
return newObj;
};

export const parseIfDate = (k: string | undefined, v: unknown) => {
if (typeof v === "string" && (k?.startsWith("time_") || k === "timestamp")) {
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?Z$/;

/**
* Use regex to recognize a likely date and then attempt to parse it. Original
* value is passed through if either step fails.
*/
export const parseIfDate = (_k: string | undefined, v: unknown) => {
if (typeof v === "string" && isoDateRegex.test(v)) {
const d = new Date(v);
if (isNaN(d.getTime())) return v;
return d;
Expand Down

0 comments on commit 14446f5

Please sign in to comment.