From e351ddcecfab05fc51932015efc741f018ea63d3 Mon Sep 17 00:00:00 2001 From: Jimmy Callin Date: Fri, 19 Dec 2025 11:56:59 +0100 Subject: [PATCH] supports date format --- source/session.ts | 26 ++++++++++- source/util/convert_to_iso_string.ts | 11 +++++ test/convert_to_iso_string.test.ts | 23 +++++++++- test/session.test.ts | 69 ++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/source/session.ts b/source/session.ts index 042951d7..32314804 100644 --- a/source/session.ts +++ b/source/session.ts @@ -34,7 +34,10 @@ import type { SessionOptions, UpdateResponse, } from "./types.js"; -import { convertToIsoString } from "./util/convert_to_iso_string.js"; +import { + convertToIsoString, + isDateOnly, +} from "./util/convert_to_iso_string.js"; import { Uploader } from "./uploader.js"; import getSchemaMappingFromSchemas from "./util/get_schema_mapping.js"; @@ -374,6 +377,9 @@ export class Session< if (data.__type__ === "datetime") { return this._decodeDateTimeAsIso(data); } + if (data.__type__ === "date") { + return this._decodeDateAsString(data); + } return this._decodePlainObject(data, identityMap, { ensureSerializableResponse, }); @@ -400,6 +406,24 @@ export class Session< return new Date(dateValue).toISOString(); } + /** + * Decode date *data* into date-only strings (YYYY-MM-DD). + * + * Translate objects with __type__ equal to 'date' into date-only strings + * without time information. Validates the format is YYYY-MM-DD. + * @private + */ + private _decodeDateAsString(data: any) { + const dateValue = data.value; + // Validate that the server sends date-only format (YYYY-MM-DD) + if (!isDateOnly(dateValue)) { + throw new Error( + `Invalid date format: expected YYYY-MM-DD, got ${dateValue}`, + ); + } + return dateValue; + } + /** * Return new object where all values have been decoded. * @private diff --git a/source/util/convert_to_iso_string.ts b/source/util/convert_to_iso_string.ts index 8e6e9ebc..c4527d3e 100644 --- a/source/util/convert_to_iso_string.ts +++ b/source/util/convert_to_iso_string.ts @@ -15,6 +15,17 @@ function isIsoDate(str: string) { ); } +/** + * Checks if string is in date-only format (YYYY-MM-DD). + * Used for __date__ type that doesn't include time information. + * + * Matches: + * - YYYY-MM-DD + */ +export function isDateOnly(str: string) { + return /^\d{4}-\d\d-\d\d$/i.test(str); +} + /** * Converts a string or date object to ISO 6801 compatible string. * Supports converting regular date objects, or any object that has toISOString() method such as dayjs. diff --git a/test/convert_to_iso_string.test.ts b/test/convert_to_iso_string.test.ts index 39c58677..e3f6a9c8 100644 --- a/test/convert_to_iso_string.test.ts +++ b/test/convert_to_iso_string.test.ts @@ -1,6 +1,9 @@ // :copyright: Copyright (c) 2022 ftrack -import { convertToIsoString } from "../source/util/convert_to_iso_string.js"; +import { + convertToIsoString, + isDateOnly, +} from "../source/util/convert_to_iso_string.js"; import dayjs from "dayjs"; import { describe, it, expect } from "vitest"; @@ -76,3 +79,21 @@ describe("convertToIsoString", () => { expect(converted).toEqual(null); }); }); + +describe("isDateOnly", () => { + it("should validate correct date-only format", () => { + expect(isDateOnly("2025-12-01")).toBe(true); + expect(isDateOnly("2023-01-15")).toBe(true); + expect(isDateOnly("1999-12-31")).toBe(true); + }); + + it("should reject invalid date-only formats", () => { + expect(isDateOnly("2025-12-01T00:00:00Z")).toBe(false); + expect(isDateOnly("2025-12-01T00:00:00")).toBe(false); + expect(isDateOnly("not-a-date")).toBe(false); + expect(isDateOnly("2025-12")).toBe(false); + expect(isDateOnly("2025")).toBe(false); + expect(isDateOnly("12-01-2025")).toBe(false); + expect(isDateOnly("")).toBe(false); + }); +}); diff --git a/test/session.test.ts b/test/session.test.ts index 9bd2bf9a..51b4c196 100755 --- a/test/session.test.ts +++ b/test/session.test.ts @@ -738,6 +738,75 @@ describe("Encoding entities", () => { ); expect(output.foo).toEqual(now.toISOString()); }); + + it("Should support decoding date as date-only string", () => { + //@ts-ignore - Otherwise internal method used for testing purposes + const output = session.decode( + { + foo: { + __type__: "date", + value: "2025-12-01", + }, + }, + {}, + ); + expect(output.foo).toEqual("2025-12-01"); + }); + + it("Should handle date type in nested objects", () => { + //@ts-ignore - Otherwise internal method used for testing purposes + const output = session.decode( + { + task: { + name: "Test Task", + start_date: { + __type__: "date", + value: "2025-12-01", + }, + end_date: { + __type__: "date", + value: "2025-12-31", + }, + }, + }, + {}, + ); + expect(output.task.start_date).toEqual("2025-12-01"); + expect(output.task.end_date).toEqual("2025-12-31"); + }); + + it("Should handle arrays of date types", () => { + //@ts-ignore - Otherwise internal method used for testing purposes + const output = session.decode( + [ + { + __type__: "date", + value: "2025-12-01", + }, + { + __type__: "date", + value: "2025-12-15", + }, + ], + {}, + ); + expect(output).toEqual(["2025-12-01", "2025-12-15"]); + }); + + it("Should throw error for invalid date format", () => { + expect(() => { + //@ts-ignore - Otherwise internal method used for testing purposes + session.decode( + { + foo: { + __type__: "date", + value: "2025-12-01T00:00:00Z", + }, + }, + {}, + ); + }).toThrow("Invalid date format: expected YYYY-MM-DD"); + }); }); it("Should support encoding Date object dates", () => {