ts-time-utils 0.0.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.
Files changed (80) hide show
  1. package/README.md +343 -0
  2. package/dist/age.d.ts +49 -0
  3. package/dist/age.d.ts.map +1 -0
  4. package/dist/age.js +106 -0
  5. package/dist/calculate.d.ts +49 -0
  6. package/dist/calculate.d.ts.map +1 -0
  7. package/dist/calculate.js +179 -0
  8. package/dist/calendar.d.ts +82 -0
  9. package/dist/calendar.d.ts.map +1 -0
  10. package/dist/calendar.js +154 -0
  11. package/dist/constants.d.ts +35 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +17 -0
  14. package/dist/esm/age.d.ts +49 -0
  15. package/dist/esm/age.d.ts.map +1 -0
  16. package/dist/esm/age.js +106 -0
  17. package/dist/esm/calculate.d.ts +49 -0
  18. package/dist/esm/calculate.d.ts.map +1 -0
  19. package/dist/esm/calculate.js +179 -0
  20. package/dist/esm/calendar.d.ts +82 -0
  21. package/dist/esm/calendar.d.ts.map +1 -0
  22. package/dist/esm/calendar.js +154 -0
  23. package/dist/esm/constants.d.ts +35 -0
  24. package/dist/esm/constants.d.ts.map +1 -0
  25. package/dist/esm/constants.js +17 -0
  26. package/dist/esm/format.d.ts +25 -0
  27. package/dist/esm/format.d.ts.map +1 -0
  28. package/dist/esm/format.js +189 -0
  29. package/dist/esm/index.d.ts +17 -0
  30. package/dist/esm/index.d.ts.map +1 -0
  31. package/dist/esm/index.js +28 -0
  32. package/dist/esm/interval.d.ts +30 -0
  33. package/dist/esm/interval.d.ts.map +1 -0
  34. package/dist/esm/interval.js +86 -0
  35. package/dist/esm/parse.d.ts +31 -0
  36. package/dist/esm/parse.d.ts.map +1 -0
  37. package/dist/esm/parse.js +217 -0
  38. package/dist/esm/performance.d.ts +110 -0
  39. package/dist/esm/performance.d.ts.map +1 -0
  40. package/dist/esm/performance.js +222 -0
  41. package/dist/esm/rangePresets.d.ts +45 -0
  42. package/dist/esm/rangePresets.d.ts.map +1 -0
  43. package/dist/esm/rangePresets.js +124 -0
  44. package/dist/esm/timezone.d.ts +38 -0
  45. package/dist/esm/timezone.d.ts.map +1 -0
  46. package/dist/esm/timezone.js +99 -0
  47. package/dist/esm/validate.d.ts +62 -0
  48. package/dist/esm/validate.d.ts.map +1 -0
  49. package/dist/esm/validate.js +108 -0
  50. package/dist/esm/workingHours.d.ts +25 -0
  51. package/dist/esm/workingHours.d.ts.map +1 -0
  52. package/dist/esm/workingHours.js +107 -0
  53. package/dist/format.d.ts +25 -0
  54. package/dist/format.d.ts.map +1 -0
  55. package/dist/format.js +189 -0
  56. package/dist/index.d.ts +17 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +28 -0
  59. package/dist/interval.d.ts +30 -0
  60. package/dist/interval.d.ts.map +1 -0
  61. package/dist/interval.js +86 -0
  62. package/dist/parse.d.ts +31 -0
  63. package/dist/parse.d.ts.map +1 -0
  64. package/dist/parse.js +217 -0
  65. package/dist/performance.d.ts +110 -0
  66. package/dist/performance.d.ts.map +1 -0
  67. package/dist/performance.js +222 -0
  68. package/dist/rangePresets.d.ts +45 -0
  69. package/dist/rangePresets.d.ts.map +1 -0
  70. package/dist/rangePresets.js +124 -0
  71. package/dist/timezone.d.ts +38 -0
  72. package/dist/timezone.d.ts.map +1 -0
  73. package/dist/timezone.js +99 -0
  74. package/dist/validate.d.ts +62 -0
  75. package/dist/validate.d.ts.map +1 -0
  76. package/dist/validate.js +108 -0
  77. package/dist/workingHours.d.ts +25 -0
  78. package/dist/workingHours.d.ts.map +1 -0
  79. package/dist/workingHours.js +107 -0
  80. package/package.json +102 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Check if a date is valid
