Skip to content

Commit 61a343b

Browse files
b2lclaude
andcommitted
[typescript-fetch] rebase on master: fold the #24509 null guards into the date helpers
Upstream #24509 null-guarded required date properties; keep that guard but route the conversion through parseDate/serializeDate. Port the form param date handling into the apisFormParams partial extracted by #23935, and regenerate the affected samples. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9f17f40 commit 61a343b

6 files changed

Lines changed: 62 additions & 11 deletions

File tree

modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,9 +1090,9 @@ public void testDateLibraryDateIsTheDefault() throws IOException {
10901090
Path event = Paths.get(output + "/models/Event.ts");
10911091
TestUtils.assertFileContains(event, "startsOn: Date;");
10921092
TestUtils.assertFileContains(event, "createdAt?: Date;");
1093-
TestUtils.assertFileContains(event, "'startsOn': (parseDate(json['startsOn']))");
1093+
TestUtils.assertFileContains(event, "'startsOn': (json['startsOn'] == null ? json['startsOn'] : parseDate(json['startsOn']))");
10941094
TestUtils.assertFileContains(event, "'createdAt': json['createdAt'] == null ? undefined : (parseDateTime(json['createdAt']))");
1095-
TestUtils.assertFileContains(event, "'startsOn': serializeDate(value['startsOn'])");
1095+
TestUtils.assertFileContains(event, "'startsOn': value['startsOn'] == null ? value['startsOn'] : serializeDate(value['startsOn'])");
10961096

10971097
Path runtime = Paths.get(output + "/runtime.ts");
10981098
TestUtils.assertFileContains(runtime, "export function parseDate(");

samples/client/petstore/typescript-fetch/builds/date-library-date/models/Event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function EventFromJSONTyped(json: any, ignoreDiscriminator: boolean): Eve
5151
}
5252
return {
5353

54-
'startsOn': (parseDate(json['startsOn'])),
54+
'startsOn': (json['startsOn'] == null ? json['startsOn'] : parseDate(json['startsOn'])),
5555
'endsOn': json['endsOn'] === undefined ? undefined : json['endsOn'] === null ? null : (parseDate(json['endsOn'])),
5656
'createdAt': json['createdAt'] == null ? undefined : (parseDateTime(json['createdAt'])),
5757
};
@@ -68,7 +68,7 @@ export function EventToJSONTyped(value?: Event | null, ignoreDiscriminator: bool
6868

6969
return {
7070

71-
'startsOn': serializeDate(value['startsOn']),
71+
'startsOn': value['startsOn'] == null ? value['startsOn'] : serializeDate(value['startsOn']),
7272
'endsOn': value['endsOn'] == null ? value['endsOn'] : serializeDate(value['endsOn']),
7373
'createdAt': value['createdAt'] == null ? value['createdAt'] : serializeDateTime(value['createdAt']),
7474
};

samples/client/petstore/typescript-fetch/builds/default-v3.0/models/FormatTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export function FormatTestFromJSONTyped(json: any, ignoreDiscriminator: boolean)
116116
'string': json['string'] == null ? undefined : json['string'],
117117
'_byte': json['byte'],
118118
'binary': json['binary'] == null ? undefined : json['binary'],
119-
'date': (parseDate(json['date'])),
119+
'date': (json['date'] == null ? json['date'] : parseDate(json['date'])),
120120
'dateTime': json['dateTime'] == null ? undefined : (parseDateTime(json['dateTime'])),
121121
'uuid': json['uuid'] == null ? undefined : json['uuid'],
122122
'password': json['password'],
@@ -146,7 +146,7 @@ export function FormatTestToJSONTyped(value?: FormatTest | null, ignoreDiscrimin
146146
'string': value['string'],
147147
'byte': value['_byte'],
148148
'binary': value['binary'],
149-
'date': serializeDate(value['date']),
149+
'date': value['date'] == null ? value['date'] : serializeDate(value['date']),
150150
'dateTime': value['dateTime'] == null ? value['dateTime'] : serializeDateTime(value['dateTime']),
151151
'uuid': value['uuid'],
152152
'password': value['password'],

samples/client/petstore/typescript-fetch/builds/kebab-case/models/format-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export function FormatTestFromJSONTyped(json: any, ignoreDiscriminator: boolean)
116116
'string': json['string'] == null ? undefined : json['string'],
117117
'_byte': json['byte'],
118118
'binary': json['binary'] == null ? undefined : json['binary'],
119-
'date': (parseDate(json['date'])),
119+
'date': (json['date'] == null ? json['date'] : parseDate(json['date'])),
120120
'dateTime': json['dateTime'] == null ? undefined : (parseDateTime(json['dateTime'])),
121121
'uuid': json['uuid'] == null ? undefined : json['uuid'],
122122
'password': json['password'],
@@ -146,7 +146,7 @@ export function FormatTestToJSONTyped(value?: FormatTest | null, ignoreDiscrimin
146146
'string': value['string'],
147147
'byte': value['_byte'],
148148
'binary': value['binary'],
149-
'date': serializeDate(value['date']),
149+
'date': value['date'] == null ? value['date'] : serializeDate(value['date']),
150150
'dateTime': value['dateTime'] == null ? value['dateTime'] : serializeDateTime(value['dateTime']),
151151
'uuid': value['uuid'],
152152
'password': value['password'],

samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/FormatTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export function FormatTestFromJSONTyped(json: any, ignoreDiscriminator: boolean)
116116
'string': json['string'] == null ? undefined : json['string'],
117117
'_byte': json['byte'],
118118
'binary': json['binary'] == null ? undefined : json['binary'],
119-
'date': (parseDate(json['date'])),
119+
'date': (json['date'] == null ? json['date'] : parseDate(json['date'])),
120120
'dateTime': json['dateTime'] == null ? undefined : (parseDateTime(json['dateTime'])),
121121
'uuid': json['uuid'] == null ? undefined : json['uuid'],
122122
'password': json['password'],
@@ -146,7 +146,7 @@ export function FormatTestToJSONTyped(value?: FormatTest | null, ignoreDiscrimin
146146
'string': value['string'],
147147
'byte': value['_byte'],
148148
'binary': value['binary'],
149-
'date': serializeDate(value['date']),
149+
'date': value['date'] == null ? value['date'] : serializeDate(value['date']),
150150
'dateTime': value['dateTime'] == null ? value['dateTime'] : serializeDateTime(value['dateTime']),
151151
'uuid': value['uuid'],
152152
'password': value['password'],

samples/client/petstore/typescript-fetch/builds/split-by-content-type/src/runtime.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ function querystringSingleKey(key: string, value: string | number | null | undef
359359
return querystringSingleKey(key, valueAsArray, keyPrefix);
360360
}
361361
if (value instanceof Date) {
362-
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
362+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(serializeDateTime(value))}`;
363363
}
364364
if (value instanceof Object) {
365365
return querystring(value as HTTPQuery, fullKey);
@@ -372,6 +372,57 @@ export function exists(json: any, key: string) {
372372
return value !== null && value !== undefined;
373373
}
374374

375+
/**
376+
* Every generated date call site routes through these.
377+
*
378+
* `format: date` is a calendar date, with no time and no offset, so it is converted
379+
* against the local calendar on both ends: they have to agree or the date shifts by
380+
* a day. `format: date-time` is an instant and uses UTC.
381+
*/
382+
export function serializeDateTime(value: Date): string {
383+
return value.toISOString();
384+
}
385+
386+
export function serializeDate(value: Date): string {
387+
if (isNaN(value.getTime())) {
388+
throw new RangeError('Invalid time value');
389+
}
390+
const year = ('000' + value.getFullYear()).slice(-4);
391+
const month = ('0' + (value.getMonth() + 1)).slice(-2);
392+
const day = ('0' + value.getDate()).slice(-2);
393+
return `${year}-${month}-${day}`;
394+
}
395+
396+
397+
export function parseDate(value: any): Date {
398+
if (value instanceof Date) {
399+
return value;
400+
}
401+
// `new Date("2026-08-05")` would parse as UTC midnight: a different day west of UTC.
402+
// Local midnight is the stated day everywhere. setFullYear avoids the 1900 offset the
403+
// multi-argument constructor applies to years 0-99.
404+
const fullDate = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value));
405+
if (fullDate) {
406+
const year = Number(fullDate[1]);
407+
const month = Number(fullDate[2]) - 1;
408+
const day = Number(fullDate[3]);
409+
const date = new Date(0);
410+
date.setFullYear(year, month, day);
411+
date.setHours(0, 0, 0, 0);
412+
// Out-of-range components (or a day the local zone skipped) silently roll over,
413+
// which would hand back a date the server never sent.
414+
if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
415+
return new Date(NaN);
416+
}
417+
return date;
418+
}
419+
return new Date(value);
420+
}
421+
422+
export function parseDateTime(value: any): Date {
423+
return new Date(value);
424+
}
425+
375426
export function mapValues(data: any, fn: (item: any) => any) {
376427
const result: { [key: string]: any } = {};
377428
for (const key of Object.keys(data)) {

0 commit comments

Comments
 (0)