@forcecalendar/core 0.2.0 → 0.2.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,294 @@
1
+ /**
2
+ * TimezoneDatabase - Comprehensive IANA timezone database
3
+ * Contains timezone rules for all major zones worldwide
4
+ */
5
+
6
+ export class TimezoneDatabase {
7
+ constructor() {
8
+ // Comprehensive IANA timezone offset data (Standard Time)
9
+ // Offsets in minutes from UTC
10
+ this.timezones = {
11
+ // UTC
12
+ 'UTC': { offset: 0, dst: null },
13
+ 'GMT': { offset: 0, dst: null },
14
+
15
+ // Africa
16
+ 'Africa/Abidjan': { offset: 0, dst: null },
17
+ 'Africa/Accra': { offset: 0, dst: null },
18
+ 'Africa/Addis_Ababa': { offset: 180, dst: null },
19
+ 'Africa/Algiers': { offset: 60, dst: null },
20
+ 'Africa/Cairo': { offset: 120, dst: null },
21
+ 'Africa/Casablanca': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
22
+ 'Africa/Johannesburg': { offset: 120, dst: null },
23
+ 'Africa/Lagos': { offset: 60, dst: null },
24
+ 'Africa/Nairobi': { offset: 180, dst: null },
25
+
26
+ // Americas
27
+ 'America/Anchorage': { offset: -540, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
28
+ 'America/Argentina/Buenos_Aires': { offset: -180, dst: null },
29
+ 'America/Bogota': { offset: -300, dst: null },
30
+ 'America/Caracas': { offset: -240, dst: null },
31
+ 'America/Chicago': { offset: -360, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
32
+ 'America/Denver': { offset: -420, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
33
+ 'America/Detroit': { offset: -300, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
34
+ 'America/Halifax': { offset: -240, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
35
+ 'America/Los_Angeles': { offset: -480, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
36
+ 'America/Mexico_City': { offset: -360, dst: { start: { month: 4, week: 1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
37
+ 'America/New_York': { offset: -300, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
38
+ 'America/Phoenix': { offset: -420, dst: null },
39
+ 'America/Regina': { offset: -360, dst: null },
40
+ 'America/Santiago': { offset: -180, dst: { start: { month: 9, week: 1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
41
+ 'America/Sao_Paulo': { offset: -180, dst: { start: { month: 10, week: 3, day: 0 }, end: { month: 2, week: 3, day: 0 }, offset: 60 }},
42
+ 'America/St_Johns': { offset: -210, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
43
+ 'America/Toronto': { offset: -300, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
44
+ 'America/Vancouver': { offset: -480, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
45
+
46
+ // Asia
47
+ 'Asia/Baghdad': { offset: 180, dst: null },
48
+ 'Asia/Bangkok': { offset: 420, dst: null },
49
+ 'Asia/Dubai': { offset: 240, dst: null },
50
+ 'Asia/Hong_Kong': { offset: 480, dst: null },
51
+ 'Asia/Jakarta': { offset: 420, dst: null },
52
+ 'Asia/Jerusalem': { offset: 120, dst: { start: { month: 3, week: -1, day: 5 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
53
+ 'Asia/Karachi': { offset: 300, dst: null },
54
+ 'Asia/Kolkata': { offset: 330, dst: null },
55
+ 'Asia/Kuala_Lumpur': { offset: 480, dst: null },
56
+ 'Asia/Manila': { offset: 480, dst: null },
57
+ 'Asia/Seoul': { offset: 540, dst: null },
58
+ 'Asia/Shanghai': { offset: 480, dst: null },
59
+ 'Asia/Singapore': { offset: 480, dst: null },
60
+ 'Asia/Taipei': { offset: 480, dst: null },
61
+ 'Asia/Tehran': { offset: 210, dst: { start: { month: 3, week: 4, day: 0 }, end: { month: 9, week: 4, day: 0 }, offset: 60 }},
62
+ 'Asia/Tokyo': { offset: 540, dst: null },
63
+
64
+ // Atlantic
65
+ 'Atlantic/Azores': { offset: -60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
66
+ 'Atlantic/Bermuda': { offset: -240, dst: { start: { month: 3, week: 2, day: 0 }, end: { month: 11, week: 1, day: 0 }, offset: 60 }},
67
+ 'Atlantic/Reykjavik': { offset: 0, dst: null },
68
+
69
+ // Australia & Pacific
70
+ 'Australia/Adelaide': { offset: 570, dst: { start: { month: 10, week: 1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
71
+ 'Australia/Brisbane': { offset: 600, dst: null },
72
+ 'Australia/Darwin': { offset: 570, dst: null },
73
+ 'Australia/Hobart': { offset: 600, dst: { start: { month: 10, week: 1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
74
+ 'Australia/Melbourne': { offset: 600, dst: { start: { month: 10, week: 1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
75
+ 'Australia/Perth': { offset: 480, dst: null },
76
+ 'Australia/Sydney': { offset: 600, dst: { start: { month: 10, week: 1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
77
+
78
+ // Europe
79
+ 'Europe/Amsterdam': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
80
+ 'Europe/Athens': { offset: 120, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
81
+ 'Europe/Berlin': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
82
+ 'Europe/Brussels': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
83
+ 'Europe/Budapest': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
84
+ 'Europe/Copenhagen': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
85
+ 'Europe/Dublin': { offset: 0, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
86
+ 'Europe/Helsinki': { offset: 120, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
87
+ 'Europe/Istanbul': { offset: 180, dst: null },
88
+ 'Europe/Kiev': { offset: 120, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
89
+ 'Europe/Lisbon': { offset: 0, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
90
+ 'Europe/London': { offset: 0, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
91
+ 'Europe/Madrid': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
92
+ 'Europe/Moscow': { offset: 180, dst: null },
93
+ 'Europe/Oslo': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
94
+ 'Europe/Paris': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
95
+ 'Europe/Prague': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
96
+ 'Europe/Rome': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
97
+ 'Europe/Stockholm': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
98
+ 'Europe/Vienna': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
99
+ 'Europe/Warsaw': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
100
+ 'Europe/Zurich': { offset: 60, dst: { start: { month: 3, week: -1, day: 0 }, end: { month: 10, week: -1, day: 0 }, offset: 60 }},
101
+
102
+ // Indian
103
+ 'Indian/Maldives': { offset: 300, dst: null },
104
+ 'Indian/Mauritius': { offset: 240, dst: null },
105
+
106
+ // Pacific
107
+ 'Pacific/Auckland': { offset: 720, dst: { start: { month: 9, week: -1, day: 0 }, end: { month: 4, week: 1, day: 0 }, offset: 60 }},
108
+ 'Pacific/Fiji': { offset: 720, dst: { start: { month: 11, week: 1, day: 0 }, end: { month: 1, week: 3, day: 0 }, offset: 60 }},
109
+ 'Pacific/Guam': { offset: 600, dst: null },
110
+ 'Pacific/Honolulu': { offset: -600, dst: null },
111
+ 'Pacific/Midway': { offset: -660, dst: null },
112
+ 'Pacific/Noumea': { offset: 660, dst: null },
113
+ 'Pacific/Pago_Pago': { offset: -660, dst: null },
114
+ 'Pacific/Port_Moresby': { offset: 600, dst: null },
115
+ 'Pacific/Tahiti': { offset: -600, dst: null }
116
+ };
117
+
118
+ // Timezone aliases and abbreviations
119
+ this.aliases = {
120
+ // Common abbreviations to IANA
121
+ 'EST': 'America/New_York',
122
+ 'EDT': 'America/New_York',
123
+ 'CST': 'America/Chicago',
124
+ 'CDT': 'America/Chicago',
125
+ 'MST': 'America/Denver',
126
+ 'MDT': 'America/Denver',
127
+ 'PST': 'America/Los_Angeles',
128
+ 'PDT': 'America/Los_Angeles',
129
+ 'AKST': 'America/Anchorage',
130
+ 'AKDT': 'America/Anchorage',
131
+ 'HST': 'Pacific/Honolulu',
132
+ 'AST': 'America/Halifax',
133
+ 'ADT': 'America/Halifax',
134
+ 'NST': 'America/St_Johns',
135
+ 'NDT': 'America/St_Johns',
136
+ 'BST': 'Europe/London',
137
+ 'IST': 'Asia/Kolkata',
138
+ 'WET': 'Europe/Lisbon',
139
+ 'WEST': 'Europe/Lisbon',
140
+ 'CET': 'Europe/Paris',
141
+ 'CEST': 'Europe/Paris',
142
+ 'EET': 'Europe/Athens',
143
+ 'EEST': 'Europe/Athens',
144
+ 'MSK': 'Europe/Moscow',
145
+ 'JST': 'Asia/Tokyo',
146
+ 'KST': 'Asia/Seoul',
147
+ 'CST_CN': 'Asia/Shanghai',
148
+ 'HKT': 'Asia/Hong_Kong',
149
+ 'SGT': 'Asia/Singapore',
150
+ 'AEST': 'Australia/Sydney',
151
+ 'AEDT': 'Australia/Sydney',
152
+ 'ACST': 'Australia/Adelaide',
153
+ 'ACDT': 'Australia/Adelaide',
154
+ 'AWST': 'Australia/Perth',
155
+ 'NZST': 'Pacific/Auckland',
156
+ 'NZDT': 'Pacific/Auckland',
157
+
158
+ // City/Country aliases
159
+ 'US/Eastern': 'America/New_York',
160
+ 'US/Central': 'America/Chicago',
161
+ 'US/Mountain': 'America/Denver',
162
+ 'US/Pacific': 'America/Los_Angeles',
163
+ 'US/Alaska': 'America/Anchorage',
164
+ 'US/Hawaii': 'Pacific/Honolulu',
165
+ 'Canada/Eastern': 'America/Toronto',
166
+ 'Canada/Central': 'America/Regina',
167
+ 'Canada/Mountain': 'America/Denver',
168
+ 'Canada/Pacific': 'America/Vancouver',
169
+ 'Canada/Atlantic': 'America/Halifax',
170
+ 'Canada/Newfoundland': 'America/St_Johns',
171
+ 'Mexico/General': 'America/Mexico_City',
172
+ 'Brazil/East': 'America/Sao_Paulo',
173
+ 'Chile/Continental': 'America/Santiago',
174
+ 'GB': 'Europe/London',
175
+ 'GB-Eire': 'Europe/London',
176
+ 'Eire': 'Europe/Dublin',
177
+ 'Israel': 'Asia/Jerusalem',
178
+ 'Japan': 'Asia/Tokyo',
179
+ 'Singapore': 'Asia/Singapore',
180
+ 'Hongkong': 'Asia/Hong_Kong',
181
+ 'ROK': 'Asia/Seoul',
182
+ 'PRC': 'Asia/Shanghai',
183
+ 'Australia/NSW': 'Australia/Sydney',
184
+ 'Australia/Victoria': 'Australia/Melbourne',
185
+ 'Australia/Queensland': 'Australia/Brisbane',
186
+ 'Australia/South': 'Australia/Adelaide',
187
+ 'Australia/Tasmania': 'Australia/Hobart',
188
+ 'Australia/West': 'Australia/Perth',
189
+ 'Australia/North': 'Australia/Darwin',
190
+ 'NZ': 'Pacific/Auckland'
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Get timezone data by identifier
196
+ * @param {string} timezone - Timezone identifier or alias
197
+ * @returns {Object|null} Timezone data or null if not found
198
+ */
199
+ getTimezone(timezone) {
200
+ // Check for alias first
201
+ if (this.aliases[timezone]) {
202
+ timezone = this.aliases[timezone];
203
+ }
204
+
205
+ return this.timezones[timezone] || null;
206
+ }
207
+
208
+ /**
209
+ * Get all available timezone identifiers
210
+ * @returns {string[]} Array of timezone identifiers
211
+ */
212
+ getAllTimezones() {
213
+ return Object.keys(this.timezones);
214
+ }
215
+
216
+ /**
217
+ * Check if a timezone identifier is valid
218
+ * @param {string} timezone - Timezone identifier
219
+ * @returns {boolean} True if valid
220
+ */
221
+ isValidTimezone(timezone) {
222
+ return this.aliases[timezone] !== undefined || this.timezones[timezone] !== undefined;
223
+ }
224
+
225
+ /**
226
+ * Resolve timezone alias to canonical identifier
227
+ * @param {string} timezone - Timezone identifier or alias
228
+ * @returns {string} Canonical timezone identifier
229
+ */
230
+ resolveAlias(timezone) {
231
+ return this.aliases[timezone] || timezone;
232
+ }
233
+
234
+ /**
235
+ * Get timezones by offset
236
+ * @param {number} offsetMinutes - Offset in minutes from UTC
237
+ * @returns {string[]} Array of timezone identifiers
238
+ */
239
+ getTimezonesByOffset(offsetMinutes) {
240
+ return Object.entries(this.timezones)
241
+ .filter(([_, data]) => data.offset === offsetMinutes)
242
+ .map(([id, _]) => id);
243
+ }
244
+
245
+ /**
246
+ * Get common timezones for quick selection
247
+ * @returns {Object} Grouped timezones by region
248
+ */
249
+ getCommonTimezones() {
250
+ return {
251
+ 'Americas': [
252
+ 'America/New_York',
253
+ 'America/Chicago',
254
+ 'America/Denver',
255
+ 'America/Los_Angeles',
256
+ 'America/Toronto',
257
+ 'America/Mexico_City',
258
+ 'America/Sao_Paulo'
259
+ ],
260
+ 'Europe': [
261
+ 'Europe/London',
262
+ 'Europe/Paris',
263
+ 'Europe/Berlin',
264
+ 'Europe/Moscow',
265
+ 'Europe/Rome',
266
+ 'Europe/Madrid',
267
+ 'Europe/Amsterdam'
268
+ ],
269
+ 'Asia': [
270
+ 'Asia/Tokyo',
271
+ 'Asia/Shanghai',
272
+ 'Asia/Hong_Kong',
273
+ 'Asia/Singapore',
274
+ 'Asia/Kolkata',
275
+ 'Asia/Dubai',
276
+ 'Asia/Seoul'
277
+ ],
278
+ 'Australia/Pacific': [
279
+ 'Australia/Sydney',
280
+ 'Australia/Melbourne',
281
+ 'Australia/Brisbane',
282
+ 'Australia/Perth',
283
+ 'Pacific/Auckland',
284
+ 'Pacific/Honolulu'
285
+ ],
286
+ 'Africa': [
287
+ 'Africa/Cairo',
288
+ 'Africa/Lagos',
289
+ 'Africa/Johannesburg',
290
+ 'Africa/Nairobi'
291
+ ]
292
+ };
293
+ }
294
+ }