3
+ * @param date - date to validate
4
+ */
5
+ export declare function isValidDate(date: Date | string | number): boolean;
6
+ /**
7
+ * Check if a year is a leap year
8
+ * @param year - year to check
9
+ */
10
+ export declare function isLeapYear(year: number): boolean;
11
+ /**
12
+ * Check if a date is in the past
13
+ * @param date - date to check
14
+ */
15
+ export declare function isPast(date: Date): boolean;
16
+ /**
17
+ * Check if a date is in the future
18
+ * @param date - date to check
19
+ */
20
+ export declare function isFuture(date: Date): boolean;
21
+ /**
22
+ * Check if a date is today
23
+ * @param date - date to check
24
+ */
25
+ export declare function isToday(date: Date): boolean;
26
+ /**
27
+ * Check if a date is yesterday
28
+ * @param date - date to check
29
+ */
30
+ export declare function isYesterday(date: Date): boolean;
31
+ /**
32
+ * Check if a date is tomorrow
33
+ * @param date - date to check
34
+ */
35
+ export declare function isTomorrow(date: Date): boolean;
36
+ /**
37
+ * Check if two dates are the same day
38
+ * @param date1 - first date
39
+ * @param date2 - second date
40
+ */
41
+ export declare function isSameDay(date1: Date, date2: Date): boolean;
42
+ /**
43
+ * Check if a date is a weekend (Saturday or Sunday)
44
+ * @param date - date to check
45
+ */
46
+ export declare function isWeekend(date: Date): boolean;
47
+ /**
48
+ * Check if a date is a weekday (Monday through Friday)
49
+ * @param date - date to check
50
+ */
51
+ export declare function isWeekday(date: Date): boolean;
52
+ /**
53
+ * Check if a time string is valid (HH:MM or HH:MM:SS format)
54
+ * @param time - time string to validate
55
+ */
56
+ export declare function isValidTimeString(time: string): boolean;
57
+ /**
58
+ * Check if a date string is valid ISO 8601 format
59
+ * @param dateString - date string to validate
60
+ */
61
+ export declare function isValidISOString(dateString: string): boolean;
62
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAWjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE1C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE5C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAO3C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAM3D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAG7C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG5D"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Check if a date is valid
3
+ * @param date - date to validate
4
+ */
5
+ export function isValidDate(date) {
6
+ if (date instanceof Date) {
7
+ return !isNaN(date.getTime());
8
+ }
9
+ if (typeof date === 'string' || typeof date === 'number') {
10
+ const parsed = new Date(date);
11
+ return !isNaN(parsed.getTime());
12
+ }
13
+ return false;
14
+ }
15
+ /**
16
+ * Check if a year is a leap year
17
+ * @param year - year to check
18
+ */
19
+ export function isLeapYear(year) {
20
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
21
+ }
22
+ /**
23
+ * Check if a date is in the past
24
+ * @param date - date to check
25
+ */
26
+ export function isPast(date) {
27
+ return date.getTime() < new Date().getTime();
28
+ }
29
+ /**
30
+ * Check if a date is in the future
31
+ * @param date - date to check
32
+ */
33
+ export function isFuture(date) {
34
+ return date.getTime() > new Date().getTime();
35
+ }
36
+ /**
37
+ * Check if a date is today
38
+ * @param date - date to check
39
+ */
40
+ export function isToday(date) {
41
+ const today = new Date();
42
+ return (date.getDate() === today.getDate() &&
43
+ date.getMonth() === today.getMonth() &&
44
+ date.getFullYear() === today.getFullYear());
45
+ }
46
+ /**
47
+ * Check if a date is yesterday
48
+ * @param date - date to check
49
+ */
50
+ export function isYesterday(date) {
51
+ const yesterday = new Date();
52
+ yesterday.setDate(yesterday.getDate() - 1);
53
+ return (date.getDate() === yesterday.getDate() &&
54
+ date.getMonth() === yesterday.getMonth() &&
55
+ date.getFullYear() === yesterday.getFullYear());
56
+ }
57
+ /**
58
+ * Check if a date is tomorrow
59
+ * @param date - date to check
60
+ */
61
+ export function isTomorrow(date) {
62
+ const tomorrow = new Date();
63
+ tomorrow.setDate(tomorrow.getDate() + 1);
64
+ return (date.getDate() === tomorrow.getDate() &&
65
+ date.getMonth() === tomorrow.getMonth() &&
66
+ date.getFullYear() === tomorrow.getFullYear());
67
+ }
68
+ /**
69
+ * Check if two dates are the same day
70
+ * @param date1 - first date
71
+ * @param date2 - second date
72
+ */
73
+ export function isSameDay(date1, date2) {
74
+ return (date1.getDate() === date2.getDate() &&
75
+ date1.getMonth() === date2.getMonth() &&
76
+ date1.getFullYear() === date2.getFullYear());
77
+ }
78
+ /**
79
+ * Check if a date is a weekend (Saturday or Sunday)
80
+ * @param date - date to check
81
+ */
82
+ export function isWeekend(date) {
83
+ const dayOfWeek = date.getDay();
84
+ return dayOfWeek === 0 || dayOfWeek === 6; // Sunday or Saturday
85
+ }
86
+ /**
87
+ * Check if a date is a weekday (Monday through Friday)
88
+ * @param date - date to check
89
+ */
90
+ export function isWeekday(date) {
91
+ return !isWeekend(date);
92
+ }
93
+ /**
94
+ * Check if a time string is valid (HH:MM or HH:MM:SS format)
95
+ * @param time - time string to validate
96
+ */
97
+ export function isValidTimeString(time) {
98
+ const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/;
99
+ return timeRegex.test(time);
100
+ }
101
+ /**
102
+ * Check if a date string is valid ISO 8601 format
103
+ * @param dateString - date string to validate
104
+ */
105
+ export function isValidISOString(dateString) {
106
+ const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
107
+ return isoRegex.test(dateString) && isValidDate(dateString);
108
+ }
@@ -0,0 +1,25 @@
1
+ /** Working hours utilities */
2
+ export interface WorkingHoursConfig {
3
+ workingDays: number[];
4
+ hours: {
5
+ start: number;
6
+ end: number;
7
+ };
8
+ breaks?: {
9
+ start: number;
10
+ end: number;
11
+ }[];
12
+ timezone?: string;
13
+ }
14
+ export declare const DEFAULT_WORKING_HOURS: WorkingHoursConfig;
15
+ /** Check if a date is a configured working day */
16
+ export declare function isWorkingDay(date: Date, config?: WorkingHoursConfig): boolean;
17
+ /** Check if inside working hours (excluding breaks) */
18
+ export declare function isWorkingTime(date: Date, config?: WorkingHoursConfig): boolean;
19
+ /** Move date forward to next working minute */
20
+ export declare function nextWorkingTime(date: Date, config?: WorkingHoursConfig): Date;
21
+ /** Compute working time (ms) between two dates */
22
+ export declare function workingTimeBetween(start: Date, end: Date, config?: WorkingHoursConfig): number;
23
+ /** Advance by working hours amount (simple iterative approach) */
24
+ export declare function addWorkingHours(start: Date, hours: number, config?: WorkingHoursConfig): Date;
25
+ //# sourceMappingURL=workingHours.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workingHours.d.ts","sourceRoot":"","sources":["../../src/workingHours.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAE9B,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,qBAAqB,EAAE,kBAInC,CAAC;AAEF,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,kBAA0C,GAAG,OAAO,CAEpG;AAOD,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,kBAA0C,GAAG,OAAO,CAUrG;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,kBAA0C,GAAG,IAAI,CAMpG;AAYD,kDAAkD;AAClD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAE,kBAA0C,GAAG,MAAM,CA+BrH;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,kBAA0C,GAAG,IAAI,CAcpH"}
@@ -0,0 +1,107 @@
1
+ /** Working hours utilities */
2
+ export const DEFAULT_WORKING_HOURS = {
3
+ workingDays: [1, 2, 3, 4, 5],
4
+ hours: { start: 9, end: 17 },
5
+ breaks: [{ start: 12, end: 13 }]
6
+ };
7
+ /** Check if a date is a configured working day */
8
+ export function isWorkingDay(date, config = DEFAULT_WORKING_HOURS) {
9
+ return config.workingDays.includes(date.getDay());
10
+ }
11
+ /** Convert date to fractional hour */
12
+ function toHourFraction(date) {
13
+ return date.getHours() + date.getMinutes() / 60 + date.getSeconds() / 3600;
14
+ }
15
+ /** Check if inside working hours (excluding breaks) */
16
+ export function isWorkingTime(date, config = DEFAULT_WORKING_HOURS) {
17
+ if (!isWorkingDay(date, config))
18
+ return false;
19
+ const h = toHourFraction(date);
20
+ if (h < config.hours.start || h >= config.hours.end)
21
+ return false;
22
+ if (config.breaks) {
23
+ for (const b of config.breaks) {
24
+ if (h >= b.start && h < b.end)
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ /** Move date forward to next working minute */
31
+ export function nextWorkingTime(date, config = DEFAULT_WORKING_HOURS) {
32
+ const d = new Date(date);
33
+ while (!isWorkingTime(d, config)) {
34
+ d.setMinutes(d.getMinutes() + 1);
35
+ }
36
+ return d;
37
+ }
38
+ /** Clamp a date into working window of its day */
39
+ function clampToWorkingWindow(date, config) {
40
+ if (!isWorkingDay(date, config))
41
+ return null;
42
+ const start = new Date(date);
43
+ start.setHours(config.hours.start, 0, 0, 0);
44
+ const end = new Date(date);
45
+ end.setHours(config.hours.end, 0, 0, 0);
46
+ if (date < start)
47
+ return start;
48
+ if (date > end)
49
+ return null;
50
+ return date;
51
+ }
52
+ /** Compute working time (ms) between two dates */
53
+ export function workingTimeBetween(start, end, config = DEFAULT_WORKING_HOURS) {
54
+ if (end <= start)
55
+ return 0;
56
+ let total = 0;
57
+ const cursor = new Date(start);
58
+ while (cursor < end) {
59
+ if (isWorkingDay(cursor, config)) {
60
+ const windowStart = new Date(cursor);
61
+ windowStart.setHours(config.hours.start, 0, 0, 0);
62
+ const windowEnd = new Date(cursor);
63
+ windowEnd.setHours(config.hours.end, 0, 0, 0);
64
+ const rangeStart = cursor > windowStart ? cursor : windowStart;
65
+ const rangeEnd = end < windowEnd ? end : windowEnd;
66
+ if (rangeStart < rangeEnd) {
67
+ let segment = (rangeEnd.getTime() - rangeStart.getTime()) / 1000 / 60 / 60; // hours
68
+ // subtract breaks
69
+ if (config.breaks) {
70
+ for (const b of config.breaks) {
71
+ const bStart = new Date(rangeStart);
72
+ bStart.setHours(Math.floor(b.start), (b.start % 1) * 60, 0, 0);
73
+ const bEnd = new Date(rangeStart);
74
+ bEnd.setHours(Math.floor(b.end), (b.end % 1) * 60, 0, 0);
75
+ const overlapStart = bStart > rangeStart ? bStart : rangeStart;
76
+ const overlapEnd = bEnd < rangeEnd ? bEnd : rangeEnd;
77
+ if (overlapStart < overlapEnd) {
78
+ segment -= (overlapEnd.getTime() - overlapStart.getTime()) / 1000 / 60 / 60;
79
+ }
80
+ }
81
+ }
82
+ total += segment;
83
+ }
84
+ }
85
+ // advance to next day start
86
+ cursor.setHours(24, 0, 0, 0);
87
+ }
88
+ return total * 60 * 60 * 1000; // ms
89
+ }
90
+ /** Advance by working hours amount (simple iterative approach) */
91
+ export function addWorkingHours(start, hours, config = DEFAULT_WORKING_HOURS) {
92
+ if (hours <= 0)
93
+ return new Date(start);
94
+ let remaining = hours * 60; // minutes
95
+ let cursor = new Date(start);
96
+ cursor = nextWorkingTime(cursor, config);
97
+ while (remaining > 0) {
98
+ if (isWorkingTime(cursor, config)) {
99
+ cursor.setMinutes(cursor.getMinutes() + 1);
100
+ remaining -= 1;
101
+ }
102
+ else {
103
+ cursor = nextWorkingTime(cursor, config);
104
+ }
105
+ }
106
+ return cursor;
107
+ }
@@ -0,0 +1,25 @@
1
+ import { FormatOptions } from './constants.js';
2
+ /**
3
+ * Convert milliseconds to a human-readable duration.
4
+ * @param ms - milliseconds
5
+ * @param options - formatting options
6
+ */
7
+ export declare function formatDuration(ms: number, options?: FormatOptions): string;
8
+ /**
9
+ * Return a human-readable "time ago" string.
10
+ * @param date - past or future date
11
+ * @param options - formatting options
12
+ */
13
+ export declare function timeAgo(date: Date, options?: FormatOptions): string;
14
+ /**
15
+ * Format a date to a human-readable time string
16
+ * @param date - date to format
17
+ * @param format - format type
18
+ */
19
+ export declare function formatTime(date: Date, format?: '12h' | '24h' | 'iso'): string;
20
+ /**
21
+ * Parse a duration string like "1h 30m" into milliseconds
22
+ * @param duration - duration string (e.g., "1h 30m", "2d", "45s")
23
+ */
24
+ export declare function parseDuration(duration: string): number;
25
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA2E9E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA+CvE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,KAAK,GAAG,KAAK,GAAG,KAAa,GAAG,MAAM,CAcpF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyDtD"}
package/dist/format.js ADDED
@@ -0,0 +1,189 @@
1
+ import { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR } from './constants.js';
2
+ /**
3
+ * Convert milliseconds to a human-readable duration.
4
+ * @param ms - milliseconds
5
+ * @param options - formatting options
6
+ */
7
+ export function formatDuration(ms, options = {}) {
8
+ const { includeMs = false, short = false, maxUnits = 2, round = false } = options;
9
+ if (ms < 0)
10
+ return '0' + (short ? 's' : ' seconds');
11
+ const mathFn = round ? Math.round : Math.floor;
12
+ const parts = [];
13
+ let remaining = ms;
14
+ // Years
15
+ if (remaining >= MILLISECONDS_PER_YEAR) {
16
+ const years = mathFn(remaining / MILLISECONDS_PER_YEAR);
17
+ parts.push(years + (short ? 'y' : ` year${years !== 1 ? 's' : ''}`));
18
+ remaining %= MILLISECONDS_PER_YEAR;
19
+ }
20
+ // Months
21
+ if (remaining >= MILLISECONDS_PER_MONTH && parts.length < maxUnits) {
22
+ const months = mathFn(remaining / MILLISECONDS_PER_MONTH);
23
+ parts.push(months + (short ? 'mo' : ` month${months !== 1 ? 's' : ''}`));
24
+ remaining %= MILLISECONDS_PER_MONTH;
25
+ }
26
+ // Weeks
27
+ if (remaining >= MILLISECONDS_PER_WEEK && parts.length < maxUnits) {
28
+ const weeks = mathFn(remaining / MILLISECONDS_PER_WEEK);
29
+ parts.push(weeks + (short ? 'w' : ` week${weeks !== 1 ? 's' : ''}`));
30
+ remaining %= MILLISECONDS_PER_WEEK;
31
+ }
32
+ // Days
33
+ if (remaining >= MILLISECONDS_PER_DAY && parts.length < maxUnits) {
34
+ const days = mathFn(remaining / MILLISECONDS_PER_DAY);
35
+ parts.push(days + (short ? 'd' : ` day${days !== 1 ? 's' : ''}`));
36
+ remaining %= MILLISECONDS_PER_DAY;
37
+ }
38
+ // Hours
39
+ if (remaining >= MILLISECONDS_PER_HOUR && parts.length < maxUnits) {
40
+ const hours = mathFn(remaining / MILLISECONDS_PER_HOUR);
41
+ parts.push(hours + (short ? 'h' : ` hour${hours !== 1 ? 's' : ''}`));
42
+ remaining %= MILLISECONDS_PER_HOUR;
43
+ }
44
+ // Minutes
45
+ if (remaining >= MILLISECONDS_PER_MINUTE && parts.length < maxUnits) {
46
+ const minutes = mathFn(remaining / MILLISECONDS_PER_MINUTE);
47
+ parts.push(minutes + (short ? 'm' : ` minute${minutes !== 1 ? 's' : ''}`));
48
+ remaining %= MILLISECONDS_PER_MINUTE;
49
+ }
50
+ // Seconds
51
+ if (remaining >= MILLISECONDS_PER_SECOND && parts.length < maxUnits) {
52
+ const seconds = mathFn(remaining / MILLISECONDS_PER_SECOND);
53
+ parts.push(seconds + (short ? 's' : ` second${seconds !== 1 ? 's' : ''}`));
54
+ remaining %= MILLISECONDS_PER_SECOND;
55
+ }
56
+ // Milliseconds
57
+ if ((remaining > 0 || parts.length === 0) && includeMs && parts.length < maxUnits) {
58
+ const milliseconds = mathFn(remaining);
59
+ parts.push(milliseconds + (short ? 'ms' : ` millisecond${milliseconds !== 1 ? 's' : ''}`));
60
+ }
61
+ if (parts.length === 0) {
62
+ return '0' + (short ? 's' : ' seconds');
63
+ }
64
+ return short ? parts.join(' ') : parts.join(', ');
65
+ }
66
+ /**
67
+ * Return a human-readable "time ago" string.
68
+ * @param date - past or future date
69
+ * @param options - formatting options
70
+ */
71
+ export function timeAgo(date, options = {}) {
72
+ const now = new Date();
73
+ const diffMs = now.getTime() - date.getTime();
74
+ const { short = false } = options;
75
+ const isFuture = diffMs < 0;
76
+ const absDiffMs = Math.abs(diffMs);
77
+ if (absDiffMs < MILLISECONDS_PER_MINUTE) {
78
+ const seconds = Math.floor(absDiffMs / MILLISECONDS_PER_SECOND);
79
+ const unit = short ? 's' : ` second${seconds !== 1 ? 's' : ''}`;
80
+ return isFuture ? `in ${seconds}${unit}` : `${seconds}${unit} ago`;
81
+ }
82
+ if (absDiffMs < MILLISECONDS_PER_HOUR) {
83
+ const minutes = Math.floor(absDiffMs / MILLISECONDS_PER_MINUTE);
84
+ const unit = short ? 'm' : ` minute${minutes !== 1 ? 's' : ''}`;
85
+ return isFuture ? `in ${minutes}${unit}` : `${minutes}${unit} ago`;
86
+ }
87
+ if (absDiffMs < MILLISECONDS_PER_DAY) {
88
+ const hours = Math.floor(absDiffMs / MILLISECONDS_PER_HOUR);
89
+ const unit = short ? 'h' : ` hour${hours !== 1 ? 's' : ''}`;
90
+ return isFuture ? `in ${hours}${unit}` : `${hours}${unit} ago`;
91
+ }
92
+ if (absDiffMs < MILLISECONDS_PER_WEEK) {
93
+ const days = Math.floor(absDiffMs / MILLISECONDS_PER_DAY);
94
+ const unit = short ? 'd' : ` day${days !== 1 ? 's' : ''}`;
95
+ return isFuture ? `in ${days}${unit}` : `${days}${unit} ago`;
96
+ }
97
+ if (absDiffMs < MILLISECONDS_PER_MONTH) {
98
+ const weeks = Math.floor(absDiffMs / MILLISECONDS_PER_WEEK);
99
+ const unit = short ? 'w' : ` week${weeks !== 1 ? 's' : ''}`;
100
+ return isFuture ? `in ${weeks}${unit}` : `${weeks}${unit} ago`;
101
+ }
102
+ if (absDiffMs < MILLISECONDS_PER_YEAR) {
103
+ const months = Math.floor(absDiffMs / MILLISECONDS_PER_MONTH);
104
+ const unit = short ? 'mo' : ` month${months !== 1 ? 's' : ''}`;
105
+ return isFuture ? `in ${months}${unit}` : `${months}${unit} ago`;
106
+ }
107
+ const years = Math.floor(absDiffMs / MILLISECONDS_PER_YEAR);
108
+ const unit = short ? 'y' : ` year${years !== 1 ? 's' : ''}`;
109
+ return isFuture ? `in ${years}${unit}` : `${years}${unit} ago`;
110
+ }
111
+ /**
112
+ * Format a date to a human-readable time string
113
+ * @param date - date to format
114
+ * @param format - format type
115
+ */
116
+ export function formatTime(date, format = '24h') {
117
+ switch (format) {
118
+ case '12h':
119
+ return date.toLocaleTimeString('en-US', {
120
+ hour: 'numeric',
121
+ minute: '2-digit',
122
+ hour12: true
123
+ });
124
+ case 'iso':
125
+ return date.toISOString();
126
+ case '24h':
127
+ default:
128
+ return date.toTimeString().split(' ')[0].slice(0, 5); // HH:MM
129
+ }
130
+ }
131
+ /**
132
+ * Parse a duration string like "1h 30m" into milliseconds
133
+ * @param duration - duration string (e.g., "1h 30m", "2d", "45s")
134
+ */
135
+ export function parseDuration(duration) {
136
+ const regex = /(\d+(?:\.\d+)?)\s*([a-zA-Z]+)/g;
137
+ let totalMs = 0;
138
+ let match;
139
+ while ((match = regex.exec(duration)) !== null) {
140
+ const value = parseFloat(match[1]);
141
+ const unit = match[2].toLowerCase();
142
+ switch (unit) {
143
+ case 'ms':
144
+ case 'millisecond':
145
+ case 'milliseconds':
146
+ totalMs += value;
147
+ break;
148
+ case 's':
149
+ case 'sec':
150
+ case 'second':
151
+ case 'seconds':
152
+ totalMs += value * MILLISECONDS_PER_SECOND;
153
+ break;
154
+ case 'm':
155
+ case 'min':
156
+ case 'minute':
157
+ case 'minutes':
158
+ totalMs += value * MILLISECONDS_PER_MINUTE;
159
+ break;
160
+ case 'h':
161
+ case 'hr':
162
+ case 'hour':
163
+ case 'hours':
164
+ totalMs += value * MILLISECONDS_PER_HOUR;
165
+ break;
166
+ case 'd':
167
+ case 'day':
168
+ case 'days':
169
+ totalMs += value * MILLISECONDS_PER_DAY;
170
+ break;
171
+ case 'w':
172
+ case 'week':
173
+ case 'weeks':
174
+ totalMs += value * MILLISECONDS_PER_WEEK;
175
+ break;
176
+ case 'mo':
177
+ case 'month':
178
+ case 'months':
179
+ totalMs += value * MILLISECONDS_PER_MONTH;
180
+ break;
181
+ case 'y':
182
+ case 'year':
183
+ case 'years':
184
+ totalMs += value * MILLISECONDS_PER_YEAR;
185
+ break;
186
+ }
187
+ }
188
+ return totalMs;
189
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @fileoverview Main entry point for ts-time-utils library
3
+ * Re-exports all functions from individual modules for convenience
4
+ */
5
+ export { formatDuration, timeAgo, formatTime, parseDuration } from './format.js';
6
+ export { differenceInUnits, addTime, subtractTime, startOf, endOf, isBetween, businessDaysBetween } from './calculate.js';
7
+ export { isValidDate, isLeapYear, isPast, isFuture, isToday, isYesterday, isTomorrow, isSameDay, isWeekend, isWeekday, isValidTimeString, isValidISOString } from './validate.js';
8
+ export { calculateAge, getAgeInUnits, getLifeStage, getNextBirthday, getDaysUntilBirthday, isBirthday, type AgeResult } from './age.js';
9
+ export { getWeekNumber, getWeekOfMonth, getQuarter, getDayOfYear, getWeeksInYear, getDaysInMonth, getDaysInYear, getEaster, getMonthsInYear, getDaysInMonthArray, getWeekdaysInMonth, getFirstDayOfMonth, getLastDayOfMonth, getFirstDayOfYear, getLastDayOfYear } from './calendar.js';
10
+ export { parseDate, parseRelativeDate, parseTimeAgo, parseCustomFormat, parseManyFormats } from './parse.js';
11
+ export { sleep, timeout, debounce, throttle, retry, createStopwatch, measureTime, measureAsync, benchmark, Stopwatch, type BenchmarkResult } from './performance.js';
12
+ export { createInterval, isValidInterval, intervalDuration, intervalContains, intervalsOverlap, intervalIntersection, mergeIntervals, subtractInterval, splitIntervalByDay, totalIntervalCoverage, normalizeIntervals, type Interval } from './interval.js';
13
+ export { getTimezoneOffset, formatInTimeZone, getZonedTime, convertDateToZone, isValidTimeZone, COMMON_TIMEZONES, getLocalOffset, compareZoneOffsets, reinterpretAsZone, type ZonedTime } from './timezone.js';
14
+ export { DEFAULT_WORKING_HOURS, isWorkingDay, isWorkingTime, nextWorkingTime, workingTimeBetween, addWorkingHours, type WorkingHoursConfig } from './workingHours.js';
15
+ export { today, yesterday, tomorrow, lastNDays, nextNDays, thisWeek, lastWeek, nextWeek, thisMonth, lastMonth, nextMonth, thisYear, lastYear, nextYear, rollingWindowDays, quarterRange, lastQuarter, nextQuarter, RANGE_PRESETS, type DateRange } from './rangePresets.js';
16
+ export { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, type TimeUnit, type FormatOptions } from './constants.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,cAAc,EACd,OAAO,EACP,UAAU,EACV,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,OAAO,EACP,YAAY,EACZ,OAAO,EACP,KAAK,EACL,SAAS,EACT,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,WAAW,EACX,UAAU,EACV,MAAM,EACN,QAAQ,EACR,OAAO,EACP,WAAW,EACX,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,UAAU,EACV,KAAK,SAAS,EACf,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,aAAa,EACb,cAAc,EACd,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,eAAe,EACf,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACT,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,SAAS,EACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,KAAK,EACL,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,aAAa,EACb,KAAK,SAAS,EACf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Main entry point for ts-time-utils library
3
+ * Re-exports all functions from individual modules for convenience
4
+ */
5
+ // Format utilities
6
+ export { formatDuration, timeAgo, formatTime, parseDuration } from './format.js';
7
+ // Calculation utilities
8
+ export { differenceInUnits, addTime, subtractTime, startOf, endOf, isBetween, businessDaysBetween } from './calculate.js';
9
+ // Validation utilities
10
+ export { isValidDate, isLeapYear, isPast, isFuture, isToday, isYesterday, isTomorrow, isSameDay, isWeekend, isWeekday, isValidTimeString, isValidISOString } from './validate.js';
11
+ // Age utilities
12
+ export { calculateAge, getAgeInUnits, getLifeStage, getNextBirthday, getDaysUntilBirthday, isBirthday } from './age.js';
13
+ // Calendar utilities
14
+ export { getWeekNumber, getWeekOfMonth, getQuarter, getDayOfYear, getWeeksInYear, getDaysInMonth, getDaysInYear, getEaster, getMonthsInYear, getDaysInMonthArray, getWeekdaysInMonth, getFirstDayOfMonth, getLastDayOfMonth, getFirstDayOfYear, getLastDayOfYear } from './calendar.js';
15
+ // Parse utilities
16
+ export { parseDate, parseRelativeDate, parseTimeAgo, parseCustomFormat, parseManyFormats } from './parse.js';
17
+ // Performance utilities
18
+ export { sleep, timeout, debounce, throttle, retry, createStopwatch, measureTime, measureAsync, benchmark, Stopwatch } from './performance.js';
19
+ // Interval utilities
20
+ export { createInterval, isValidInterval, intervalDuration, intervalContains, intervalsOverlap, intervalIntersection, mergeIntervals, subtractInterval, splitIntervalByDay, totalIntervalCoverage, normalizeIntervals } from './interval.js';
21
+ // Timezone utilities
22
+ export { getTimezoneOffset, formatInTimeZone, getZonedTime, convertDateToZone, isValidTimeZone, COMMON_TIMEZONES, getLocalOffset, compareZoneOffsets, reinterpretAsZone } from './timezone.js';
23
+ // Working hours utilities
24
+ export { DEFAULT_WORKING_HOURS, isWorkingDay, isWorkingTime, nextWorkingTime, workingTimeBetween, addWorkingHours } from './workingHours.js';
25
+ // Range preset utilities
26
+ export { today, yesterday, tomorrow, lastNDays, nextNDays, thisWeek, lastWeek, nextWeek, thisMonth, lastMonth, nextMonth, thisYear, lastYear, nextYear, rollingWindowDays, quarterRange, lastQuarter, nextQuarter, RANGE_PRESETS } from './rangePresets.js';
27
+ // Constants and types
28
+ export { MILLISECONDS_PER_SECOND, MILLISECONDS_PER_MINUTE, MILLISECONDS_PER_HOUR, MILLISECONDS_PER_DAY, MILLISECONDS_PER_WEEK, MILLISECONDS_PER_MONTH, MILLISECONDS_PER_YEAR, SECONDS_PER_MINUTE, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK } from './constants.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Interval utilities: operations on time intervals [start, end)
3
+ */
4
+ export interface Interval {
5
+ start: Date;
6
+ end: Date;
7
+ }
8
+ /** Create an interval ensuring start <= end */
9
+ export declare function createInterval(start: Date | string | number, end: Date | string | number): Interval | null;
10
+ /** Validate an object is a proper interval */
11
+ export declare function isValidInterval(i: any): i is Interval;
12
+ /** Duration of interval in ms */
13
+ export declare function intervalDuration(i: Interval): number;
14
+ /** Whether interval contains date (inclusive start, exclusive end) */
15
+ export declare function intervalContains(i: Interval, date: Date | number): boolean;
16
+ /** Whether two intervals overlap */
17
+ export declare function intervalsOverlap(a: Interval, b: Interval): boolean;
18
+ /** Intersection of two intervals, or null */
19
+ export declare function intervalIntersection(a: Interval, b: Interval): Interval | null;
20
+ /** Merge overlapping or touching intervals into a minimal set */
21
+ export declare function mergeIntervals(intervals: Interval[]): Interval[];
22
+ /** Subtract interval b from a (can split into 0,1,2 intervals) */
23
+ export declare function subtractInterval(a: Interval, b: Interval): Interval[];
24
+ /** Split an interval into day-boundary intervals (UTC based) */
25
+ export declare function splitIntervalByDay(i: Interval): Interval[];
26
+ /** Total covered duration of possibly overlapping intervals */
27
+ export declare function totalIntervalCoverage(intervals: Interval[]): number;
28
+ /** Normalize array: filter invalid and merge */
29
+ export declare function normalizeIntervals(intervals: (Interval | null | undefined)[]): Interval[];
30
+ //# sourceMappingURL=interval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interval.d.ts","sourceRoot":"","sources":["../src/interval.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;CACX;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,CAK1G;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI,QAAQ,CAErD;AAED,iCAAiC;AACjC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAG1E;AAED,oCAAoC;AACpC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAElE;AAED,6CAA6C;AAC7C,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAI9E;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAgBhE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAMrE;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAU1D;AAED,+DAA+D;AAC/D,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAEnE;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,CAAC,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,CAEzF"}