@brightspace-ui/intl 3.29.0 → 3.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Indian Ocean Timezone Mappings
3
+ * Maps IANA timezone identifiers to country codes and source files
4
+ */
5
+
6
+ export const indianTimezones = {
7
+ 'Indian/Antananarivo': {
8
+ regionCode: 'MG',
9
+ ianaSource: 'africa'
10
+ },
11
+ 'Indian/Chagos': {
12
+ regionCode: 'IO',
13
+ ianaSource: 'asia'
14
+ },
15
+ 'Indian/Christmas': {
16
+ regionCode: 'CX',
17
+ ianaSource: 'australasia'
18
+ },
19
+ 'Indian/Cocos': {
20
+ regionCode: 'CC',
21
+ ianaSource: 'australasia'
22
+ },
23
+ 'Indian/Comoro': {
24
+ regionCode: 'KM',
25
+ ianaSource: 'africa'
26
+ },
27
+ 'Indian/Kerguelen': {
28
+ regionCode: 'TF',
29
+ ianaSource: 'antarctica'
30
+ },
31
+ 'Indian/Mahe': {
32
+ regionCode: 'SC',
33
+ ianaSource: 'africa'
34
+ },
35
+ 'Indian/Maldives': {
36
+ regionCode: 'MV',
37
+ ianaSource: 'asia'
38
+ },
39
+ 'Indian/Mauritius': {
40
+ regionCode: 'MU',
41
+ ianaSource: 'africa'
42
+ },
43
+ 'Indian/Mayotte': {
44
+ regionCode: 'YT',
45
+ ianaSource: 'africa'
46
+ },
47
+ 'Indian/Reunion': {
48
+ regionCode: 'RE',
49
+ ianaSource: 'africa'
50
+ }
51
+ };
52
+
53
+ export default indianTimezones;
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Pacific Timezone Mappings
3
+ * Maps IANA timezone identifiers to country codes and source files
4
+ */
5
+
6
+ export const pacificTimezones = {
7
+ 'Pacific/Apia': {
8
+ regionCode: 'WS',
9
+ ianaSource: 'australasia'
10
+ },
11
+ 'Pacific/Auckland': {
12
+ regionCode: 'NZ',
13
+ ianaSource: 'australasia'
14
+ },
15
+ 'Pacific/Bougainville': {
16
+ regionCode: 'PG',
17
+ ianaSource: 'australasia'
18
+ },
19
+ 'Pacific/Chatham': {
20
+ regionCode: 'NZ',
21
+ ianaSource: 'australasia'
22
+ },
23
+ 'Pacific/Chuuk': {
24
+ regionCode: 'FM',
25
+ ianaSource: 'australasia'
26
+ },
27
+ 'Pacific/Easter': {
28
+ regionCode: 'CL',
29
+ ianaSource: 'southamerica'
30
+ },
31
+ 'Pacific/Efate': {
32
+ regionCode: 'VU',
33
+ ianaSource: 'australasia'
34
+ },
35
+ 'Pacific/Enderbury': {
36
+ regionCode: 'KI',
37
+ ianaSource: 'australasia'
38
+ },
39
+ 'Pacific/Fakaofo': {
40
+ regionCode: 'TK',
41
+ ianaSource: 'australasia'
42
+ },
43
+ 'Pacific/Fiji': {
44
+ regionCode: 'FJ',
45
+ ianaSource: 'australasia'
46
+ },
47
+ 'Pacific/Funafuti': {
48
+ regionCode: 'TV',
49
+ ianaSource: 'australasia'
50
+ },
51
+ 'Pacific/Galapagos': {
52
+ regionCode: 'EC',
53
+ ianaSource: 'southamerica'
54
+ },
55
+ 'Pacific/Gambier': {
56
+ regionCode: 'PF',
57
+ ianaSource: 'australasia'
58
+ },
59
+ 'Pacific/Guadalcanal': {
60
+ regionCode: 'SB',
61
+ ianaSource: 'australasia'
62
+ },
63
+ 'Pacific/Guam': {
64
+ regionCode: 'GU',
65
+ ianaSource: 'australasia'
66
+ },
67
+ 'Pacific/Honolulu': {
68
+ regionCode: 'US',
69
+ ianaSource: 'northamerica'
70
+ },
71
+ 'Pacific/Kiritimati': {
72
+ regionCode: 'KI',
73
+ ianaSource: 'australasia'
74
+ },
75
+ 'Pacific/Kosrae': {
76
+ regionCode: 'FM',
77
+ ianaSource: 'australasia'
78
+ },
79
+ 'Pacific/Kwajalein': {
80
+ regionCode: 'MH',
81
+ ianaSource: 'australasia'
82
+ },
83
+ 'Pacific/Majuro': {
84
+ regionCode: 'MH',
85
+ ianaSource: 'australasia'
86
+ },
87
+ 'Pacific/Marquesas': {
88
+ regionCode: 'PF',
89
+ ianaSource: 'australasia'
90
+ },
91
+ 'Pacific/Midway': {
92
+ regionCode: 'UM',
93
+ ianaSource: 'australasia'
94
+ },
95
+ 'Pacific/Nauru': {
96
+ regionCode: 'NR',
97
+ ianaSource: 'australasia'
98
+ },
99
+ 'Pacific/Niue': {
100
+ regionCode: 'NU',
101
+ ianaSource: 'australasia'
102
+ },
103
+ 'Pacific/Norfolk': {
104
+ regionCode: 'NF',
105
+ ianaSource: 'australasia'
106
+ },
107
+ 'Pacific/Noumea': {
108
+ regionCode: 'NC',
109
+ ianaSource: 'australasia'
110
+ },
111
+ 'Pacific/Pago_Pago': {
112
+ regionCode: 'AS',
113
+ ianaSource: 'australasia'
114
+ },
115
+ 'Pacific/Palau': {
116
+ regionCode: 'PW',
117
+ ianaSource: 'australasia'
118
+ },
119
+ 'Pacific/Pitcairn': {
120
+ regionCode: 'PN',
121
+ ianaSource: 'australasia'
122
+ },
123
+ 'Pacific/Pohnpei': {
124
+ regionCode: 'FM',
125
+ ianaSource: 'australasia'
126
+ },
127
+ 'Pacific/Port_Moresby': {
128
+ regionCode: 'PG',
129
+ ianaSource: 'australasia'
130
+ },
131
+ 'Pacific/Rarotonga': {
132
+ regionCode: 'CK',
133
+ ianaSource: 'australasia'
134
+ },
135
+ 'Pacific/Saipan': {
136
+ regionCode: 'MP',
137
+ ianaSource: 'australasia'
138
+ },
139
+ 'Pacific/Tahiti': {
140
+ regionCode: 'PF',
141
+ ianaSource: 'australasia'
142
+ },
143
+ 'Pacific/Tarawa': {
144
+ regionCode: 'KI',
145
+ ianaSource: 'australasia'
146
+ },
147
+ 'Pacific/Tongatapu': {
148
+ regionCode: 'TO',
149
+ ianaSource: 'australasia'
150
+ },
151
+ 'Pacific/Wake': {
152
+ regionCode: 'UM',
153
+ ianaSource: 'australasia'
154
+ },
155
+ 'Pacific/Wallis': {
156
+ regionCode: 'WF',
157
+ ianaSource: 'australasia'
158
+ }
159
+ };
160
+
161
+ export default pacificTimezones;
@@ -0,0 +1,236 @@
1
+ /**
2
+ * TimeZone Utilities
3
+ *
4
+ * This module provides utilities for working with IANA timeZone identifiers
5
+ * and their corresponding region information using ES modules.
6
+ *
7
+ * Data is based on the IANA Time Zone Database (tzdata) and excludes
8
+ * backward compatibility and etcetera entries.
9
+ */
10
+
11
+ import { getDocumentLocaleSettings } from '../common.js';
12
+
13
+ export class TimeZoneMapper {
14
+ constructor(modules) {
15
+ this.mappings = new Map();
16
+ this.loaded = false;
17
+ this.modules = modules ? [].concat(modules).map(m => m.toLowerCase()) : [
18
+ 'africa',
19
+ 'america',
20
+ 'asia',
21
+ 'europe',
22
+ 'pacific',
23
+ 'australia',
24
+ 'antarctica',
25
+ 'atlantic',
26
+ 'indian'
27
+ ];
28
+ }
29
+
30
+ /**
31
+ * Get all countries with their localized names
32
+ * @param {string|string[]} locales - Locale(s) for display names
33
+ * @returns {Array<{code: string, name: string}>} Array of region objects
34
+ */
35
+ getAllCountries(locales = 'en') {
36
+ const regionCodes = this.getAllRegionCodes();
37
+ const displayNames = new Intl.DisplayNames(locales, { type: 'region' });
38
+
39
+ return regionCodes.map(code => ({
40
+ code,
41
+ name: displayNames.of(code)
42
+ })).sort((a, b) => a.name.localeCompare(b.name, locales));
43
+ }
44
+ /**
45
+ * Get all unique region codes
46
+ * @returns {string[]} Sorted array of all region codes
47
+ */
48
+ getAllRegionCodes() {
49
+ this._ensureLoaded();
50
+ const regionCodes = new Set();
51
+
52
+ for (const info of this.mappings.values()) {
53
+ regionCodes.add(info.regionCode);
54
+ }
55
+
56
+ return Array.from(regionCodes).sort();
57
+ }
58
+ /**
59
+ * Get all available timeZone identifiers
60
+ * @returns {string[]} Sorted array of all timeZone identifiers
61
+ */
62
+ getAllTimeZones() {
63
+ this._ensureLoaded();
64
+ return Array.from(this.mappings.keys()).sort();
65
+ }
66
+ /**
67
+ * Get IANA source file for a timeZone
68
+ * @param {string} timeZoneId - IANA timeZone identifier
69
+ * @returns {string|null} Source file name or null if not found
70
+ */
71
+ getIanaSource(timeZoneId) {
72
+ this._ensureLoaded();
73
+ const mapping = this.mappings.get(timeZoneId);
74
+ return mapping ? mapping.ianaSource : null;
75
+ }
76
+ /**
77
+ * Get region code for a timeZone
78
+ * @param {string} timeZoneId - IANA timeZone identifier (e.g., "America/New_York")
79
+ * @returns {string|null} ISO 3166-1 alpha-2 region code or null if not found
80
+ */
81
+ getRegionCode(timeZoneId) {
82
+ this._ensureLoaded();
83
+ const mapping = this.mappings.get(timeZoneId);
84
+ return mapping ? mapping.regionCode : null;
85
+ }
86
+ /**
87
+ * Get localized region name using Intl.DisplayNames
88
+ * @param {string} timeZoneId - IANA timeZone identifier
89
+ * @param {string|string[]} locales - Locale(s) for display names
90
+ * @returns {string|null} Localized region name or null if not found
91
+ */
92
+ getRegionName(timeZoneId, locale = getDocumentLocaleSettings().language) {
93
+ const regionCode = this.getRegionCode(timeZoneId);
94
+ if (!regionCode) return null;
95
+
96
+ try {
97
+ const displayNames = new Intl.DisplayNames(locale, { type: 'region' });
98
+ return displayNames.of(regionCode);
99
+ } catch (error) {
100
+ console.warn('Failed to get localized region name:', error);
101
+ return null;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get complete mapping information for a timeZone
107
+ * @param {string} timeZoneId - IANA timeZone identifier
108
+ * @returns {Object|null} Complete mapping object or null if not found
109
+ */
110
+ getTimeZoneInfo(timeZoneId) {
111
+ this._ensureLoaded();
112
+ return this.mappings.get(timeZoneId) || null;
113
+ }
114
+ /**
115
+ * Group timeZones by region
116
+ * @param {string|string[]} locales - Locale(s) for region names
117
+ * @returns {Object} Object with region codes as keys and timeZone arrays as values
118
+ */
119
+ getTimeZonesByRegion(locales = 'en') {
120
+ this._ensureLoaded();
121
+ const result = {};
122
+
123
+ for (const [timeZone, info] of this.mappings.entries()) {
124
+ const regionCode = info.regionCode;
125
+ if (!result[regionCode]) {
126
+ result[regionCode] = {
127
+ name: this.getRegionName(timeZone, locales),
128
+ timeZones: []
129
+ };
130
+ }
131
+ result[regionCode].timeZones.push(timeZone);
132
+ }
133
+
134
+ // Sort timeZones within each region
135
+ for (const region of Object.values(result)) {
136
+ region.timeZones.sort();
137
+ }
138
+
139
+ return result;
140
+ }
141
+ /**
142
+ * Get all timeZones for a specific region
143
+ * @param {string} regionCode - ISO 3166-1 alpha-2 region code
144
+ * @returns {string[]} Array of timeZone identifiers
145
+ */
146
+ getTimeZonesForRegion(regionCode) {
147
+ this._ensureLoaded();
148
+ const timeZones = [];
149
+
150
+ for (const [timeZone, info] of this.mappings.entries()) {
151
+ if (info.regionCode === regionCode.toUpperCase()) {
152
+ timeZones.push(timeZone);
153
+ }
154
+ }
155
+
156
+ return timeZones.sort();
157
+ }
158
+ /**
159
+ * Load timeZone mappings from a single combined object
160
+ * @param {Object} timeZoneData - Combined timeZone data object
161
+ */
162
+ loadFromData(timeZoneData) {
163
+ this.mappings.clear();
164
+ for (const [timeZone, info] of Object.entries(timeZoneData)) {
165
+ this.mappings.set(timeZone, info);
166
+ }
167
+ this.loaded = true;
168
+ }
169
+
170
+ /**
171
+ * Load timeZone mappings from JavaScript modules
172
+ * @param {string} basePath - Base path to the timeZone mappings directory
173
+ * @returns {Promise<void>}
174
+ */
175
+ async loadMappings() {
176
+ if (this.loaded) return;
177
+
178
+ try {
179
+ for (const moduleName of this.modules) {
180
+ const module = await import(`./data/${moduleName}.js`);
181
+ const timeZoneData = module.default || module[`${moduleName}TimeZones`];
182
+
183
+ // Merge all timeZone data into the main mappings
184
+ for (const [timeZone, info] of Object.entries(timeZoneData)) {
185
+ this.mappings.set(timeZone, info);
186
+ }
187
+ }
188
+ this.loaded = true;
189
+ } catch (error) {
190
+ console.error('Failed to load timeZone mappings:', error);
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Search timeZones by partial name match
197
+ * @param {string} query - Search query
198
+ * @param {string|string[]} locales - Locale(s) for region name matching
199
+ * @returns {string[]} Array of matching timeZone identifiers
200
+ */
201
+ searchTimeZones(query, locales = 'en') {
202
+ this._ensureLoaded();
203
+ const lowerQuery = query.toLowerCase();
204
+ const results = [];
205
+
206
+ for (const [timeZone] of this.mappings.entries()) {
207
+ // Match timeZone identifier
208
+ if (timeZone.toLowerCase().includes(lowerQuery)) {
209
+ results.push(timeZone);
210
+ continue;
211
+ }
212
+
213
+ // Match region name
214
+ try {
215
+ const regionName = this.getRegionName(timeZone, locales);
216
+ if (regionName && regionName.toLowerCase().includes(lowerQuery)) {
217
+ results.push(timeZone);
218
+ }
219
+ } catch {
220
+ // Continue if region name lookup fails
221
+ }
222
+ }
223
+
224
+ return results.sort();
225
+ }
226
+
227
+ /**
228
+ * Check if mappings are loaded
229
+ * @private
230
+ */
231
+ _ensureLoaded() {
232
+ if (!this.loaded) {
233
+ throw new Error('TimeZone mappings not loaded. Call loadMappings() first.');
234
+ }
235
+ }
236
+ }
@@ -0,0 +1,59 @@
1
+ import { getDocumentLocaleSettings } from './common.js';
2
+ import { TimeZoneMapper } from './_timeZones/mapper.js';
3
+
4
+ let timeZoneData;
5
+ getDocumentLocaleSettings().addChangeListener(() => timeZoneData = null);
6
+
7
+ const tzMap = {
8
+ 'Asia/Calcutta': 'Asia/Kolkata',
9
+ };
10
+
11
+ export const timeZoneIdentifiers = Intl.supportedValuesOf('timeZone').map(t => tzMap[t] || t);
12
+
13
+ export async function getTimeZonesData(region, modules) {
14
+ let timeZones;
15
+ const mapper = new TimeZoneMapper(modules);
16
+ await mapper.loadMappings();
17
+
18
+ if (region) {
19
+ timeZones = new Intl.Locale(`en-${region}`).getTimeZones?.();
20
+ if (!timeZones?.length) {
21
+ timeZones = mapper.getTimeZonesForRegion(region);
22
+ }
23
+ } else {
24
+ if (timeZoneData) return timeZoneData;
25
+ timeZones = timeZoneIdentifiers;
26
+ }
27
+ return await Promise.all(timeZones.map(l => getTimeZoneData(l, { mapper })));
28
+ }
29
+
30
+ export async function getTimeZoneData(
31
+ identifier = getDocumentLocaleSettings().timezone.identifier,
32
+ {
33
+ modules,
34
+ mapper,
35
+ locale = getDocumentLocaleSettings().language || 'en'
36
+ } = {}) {
37
+ mapper ??= new TimeZoneMapper(modules);
38
+ await mapper.loadMappings();
39
+ const [localName, abbreviation, offset] = ['long', 'short', 'longOffset'].map(timeZoneName => new Intl.DateTimeFormat(locale, { timeZone: identifier, timeZoneName }).formatToParts().find(({ type }) => type === 'timeZoneName').value);
40
+ const city = identifier.replace(/[^/]*\//, '').replace('_', ' ');
41
+ const country = mapper.getRegionName(identifier) || '';
42
+ const friendlyName = `${country && `${country} - `}${city}`;
43
+ const inputName = `(${offset}) ${city} - ${localName}`;
44
+
45
+ return {
46
+ identifier,
47
+ abbreviation,
48
+ city,
49
+ country,
50
+ friendlyName,
51
+ inputName,
52
+ localName,
53
+ offset
54
+ };
55
+ }
56
+
57
+ export function validateTimeZone(identifier = getDocumentLocaleSettings().timezone.identifier) {
58
+ return timeZoneIdentifiers.includes(identifier);
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/intl",
3
- "version": "3.29.0",
3
+ "version": "3.30.1",
4
4
  "description": "Internationalization APIs for number, date, time and file size formatting and parsing in D2L Brightspace.",
5
5
  "main": "lib/number.js",
6
6
  "type": "module",