ts-time-utils 1.0.0 → 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.
Files changed (40) hide show
  1. package/README.md +226 -1
  2. package/dist/calculate.d.ts.map +1 -1
  3. package/dist/calculate.js +24 -10
  4. package/dist/countdown.d.ts +217 -0
  5. package/dist/countdown.d.ts.map +1 -0
  6. package/dist/countdown.js +298 -0
  7. package/dist/dateRange.d.ts +266 -0
  8. package/dist/dateRange.d.ts.map +1 -0
  9. package/dist/dateRange.js +433 -0
  10. package/dist/esm/calculate.d.ts.map +1 -1
  11. package/dist/esm/calculate.js +24 -10
  12. package/dist/esm/countdown.d.ts +217 -0
  13. package/dist/esm/countdown.d.ts.map +1 -0
  14. package/dist/esm/countdown.js +298 -0
  15. package/dist/esm/dateRange.d.ts +266 -0
  16. package/dist/esm/dateRange.d.ts.map +1 -0
  17. package/dist/esm/dateRange.js +433 -0
  18. package/dist/esm/index.d.ts +5 -1
  19. package/dist/esm/index.d.ts.map +1 -1
  20. package/dist/esm/index.js +8 -0
  21. package/dist/esm/naturalLanguage.d.ts +107 -0
  22. package/dist/esm/naturalLanguage.d.ts.map +1 -0
  23. package/dist/esm/naturalLanguage.js +344 -0
  24. package/dist/esm/recurrence.d.ts +149 -0
  25. package/dist/esm/recurrence.d.ts.map +1 -0
  26. package/dist/esm/recurrence.js +404 -0
  27. package/dist/esm/types.d.ts +21 -0
  28. package/dist/esm/types.d.ts.map +1 -1
  29. package/dist/index.d.ts +5 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +8 -0
  32. package/dist/naturalLanguage.d.ts +107 -0
  33. package/dist/naturalLanguage.d.ts.map +1 -0
  34. package/dist/naturalLanguage.js +344 -0
  35. package/dist/recurrence.d.ts +149 -0
  36. package/dist/recurrence.d.ts.map +1 -0
  37. package/dist/recurrence.js +404 -0
  38. package/dist/types.d.ts +21 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +30 -2
@@ -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
+ }