@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
195 lines • 7.78 kB
JavaScript
/*
* AUTO-GENERATED FILE — DO NOT EDIT.
* Source of truth: packages/style-rules/src/
* Regenerate with `pnpm generate:shared` (runs automatically at build/dev/install).
*/
/**
*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Temporal } from 'temporal-polyfill';
const MS_PER_DAY = 86400000;
function plainTimeToMs(pt) {
return (pt.hour * 3600000 +
pt.minute * 60000 +
pt.second * 1000 +
pt.millisecond);
}
function parseOffsetMs(offsetStr) {
const match = offsetStr.match(/^([+-])(\d{2}):(\d{2})$/);
if (!match) {
return null;
}
const sign = match[1] === '+' ? 1 : -1;
return (sign *
(parseInt(match[2], 10) * 3600000 + parseInt(match[3], 10) * 60000));
}
/**
* Parses an ISO 8601 time string to UTC milliseconds since midnight
* (0–86,399,999). Returns `null` if the string cannot be parsed.
*
* Three formats are accepted:
* 1. Local time (no offset): `"14:30:00"`, `"14:30:00.500"` — treated as UTC
* 2. UTC time with Z suffix: `"14:30:00Z"` — treated identically to the local form
* 3. Offset time: `"14:30:00+02:00"` — the offset is applied so the result
* is the equivalent UTC time of day. `"12:00:00+01:00"` resolves to
* 39,600,000 (11:00 UTC), which is less than `"12:00:00+00:00"` (43,200,000).
*
* Values wrap around midnight: `"00:00:00+01:00"` → 82,800,000 (23:00 UTC).
*/
export function parseTimeString(s) {
// Reject datetime strings and anything with an IANA timezone bracket.
// Temporal.PlainTime.from silently extracts the time portion from strings
// containing a 'T' separator. IANA brackets are meaningless for time-only
// values (there is no date to anchor DST to), so they are also rejected.
if (s.includes('T') || s.includes('[')) {
return null;
}
// Offset time: detect "+HH:MM" or "-HH:MM" suffix first. Temporal.PlainTime.from
// silently strips offsets and returns the clock-face time, so we must handle
// offset strings explicitly before falling through to the plain parse.
const offsetMatch = s.match(/([+-]\d{2}:\d{2})(\[.*\])?$/);
if (offsetMatch) {
const offsetStr = offsetMatch[1];
const timeStr = s.slice(0, s.indexOf(offsetStr));
const offsetMs = parseOffsetMs(offsetStr);
if (offsetMs !== null) {
try {
const localMs = plainTimeToMs(Temporal.PlainTime.from(timeStr));
// Subtract the UTC offset to normalise to UTC time-of-day; wrap around midnight
return (((localMs - offsetMs) % MS_PER_DAY) + MS_PER_DAY) % MS_PER_DAY;
}
catch (_a) {
/* empty */
}
}
return null;
}
// UTC time with Z suffix (e.g. "14:30:00Z") — strip Z and treat as local (UTC) time.
// Only apply when there is no 'T' in the string; a datetime string like
// "2024-06-15T14:30:00Z" must not be accepted here.
const normalized = s.endsWith('Z') && !s.includes('T') ? s.slice(0, -1) : s;
// Local time (no offset) — treated as UTC
try {
return plainTimeToMs(Temporal.PlainTime.from(normalized));
}
catch (_b) {
/* empty */
}
return null;
}
/**
* Parses an ISO 8601 or RFC 9557 date/datetime string to a Unix millisecond
* timestamp. Returns `null` if the string cannot be parsed.
*
* Three formats are tried in order:
* 1. RFC 9557 ZonedDateTime with IANA bracket notation
* e.g. `"2024-06-15T14:00:00[Europe/Stockholm]"` (offset inferred from the
* named timezone, including DST) or `"2024-06-15T14:00:00+02:00[Europe/Stockholm]"`
* (offset explicit; bracket must agree with the IANA zone)
* 2. UTC or fixed-offset Instant
* e.g. `"2024-01-01T00:00:00Z"`, `"2024-01-01T00:00:00+02:00"`
* 3. Plain date or datetime (no offset, `Z`, or IANA bracket), interpreted as UTC
* e.g. `"2024-01-01"`, `"2024-01-01T14:30:00"`, `"2024-01-01T14:30:00.500"`
*
* Strings with a time component that lack `Z`, an offset, or an IANA bracket are
* parsed as UTC wall-clock time (same semantics as Neo4j `LocalDateTime`).
*/
export function parseDateString(s) {
// RFC 9557 / ZonedDateTime with IANA bracket notation
try {
return Temporal.ZonedDateTime.from(s).epochMilliseconds;
}
catch (_a) {
/* empty */
}
// UTC or fixed-offset datetime (no IANA name)
try {
return Temporal.Instant.from(s).epochMilliseconds;
}
catch (_b) {
/* empty */
}
// Plain datetime — interpret as UTC.
// Also covers date-only strings like "2024-01-01" (PlainDateTime.from parses
// them as midnight), so a separate PlainDate path is not needed.
try {
return Temporal.PlainDateTime.from(s).toZonedDateTime('UTC')
.epochMilliseconds;
}
catch (_c) {
/* empty */
}
return null;
}
/**
* Parses a plain (unzoned) ISO 8601 date or datetime string to a Unix
* millisecond timestamp, interpreting the wall-clock time in the given
* `timeZone` (defaults to the browser's local timezone via
* `Temporal.Now.timeZoneId()`). Returns `null` if the string cannot be
* parsed or contains any timezone qualifier.
*
* Only plain date/datetime strings are accepted — strings with `Z`, a
* fixed UTC offset (`+02:00`), or an IANA bracket (`[Europe/Stockholm]`)
* are **rejected** and return `null`. This mirrors the semantics of
* Neo4j `LocalDateTime`, which has no associated timezone.
*
* Accepted formats:
* - Plain datetime: `"2024-01-01T14:30:00"`, `"2024-01-01T14:30:00.500"`
* - Date-only (treated as local midnight): `"2024-01-01"`
*
* The optional `timeZone` parameter overrides the browser's local timezone,
* which is useful for deterministic testing.
*/
export function parseLocalDateString(s, timeZone) {
// Reject strings that carry explicit timezone information. This mirrors the
// cascade in parseDateString but uses successful parsing as a rejection
// signal rather than an acceptance signal:
//
// - ZonedDateTime.from succeeds → string has an IANA bracket → reject
// - Instant.from succeeds → string has Z or a fixed offset → reject
// - PlainDateTime.from succeeds → truly plain string → accept
//
// The polyfill's PlainDateTime.from() silently strips offsets/brackets
// instead of throwing, so these two pre-checks are necessary to enforce
// the plain-only constraint.
try {
Temporal.ZonedDateTime.from(s);
return null;
}
catch (_a) {
/* not a zoned datetime — continue */
}
try {
Temporal.Instant.from(s);
return null;
}
catch (_b) {
/* not a UTC/offset instant — continue */
}
const tz = timeZone !== null && timeZone !== void 0 ? timeZone : Temporal.Now.timeZoneId();
try {
return Temporal.PlainDateTime.from(s).toZonedDateTime(tz).epochMilliseconds;
}
catch (_c) {
/* empty */
}
return null;
}
//# sourceMappingURL=date-utils.js.map