Skip to content

Fix/issue 1673 datetime input parsing#1960

Open
Varun-S10 wants to merge 4 commits into
a2ui-project:mainfrom
Varun-S10:fix/issue-1673-datetime-input-parsing
Open

Fix/issue 1673 datetime input parsing#1960
Varun-S10 wants to merge 4 commits into
a2ui-project:mainfrom
Varun-S10:fix/issue-1673-datetime-input-parsing

Conversation

@Varun-S10

Copy link
Copy Markdown
Collaborator

Description

This PR fixes date and time parsing in the DateTimeInput component across Angular, Lit, and React (v0_9) so it works reliably and follows the A2UI spec.

Before this change, DateTimeInput used a simple string split (value.split('T')[0]) to get the date and time. Because browser input boxes (date, time, and datetime-local) are very strict about format, this caused the browser to reject slightly different date formats or timestamps and show an empty (cleared) input box on screen.

What this PR changes:

  • Adds a normalizeDateTimeValue(value, type) helper function across Angular, Lit, and React.
  • Uses regular expressions (YYYY-MM-DD and HH:mm) to read exact date and time strings without shifting the date across timezones.
  • Adds a safe fallback to standard JavaScript Date parsing for timestamps or irregular formats, formatting them with leading zeros as required by HTML input boxes.
  • Changes the example timestamp in 30_live-invitation-builder.json from 19:00:00Z to 10:00:00Z so automated tests don't roll over to the next day in timezones like IST (India).

Fixes #1673

Pre-launch Checklist

One time:

For this PR:

  • I have added updates to the CHANGELOG.
  • I updated/added relevant documentation.
  • My code changes (if any) have tests.
  • If my branch is on fork, I have verified that scripts/e2e_test.sh passes.

If you need help, consider asking for advice on the discussion board.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves date and time string parsing in the DateTimeInput component across the Angular, Lit, and React renderers to align with the specification, adding corresponding unit tests. The reviewer identified a timezone shift bug in all three implementations when parsing date-only strings for a datetime-local input type, which causes the fallback parser to mix UTC dates with local timezones. To resolve this, the reviewer suggested refactoring the normalizeDateTimeValue helper function to avoid fallback parsing when a literal date is already extracted, and to make the date and time regular expressions more robust for single-digit values.

