ts-time-utils 0.0.1 → 1.1.0
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/README.md +590 -1
- package/dist/age.d.ts +1 -10
- package/dist/age.d.ts.map +1 -1
- package/dist/calculate.d.ts.map +1 -1
- package/dist/calculate.js +24 -10
- package/dist/constants.d.ts +2 -21
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +12 -13
- package/dist/countdown.d.ts +217 -0
- package/dist/countdown.d.ts.map +1 -0
- package/dist/countdown.js +298 -0
- package/dist/dateRange.d.ts +266 -0
- package/dist/dateRange.d.ts.map +1 -0
- package/dist/dateRange.js +433 -0
- package/dist/duration.d.ts +171 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +382 -0
- package/dist/esm/age.d.ts +1 -10
- package/dist/esm/age.d.ts.map +1 -1
- package/dist/esm/calculate.d.ts.map +1 -1
- package/dist/esm/calculate.js +24 -10
- package/dist/esm/constants.d.ts +2 -21
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +12 -13
- package/dist/esm/countdown.d.ts +217 -0
- package/dist/esm/countdown.d.ts.map +1 -0
- package/dist/esm/countdown.js +298 -0
- package/dist/esm/dateRange.d.ts +266 -0
- package/dist/esm/dateRange.d.ts.map +1 -0
- package/dist/esm/dateRange.js +433 -0
- package/dist/esm/duration.d.ts +171 -0
- package/dist/esm/duration.d.ts.map +1 -0
- package/dist/esm/duration.js +382 -0
- package/dist/esm/format.d.ts.map +1 -1
- package/dist/esm/index.d.ts +14 -6
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +16 -0
- package/dist/esm/interval.d.ts +3 -6
- package/dist/esm/interval.d.ts.map +1 -1
- package/dist/esm/locale.d.ts +94 -0
- package/dist/esm/locale.d.ts.map +1 -0
- package/dist/esm/locale.js +1087 -0
- package/dist/esm/naturalLanguage.d.ts +107 -0
- package/dist/esm/naturalLanguage.d.ts.map +1 -0
- package/dist/esm/naturalLanguage.js +344 -0
- package/dist/esm/performance.d.ts +2 -9
- package/dist/esm/performance.d.ts.map +1 -1
- package/dist/esm/performance.js +7 -8
- package/dist/esm/rangePresets.d.ts +7 -8
- package/dist/esm/rangePresets.d.ts.map +1 -1
- package/dist/esm/rangePresets.js +11 -9
- package/dist/esm/recurrence.d.ts +149 -0
- package/dist/esm/recurrence.d.ts.map +1 -0
- package/dist/esm/recurrence.js +404 -0
- package/dist/esm/serialize.d.ts +73 -0
- package/dist/esm/serialize.d.ts.map +1 -0
- package/dist/esm/serialize.js +365 -0
- package/dist/esm/timezone.d.ts +2 -6
- package/dist/esm/timezone.d.ts.map +1 -1
- package/dist/esm/timezone.js +1 -1
- package/dist/esm/types.d.ts +250 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +25 -0
- package/dist/esm/workingHours.d.ts +4 -13
- package/dist/esm/workingHours.d.ts.map +1 -1
- package/dist/esm/workingHours.js +3 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/interval.d.ts +3 -6
- package/dist/interval.d.ts.map +1 -1
- package/dist/locale.d.ts +94 -0
- package/dist/locale.d.ts.map +1 -0
- package/dist/locale.js +1087 -0
- package/dist/naturalLanguage.d.ts +107 -0
- package/dist/naturalLanguage.d.ts.map +1 -0
- package/dist/naturalLanguage.js +344 -0
- package/dist/performance.d.ts +2 -9
- package/dist/performance.d.ts.map +1 -1
- package/dist/performance.js +7 -8
- package/dist/rangePresets.d.ts +7 -8
- package/dist/rangePresets.d.ts.map +1 -1
- package/dist/rangePresets.js +11 -9
- package/dist/recurrence.d.ts +149 -0
- package/dist/recurrence.d.ts.map +1 -0
- package/dist/recurrence.js +404 -0
- package/dist/serialize.d.ts +73 -0
- package/dist/serialize.d.ts.map +1 -0
- package/dist/serialize.js +365 -0
- package/dist/timezone.d.ts +2 -6
- package/dist/timezone.d.ts.map +1 -1
- package/dist/timezone.js +1 -1
- package/dist/types.d.ts +250 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/workingHours.d.ts +4 -13
- package/dist/workingHours.d.ts.map +1 -1
- package/dist/workingHours.js +3 -1
- package/package.json +67 -3
package/dist/esm/constants.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Constants for time conversions and utility types
|
|
3
3
|
*/
|
|
4
|
+
// Milliseconds per unit
|
|
4
5
|
export const MILLISECONDS_PER_SECOND = 1000;
|
|
5
|
-
export const MILLISECONDS_PER_MINUTE = 60 *
|
|
6
|
-
export const MILLISECONDS_PER_HOUR = 60 *
|
|
7
|
-
export const MILLISECONDS_PER_DAY = 24 *
|
|
8
|
-
export const MILLISECONDS_PER_WEEK = 7 *
|
|
9
|
-
export const MILLISECONDS_PER_MONTH = 30 *
|
|
10
|
-
export const MILLISECONDS_PER_YEAR = 365 *
|
|
11
|
-
|
|
12
|
-
* Second-based constants
|
|
13
|
-
*/
|
|
6
|
+
export const MILLISECONDS_PER_MINUTE = 60 * 1000;
|
|
7
|
+
export const MILLISECONDS_PER_HOUR = 60 * 60 * 1000;
|
|
8
|
+
export const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
9
|
+
export const MILLISECONDS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
|
|
10
|
+
export const MILLISECONDS_PER_MONTH = 30 * 24 * 60 * 60 * 1000; // Approximate
|
|
11
|
+
export const MILLISECONDS_PER_YEAR = 365 * 24 * 60 * 60 * 1000; // Approximate
|
|
12
|
+
// Seconds per unit
|
|
14
13
|
export const SECONDS_PER_MINUTE = 60;
|
|
15
|
-
export const SECONDS_PER_HOUR = 60 *
|
|
16
|
-
export const SECONDS_PER_DAY = 24 *
|
|
17
|
-
export const SECONDS_PER_WEEK = 7 *
|
|
14
|
+
export const SECONDS_PER_HOUR = 60 * 60;
|
|
15
|
+
export const SECONDS_PER_DAY = 24 * 60 * 60;
|
|
16
|
+
export const SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Countdown and timer utilities for tracking time until/since a target date
|
|
3
|
+
* Provides countdown timers, remaining time calculations, and progress tracking
|
|
4
|
+
*/
|
|
5
|
+
import type { DateInput } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Represents the remaining time broken down by units
|
|
8
|
+
*/
|
|
9
|
+
export interface RemainingTime {
|
|
10
|
+
/** Total milliseconds remaining */
|
|
11
|
+
totalMilliseconds: number;
|
|
12
|
+
/** Total seconds remaining */
|
|
13
|
+
totalSeconds: number;
|
|
14
|
+
/** Total minutes remaining */
|
|
15
|
+
totalMinutes: number;
|
|
16
|
+
/** Total hours remaining */
|
|
17
|
+
totalHours: number;
|
|
18
|
+
/** Total days remaining */
|
|
19
|
+
totalDays: number;
|
|
20
|
+
/** Milliseconds component (0-999) */
|
|
21
|
+
milliseconds: number;
|
|
22
|
+
/** Seconds component (0-59) */
|
|
23
|
+
seconds: number;
|
|
24
|
+
/** Minutes component (0-59) */
|
|
25
|
+
minutes: number;
|
|
26
|
+
/** Hours component (0-23) */
|
|
27
|
+
hours: number;
|
|
28
|
+
/** Days component */
|
|
29
|
+
days: number;
|
|
30
|
+
/** Weeks component */
|
|
31
|
+
weeks: number;
|
|
32
|
+
/** Whether the target date has passed */
|
|
33
|
+
isExpired: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Options for countdown creation
|
|
37
|
+
*/
|
|
38
|
+
export interface CountdownOptions {
|
|
39
|
+
/** Callback fired on each tick */
|
|
40
|
+
onTick?: (remaining: RemainingTime) => void;
|
|
41
|
+
/** Callback fired when countdown reaches zero */
|
|
42
|
+
onComplete?: () => void;
|
|
43
|
+
/** Callback fired if target date is in the past */
|
|
44
|
+
onExpired?: () => void;
|
|
45
|
+
/** Tick interval in milliseconds (default: 1000) */
|
|
46
|
+
interval?: number;
|
|
47
|
+
/** Whether to fire onTick immediately (default: true) */
|
|
48
|
+
immediate?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Countdown timer instance
|
|
52
|
+
*/
|
|
53
|
+
export interface Countdown {
|
|
54
|
+
/** Start the countdown */
|
|
55
|
+
start: () => void;
|
|
56
|
+
/** Stop the countdown */
|
|
57
|
+
stop: () => void;
|
|
58
|
+
/** Reset countdown with a new target date */
|
|
59
|
+
reset: (targetDate: DateInput) => void;
|
|
60
|
+
/** Get current remaining time */
|
|
61
|
+
getRemaining: () => RemainingTime;
|
|
62
|
+
/** Check if countdown is running */
|
|
63
|
+
isRunning: () => boolean;
|
|
64
|
+
/** Check if target date has passed */
|
|
65
|
+
isExpired: () => boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a countdown timer to a target date
|
|
69
|
+
* @param targetDate - The date to count down to
|
|
70
|
+
* @param options - Countdown options and callbacks
|
|
71
|
+
* @returns A countdown instance with control methods
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const countdown = createCountdown(
|
|
76
|
+
* new Date('2024-12-31T23:59:59'),
|
|
77
|
+
* {
|
|
78
|
+
* onTick: (remaining) => {
|
|
79
|
+
* console.log(`${remaining.days}d ${remaining.hours}h ${remaining.minutes}m ${remaining.seconds}s`);
|
|
80
|
+
* },
|
|
81
|
+
* onComplete: () => {
|
|
82
|
+
* console.log('Happy New Year!');
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* );
|
|
86
|
+
*
|
|
87
|
+
* countdown.start();
|
|
88
|
+
* // Later...
|
|
89
|
+
* countdown.stop();
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function createCountdown(targetDate: DateInput, options?: CountdownOptions): Countdown;
|
|
93
|
+
/**
|
|
94
|
+
* Gets the remaining time until/since a target date
|
|
95
|
+
* @param targetDate - The target date
|
|
96
|
+
* @param fromDate - The date to calculate from (defaults to now)
|
|
97
|
+
* @returns Object with remaining time broken down by units
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const remaining = getRemainingTime(new Date('2024-12-31'));
|
|
102
|
+
* console.log(`${remaining.days} days, ${remaining.hours} hours remaining`);
|
|
103
|
+
*
|
|
104
|
+
* // Check if expired
|
|
105
|
+
* if (remaining.isExpired) {
|
|
106
|
+
* console.log('Target date has passed');
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function getRemainingTime(targetDate: DateInput, fromDate?: DateInput): RemainingTime;
|
|
111
|
+
/**
|
|
112
|
+
* Formats the remaining time as a human-readable string
|
|
113
|
+
* @param targetDate - The target date
|
|
114
|
+
* @param options - Formatting options
|
|
115
|
+
* @returns Formatted countdown string
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* formatCountdown(new Date('2024-12-31'));
|
|
120
|
+
* // "45d 12h 30m 15s"
|
|
121
|
+
*
|
|
122
|
+
* formatCountdown(new Date('2024-12-31'), { units: ['days', 'hours'] });
|
|
123
|
+
* // "45 days, 12 hours"
|
|
124
|
+
*
|
|
125
|
+
* formatCountdown(new Date('2024-12-31'), { short: false });
|
|
126
|
+
* // "45 days 12 hours 30 minutes 15 seconds"
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export declare function formatCountdown(targetDate: DateInput, options?: {
|
|
130
|
+
/** Date to calculate from (defaults to now) */
|
|
131
|
+
from?: DateInput;
|
|
132
|
+
/** Units to include in output */
|
|
133
|
+
units?: ('weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds')[];
|
|
134
|
+
/** Use short format (d, h, m, s) */
|
|
135
|
+
short?: boolean;
|
|
136
|
+
/** Maximum number of units to show */
|
|
137
|
+
maxUnits?: number;
|
|
138
|
+
/** Show zero values */
|
|
139
|
+
showZero?: boolean;
|
|
140
|
+
/** Separator between units */
|
|
141
|
+
separator?: string;
|
|
142
|
+
}): string;
|
|
143
|
+
/**
|
|
144
|
+
* Checks if a date has expired (is in the past)
|
|
145
|
+
* @param date - The date to check
|
|
146
|
+
* @param fromDate - The reference date (defaults to now)
|
|
147
|
+
* @returns True if the date is in the past
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* isExpired(new Date('2020-01-01')); // true
|
|
152
|
+
* isExpired(new Date('2030-01-01')); // false
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export declare function isExpired(date: DateInput, fromDate?: DateInput): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Calculates the progress percentage between two dates
|
|
158
|
+
* @param startDate - The start date
|
|
159
|
+
* @param endDate - The end date
|
|
160
|
+
* @param currentDate - The current date (defaults to now)
|
|
161
|
+
* @returns Progress percentage (0-100), clamped to range
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* const progress = getProgressPercentage(
|
|
166
|
+
* new Date('2024-01-01'),
|
|
167
|
+
* new Date('2024-12-31'),
|
|
168
|
+
* new Date('2024-07-01')
|
|
169
|
+
* );
|
|
170
|
+
* console.log(`${progress}% complete`); // ~50% complete
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function getProgressPercentage(startDate: DateInput, endDate: DateInput, currentDate?: DateInput): number;
|
|
174
|
+
/**
|
|
175
|
+
* Gets time until a target date in a specific unit
|
|
176
|
+
* @param targetDate - The target date
|
|
177
|
+
* @param unit - The unit to return
|
|
178
|
+
* @param fromDate - The date to calculate from (defaults to now)
|
|
179
|
+
* @returns Time remaining in the specified unit
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* getTimeUntil(new Date('2024-12-31'), 'days'); // 45.5
|
|
184
|
+
* getTimeUntil(new Date('2024-12-31'), 'hours'); // 1092
|
|
185
|
+
* getTimeUntil(new Date('2024-12-31'), 'weeks'); // 6.5
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
export declare function getTimeUntil(targetDate: DateInput, unit: 'milliseconds' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks', fromDate?: DateInput): number;
|
|
189
|
+
/**
|
|
190
|
+
* Creates a deadline object with useful methods
|
|
191
|
+
* @param targetDate - The deadline date
|
|
192
|
+
* @returns An object with deadline-related methods
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const deadline = createDeadline(new Date('2024-12-31'));
|
|
197
|
+
*
|
|
198
|
+
* deadline.isExpired(); // false
|
|
199
|
+
* deadline.daysRemaining(); // 45
|
|
200
|
+
* deadline.hoursRemaining(); // 1092
|
|
201
|
+
* deadline.formatRemaining(); // "45d 12h 30m"
|
|
202
|
+
* deadline.progressFrom(new Date('2024-01-01')); // 67.5%
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export declare function createDeadline(targetDate: DateInput): {
|
|
206
|
+
target: Date;
|
|
207
|
+
isExpired: () => boolean;
|
|
208
|
+
getRemaining: () => RemainingTime;
|
|
209
|
+
daysRemaining: () => number;
|
|
210
|
+
hoursRemaining: () => number;
|
|
211
|
+
minutesRemaining: () => number;
|
|
212
|
+
secondsRemaining: () => number;
|
|
213
|
+
formatRemaining: (options?: Parameters<typeof formatCountdown>[1]) => string;
|
|
214
|
+
progressFrom: (startDate: DateInput) => number;
|
|
215
|
+
countdown: (options?: CountdownOptions) => Countdown;
|
|
216
|
+
};
|
|
217
|
+
//# sourceMappingURL=countdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countdown.d.ts","sourceRoot":"","sources":["../../src/countdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Countdown and timer utilities for tracking time until/since a target date
|
|
3
|
+
* Provides countdown timers, remaining time calculations, and progress tracking
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a countdown timer to a target date
|
|
7
|
+
* @param targetDate - The date to count down to
|
|
8
|
+
* @param options - Countdown options and callbacks
|
|
9
|
+
* @returns A countdown instance with control methods
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const countdown = createCountdown(
|
|
14
|
+
* new Date('2024-12-31T23:59:59'),
|
|
15
|
+
* {
|
|
16
|
+
* onTick: (remaining) => {
|
|
17
|
+
* console.log(`${remaining.days}d ${remaining.hours}h ${remaining.minutes}m ${remaining.seconds}s`);
|
|
18
|
+
* },
|
|
19
|
+
* onComplete: () => {
|
|
20
|
+
* console.log('Happy New Year!');
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* countdown.start();
|
|
26
|
+
* // Later...
|
|
27
|
+
* countdown.stop();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function createCountdown(targetDate, options = {}) {
|
|
31
|
+
let target = new Date(targetDate);
|
|
32
|
+
let intervalId = null;
|
|
33
|
+
let running = false;
|
|
34
|
+
const { onTick, onComplete, onExpired, interval = 1000, immediate = true } = options;
|
|
35
|
+
const getRemaining = () => {
|
|
36
|
+
return getRemainingTime(target);
|
|
37
|
+
};
|
|
38
|
+
const tick = () => {
|
|
39
|
+
const remaining = getRemaining();
|
|
40
|
+
if (onTick) {
|
|
41
|
+
onTick(remaining);
|
|
42
|
+
}
|
|
43
|
+
if (remaining.isExpired) {
|
|
44
|
+
stop();
|
|
45
|
+
if (onComplete) {
|
|
46
|
+
onComplete();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const start = () => {
|
|
51
|
+
if (running)
|
|
52
|
+
return;
|
|
53
|
+
const remaining = getRemaining();
|
|
54
|
+
if (remaining.totalMilliseconds < 0) {
|
|
55
|
+
if (onExpired) {
|
|
56
|
+
onExpired();
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
running = true;
|
|
61
|
+
if (immediate) {
|
|
62
|
+
tick();
|
|
63
|
+
}
|
|
64
|
+
intervalId = setInterval(tick, interval);
|
|
65
|
+
};
|
|
66
|
+
const stop = () => {
|
|
67
|
+
if (!running)
|
|
68
|
+
return;
|
|
69
|
+
running = false;
|
|
70
|
+
if (intervalId !== null) {
|
|
71
|
+
clearInterval(intervalId);
|
|
72
|
+
intervalId = null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const reset = (newTarget) => {
|
|
76
|
+
stop();
|
|
77
|
+
target = new Date(newTarget);
|
|
78
|
+
};
|
|
79
|
+
const isRunning = () => running;
|
|
80
|
+
const isExpiredCheck = () => {
|
|
81
|
+
return getRemaining().isExpired;
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
start,
|
|
85
|
+
stop,
|
|
86
|
+
reset,
|
|
87
|
+
getRemaining,
|
|
88
|
+
isRunning,
|
|
89
|
+
isExpired: isExpiredCheck
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Gets the remaining time until/since a target date
|
|
94
|
+
* @param targetDate - The target date
|
|
95
|
+
* @param fromDate - The date to calculate from (defaults to now)
|
|
96
|
+
* @returns Object with remaining time broken down by units
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const remaining = getRemainingTime(new Date('2024-12-31'));
|
|
101
|
+
* console.log(`${remaining.days} days, ${remaining.hours} hours remaining`);
|
|
102
|
+
*
|
|
103
|
+
* // Check if expired
|
|
104
|
+
* if (remaining.isExpired) {
|
|
105
|
+
* console.log('Target date has passed');
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function getRemainingTime(targetDate, fromDate = new Date()) {
|
|
110
|
+
const target = new Date(targetDate);
|
|
111
|
+
const from = new Date(fromDate);
|
|
112
|
+
const totalMilliseconds = target.getTime() - from.getTime();
|
|
113
|
+
const isExpired = totalMilliseconds <= 0;
|
|
114
|
+
// Use absolute values for calculations
|
|
115
|
+
const absTotalMs = Math.abs(totalMilliseconds);
|
|
116
|
+
const totalSeconds = Math.floor(absTotalMs / 1000);
|
|
117
|
+
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
118
|
+
const totalHours = Math.floor(totalMinutes / 60);
|
|
119
|
+
const totalDays = Math.floor(totalHours / 24);
|
|
120
|
+
const milliseconds = Math.floor(absTotalMs % 1000);
|
|
121
|
+
const seconds = totalSeconds % 60;
|
|
122
|
+
const minutes = totalMinutes % 60;
|
|
123
|
+
const hours = totalHours % 24;
|
|
124
|
+
const days = totalDays; // Don't mod by 7 - let the formatter decide
|
|
125
|
+
const weeks = Math.floor(totalDays / 7);
|
|
126
|
+
return {
|
|
127
|
+
totalMilliseconds,
|
|
128
|
+
totalSeconds: totalMilliseconds >= 0 ? totalSeconds : -totalSeconds,
|
|
129
|
+
totalMinutes: totalMilliseconds >= 0 ? totalMinutes : -totalMinutes,
|
|
130
|
+
totalHours: totalMilliseconds >= 0 ? totalHours : -totalHours,
|
|
131
|
+
totalDays: totalMilliseconds >= 0 ? totalDays : -totalDays,
|
|
132
|
+
milliseconds,
|
|
133
|
+
seconds,
|
|
134
|
+
minutes,
|
|
135
|
+
hours,
|
|
136
|
+
days,
|
|
137
|
+
weeks,
|
|
138
|
+
isExpired
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Formats the remaining time as a human-readable string
|
|
143
|
+
* @param targetDate - The target date
|
|
144
|
+
* @param options - Formatting options
|
|
145
|
+
* @returns Formatted countdown string
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* formatCountdown(new Date('2024-12-31'));
|
|
150
|
+
* // "45d 12h 30m 15s"
|
|
151
|
+
*
|
|
152
|
+
* formatCountdown(new Date('2024-12-31'), { units: ['days', 'hours'] });
|
|
153
|
+
* // "45 days, 12 hours"
|
|
154
|
+
*
|
|
155
|
+
* formatCountdown(new Date('2024-12-31'), { short: false });
|
|
156
|
+
* // "45 days 12 hours 30 minutes 15 seconds"
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function formatCountdown(targetDate, options = {}) {
|
|
160
|
+
const { from, units = ['days', 'hours', 'minutes', 'seconds'], short = true, maxUnits, showZero = false, separator = ' ' } = options;
|
|
161
|
+
const remaining = getRemainingTime(targetDate, from);
|
|
162
|
+
if (remaining.isExpired) {
|
|
163
|
+
return 'Expired';
|
|
164
|
+
}
|
|
165
|
+
const parts = [];
|
|
166
|
+
for (const unit of units) {
|
|
167
|
+
const value = remaining[unit];
|
|
168
|
+
if (value === 0 && !showZero && parts.length === 0) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (value === 0 && !showZero) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (maxUnits && parts.length >= maxUnits) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
if (short) {
|
|
178
|
+
const shortUnit = unit[0];
|
|
179
|
+
parts.push(`${value}${shortUnit}`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const unitName = value === 1 ? unit.slice(0, -1) : unit;
|
|
183
|
+
parts.push(`${value} ${unitName}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return parts.length > 0 ? parts.join(separator) : '0s';
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Checks if a date has expired (is in the past)
|
|
190
|
+
* @param date - The date to check
|
|
191
|
+
* @param fromDate - The reference date (defaults to now)
|
|
192
|
+
* @returns True if the date is in the past
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* isExpired(new Date('2020-01-01')); // true
|
|
197
|
+
* isExpired(new Date('2030-01-01')); // false
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export function isExpired(date, fromDate = new Date()) {
|
|
201
|
+
const checkDate = new Date(date);
|
|
202
|
+
const from = new Date(fromDate);
|
|
203
|
+
return checkDate.getTime() < from.getTime();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Calculates the progress percentage between two dates
|
|
207
|
+
* @param startDate - The start date
|
|
208
|
+
* @param endDate - The end date
|
|
209
|
+
* @param currentDate - The current date (defaults to now)
|
|
210
|
+
* @returns Progress percentage (0-100), clamped to range
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* const progress = getProgressPercentage(
|
|
215
|
+
* new Date('2024-01-01'),
|
|
216
|
+
* new Date('2024-12-31'),
|
|
217
|
+
* new Date('2024-07-01')
|
|
218
|
+
* );
|
|
219
|
+
* console.log(`${progress}% complete`); // ~50% complete
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export function getProgressPercentage(startDate, endDate, currentDate = new Date()) {
|
|
223
|
+
const start = new Date(startDate).getTime();
|
|
224
|
+
const end = new Date(endDate).getTime();
|
|
225
|
+
const current = new Date(currentDate).getTime();
|
|
226
|
+
if (start >= end) {
|
|
227
|
+
throw new Error('Start date must be before end date');
|
|
228
|
+
}
|
|
229
|
+
const total = end - start;
|
|
230
|
+
const elapsed = current - start;
|
|
231
|
+
const percentage = (elapsed / total) * 100;
|
|
232
|
+
// Clamp between 0 and 100
|
|
233
|
+
return Math.max(0, Math.min(100, percentage));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Gets time until a target date in a specific unit
|
|
237
|
+
* @param targetDate - The target date
|
|
238
|
+
* @param unit - The unit to return
|
|
239
|
+
* @param fromDate - The date to calculate from (defaults to now)
|
|
240
|
+
* @returns Time remaining in the specified unit
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* getTimeUntil(new Date('2024-12-31'), 'days'); // 45.5
|
|
245
|
+
* getTimeUntil(new Date('2024-12-31'), 'hours'); // 1092
|
|
246
|
+
* getTimeUntil(new Date('2024-12-31'), 'weeks'); // 6.5
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
export function getTimeUntil(targetDate, unit, fromDate = new Date()) {
|
|
250
|
+
const remaining = getRemainingTime(targetDate, fromDate);
|
|
251
|
+
switch (unit) {
|
|
252
|
+
case 'milliseconds':
|
|
253
|
+
return remaining.totalMilliseconds;
|
|
254
|
+
case 'seconds':
|
|
255
|
+
return remaining.totalSeconds;
|
|
256
|
+
case 'minutes':
|
|
257
|
+
return remaining.totalMinutes;
|
|
258
|
+
case 'hours':
|
|
259
|
+
return remaining.totalHours;
|
|
260
|
+
case 'days':
|
|
261
|
+
return remaining.totalDays;
|
|
262
|
+
case 'weeks':
|
|
263
|
+
return remaining.totalDays / 7;
|
|
264
|
+
default:
|
|
265
|
+
return remaining.totalMilliseconds;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Creates a deadline object with useful methods
|
|
270
|
+
* @param targetDate - The deadline date
|
|
271
|
+
* @returns An object with deadline-related methods
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* const deadline = createDeadline(new Date('2024-12-31'));
|
|
276
|
+
*
|
|
277
|
+
* deadline.isExpired(); // false
|
|
278
|
+
* deadline.daysRemaining(); // 45
|
|
279
|
+
* deadline.hoursRemaining(); // 1092
|
|
280
|
+
* deadline.formatRemaining(); // "45d 12h 30m"
|
|
281
|
+
* deadline.progressFrom(new Date('2024-01-01')); // 67.5%
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
export function createDeadline(targetDate) {
|
|
285
|
+
const target = new Date(targetDate);
|
|
286
|
+
return {
|
|
287
|
+
target,
|
|
288
|
+
isExpired: () => isExpired(target),
|
|
289
|
+
getRemaining: () => getRemainingTime(target),
|
|
290
|
+
daysRemaining: () => getTimeUntil(target, 'days'),
|
|
291
|
+
hoursRemaining: () => getTimeUntil(target, 'hours'),
|
|
292
|
+
minutesRemaining: () => getTimeUntil(target, 'minutes'),
|
|
293
|
+
secondsRemaining: () => getTimeUntil(target, 'seconds'),
|
|
294
|
+
formatRemaining: (options) => formatCountdown(target, options),
|
|
295
|
+
progressFrom: (startDate) => getProgressPercentage(startDate, target),
|
|
296
|
+
countdown: (options) => createCountdown(target, options)
|
|
297
|
+
};
|
|
298
|
+
}
|