@getmicdrop/svelte-components 5.3.12 → 5.3.13
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.
- package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
- package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
- package/dist/calendar/FAQs/FAQs.svelte +75 -75
- package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
- package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
- package/dist/calendar/PublicCard/PublicCard.svelte +145 -145
- package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
- package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
- package/dist/components/Layout/Grid.svelte +109 -109
- package/dist/components/Layout/Section.svelte +80 -80
- package/dist/components/Layout/Sidebar.svelte +108 -108
- package/dist/components/Layout/Stack.svelte +90 -90
- package/dist/constants/formOptions.js +26 -26
- package/dist/constants/validation.js +91 -91
- package/dist/constants/validation.spec.js +64 -64
- package/dist/datetime/__tests__/format.test.d.ts +2 -0
- package/dist/datetime/__tests__/format.test.d.ts.map +1 -0
- package/dist/datetime/__tests__/format.test.js +268 -0
- package/dist/datetime/__tests__/integration.test.d.ts +2 -0
- package/dist/datetime/__tests__/integration.test.d.ts.map +1 -0
- package/dist/datetime/__tests__/integration.test.js +243 -0
- package/dist/datetime/__tests__/parse.test.d.ts +2 -0
- package/dist/datetime/__tests__/parse.test.d.ts.map +1 -0
- package/dist/datetime/__tests__/parse.test.js +261 -0
- package/dist/datetime/__tests__/timezone.test.d.ts +2 -0
- package/dist/datetime/__tests__/timezone.test.d.ts.map +1 -0
- package/dist/datetime/__tests__/timezone.test.js +214 -0
- package/dist/datetime/constants.d.ts +133 -0
- package/dist/datetime/constants.d.ts.map +1 -0
- package/dist/datetime/constants.js +112 -0
- package/dist/datetime/format.d.ts +158 -0
- package/dist/datetime/format.d.ts.map +1 -0
- package/dist/datetime/format.js +315 -0
- package/dist/datetime/index.d.ts +42 -0
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/datetime/index.js +44 -0
- package/dist/datetime/parse.d.ts +149 -0
- package/dist/datetime/parse.d.ts.map +1 -0
- package/dist/datetime/parse.js +276 -0
- package/dist/datetime/timezone.d.ts +95 -0
- package/dist/datetime/timezone.d.ts.map +1 -0
- package/dist/datetime/timezone.js +241 -0
- package/dist/datetime/types.d.ts +105 -0
- package/dist/datetime/types.d.ts.map +1 -0
- package/dist/datetime/types.js +31 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +232 -218
- package/dist/patterns/data/DataGrid.svelte +45 -45
- package/dist/patterns/data/DataList.svelte +24 -24
- package/dist/patterns/data/DataTable.svelte +40 -40
- package/dist/patterns/forms/FormActions.spec.js +88 -88
- package/dist/patterns/forms/FormActions.stories.svelte +97 -97
- package/dist/patterns/forms/FormActions.svelte +46 -46
- package/dist/patterns/forms/FormGrid.svelte +33 -33
- package/dist/patterns/forms/FormSection.svelte +32 -32
- package/dist/patterns/forms/FormValidationSummary.spec.js +203 -203
- package/dist/patterns/forms/FormValidationSummary.stories.svelte +97 -97
- package/dist/patterns/forms/FormValidationSummary.svelte +67 -67
- package/dist/patterns/layout/Grid.svelte +35 -35
- package/dist/patterns/layout/Sidebar.svelte +39 -39
- package/dist/patterns/layout/Stack.svelte +45 -45
- package/dist/patterns/navigation/BottomNav.spec.js +130 -130
- package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
- package/dist/patterns/navigation/BottomNav.svelte +54 -54
- package/dist/patterns/navigation/Header.spec.js +203 -203
- package/dist/patterns/navigation/Header.stories.svelte +77 -77
- package/dist/patterns/navigation/Header.svelte +240 -240
- package/dist/patterns/page/PageHeader.svelte +36 -36
- package/dist/patterns/page/PageLayout.svelte +40 -40
- package/dist/patterns/page/PageLoader.spec.js +54 -54
- package/dist/patterns/page/PageLoader.stories.svelte +137 -137
- package/dist/patterns/page/PageLoader.svelte +41 -41
- package/dist/patterns/page/SectionHeader.svelte +41 -41
- package/dist/presets/badges.js +112 -112
- package/dist/presets/buttons.js +76 -76
- package/dist/presets/index.js +9 -9
- package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
- package/dist/primitives/Accordion/Accordion.svelte +61 -61
- package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
- package/dist/primitives/Alert/Alert.spec.js +170 -170
- package/dist/primitives/Alert/Alert.stories.svelte +88 -88
- package/dist/primitives/Alert/Alert.svelte +65 -65
- package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
- package/dist/primitives/Avatar/Avatar.svelte +66 -66
- package/dist/primitives/Badges/Badge.spec.js +103 -103
- package/dist/primitives/Badges/Badge.stories.svelte +86 -86
- package/dist/primitives/Badges/Badge.svelte +142 -142
- package/dist/primitives/BottomSheet/BottomSheet.spec.js +127 -127
- package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
- package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
- package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +120 -120
- package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
- package/dist/primitives/Button/Button.spec.js +211 -211
- package/dist/primitives/Button/Button.stories.svelte +76 -76
- package/dist/primitives/Button/Button.svelte +301 -301
- package/dist/primitives/Button/ButtonSaveDemo.spec.js +48 -48
- package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
- package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
- package/dist/primitives/Card.spec.js +49 -49
- package/dist/primitives/Card.stories.svelte +22 -22
- package/dist/primitives/Card.svelte +28 -28
- package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
- package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
- package/dist/primitives/DarkModeToggle.spec.js +357 -357
- package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
- package/dist/primitives/DarkModeToggle.svelte +136 -136
- package/dist/primitives/Drawer/Drawer.stories.svelte +100 -100
- package/dist/primitives/Drawer/Drawer.svelte +214 -214
- package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
- package/dist/primitives/Dropdown/Dropdown.svelte +148 -148
- package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
- package/dist/primitives/Icons/ArrowLeft.svelte +20 -20
- package/dist/primitives/Icons/ArrowRight.svelte +20 -20
- package/dist/primitives/Icons/Availability.svelte +26 -26
- package/dist/primitives/Icons/Back.svelte +26 -26
- package/dist/primitives/Icons/CheckCircle.svelte +18 -18
- package/dist/primitives/Icons/CheckCircleOutline.svelte +27 -27
- package/dist/primitives/Icons/ChevronLeft.svelte +16 -16
- package/dist/primitives/Icons/ChevronRight.svelte +16 -16
- package/dist/primitives/Icons/Copy.svelte +27 -27
- package/dist/primitives/Icons/Cross.svelte +17 -17
- package/dist/primitives/Icons/DownArrow.svelte +20 -20
- package/dist/primitives/Icons/ErrorCircle.svelte +18 -18
- package/dist/primitives/Icons/FacebookIcon.svelte +13 -13
- package/dist/primitives/Icons/Home.svelte +27 -27
- package/dist/primitives/Icons/Icon.spec.js +175 -175
- package/dist/primitives/Icons/Icon.stories.svelte +100 -100
- package/dist/primitives/Icons/Icon.svelte +63 -63
- package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
- package/dist/primitives/Icons/ImageOutline.svelte +19 -19
- package/dist/primitives/Icons/Info.svelte +19 -19
- package/dist/primitives/Icons/InstagramIcon.svelte +19 -19
- package/dist/primitives/Icons/LogoInstagram.svelte +15 -15
- package/dist/primitives/Icons/Message.svelte +27 -27
- package/dist/primitives/Icons/MoonIcon.svelte +16 -16
- package/dist/primitives/Icons/More.svelte +33 -33
- package/dist/primitives/Icons/MoreHori.spec.js +67 -67
- package/dist/primitives/Icons/MoreHori.svelte +34 -34
- package/dist/primitives/Icons/Notification.svelte +26 -26
- package/dist/primitives/Icons/Payment.svelte +26 -26
- package/dist/primitives/Icons/Profile.svelte +33 -33
- package/dist/primitives/Icons/Reload.svelte +41 -41
- package/dist/primitives/Icons/Shows.svelte +33 -33
- package/dist/primitives/Icons/Signout.svelte +33 -33
- package/dist/primitives/Icons/SunIcon.svelte +19 -19
- package/dist/primitives/Icons/TiktokIcon.svelte +13 -13
- package/dist/primitives/Icons/TrashBinOutline.svelte +19 -19
- package/dist/primitives/Icons/TwitterIcon.svelte +13 -13
- package/dist/primitives/Icons/WarningIcon.spec.js +30 -30
- package/dist/primitives/Icons/WarningIcon.svelte +24 -24
- package/dist/primitives/Input/Input.spec.js +573 -573
- package/dist/primitives/Input/Input.stories.svelte +139 -139
- package/dist/primitives/Input/Input.svelte +444 -444
- package/dist/primitives/Input/Select.spec.js +218 -218
- package/dist/primitives/Input/Select.stories.svelte +112 -112
- package/dist/primitives/Input/Select.svelte +232 -232
- package/dist/primitives/Input/Textarea.stories.svelte +137 -137
- package/dist/primitives/Input/Textarea.svelte +79 -79
- package/dist/primitives/Label/Label.svelte +37 -37
- package/dist/primitives/Modal/Modal.spec.js +95 -95
- package/dist/primitives/Modal/Modal.stories.svelte +86 -86
- package/dist/primitives/Modal/Modal.svelte +158 -158
- package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
- package/dist/primitives/Pagination/Pagination.svelte +261 -261
- package/dist/primitives/Radio/Radio.stories.svelte +80 -80
- package/dist/primitives/Radio/Radio.svelte +67 -67
- package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
- package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
- package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
- package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
- package/dist/primitives/Skeleton/Skeleton.svelte +52 -52
- package/dist/primitives/Spinner/Spinner.spec.js +75 -75
- package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
- package/dist/primitives/Spinner/Spinner.svelte +57 -57
- package/dist/primitives/Tabs/TabItem.svelte +51 -51
- package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
- package/dist/primitives/Tabs/Tabs.svelte +128 -128
- package/dist/primitives/Toggle.spec.js +127 -127
- package/dist/primitives/Toggle.stories.svelte +92 -92
- package/dist/primitives/Toggle.svelte +71 -71
- package/dist/primitives/Typography/Typography.svelte +53 -53
- package/dist/primitives/ValidationError.spec.js +103 -103
- package/dist/primitives/ValidationError.stories.svelte +111 -111
- package/dist/primitives/ValidationError.svelte +29 -29
- package/dist/recipes/CropImage/CropImage.spec.js +216 -216
- package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
- package/dist/recipes/CropImage/CropImage.svelte +238 -238
- package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
- package/dist/recipes/ImageUploader/ImageUploader.svelte +980 -980
- package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
- package/dist/recipes/feedback/EmptyState/EmptyState.svelte +47 -47
- package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
- package/dist/recipes/feedback/ErrorDisplay.stories.svelte +112 -112
- package/dist/recipes/feedback/ErrorDisplay.svelte +38 -38
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +129 -129
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +167 -167
- package/dist/recipes/fields/CheckboxField.svelte +85 -85
- package/dist/recipes/fields/FormField.svelte +58 -58
- package/dist/recipes/fields/RadioGroup.svelte +95 -95
- package/dist/recipes/fields/SelectField.svelte +82 -82
- package/dist/recipes/fields/TextareaField.svelte +101 -101
- package/dist/recipes/fields/ToggleField.svelte +60 -60
- package/dist/recipes/fields/index.js +7 -7
- package/dist/recipes/inputs/MultiSelect.spec.js +257 -257
- package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
- package/dist/recipes/inputs/MultiSelect.svelte +244 -244
- package/dist/recipes/inputs/OTPInput.spec.js +238 -238
- package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
- package/dist/recipes/inputs/OTPInput.svelte +102 -102
- package/dist/recipes/inputs/PasswordInput.svelte +100 -100
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +173 -173
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +108 -108
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +300 -300
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +165 -165
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +337 -337
- package/dist/recipes/inputs/Search.svelte +85 -85
- package/dist/recipes/inputs/SelectDropdown.svelte +161 -161
- package/dist/recipes/modals/AlertModal.svelte +130 -130
- package/dist/recipes/modals/ConfirmationModal.spec.js +191 -191
- package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
- package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
- package/dist/recipes/modals/InputModal.svelte +182 -182
- package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
- package/dist/recipes/modals/ModalStateManager.svelte +77 -77
- package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
- package/dist/recipes/modals/StatusModal.svelte +206 -206
- package/dist/services/EventService.js +75 -75
- package/dist/services/EventService.spec.js +217 -217
- package/dist/services/ShowService.spec.js +342 -342
- package/dist/stores/auth.js +93 -6
- package/dist/stores/auth.spec.js +310 -2
- package/dist/stores/toaster.js +13 -13
- package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
- package/dist/stories/ButtonAuditReview.svelte +427 -427
- package/dist/stories/PatternsGallery.stories.svelte +19 -19
- package/dist/stories/PatternsGallery.svelte +388 -388
- package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
- package/dist/stories/PrimitivesGallery.svelte +752 -752
- package/dist/stories/RecipesGallery.stories.svelte +19 -19
- package/dist/stories/RecipesGallery.svelte +441 -441
- package/dist/stories/button-audit-manifest.json +11186 -11186
- package/dist/tailwind/preset.cjs +82 -82
- package/dist/telemetry.js +357 -357
- package/dist/tokens/tokens.css +87 -87
- package/dist/utils/apiConfig.js +49 -49
- package/dist/utils/utils.js +9 -1
- package/package.json +233 -191
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { combineDateAndTime, formatDateTimeForAPI, isNextDayTime, minutesToTimeString, parseDateTimeFromAPI, parseEndOfDay, parseLocalToUTC, parseStartOfDay, parseTimeToMinutes, parseUTCToLocal, stripNextDayPrefix, } from '../parse';
|
|
3
|
+
import { DateTimeError, DateTimeErrorCode } from '../types';
|
|
4
|
+
describe('parse utilities', () => {
|
|
5
|
+
describe('parseLocalToUTC', () => {
|
|
6
|
+
it('converts local datetime to UTC', () => {
|
|
7
|
+
// 7 PM on Dec 25 in LA = 3 AM Dec 26 UTC
|
|
8
|
+
const local = '2023-12-25T19:00:00';
|
|
9
|
+
const utc = parseLocalToUTC(local, 'America/Los_Angeles');
|
|
10
|
+
expect(utc).toBe('2023-12-26T03:00:00.000Z');
|
|
11
|
+
});
|
|
12
|
+
it('handles midnight correctly', () => {
|
|
13
|
+
// Midnight Dec 25 in LA = 8 AM Dec 25 UTC
|
|
14
|
+
const local = '2023-12-25T00:00:00';
|
|
15
|
+
const utc = parseLocalToUTC(local, 'America/Los_Angeles');
|
|
16
|
+
expect(utc).toBe('2023-12-25T08:00:00.000Z');
|
|
17
|
+
});
|
|
18
|
+
it('handles different timezones', () => {
|
|
19
|
+
const local = '2023-12-25T12:00:00';
|
|
20
|
+
// Noon in NY = 5 PM UTC
|
|
21
|
+
const nyUtc = parseLocalToUTC(local, 'America/New_York');
|
|
22
|
+
expect(nyUtc).toBe('2023-12-25T17:00:00.000Z');
|
|
23
|
+
// Noon in London = noon UTC (winter)
|
|
24
|
+
const londonUtc = parseLocalToUTC(local, 'Europe/London');
|
|
25
|
+
expect(londonUtc).toBe('2023-12-25T12:00:00.000Z');
|
|
26
|
+
});
|
|
27
|
+
it('handles DST transitions correctly', () => {
|
|
28
|
+
// March 12, 2023 - DST starts in LA at 2 AM (skips to 3 AM)
|
|
29
|
+
const beforeDST = '2023-03-12T01:30:00';
|
|
30
|
+
const afterDST = '2023-03-12T03:30:00';
|
|
31
|
+
const utcBefore = parseLocalToUTC(beforeDST, 'America/Los_Angeles');
|
|
32
|
+
const utcAfter = parseLocalToUTC(afterDST, 'America/Los_Angeles');
|
|
33
|
+
// 1:30 AM PST = 9:30 AM UTC
|
|
34
|
+
expect(utcBefore).toBe('2023-03-12T09:30:00.000Z');
|
|
35
|
+
// 3:30 AM PDT = 10:30 AM UTC
|
|
36
|
+
expect(utcAfter).toBe('2023-03-12T10:30:00.000Z');
|
|
37
|
+
});
|
|
38
|
+
it('throws on invalid local datetime', () => {
|
|
39
|
+
expect(() => parseLocalToUTC('invalid', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
40
|
+
});
|
|
41
|
+
it('throws on empty string', () => {
|
|
42
|
+
expect(() => parseLocalToUTC('', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
43
|
+
});
|
|
44
|
+
it('throws on null', () => {
|
|
45
|
+
expect(() => parseLocalToUTC(null, 'America/Los_Angeles')).toThrow();
|
|
46
|
+
});
|
|
47
|
+
it('throws on invalid timezone', () => {
|
|
48
|
+
expect(() => parseLocalToUTC('2023-12-25T19:00:00', 'Invalid/Zone')).toThrow(DateTimeError);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('parseUTCToLocal', () => {
|
|
52
|
+
it('converts UTC to local datetime', () => {
|
|
53
|
+
// 3 AM UTC Dec 26 = 7 PM Dec 25 in LA
|
|
54
|
+
const utc = '2023-12-26T03:00:00Z';
|
|
55
|
+
const local = parseUTCToLocal(utc, 'America/Los_Angeles');
|
|
56
|
+
expect(local).toBe('2023-12-25T19:00:00');
|
|
57
|
+
});
|
|
58
|
+
it('handles midnight UTC', () => {
|
|
59
|
+
// Midnight UTC Dec 25 = 4 PM Dec 24 in LA
|
|
60
|
+
const utc = '2023-12-25T00:00:00Z';
|
|
61
|
+
const local = parseUTCToLocal(utc, 'America/Los_Angeles');
|
|
62
|
+
expect(local).toBe('2023-12-24T16:00:00');
|
|
63
|
+
});
|
|
64
|
+
it('handles different timezones', () => {
|
|
65
|
+
const utc = '2023-12-25T17:00:00Z';
|
|
66
|
+
// 5 PM UTC = noon in NY
|
|
67
|
+
const nyLocal = parseUTCToLocal(utc, 'America/New_York');
|
|
68
|
+
expect(nyLocal).toBe('2023-12-25T12:00:00');
|
|
69
|
+
// 5 PM UTC = 2 AM next day in Tokyo
|
|
70
|
+
const tokyoLocal = parseUTCToLocal(utc, 'Asia/Tokyo');
|
|
71
|
+
expect(tokyoLocal).toBe('2023-12-26T02:00:00');
|
|
72
|
+
});
|
|
73
|
+
it('throws on invalid UTC string', () => {
|
|
74
|
+
expect(() => parseUTCToLocal('invalid', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
75
|
+
});
|
|
76
|
+
it('throws on invalid timezone', () => {
|
|
77
|
+
expect(() => parseUTCToLocal('2023-12-25T17:00:00Z', 'Invalid/Zone')).toThrow(DateTimeError);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('combineDateAndTime', () => {
|
|
81
|
+
it('combines date and time strings', () => {
|
|
82
|
+
const result = combineDateAndTime('2023-12-25', '19:00', 'America/Los_Angeles');
|
|
83
|
+
expect(result).toBe('2023-12-26T03:00:00.000Z');
|
|
84
|
+
});
|
|
85
|
+
it('handles 24-hour time format', () => {
|
|
86
|
+
const result = combineDateAndTime('2023-12-25', '23:30', 'America/Los_Angeles');
|
|
87
|
+
expect(result).toBe('2023-12-26T07:30:00.000Z');
|
|
88
|
+
});
|
|
89
|
+
it('handles midnight', () => {
|
|
90
|
+
const result = combineDateAndTime('2023-12-25', '00:00', 'America/Los_Angeles');
|
|
91
|
+
expect(result).toBe('2023-12-25T08:00:00.000Z');
|
|
92
|
+
});
|
|
93
|
+
it('handles time with seconds', () => {
|
|
94
|
+
const result = combineDateAndTime('2023-12-25', '19:30:45', 'America/Los_Angeles');
|
|
95
|
+
expect(result).toBe('2023-12-26T03:30:45.000Z');
|
|
96
|
+
});
|
|
97
|
+
it('throws on invalid date', () => {
|
|
98
|
+
expect(() => combineDateAndTime('invalid', '19:00', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
99
|
+
expect(() => combineDateAndTime('2023-13-45', '19:00', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
100
|
+
});
|
|
101
|
+
it('throws on invalid time', () => {
|
|
102
|
+
expect(() => combineDateAndTime('2023-12-25', 'invalid', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
103
|
+
expect(() => combineDateAndTime('2023-12-25', '25:00', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
104
|
+
expect(() => combineDateAndTime('2023-12-25', '12:60', 'America/Los_Angeles')).toThrow(DateTimeError);
|
|
105
|
+
});
|
|
106
|
+
it('throws on invalid timezone', () => {
|
|
107
|
+
expect(() => combineDateAndTime('2023-12-25', '19:00', 'Invalid/Zone')).toThrow(DateTimeError);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('parseDateTimeFromAPI', () => {
|
|
111
|
+
it('parses UTC to date and time components', () => {
|
|
112
|
+
const result = parseDateTimeFromAPI('2023-12-26T03:00:00Z', 'America/Los_Angeles');
|
|
113
|
+
expect(result).toEqual({
|
|
114
|
+
date: '2023-12-25',
|
|
115
|
+
time: '19:00',
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
it('handles midnight', () => {
|
|
119
|
+
const result = parseDateTimeFromAPI('2023-12-25T08:00:00Z', 'America/Los_Angeles');
|
|
120
|
+
expect(result).toEqual({
|
|
121
|
+
date: '2023-12-25',
|
|
122
|
+
time: '00:00',
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it('handles different timezones', () => {
|
|
126
|
+
const utc = '2023-12-25T17:00:00Z';
|
|
127
|
+
const nyResult = parseDateTimeFromAPI(utc, 'America/New_York');
|
|
128
|
+
expect(nyResult).toEqual({ date: '2023-12-25', time: '12:00' });
|
|
129
|
+
const tokyoResult = parseDateTimeFromAPI(utc, 'Asia/Tokyo');
|
|
130
|
+
expect(tokyoResult).toEqual({ date: '2023-12-26', time: '02:00' });
|
|
131
|
+
});
|
|
132
|
+
it('throws on invalid inputs', () => {
|
|
133
|
+
expect(() => parseDateTimeFromAPI('invalid', 'America/Los_Angeles')).toThrow();
|
|
134
|
+
expect(() => parseDateTimeFromAPI('2023-12-25T17:00:00Z', 'Invalid/Zone')).toThrow();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('formatDateTimeForAPI', () => {
|
|
138
|
+
it('formats Date to UTC ISO string', () => {
|
|
139
|
+
const date = new Date('2023-12-25T19:00:00-08:00');
|
|
140
|
+
const result = formatDateTimeForAPI(date);
|
|
141
|
+
expect(result).toBe('2023-12-26T03:00:00.000Z');
|
|
142
|
+
});
|
|
143
|
+
it('throws on invalid Date', () => {
|
|
144
|
+
expect(() => formatDateTimeForAPI(new Date('invalid'))).toThrow(DateTimeError);
|
|
145
|
+
expect(() => formatDateTimeForAPI(null)).toThrow(DateTimeError);
|
|
146
|
+
expect(() => formatDateTimeForAPI('string')).toThrow(DateTimeError);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('parseStartOfDay', () => {
|
|
150
|
+
it('returns UTC for start of day', () => {
|
|
151
|
+
const result = parseStartOfDay('2023-12-25', 'America/Los_Angeles');
|
|
152
|
+
// Midnight LA = 8 AM UTC
|
|
153
|
+
expect(result).toBe('2023-12-25T08:00:00.000Z');
|
|
154
|
+
});
|
|
155
|
+
it('handles different timezones', () => {
|
|
156
|
+
// Midnight NY = 5 AM UTC
|
|
157
|
+
const nyResult = parseStartOfDay('2023-12-25', 'America/New_York');
|
|
158
|
+
expect(nyResult).toBe('2023-12-25T05:00:00.000Z');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
describe('parseEndOfDay', () => {
|
|
162
|
+
it('returns UTC for end of day', () => {
|
|
163
|
+
const result = parseEndOfDay('2023-12-25', 'America/Los_Angeles');
|
|
164
|
+
// 11:59:59 PM LA = 7:59:59 AM next day UTC
|
|
165
|
+
expect(result).toBe('2023-12-26T07:59:59.000Z');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe('isNextDayTime', () => {
|
|
169
|
+
it('returns true for +1_ prefixed times', () => {
|
|
170
|
+
expect(isNextDayTime('+1_02:00')).toBe(true);
|
|
171
|
+
expect(isNextDayTime('+1_00:00')).toBe(true);
|
|
172
|
+
expect(isNextDayTime('+1_23:59')).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
it('returns false for regular times', () => {
|
|
175
|
+
expect(isNextDayTime('14:00')).toBe(false);
|
|
176
|
+
expect(isNextDayTime('00:00')).toBe(false);
|
|
177
|
+
expect(isNextDayTime('23:59')).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
it('returns false for invalid input', () => {
|
|
180
|
+
expect(isNextDayTime('')).toBe(false);
|
|
181
|
+
expect(isNextDayTime(null)).toBe(false);
|
|
182
|
+
expect(isNextDayTime(undefined)).toBe(false);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
describe('stripNextDayPrefix', () => {
|
|
186
|
+
it('strips +1_ prefix', () => {
|
|
187
|
+
expect(stripNextDayPrefix('+1_02:00')).toBe('02:00');
|
|
188
|
+
expect(stripNextDayPrefix('+1_23:30')).toBe('23:30');
|
|
189
|
+
});
|
|
190
|
+
it('returns regular times unchanged', () => {
|
|
191
|
+
expect(stripNextDayPrefix('14:00')).toBe('14:00');
|
|
192
|
+
expect(stripNextDayPrefix('00:00')).toBe('00:00');
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('parseTimeToMinutes', () => {
|
|
196
|
+
it('converts time to minutes', () => {
|
|
197
|
+
expect(parseTimeToMinutes('00:00')).toBe(0);
|
|
198
|
+
expect(parseTimeToMinutes('01:00')).toBe(60);
|
|
199
|
+
expect(parseTimeToMinutes('12:00')).toBe(720);
|
|
200
|
+
expect(parseTimeToMinutes('14:30')).toBe(870);
|
|
201
|
+
expect(parseTimeToMinutes('23:59')).toBe(1439);
|
|
202
|
+
});
|
|
203
|
+
it('handles next-day times', () => {
|
|
204
|
+
expect(parseTimeToMinutes('+1_00:00')).toBe(1440);
|
|
205
|
+
expect(parseTimeToMinutes('+1_02:00')).toBe(1560);
|
|
206
|
+
expect(parseTimeToMinutes('+1_12:00')).toBe(2160);
|
|
207
|
+
});
|
|
208
|
+
it('throws on invalid time format', () => {
|
|
209
|
+
expect(() => parseTimeToMinutes('invalid')).toThrow(DateTimeError);
|
|
210
|
+
expect(() => parseTimeToMinutes('25:00')).toThrow(DateTimeError);
|
|
211
|
+
expect(() => parseTimeToMinutes('12:60')).toThrow(DateTimeError);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe('minutesToTimeString', () => {
|
|
215
|
+
it('converts minutes to time string', () => {
|
|
216
|
+
expect(minutesToTimeString(0)).toBe('00:00');
|
|
217
|
+
expect(minutesToTimeString(60)).toBe('01:00');
|
|
218
|
+
expect(minutesToTimeString(720)).toBe('12:00');
|
|
219
|
+
expect(minutesToTimeString(870)).toBe('14:30');
|
|
220
|
+
expect(minutesToTimeString(1439)).toBe('23:59');
|
|
221
|
+
});
|
|
222
|
+
it('handles overflow minutes without prefix', () => {
|
|
223
|
+
expect(minutesToTimeString(1440, false)).toBe('00:00');
|
|
224
|
+
expect(minutesToTimeString(1560, false)).toBe('02:00');
|
|
225
|
+
});
|
|
226
|
+
it('adds +1_ prefix for next-day when requested', () => {
|
|
227
|
+
expect(minutesToTimeString(1440, true)).toBe('+1_00:00');
|
|
228
|
+
expect(minutesToTimeString(1560, true)).toBe('+1_02:00');
|
|
229
|
+
expect(minutesToTimeString(2160, true)).toBe('+1_12:00');
|
|
230
|
+
});
|
|
231
|
+
it('does not add prefix for same-day minutes', () => {
|
|
232
|
+
expect(minutesToTimeString(720, true)).toBe('12:00');
|
|
233
|
+
expect(minutesToTimeString(0, true)).toBe('00:00');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe('round-trip conversions', () => {
|
|
237
|
+
it('local -> UTC -> local preserves value', () => {
|
|
238
|
+
const original = '2023-12-25T19:00:00';
|
|
239
|
+
const timezone = 'America/Los_Angeles';
|
|
240
|
+
const utc = parseLocalToUTC(original, timezone);
|
|
241
|
+
const roundTrip = parseUTCToLocal(utc, timezone);
|
|
242
|
+
expect(roundTrip).toBe(original);
|
|
243
|
+
});
|
|
244
|
+
it('UTC -> local -> UTC preserves value', () => {
|
|
245
|
+
const original = '2023-12-26T03:00:00.000Z';
|
|
246
|
+
const timezone = 'America/Los_Angeles';
|
|
247
|
+
const local = parseUTCToLocal(original, timezone);
|
|
248
|
+
const roundTrip = parseLocalToUTC(local, timezone);
|
|
249
|
+
expect(roundTrip).toBe(original);
|
|
250
|
+
});
|
|
251
|
+
it('date + time -> UTC -> date + time preserves values', () => {
|
|
252
|
+
const date = '2023-12-25';
|
|
253
|
+
const time = '19:00';
|
|
254
|
+
const timezone = 'America/Los_Angeles';
|
|
255
|
+
const utc = combineDateAndTime(date, time, timezone);
|
|
256
|
+
const parsed = parseDateTimeFromAPI(utc, timezone);
|
|
257
|
+
expect(parsed.date).toBe(date);
|
|
258
|
+
expect(parsed.time).toBe(time);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone.test.d.ts","sourceRoot":"","sources":["../../../src/lib/datetime/__tests__/timezone.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { getTimezoneDisplayName, getTimezoneOffset, getUserTimezone, getVenueTimezone, isDST, isValidTimezone, normalizeTimezone, } from '../timezone';
|
|
3
|
+
import { DateTimeError, DateTimeErrorCode } from '../types';
|
|
4
|
+
describe('timezone utilities', () => {
|
|
5
|
+
describe('isValidTimezone', () => {
|
|
6
|
+
it('returns true for valid IANA timezones', () => {
|
|
7
|
+
expect(isValidTimezone('America/Los_Angeles')).toBe(true);
|
|
8
|
+
expect(isValidTimezone('America/New_York')).toBe(true);
|
|
9
|
+
expect(isValidTimezone('Europe/London')).toBe(true);
|
|
10
|
+
expect(isValidTimezone('Asia/Tokyo')).toBe(true);
|
|
11
|
+
expect(isValidTimezone('Australia/Sydney')).toBe(true);
|
|
12
|
+
expect(isValidTimezone('UTC')).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('returns false for invalid timezones', () => {
|
|
15
|
+
expect(isValidTimezone('Invalid/Zone')).toBe(false);
|
|
16
|
+
expect(isValidTimezone('NotATimezone')).toBe(false);
|
|
17
|
+
expect(isValidTimezone('America/FakeCity')).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it('returns false for timezone abbreviations', () => {
|
|
20
|
+
expect(isValidTimezone('PST')).toBe(false);
|
|
21
|
+
expect(isValidTimezone('EST')).toBe(false);
|
|
22
|
+
expect(isValidTimezone('PDT')).toBe(false);
|
|
23
|
+
expect(isValidTimezone('CST')).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
it('returns false for empty or invalid input', () => {
|
|
26
|
+
expect(isValidTimezone('')).toBe(false);
|
|
27
|
+
expect(isValidTimezone(null)).toBe(false);
|
|
28
|
+
expect(isValidTimezone(undefined)).toBe(false);
|
|
29
|
+
expect(isValidTimezone(123)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it('caches validation results', () => {
|
|
32
|
+
// Call twice - second should use cache
|
|
33
|
+
const result1 = isValidTimezone('America/Chicago');
|
|
34
|
+
const result2 = isValidTimezone('America/Chicago');
|
|
35
|
+
expect(result1).toBe(true);
|
|
36
|
+
expect(result2).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('getVenueTimezone', () => {
|
|
40
|
+
it('returns venue timezone when present', () => {
|
|
41
|
+
const venue = { timezone: 'America/Los_Angeles' };
|
|
42
|
+
expect(getVenueTimezone(venue)).toBe('America/Los_Angeles');
|
|
43
|
+
});
|
|
44
|
+
it('works with venue objects with other properties', () => {
|
|
45
|
+
const venue = {
|
|
46
|
+
id: 123,
|
|
47
|
+
name: 'Jetpack Comedy',
|
|
48
|
+
timezone: 'America/Denver',
|
|
49
|
+
city: 'Denver',
|
|
50
|
+
};
|
|
51
|
+
expect(getVenueTimezone(venue)).toBe('America/Denver');
|
|
52
|
+
});
|
|
53
|
+
it('throws DateTimeError when timezone is missing', () => {
|
|
54
|
+
const venue = { name: 'Test Venue' };
|
|
55
|
+
expect(() => getVenueTimezone(venue)).toThrow(DateTimeError);
|
|
56
|
+
expect(() => getVenueTimezone(venue)).toThrow('Venue timezone is required');
|
|
57
|
+
});
|
|
58
|
+
it('throws DateTimeError when timezone is empty string', () => {
|
|
59
|
+
const venue = { timezone: '' };
|
|
60
|
+
expect(() => getVenueTimezone(venue)).toThrow(DateTimeError);
|
|
61
|
+
try {
|
|
62
|
+
getVenueTimezone(venue);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
expect(e.code).toBe(DateTimeErrorCode.MISSING_TIMEZONE);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
it('throws DateTimeError when timezone is whitespace', () => {
|
|
69
|
+
const venue = { timezone: ' ' };
|
|
70
|
+
expect(() => getVenueTimezone(venue)).toThrow(DateTimeError);
|
|
71
|
+
});
|
|
72
|
+
it('throws DateTimeError when timezone is invalid', () => {
|
|
73
|
+
const venue = { timezone: 'Invalid/Zone' };
|
|
74
|
+
expect(() => getVenueTimezone(venue)).toThrow(DateTimeError);
|
|
75
|
+
try {
|
|
76
|
+
getVenueTimezone(venue);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
expect(e.code).toBe(DateTimeErrorCode.INVALID_TIMEZONE);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
it('throws when venue is null or undefined', () => {
|
|
83
|
+
expect(() => getVenueTimezone(null)).toThrow();
|
|
84
|
+
expect(() => getVenueTimezone(undefined)).toThrow();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('getUserTimezone', () => {
|
|
88
|
+
it('returns a valid timezone', () => {
|
|
89
|
+
const tz = getUserTimezone();
|
|
90
|
+
expect(typeof tz).toBe('string');
|
|
91
|
+
expect(isValidTimezone(tz)).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it('returns the browser timezone', () => {
|
|
94
|
+
const tz = getUserTimezone();
|
|
95
|
+
// This should match what Intl reports
|
|
96
|
+
expect(tz).toBe(Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('getTimezoneOffset', () => {
|
|
100
|
+
it('returns offset string for valid timezone', () => {
|
|
101
|
+
const offset = getTimezoneOffset('America/Los_Angeles');
|
|
102
|
+
expect(offset).toMatch(/P[SD]T \(UTC[+-]\d+\)/);
|
|
103
|
+
});
|
|
104
|
+
it('returns correct offset for UTC', () => {
|
|
105
|
+
const offset = getTimezoneOffset('UTC');
|
|
106
|
+
expect(offset).toMatch(/UTC.*\(UTC[+-]?0?\)/);
|
|
107
|
+
});
|
|
108
|
+
it('returns different offsets for different timezones', () => {
|
|
109
|
+
const laOffset = getTimezoneOffset('America/Los_Angeles');
|
|
110
|
+
const nyOffset = getTimezoneOffset('America/New_York');
|
|
111
|
+
expect(laOffset).not.toBe(nyOffset);
|
|
112
|
+
});
|
|
113
|
+
it('respects DST when given specific date', () => {
|
|
114
|
+
// January (no DST)
|
|
115
|
+
const winterDate = new Date('2023-01-15T12:00:00Z');
|
|
116
|
+
const winterOffset = getTimezoneOffset('America/Los_Angeles', winterDate);
|
|
117
|
+
expect(winterOffset).toContain('PST');
|
|
118
|
+
// July (DST)
|
|
119
|
+
const summerDate = new Date('2023-07-15T12:00:00Z');
|
|
120
|
+
const summerOffset = getTimezoneOffset('America/Los_Angeles', summerDate);
|
|
121
|
+
expect(summerOffset).toContain('PDT');
|
|
122
|
+
});
|
|
123
|
+
it('throws DateTimeError for invalid timezone', () => {
|
|
124
|
+
expect(() => getTimezoneOffset('Invalid/Zone')).toThrow(DateTimeError);
|
|
125
|
+
try {
|
|
126
|
+
getTimezoneOffset('Invalid/Zone');
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
expect(e.code).toBe(DateTimeErrorCode.INVALID_TIMEZONE);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe('getTimezoneDisplayName', () => {
|
|
134
|
+
it('returns display name for valid timezone', () => {
|
|
135
|
+
const name = getTimezoneDisplayName('America/Los_Angeles');
|
|
136
|
+
expect(name).toMatch(/Pacific/);
|
|
137
|
+
});
|
|
138
|
+
it('returns display name for various timezones', () => {
|
|
139
|
+
expect(getTimezoneDisplayName('America/New_York')).toMatch(/Eastern/);
|
|
140
|
+
expect(getTimezoneDisplayName('Europe/London')).toMatch(/British|Greenwich/);
|
|
141
|
+
expect(getTimezoneDisplayName('Asia/Tokyo')).toMatch(/Japan/);
|
|
142
|
+
});
|
|
143
|
+
it('throws DateTimeError for invalid timezone', () => {
|
|
144
|
+
expect(() => getTimezoneDisplayName('Invalid/Zone')).toThrow(DateTimeError);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('normalizeTimezone', () => {
|
|
148
|
+
it('returns valid timezone as-is', () => {
|
|
149
|
+
expect(normalizeTimezone('America/Los_Angeles')).toBe('America/Los_Angeles');
|
|
150
|
+
expect(normalizeTimezone('Europe/London')).toBe('Europe/London');
|
|
151
|
+
});
|
|
152
|
+
it('normalizes common US abbreviations', () => {
|
|
153
|
+
expect(normalizeTimezone('PST')).toBe('America/Los_Angeles');
|
|
154
|
+
expect(normalizeTimezone('PDT')).toBe('America/Los_Angeles');
|
|
155
|
+
expect(normalizeTimezone('PT')).toBe('America/Los_Angeles');
|
|
156
|
+
expect(normalizeTimezone('EST')).toBe('America/New_York');
|
|
157
|
+
expect(normalizeTimezone('EDT')).toBe('America/New_York');
|
|
158
|
+
expect(normalizeTimezone('ET')).toBe('America/New_York');
|
|
159
|
+
expect(normalizeTimezone('CST')).toBe('America/Chicago');
|
|
160
|
+
expect(normalizeTimezone('CT')).toBe('America/Chicago');
|
|
161
|
+
expect(normalizeTimezone('MST')).toBe('America/Denver');
|
|
162
|
+
expect(normalizeTimezone('MT')).toBe('America/Denver');
|
|
163
|
+
});
|
|
164
|
+
it('normalizes case-insensitively', () => {
|
|
165
|
+
expect(normalizeTimezone('pst')).toBe('America/Los_Angeles');
|
|
166
|
+
expect(normalizeTimezone('Pacific')).toBe('America/Los_Angeles');
|
|
167
|
+
expect(normalizeTimezone('EASTERN')).toBe('America/New_York');
|
|
168
|
+
});
|
|
169
|
+
it('normalizes UTC variants', () => {
|
|
170
|
+
expect(normalizeTimezone('UTC')).toBe('UTC');
|
|
171
|
+
expect(normalizeTimezone('GMT')).toBe('UTC');
|
|
172
|
+
// 'utc' lowercase is actually valid in Intl API, so it passes through
|
|
173
|
+
expect(normalizeTimezone('utc')).toBe('utc');
|
|
174
|
+
});
|
|
175
|
+
it('trims whitespace', () => {
|
|
176
|
+
expect(normalizeTimezone(' America/Los_Angeles ')).toBe('America/Los_Angeles');
|
|
177
|
+
expect(normalizeTimezone(' PST ')).toBe('America/Los_Angeles');
|
|
178
|
+
});
|
|
179
|
+
it('throws DateTimeError for empty input', () => {
|
|
180
|
+
expect(() => normalizeTimezone('')).toThrow(DateTimeError);
|
|
181
|
+
expect(() => normalizeTimezone(null)).toThrow(DateTimeError);
|
|
182
|
+
expect(() => normalizeTimezone(undefined)).toThrow(DateTimeError);
|
|
183
|
+
});
|
|
184
|
+
it('throws DateTimeError for unknown abbreviations', () => {
|
|
185
|
+
expect(() => normalizeTimezone('UNKNOWN')).toThrow(DateTimeError);
|
|
186
|
+
expect(() => normalizeTimezone('XYZ')).toThrow(DateTimeError);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe('isDST', () => {
|
|
190
|
+
it('returns true during daylight saving time', () => {
|
|
191
|
+
// July is DST in LA
|
|
192
|
+
const summerDate = new Date('2023-07-15T12:00:00Z');
|
|
193
|
+
expect(isDST('America/Los_Angeles', summerDate)).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
it('returns false during standard time', () => {
|
|
196
|
+
// January is standard time in LA
|
|
197
|
+
const winterDate = new Date('2023-01-15T12:00:00Z');
|
|
198
|
+
expect(isDST('America/Los_Angeles', winterDate)).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
it('returns false for timezones without DST', () => {
|
|
201
|
+
// Arizona doesn't observe DST
|
|
202
|
+
const summerDate = new Date('2023-07-15T12:00:00Z');
|
|
203
|
+
expect(isDST('America/Phoenix', summerDate)).toBe(false);
|
|
204
|
+
const winterDate = new Date('2023-01-15T12:00:00Z');
|
|
205
|
+
expect(isDST('America/Phoenix', winterDate)).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
it('returns false for UTC', () => {
|
|
208
|
+
expect(isDST('UTC')).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
it('throws DateTimeError for invalid timezone', () => {
|
|
211
|
+
expect(() => isDST('Invalid/Zone')).toThrow(DateTimeError);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DateTime Constants
|
|
3
|
+
*
|
|
4
|
+
* Centralized constants for datetime operations.
|
|
5
|
+
* Use these instead of magic numbers throughout the codebase.
|
|
6
|
+
*
|
|
7
|
+
* @module datetime/constants
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Duration constants in milliseconds.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DURATIONS: {
|
|
13
|
+
/** One second in milliseconds */
|
|
14
|
+
readonly SECOND: 1000;
|
|
15
|
+
/** One minute in milliseconds */
|
|
16
|
+
readonly MINUTE: number;
|
|
17
|
+
/** One hour in milliseconds */
|
|
18
|
+
readonly HOUR: number;
|
|
19
|
+
/** One day in milliseconds */
|
|
20
|
+
readonly DAY: number;
|
|
21
|
+
/** One week in milliseconds */
|
|
22
|
+
readonly WEEK: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Duration constants in minutes.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DURATIONS_MINUTES: {
|
|
28
|
+
/** One hour in minutes */
|
|
29
|
+
readonly HOUR: 60;
|
|
30
|
+
/** One day in minutes */
|
|
31
|
+
readonly DAY: number;
|
|
32
|
+
/** One week in minutes */
|
|
33
|
+
readonly WEEK: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Date format strings for use with date-fns.
|
|
37
|
+
*/
|
|
38
|
+
export declare const DATE_FORMATS: {
|
|
39
|
+
/** API format: UTC ISO string */
|
|
40
|
+
readonly API: "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
|
41
|
+
/** Date for API without time */
|
|
42
|
+
readonly API_DATE: "yyyy-MM-dd";
|
|
43
|
+
/** Display date (e.g., "Dec 20, 2025") */
|
|
44
|
+
readonly DISPLAY_DATE: "MMM d, yyyy";
|
|
45
|
+
/** Display date without year (e.g., "Dec 20") */
|
|
46
|
+
readonly DISPLAY_DATE_SHORT: "MMM d";
|
|
47
|
+
/** Display time (e.g., "7:00 PM") */
|
|
48
|
+
readonly DISPLAY_TIME: "h:mm a";
|
|
49
|
+
/** Display time with seconds (e.g., "7:00:00 PM") */
|
|
50
|
+
readonly DISPLAY_TIME_SECONDS: "h:mm:ss a";
|
|
51
|
+
/** Display datetime (e.g., "Dec 20, 2025 at 7:00 PM") */
|
|
52
|
+
readonly DISPLAY_DATETIME: "MMM d, yyyy 'at' h:mm a";
|
|
53
|
+
/** Display full datetime with day (e.g., "Mon, Dec 20, 2025 at 7:00 PM") */
|
|
54
|
+
readonly DISPLAY_DATETIME_FULL: "EEE, MMM d, yyyy 'at' h:mm a";
|
|
55
|
+
/** Day of week short (e.g., "Mon") */
|
|
56
|
+
readonly DAY_SHORT: "EEE";
|
|
57
|
+
/** Day of week full (e.g., "Monday") */
|
|
58
|
+
readonly DAY_FULL: "EEEE";
|
|
59
|
+
/** Month short (e.g., "Dec") */
|
|
60
|
+
readonly MONTH_SHORT: "MMM";
|
|
61
|
+
/** Month full (e.g., "December") */
|
|
62
|
+
readonly MONTH_FULL: "MMMM";
|
|
63
|
+
/** Year only (e.g., "2025") */
|
|
64
|
+
readonly YEAR: "yyyy";
|
|
65
|
+
/** 24-hour time (e.g., "19:00") */
|
|
66
|
+
readonly TIME_24H: "HH:mm";
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Commonly used US timezones for quick selection.
|
|
70
|
+
*/
|
|
71
|
+
export declare const COMMON_US_TIMEZONES: readonly [{
|
|
72
|
+
readonly id: "America/Los_Angeles";
|
|
73
|
+
readonly label: "Pacific Time (PT)";
|
|
74
|
+
readonly abbr: "PT";
|
|
75
|
+
}, {
|
|
76
|
+
readonly id: "America/Denver";
|
|
77
|
+
readonly label: "Mountain Time (MT)";
|
|
78
|
+
readonly abbr: "MT";
|
|
79
|
+
}, {
|
|
80
|
+
readonly id: "America/Chicago";
|
|
81
|
+
readonly label: "Central Time (CT)";
|
|
82
|
+
readonly abbr: "CT";
|
|
83
|
+
}, {
|
|
84
|
+
readonly id: "America/New_York";
|
|
85
|
+
readonly label: "Eastern Time (ET)";
|
|
86
|
+
readonly abbr: "ET";
|
|
87
|
+
}, {
|
|
88
|
+
readonly id: "America/Phoenix";
|
|
89
|
+
readonly label: "Arizona (no DST)";
|
|
90
|
+
readonly abbr: "AZ";
|
|
91
|
+
}, {
|
|
92
|
+
readonly id: "Pacific/Honolulu";
|
|
93
|
+
readonly label: "Hawaii Time (HT)";
|
|
94
|
+
readonly abbr: "HT";
|
|
95
|
+
}, {
|
|
96
|
+
readonly id: "America/Anchorage";
|
|
97
|
+
readonly label: "Alaska Time (AKT)";
|
|
98
|
+
readonly abbr: "AKT";
|
|
99
|
+
}];
|
|
100
|
+
/**
|
|
101
|
+
* Default timezone when none is specified (should rarely be used).
|
|
102
|
+
* Prefer explicitly passing timezone in all operations.
|
|
103
|
+
*/
|
|
104
|
+
export declare const DEFAULT_TIMEZONE: "America/Los_Angeles";
|
|
105
|
+
/**
|
|
106
|
+
* Relative time thresholds for formatRelativeTime.
|
|
107
|
+
*/
|
|
108
|
+
export declare const RELATIVE_TIME_THRESHOLDS: {
|
|
109
|
+
/** Under this many seconds, show "just now" */
|
|
110
|
+
readonly JUST_NOW_SECONDS: 60;
|
|
111
|
+
/** Under this many minutes, show "X minutes ago/from now" */
|
|
112
|
+
readonly MINUTES_THRESHOLD: 60;
|
|
113
|
+
/** Under this many hours, show "X hours ago/from now" */
|
|
114
|
+
readonly HOURS_THRESHOLD: 24;
|
|
115
|
+
/** Under this many days, show "X days ago/from now" */
|
|
116
|
+
readonly DAYS_THRESHOLD: 7;
|
|
117
|
+
/** Under this many weeks, show "X weeks ago/from now" */
|
|
118
|
+
readonly WEEKS_THRESHOLD: 4;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* MicDrop-specific event defaults.
|
|
122
|
+
*/
|
|
123
|
+
export declare const EVENT_DEFAULTS: {
|
|
124
|
+
/** Default event duration in minutes */
|
|
125
|
+
readonly DEFAULT_DURATION_MINUTES: 120;
|
|
126
|
+
/** Minimum event duration in minutes */
|
|
127
|
+
readonly MIN_DURATION_MINUTES: 15;
|
|
128
|
+
/** Maximum event duration in minutes (24 hours) */
|
|
129
|
+
readonly MAX_DURATION_MINUTES: number;
|
|
130
|
+
/** Default doors open time before event start (minutes) */
|
|
131
|
+
readonly DEFAULT_DOORS_BEFORE_MINUTES: 30;
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/lib/datetime/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS;IACpB,iCAAiC;;IAEjC,iCAAiC;;IAEjC,+BAA+B;;IAE/B,8BAA8B;;IAE9B,+BAA+B;;CAEvB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,iBAAiB;IAC5B,0BAA0B;;IAE1B,yBAAyB;;IAEzB,0BAA0B;;CAElB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY;IACvB,iCAAiC;;IAEjC,gCAAgC;;IAEhC,0CAA0C;;IAE1C,iDAAiD;;IAEjD,qCAAqC;;IAErC,qDAAqD;;IAErD,yDAAyD;;IAEzD,4EAA4E;;IAE5E,sCAAsC;;IAEtC,wCAAwC;;IAExC,gCAAgC;;IAEhC,oCAAoC;;IAEpC,+BAA+B;;IAE/B,mCAAmC;;CAE3B,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQtB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAG,qBAA8B,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,wBAAwB;IACnC,+CAA+C;;IAE/C,6DAA6D;;IAE7D,yDAAyD;;IAEzD,uDAAuD;;IAEvD,yDAAyD;;CAEjD,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,cAAc;IACzB,wCAAwC;;IAExC,wCAAwC;;IAExC,mDAAmD;;IAEnD,2DAA2D;;CAEnD,CAAC"}
|