Skip to content

Commit fad8ae9

Browse files
authored
fix: Properly parse dates that have seconds in their offset (#8900)
1 parent d7eb896 commit fad8ae9

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

packages/@internationalized/date/src/string.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {Mutable} from './utils';
2020
const TIME_RE = /^(\d{2})(?::(\d{2}))?(?::(\d{2}))?(\.\d+)?$/;
2121
const DATE_RE = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})$/;
2222
const DATE_TIME_RE = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})(?:T(\d{2}))?(?::(\d{2}))?(?::(\d{2}))?(\.\d+)?$/;
23-
const ZONED_DATE_TIME_RE = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})(?:T(\d{2}))?(?::(\d{2}))?(?::(\d{2}))?(\.\d+)?(?:([+-]\d{2})(?::?(\d{2}))?)?\[(.*?)\]$/;
23+
const ZONED_DATE_TIME_RE = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})(?:T(\d{2}))?(?::(\d{2}))?(?::(\d{2}))?(\.\d+)?(?:([+-]\d{2})(?::?(\d{2}))?(?::?(\d{2}))?)?\[(.*?)\]$/;
2424
const ABSOLUTE_RE = /^([+-]\d{6}|\d{4})-(\d{2})-(\d{2})(?:T(\d{2}))?(?::(\d{2}))?(?::(\d{2}))?(\.\d+)?(?:(?:([+-]\d{2})(?::?(\d{2}))?)|Z)$/;
2525
const DATE_TIME_DURATION_RE =
2626
/^((?<negative>-)|\+)?P((?<years>\d*)Y)?((?<months>\d*)M)?((?<weeks>\d*)W)?((?<days>\d*)D)?((?<time>T)((?<hours>\d*[.,]?\d{1,9})H)?((?<minutes>\d*[.,]?\d{1,9})M)?((?<seconds>\d*[.,]?\d{1,9})S)?)?$/;
@@ -104,7 +104,7 @@ export function parseZonedDateTime(value: string, disambiguation?: Disambiguatio
104104
year < 1 ? -year + 1 : year,
105105
parseNumber(m[2], 1, 12),
106106
1,
107-
m[10],
107+
m[11],
108108
0,
109109
m[4] ? parseNumber(m[4], 0, 23) : 0,
110110
m[5] ? parseNumber(m[5], 0, 59) : 0,
@@ -118,7 +118,8 @@ export function parseZonedDateTime(value: string, disambiguation?: Disambiguatio
118118

119119
let ms: number;
120120
if (m[8]) {
121-
date.offset = parseNumber(m[8], -23, 23) * 60 * 60 * 1000 + parseNumber(m[9] ?? '0', 0, 59) * 60 * 1000;
121+
let hourOffset = parseNumber(m[8], -23, 23);
122+
date.offset = Math.sign(hourOffset) * (Math.abs(hourOffset) * 60 * 60 * 1000 + parseNumber(m[9] ?? '0', 0, 59) * 60 * 1000 + parseNumber(m[10] ?? '0', 0, 59) * 1000);
122123
ms = epochFromDate(date as ZonedDateTime) - date.offset;
123124

124125
// Validate offset against parsed date.
@@ -212,8 +213,14 @@ function offsetToString(offset: number) {
212213
let sign = Math.sign(offset) < 0 ? '-' : '+';
213214
offset = Math.abs(offset);
214215
let offsetHours = Math.floor(offset / (60 * 60 * 1000));
215-
let offsetMinutes = (offset % (60 * 60 * 1000)) / (60 * 1000);
216-
return `${sign}${String(offsetHours).padStart(2, '0')}:${String(offsetMinutes).padStart(2, '0')}`;
216+
let offsetMinutes = Math.floor((offset % (60 * 60 * 1000)) / (60 * 1000));
217+
let offsetSeconds = Math.floor((offset % (60 * 60 * 1000)) % (60 * 1000) / 1000);
218+
let stringOffset = `${sign}${String(offsetHours).padStart(2, '0')}:${String(offsetMinutes).padStart(2, '0')}`;
219+
if (offsetSeconds !== 0) {
220+
stringOffset += `:${String(offsetSeconds).padStart(2, '0')}`;
221+
}
222+
223+
return stringOffset;
217224
}
218225

219226
export function zonedDateTimeToString(date: ZonedDateTime): string {

packages/@internationalized/date/tests/string.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ describe('string conversion', function () {
327327
expect(() => parseZonedDateTime('2020-02-03T23:99[America/Los_Angeles]')).toThrow();
328328
expect(() => parseZonedDateTime('2020-02-03T12:22:99[America/Los_Angeles]')).toThrow();
329329
});
330+
331+
it('should parse dates with seconds in offset', function () {
332+
let date = parseZonedDateTime('1883-11-07T00:45[America/Los_Angeles]');
333+
let string = date.toString(); // => "1883-11-07T00:45:00-07:52:58[America/Los_Angeles]"
334+
let parseBack = parseZonedDateTime(string);
335+
expect(parseBack).toEqual(date);
336+
});
330337
});
331338

332339
describe('ZonedDateTime#toString', function () {

0 commit comments

Comments
 (0)