Comment on lines +36 to +107
function normalizeDateTimeValue(value: unknown, type: string): string {
if (value === null || value === undefined || value === '') return '';

let strValue = '';
if (value instanceof Date) {
strValue = value.toISOString();
} else {
strValue = String(value).trim();
}

if (!strValue) return '';

let datePart = '';
let timePart = '';

// 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD
const dateRegex = /(\d{4})[-/](\d{2})[-/](\d{2})/;
const dateMatch = strValue.match(dateRegex);
if (dateMatch) {
datePart = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
}

// 2. Try literal extraction of HH:mm
const timeRegex = /(\d{2}):(\d{2})(?::(\d{2}))?/;
const timeMatch = strValue.match(timeRegex);
if (timeMatch) {
timePart = `${timeMatch[1]}:${timeMatch[2]}`;
}

// 3. Fallback to Date object parsing if needed
const needDate = type === 'date' || type === 'datetime-local';
const needTime = type === 'time' || type === 'datetime-local';

if ((needDate && !datePart) || (needTime && !timePart)) {
let parsedDate: Date;
const num = Number(strValue);
if (strValue !== '' && !isNaN(num)) {
parsedDate = new Date(num);
} else {
parsedDate = new Date(strValue);
}

if (!isNaN(parsedDate.getTime())) {
if (!datePart) {
const y = parsedDate.getFullYear();
const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
const d = String(parsedDate.getDate()).padStart(2, '0');
datePart = `${y}-${m}-${d}`;
}
if (!timePart) {
const h = String(parsedDate.getHours()).padStart(2, '0');
const min = String(parsedDate.getMinutes()).padStart(2, '0');
timePart = `${h}:${min}`;
}
}
}

// If timePart is empty but we need it and datePart is present, default to '00:00'
if (type === 'datetime-local' && datePart && !timePart) {
timePart = '00:00';
}

switch (type) {
case 'date':
return datePart;
case 'time':
return timePart;
case 'datetime-local':
return datePart && timePart ? `${datePart}T${timePart}` : '';
}
return '';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a timezone shift bug when parsing date-only strings (like "2026-03-20") when type is "datetime-local".

The Issue

  1. dateRegex successfully extracts datePart = '2026-03-20', but timePart remains empty.
  2. Since needTime is true, the fallback block is executed.
  3. new Date('2026-03-20') is parsed. According to the ECMAScript specification, date-only ISO strings are parsed as UTC midnight.
  4. When parsedDate.getHours() and parsedDate.getMinutes() are called, they return the hours and minutes in the local timezone of the environment (e.g., 19:00 in UTC-5).
  5. This mixes the UTC date with the local timezone-shifted time, producing an incorrect hybrid value like '2026-03-20T19:00' and completely bypassing the intended '00:00' default.

The Solution

Only perform the Date fallback parsing if we don't already have a literally extracted datePart (unless the input is a numeric timestamp, in which case we want to parse both). This ensures that date-only strings do not get timezone-shifted times.

Additionally, the current dateRegex and timeRegex require exactly two digits for months, days, and hours (e.g., \d{2}). This will fail to match single-digit values like "9:00" or "2026-3-2". We can make the regexes more robust by matching 1 or 2 digits (\d{1,2}) and padding them with leading zeros.

function normalizeDateTimeValue(value: unknown, type: string): string {
  if (value === null || value === undefined || value === '') return '';

  let strValue = '';
  if (value instanceof Date) {
    strValue = value.toISOString();
  } else {
    strValue = String(value).trim();
  }

  if (!strValue) return '';

  let datePart = '';
  let timePart = '';

  // 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD (supporting 1 or 2 digit month/day)
  const dateRegex = /(\d{4})[-/](\d{1,2})[-/](\d{1,2})/;
  const dateMatch = strValue.match(dateRegex);
  if (dateMatch) {
    datePart = `${dateMatch[1]}-${dateMatch[2].padStart(2, '0')}-${dateMatch[3].padStart(2, '0')}`;
  }

  // 2. Try literal extraction of HH:mm (supporting 1 or 2 digit hour)
  const timeRegex = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
  const timeMatch = strValue.match(timeRegex);
  if (timeMatch) {
    timePart = `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`;
  }

  // 3. Fallback to Date object parsing if needed
  const needDate = type === 'date' || type === 'datetime-local';
  const needTime = type === 'time' || type === 'datetime-local';

  if ((needDate && !datePart) || (needTime && !timePart)) {
    const isTimestamp = strValue !== '' && !isNaN(Number(strValue));
    // Only fallback to Date parsing if we don't have a literal datePart,
    // or if the input is a numeric timestamp.
    if (!datePart || isTimestamp) {
      let parsedDate: Date;
      if (isTimestamp) {
        parsedDate = new Date(Number(strValue));
      } else {
        parsedDate = new Date(strValue);
      }

      if (!isNaN(parsedDate.getTime())) {
        if (!datePart) {
          const y = parsedDate.getFullYear();
          const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
          const d = String(parsedDate.getDate()).padStart(2, '0');
          datePart = `${y}-${m}-${d}`;
        }
        if (!timePart) {
          const h = String(parsedDate.getHours()).padStart(2, '0');
          const min = String(parsedDate.getMinutes()).padStart(2, '0');
          timePart = `${h}:${min}`;
        }
      }
    }
  }

  // If timePart is empty but we need it and datePart is present, default to '00:00'
  if (type === 'datetime-local' && datePart && !timePart) {
    timePart = '00:00';
  }

  switch (type) {
    case 'date':
      return datePart;
    case 'time':
      return timePart;
    case 'datetime-local':
      return datePart && timePart ? `${datePart}T${timePart}` : '';
  }
  return '';
}
References
  1. Maintain consistent behavior and limitations across different framework renderers (such as React, Lit, and Angular) rather than implementing complex, custom parsing logic (like robust date/time parsing) in a single renderer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed a problem.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, date parsing in javascript is a mess. @gspencergoog I wonder if we should just let users inject their date parsing/formatting and have the framework always use UTC internally. The actual datetime input controls may be tricky though.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I originally only specified UTC variants for this control mainly because of this issue. Once you add in local time, stuff gets complicated.

Injecting their own formatting is a good idea, but it adds overhead to using the catalog. Is there a default we could provide them that would be useful? Ideally one from a well maintained package that we don't have to maintain...

@ditman ditman Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injecting their own formatting is a good idea, but it adds overhead to using the catalog. Is there a default we could provide them that would be useful? Ideally one from a well maintained package that we don't have to maintain...

Yes, we could have something like @a2ui/markdown-it (imagine @a2ui/luxon or similar). I guess what library we end up using depends on what we want the date parsing/formatting API to be.

Comment on lines +31 to 102
function normalizeDateTimeValue(value: unknown, type: string): string {
if (value === null || value === undefined || value === '') return '';

const hasT = value.includes('T');
const split = value.split('T');
let strValue = '';
if (value instanceof Date) {
strValue = value.toISOString();
} else {
strValue = String(value).trim();
}

if (!strValue) return '';

let datePart = '';
let timePart = '';

// 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD
const dateRegex = /(\d{4})[-/](\d{2})[-/](\d{2})/;
const dateMatch = strValue.match(dateRegex);
if (dateMatch) {
datePart = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
}

// 2. Try literal extraction of HH:mm
const timeRegex = /(\d{2}):(\d{2})(?::(\d{2}))?/;
const timeMatch = strValue.match(timeRegex);
if (timeMatch) {
timePart = `${timeMatch[1]}:${timeMatch[2]}`;
}

// If the incoming value is not in ISO format (not hasT), use the incoming value.
// It might be just a date: '2010-07-11' or a time: '13:37', so we use value as a
// the fallback to the split.
const datePart = (hasT ? split[0] : value)?.substring(0, 10) ?? '';
const timePart = (hasT ? split[1] : value)?.substring(0, 5) ?? '';
// 3. Fallback to Date object parsing if needed
const needDate = type === 'date' || type === 'datetime-local';
const needTime = type === 'time' || type === 'datetime-local';

if ((needDate && !datePart) || (needTime && !timePart)) {
let parsedDate: Date;
const num = Number(strValue);
if (strValue !== '' && !isNaN(num)) {
parsedDate = new Date(num);
} else {
parsedDate = new Date(strValue);
}

if (!isNaN(parsedDate.getTime())) {
if (!datePart) {
const y = parsedDate.getFullYear();
const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
const d = String(parsedDate.getDate()).padStart(2, '0');
datePart = `${y}-${m}-${d}`;
}
if (!timePart) {
const h = String(parsedDate.getHours()).padStart(2, '0');
const min = String(parsedDate.getMinutes()).padStart(2, '0');
timePart = `${h}:${min}`;
}
}
}

// If timePart is empty but we need it and datePart is present, default to '00:00'
if (type === 'datetime-local' && datePart && !timePart) {
timePart = '00:00';
}

switch (type) {
case 'date':
return datePart;
case 'time':
return timePart;
case 'datetime-local':
return `${datePart}T${timePart}`;
return datePart && timePart ? `${datePart}T${timePart}` : '';
}
return '';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a timezone shift bug when parsing date-only strings (like "2026-03-20") when type is "datetime-local".

The Issue

  1. dateRegex successfully extracts datePart = '2026-03-20', but timePart remains empty.
  2. Since needTime is true, the fallback block is executed.
  3. new Date('2026-03-20') is parsed. According to the ECMAScript specification, date-only ISO strings are parsed as UTC midnight.
  4. When parsedDate.getHours() and parsedDate.getMinutes() are called, they return the hours and minutes in the local timezone of the environment (e.g., 19:00 in UTC-5).
  5. This mixes the UTC date with the local timezone-shifted time, producing an incorrect hybrid value like '2026-03-20T19:00' and completely bypassing the intended '00:00' default.

The Solution

Only perform the Date fallback parsing if we don't already have a literally extracted datePart (unless the input is a numeric timestamp, in which case we want to parse both). This ensures that date-only strings do not get timezone-shifted times.

Additionally, the current dateRegex and timeRegex require exactly two digits for months, days, and hours (e.g., \d{2}). This will fail to match single-digit values like "9:00" or "2026-3-2". We can make the regexes more robust by matching 1 or 2 digits (\d{1,2}) and padding them with leading zeros.

function normalizeDateTimeValue(value: unknown, type: string): string {
  if (value === null || value === undefined || value === '') return '';

  let strValue = '';
  if (value instanceof Date) {
    strValue = value.toISOString();
  } else {
    strValue = String(value).trim();
  }

  if (!strValue) return '';

  let datePart = '';
  let timePart = '';

  // 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD (supporting 1 or 2 digit month/day)
  const dateRegex = /(\d{4})[-/](\d{1,2})[-/](\d{1,2})/;
  const dateMatch = strValue.match(dateRegex);
  if (dateMatch) {
    datePart = `${dateMatch[1]}-${dateMatch[2].padStart(2, '0')}-${dateMatch[3].padStart(2, '0')}`;
  }

  // 2. Try literal extraction of HH:mm (supporting 1 or 2 digit hour)
  const timeRegex = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
  const timeMatch = strValue.match(timeRegex);
  if (timeMatch) {
    timePart = `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`;
  }

  // 3. Fallback to Date object parsing if needed
  const needDate = type === 'date' || type === 'datetime-local';
  const needTime = type === 'time' || type === 'datetime-local';

  if ((needDate && !datePart) || (needTime && !timePart)) {
    const isTimestamp = strValue !== '' && !isNaN(Number(strValue));
    // Only fallback to Date parsing if we don't have a literal datePart,
    // or if the input is a numeric timestamp.
    if (!datePart || isTimestamp) {
      let parsedDate: Date;
      if (isTimestamp) {
        parsedDate = new Date(Number(strValue));
      } else {
        parsedDate = new Date(strValue);
      }

      if (!isNaN(parsedDate.getTime())) {
        if (!datePart) {
          const y = parsedDate.getFullYear();
          const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
          const d = String(parsedDate.getDate()).padStart(2, '0');
          datePart = `${y}-${m}-${d}`;
        }
        if (!timePart) {
          const h = String(parsedDate.getHours()).padStart(2, '0');
          const min = String(parsedDate.getMinutes()).padStart(2, '0');
          timePart = `${h}:${min}`;
        }
      }
    }
  }

  // If timePart is empty but we need it and datePart is present, default to '00:00'
  if (type === 'datetime-local' && datePart && !timePart) {
    timePart = '00:00';
  }

  switch (type) {
    case 'date':
      return datePart;
    case 'time':
      return timePart;
    case 'datetime-local':
      return datePart && timePart ? `${datePart}T${timePart}` : '';
  }
  return '';
}
References
  1. Maintain consistent behavior and limitations across different framework renderers (such as React, Lit, and Angular) rather than implementing complex, custom parsing logic (like robust date/time parsing) in a single renderer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a problem. Also, it appears that we are doing similar work in these libraries: is there any opportunity to share code (maybe add some utility functions in the web_core lib)?

Comment on lines +22 to 93
function normalizeDateTimeValue(value: unknown, type: string): string {
if (value === null || value === undefined || value === '') return '';

const hasT = value.includes('T');
const split = value.split('T');
let strValue = '';
if (value instanceof Date) {
strValue = value.toISOString();
} else {
strValue = String(value).trim();
}

if (!strValue) return '';

let datePart = '';
let timePart = '';

// 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD
const dateRegex = /(\d{4})[-/](\d{2})[-/](\d{2})/;
const dateMatch = strValue.match(dateRegex);
if (dateMatch) {
datePart = `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}`;
}

// 2. Try literal extraction of HH:mm
const timeRegex = /(\d{2}):(\d{2})(?::(\d{2}))?/;
const timeMatch = strValue.match(timeRegex);
if (timeMatch) {
timePart = `${timeMatch[1]}:${timeMatch[2]}`;
}

// 3. Fallback to Date object parsing if needed
const needDate = type === 'date' || type === 'datetime-local';
const needTime = type === 'time' || type === 'datetime-local';

if ((needDate && !datePart) || (needTime && !timePart)) {
let parsedDate: Date;
const num = Number(strValue);
if (strValue !== '' && !isNaN(num)) {
parsedDate = new Date(num);
} else {
parsedDate = new Date(strValue);
}

const datePart = (hasT ? split[0] : value)?.substring(0, 10) ?? '';
const timePart = (hasT ? split[1] : value)?.substring(0, 5) ?? '';
if (!isNaN(parsedDate.getTime())) {
if (!datePart) {
const y = parsedDate.getFullYear();
const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
const d = String(parsedDate.getDate()).padStart(2, '0');
datePart = `${y}-${m}-${d}`;
}
if (!timePart) {
const h = String(parsedDate.getHours()).padStart(2, '0');
const min = String(parsedDate.getMinutes()).padStart(2, '0');
timePart = `${h}:${min}`;
}
}
}

// If timePart is empty but we need it and datePart is present, default to '00:00'
if (type === 'datetime-local' && datePart && !timePart) {
timePart = '00:00';
}

switch (type) {
case 'date':
return datePart;
case 'time':
return timePart;
case 'datetime-local':
return `${datePart}T${timePart}`;
return datePart && timePart ? `${datePart}T${timePart}` : '';
}
return '';
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a timezone shift bug when parsing date-only strings (like "2026-03-20") when type is "datetime-local".

The Issue

  1. dateRegex successfully extracts datePart = '2026-03-20', but timePart remains empty.
  2. Since needTime is true, the fallback block is executed.
  3. new Date('2026-03-20') is parsed. According to the ECMAScript specification, date-only ISO strings are parsed as UTC midnight.
  4. When parsedDate.getHours() and parsedDate.getMinutes() are called, they return the hours and minutes in the local timezone of the environment (e.g., 19:00 in UTC-5).
  5. This mixes the UTC date with the local timezone-shifted time, producing an incorrect hybrid value like '2026-03-20T19:00' and completely bypassing the intended '00:00' default.

The Solution

Only perform the Date fallback parsing if we don't already have a literally extracted datePart (unless the input is a numeric timestamp, in which case we want to parse both). This ensures that date-only strings do not get timezone-shifted times.

Additionally, the current dateRegex and timeRegex require exactly two digits for months, days, and hours (e.g., \d{2}). This will fail to match single-digit values like "9:00" or "2026-3-2". We can make the regexes more robust by matching 1 or 2 digits (\d{1,2}) and padding them with leading zeros.

function normalizeDateTimeValue(value: unknown, type: string): string {
  if (value === null || value === undefined || value === '') return '';

  let strValue = '';
  if (value instanceof Date) {
    strValue = value.toISOString();
  } else {
    strValue = String(value).trim();
  }

  if (!strValue) return '';

  let datePart = '';
  let timePart = '';

  // 1. Try literal extraction of YYYY-MM-DD or YYYY/MM/DD (supporting 1 or 2 digit month/day)
  const dateRegex = /(\d{4})[-/](\d{1,2})[-/](\d{1,2})/;
  const dateMatch = strValue.match(dateRegex);
  if (dateMatch) {
    datePart = `${dateMatch[1]}-${dateMatch[2].padStart(2, '0')}-${dateMatch[3].padStart(2, '0')}`;
  }

  // 2. Try literal extraction of HH:mm (supporting 1 or 2 digit hour)
  const timeRegex = /(\d{1,2}):(\d{2})(?::(\d{2}))?/;
  const timeMatch = strValue.match(timeRegex);
  if (timeMatch) {
    timePart = `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`;
  }

  // 3. Fallback to Date object parsing if needed
  const needDate = type === 'date' || type === 'datetime-local';
  const needTime = type === 'time' || type === 'datetime-local';

  if ((needDate && !datePart) || (needTime && !timePart)) {
    const isTimestamp = strValue !== '' && !isNaN(Number(strValue));
    // Only fallback to Date parsing if we don't have a literal datePart,
    // or if the input is a numeric timestamp.
    if (!datePart || isTimestamp) {
      let parsedDate: Date;
      if (isTimestamp) {
        parsedDate = new Date(Number(strValue));
      } else {
        parsedDate = new Date(strValue);
      }

      if (!isNaN(parsedDate.getTime())) {
        if (!datePart) {
          const y = parsedDate.getFullYear();
          const m = String(parsedDate.getMonth() + 1).padStart(2, '0');
          const d = String(parsedDate.getDate()).padStart(2, '0');
          datePart = `${y}-${m}-${d}`;
        }
        if (!timePart) {
          const h = String(parsedDate.getHours()).padStart(2, '0');
          const min = String(parsedDate.getMinutes()).padStart(2, '0');
          timePart = `${h}:${min}`;
        }
      }
    }
  }

  // If timePart is empty but we need it and datePart is present, default to '00:00'
  if (type === 'datetime-local' && datePart && !timePart) {
    timePart = '00:00';
  }

  switch (type) {
    case 'date':
      return datePart;
    case 'time':
      return timePart;
    case 'datetime-local':
      return datePart && timePart ? `${datePart}T${timePart}` : '';
  }
  return '';
}
References
  1. Maintain consistent behavior and limitations across different framework renderers (such as React, Lit, and Angular) rather than implementing complex, custom parsing logic (like robust date/time parsing) in a single renderer.

@ditman

ditman commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@Varun-S10 thanks for the PR! One question: Why is this needed:

  • Changes the example timestamp in 30_live-invitation-builder.json from 19:00:00Z to 10:00:00Z so automated tests don't roll over to the next day in timezones like IST (India).

If we are doing:

  • Uses regular expressions (YYYY-MM-DD and HH:mm) to read exact date and time strings without shifting the date across timezones.

Is it because of the fallback to using the new Date constructor?

@Varun-S10

Varun-S10 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

@Varun-S10 thanks for the PR! One question: Why is this needed:

  • Changes the example timestamp in 30_live-invitation-builder.json from 19:00:00Z to 10:00:00Z so automated tests don't roll over to the next day in timezones like IST (India).

If we are doing:

  • Uses regular expressions (YYYY-MM-DD and HH:mm) to read exact date and time strings without shifting the date across timezones.

Is it because of the fallback to using the new Date constructor?

@ditman, Thanks for the review.

No, it's not because of the fallback in DateTimeInput. The new regex parsing in DateTimeInput works as intended and correctly extracts "2025-07-15" and "19:00" without any timezone conversion or fallback to new Date().

The reason for the rollover is that in 30_live-invitation-builder.json, the /event/date value is used in two separate places:

  1. The Editor Input (date-input): Rendered by DateTimeInput, where our regex reads the exact date/time strings without timezone shifting.
  2. The Live Preview Card (invite-date-text): Rendered by a Text component that formats the timestamp using A2UI's built-in formatDate expression:
    "call": "formatDate",
    "args": {
      "value": { "path": "/event/date" },
      "format": "EEEE, MMMM d, yyyy 'at' h:mm a"
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DateTimeInput date parsing is not great.

3 participants