Skip to content

Commit 3da8cde

Browse files
committed
Fix VDATE formatter parsing
1 parent bccc331 commit 3da8cde

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

src/formatters/formatter.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ export abstract class Formatter {
509509
}
510510

511511
if (variableName && dateFormat) {
512-
const existingValue = this.variables.get(variableName) as string;
512+
const existingValue = this.variables.get(variableName);
513513

514514
// Check if we already have this date variable stored
515515
if (!existingValue) {
@@ -547,9 +547,38 @@ export abstract class Formatter {
547547

548548
// Format the date based on what's stored
549549
let formattedDate = "";
550-
const storedValue = this.variables.get(variableName) as string;
550+
let storedValue = this.variables.get(variableName);
551+
552+
// If a VDATE variable was pre-seeded (e.g., via API/URL) as a plain string,
553+
// attempt to coerce it into the internal @date:ISO form so formatting works.
554+
if (
555+
typeof storedValue === "string" &&
556+
storedValue &&
557+
!storedValue.startsWith("@date:")
558+
) {
559+
if (this.dateParser) {
560+
const aliasMap = settingsStore.getState().dateAliases;
561+
const normalizedInput = normalizeDateInput(storedValue, aliasMap);
562+
const parseAttempt = this.dateParser.parseDate(normalizedInput);
563+
564+
// Keep backwards compatibility: only coerce if we can parse it.
565+
if (parseAttempt) {
566+
const iso = parseAttempt.moment.toISOString();
567+
const coerced = `@date:${iso}`;
568+
this.variables.set(variableName, coerced);
569+
storedValue = coerced;
570+
}
571+
}
572+
} else if (storedValue instanceof Date) {
573+
// Some callers may pass actual Date objects through the JS API.
574+
if (!Number.isNaN(storedValue.getTime())) {
575+
const coerced = `@date:${storedValue.toISOString()}`;
576+
this.variables.set(variableName, coerced);
577+
storedValue = coerced;
578+
}
579+
}
551580

552-
if (storedValue && storedValue.startsWith("@date:")) {
581+
if (typeof storedValue === "string" && storedValue.startsWith("@date:")) {
553582
// It's a date variable, extract and format it
554583
const isoString = storedValue.substring(6);
555584

@@ -559,9 +588,12 @@ export abstract class Formatter {
559588
formattedDate = moment.format(dateFormat);
560589
}
561590
}
562-
} else if (storedValue) {
591+
} else if (typeof storedValue === "string" && storedValue) {
563592
// Backward compatibility: use the stored value as-is
564593
formattedDate = storedValue;
594+
} else if (storedValue != null) {
595+
// Fallback: avoid throwing if a non-string value is stored.
596+
formattedDate = `${storedValue}`;
565597
}
566598

567599
// Replace the specific match rather than using regex again

src/formatters/vdate-default.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,19 @@ describe('VDATE Default Value Support', () => {
244244
expect(formatter.testDateParser.parseDate).toHaveBeenCalledWith("today");
245245
expect(result).toBe("Date1: YYYY-MM-DD-formatted Date2: MM/DD/YYYY-formatted");
246246
});
247+
248+
it('should parse and format pre-seeded string variables used in VDATE', async () => {
249+
// Mirrors issue #1074: variables passed via API/URL are plain strings.
250+
formatter.variables.set('date', '2026-12-31');
251+
252+
const result = await formatter.testReplaceDateVariableInString(
253+
"Test {{VDATE:date,YYYY-MM-DD}}",
254+
);
255+
256+
// The formatter should attempt to parse the existing string into a date.
257+
expect(formatter.testDateParser.parseDate).toHaveBeenCalledWith("2026-12-31");
258+
expect(result).toBe("Test YYYY-MM-DD-formatted");
259+
});
247260
});
248261

249262
describe('Edge Cases', () => {

0 commit comments

Comments
 (0)