@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
262 lines • 10.2 kB
JavaScript
;
/**
*
* 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/>.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatTimeZone = exports.isValidUTCFormat = exports.parseCustomUTCOffset = exports.getUserTimeZone = exports.generateTimeZoneOptions = exports.generateUTCTimeZoneOptions = void 0;
/**
* Gets the offset in minutes for a timezone at a specific date
*/
const getOffsetMinutes = (timezone, date) => {
try {
// Create dates in UTC and in the target timezone
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
// Calculate the difference in minutes
const diffMs = tzDate.getTime() - utcDate.getTime();
return Math.round(diffMs / (1000 * 60));
}
catch (_a) {
return 0;
}
};
/**
* Formats a UTC offset using the Intl API for consistent formatting
* @param offsetHours - The offset in hours (can be fractional)
* @returns Formatted UTC offset string (e.g., "UTC+5:30", "UTC-3")
*/
const formatUTCOffset = (offsetHours) => {
if (offsetHours === 0) {
return 'UTC';
}
const sign = offsetHours >= 0 ? '+' : '-';
const absHours = Math.abs(offsetHours);
const hours = Math.floor(absHours);
const minutes = Math.round((absHours - hours) * 60);
// Use Intl.NumberFormat for consistent number formatting
const numberFormatter = new Intl.NumberFormat('en-US', {
maximumFractionDigits: 0,
minimumIntegerDigits: 1,
});
const formattedHours = numberFormatter.format(hours);
const formattedMinutes = minutes > 0 ? `:${String(minutes).padStart(2, '0')}` : '';
return `UTC${sign}${formattedHours}${formattedMinutes}`;
};
/**
* Extracts unique UTC offsets from all supported timezones using the Intl API
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
* @returns Set of unique offset values in minutes
*/
const getUniqueOffsetsFromAPI = (referenceDate) => {
const uniqueOffsets = new Set();
const now = referenceDate || new Date();
try {
// Get all supported timezones from the Intl API
const timezones = Intl.supportedValuesOf('timeZone');
// Extract unique offsets from each timezone
for (const tz of timezones) {
try {
const offsetMinutes = getOffsetMinutes(tz, now);
uniqueOffsets.add(offsetMinutes);
}
catch (_a) {
// Skip timezones that can't be processed
continue;
}
}
}
catch (_b) {
// Fallback to empty set if API is not available
}
// Always include UTC (offset 0)
uniqueOffsets.add(0);
return uniqueOffsets;
};
/**
* Generates UTC-only timezone options derived from the Intl API
* Uses the Intl API to discover which UTC offsets are actually used by supported timezones
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
*/
const generateUTCTimeZoneOptions = (referenceDate) => {
// Get unique offsets from all supported timezones via the Intl API
const uniqueOffsets = getUniqueOffsetsFromAPI(referenceDate);
// Convert offset minutes to hours (can be fractional)
const offsetHours = Array.from(uniqueOffsets)
.map((minutes) => minutes / 60)
.sort((a, b) => a - b);
// Convert to TimeZoneOption format
const options = offsetHours.map((offsetHours) => {
const offsetStr = formatUTCOffset(offsetHours);
return {
label: offsetStr,
offset: offsetStr,
offsetMinutes: Math.round(offsetHours * 60),
value: offsetStr,
};
});
return options;
};
exports.generateUTCTimeZoneOptions = generateUTCTimeZoneOptions;
/**
* Generates a list of common timezone options with their UTC offsets
* @param referenceDate - The date to use for calculating timezone offsets (defaults to current date)
* @param excludeUTC - Whether to exclude the UTC timezone from the list (useful when combining with UTC-only options)
*/
const generateTimeZoneOptions = (referenceDate, excludeUTC = false) => {
// Common timezones with their IANA identifiers
const timezones = Intl.supportedValuesOf('timeZone');
const now = referenceDate || new Date();
// Filter out UTC if requested
const filteredTimezones = excludeUTC
? timezones.filter((tz) => tz !== 'UTC')
: timezones;
return filteredTimezones
.map((tz) => {
var _a, _b, _c;
try {
// Get the offset for this timezone
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: tz,
timeZoneName: 'shortOffset',
});
const parts = formatter.formatToParts(now);
const offsetPart = parts.find((part) => part.type === 'timeZoneName');
const offset = (_a = offsetPart === null || offsetPart === void 0 ? void 0 : offsetPart.value) !== null && _a !== void 0 ? _a : '';
// Calculate offset in minutes for proper sorting
const offsetMinutes = getOffsetMinutes(tz, now);
// Format the label
const cityName = (_c = (_b = tz.split('/').pop()) === null || _b === void 0 ? void 0 : _b.replace(/_/g, ' ')) !== null && _c !== void 0 ? _c : tz;
const label = `${cityName} (${offset})`;
return {
label,
offset,
offsetMinutes,
value: tz,
};
}
catch (_d) {
// Fallback if timezone is not supported
return {
label: tz.replace(/_/g, ' '),
offset: '',
offsetMinutes: 0,
value: tz,
};
}
})
.sort((a, b) => {
// Sort by offset (negative to positive), then by label
if (a.offsetMinutes !== b.offsetMinutes) {
return a.offsetMinutes - b.offsetMinutes;
}
return a.label.localeCompare(b.label);
});
};
exports.generateTimeZoneOptions = generateTimeZoneOptions;
/**
* Gets the user's current timezone
*/
const getUserTimeZone = () => {
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
catch (_a) {
return 'UTC';
}
};
exports.getUserTimeZone = getUserTimeZone;
/**
* Parses a custom UTC offset string (e.g., "UTC+5:30", "UTC-3:45")
* @param input - The input string to parse
* @returns The parsed timezone value or null if invalid
*/
const parseCustomUTCOffset = (input) => {
// Match patterns like: UTC+5:30, UTC-3:45, UTC+1, UTC-12, etc.
// Also accepts single digit minutes which will be padded
const utcPattern = /^UTC([+-])?(\d{1,2})(?::(\d{1,2}))?$/i;
const match = input.trim().match(utcPattern);
if (!match) {
return null;
}
const sign = match[1] === '-' ? '-' : '+';
const hours = parseInt(match[2], 10);
const minutes = match[3] ? parseInt(match[3], 10) : 0;
// Validate ranges based on sign
if (sign === '+') {
if (hours < 0 || hours > 14) {
return null;
}
if (hours === 14 && minutes > 0) {
return null;
} // UTC+14 is max
}
else {
// sign === '-'
if (hours < 0 || hours > 12) {
return null;
} // UTC-12 is min
if (hours === 12 && minutes > 0) {
return null;
} // UTC-12 is min
}
if (minutes < 0 || minutes > 59) {
return null;
}
return `UTC${sign}${hours}${minutes > 0 ? `:${String(minutes).padStart(2, '0')}` : ''}`;
};
exports.parseCustomUTCOffset = parseCustomUTCOffset;
/**
* Validates if a string is a valid UTC timezone format
* @param input - The input string to validate
* @returns True if valid UTC timezone format
*/
const isValidUTCFormat = (input) => {
return (0, exports.parseCustomUTCOffset)(input) !== null || input.toUpperCase() === 'UTC';
};
exports.isValidUTCFormat = isValidUTCFormat;
/**
* Formats a timezone for display
* @param timezone - The IANA timezone identifier or UTC offset
* @param referenceDate - The date to use for calculating the offset (defaults to current date)
*/
const formatTimeZone = (timezone, referenceDate) => {
var _a, _b, _c;
// If it's already a UTC format, return as-is
if (timezone.toUpperCase().startsWith('UTC')) {
return timezone;
}
try {
const now = referenceDate || new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: timezone,
timeZoneName: 'shortOffset',
});
const parts = formatter.formatToParts(now);
const offsetPart = parts.find((part) => part.type === 'timeZoneName');
const offset = (_a = offsetPart === null || offsetPart === void 0 ? void 0 : offsetPart.value) !== null && _a !== void 0 ? _a : '';
const cityName = (_c = (_b = timezone.split('/').pop()) === null || _b === void 0 ? void 0 : _b.replace(/_/g, ' ')) !== null && _c !== void 0 ? _c : timezone;
return `${cityName} (${offset})`;
}
catch (_d) {
return timezone.replace(/_/g, ' ');
}
};
exports.formatTimeZone = formatTimeZone;
//# sourceMappingURL=generate-timezone-options.js.map