@intech-software/chronera 0.1.4 → 0.1.6

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 (91) hide show
  1. package/dist/core/local-date.d.ts +1 -0
  2. package/dist/core/local-date.js +1 -0
  3. package/dist/core/local-date.js.map +1 -1
  4. package/dist/holidays/countries/ae.d.ts +6 -0
  5. package/dist/holidays/countries/ae.js +70 -0
  6. package/dist/holidays/countries/ae.js.map +1 -0
  7. package/dist/holidays/countries/au.d.ts +6 -0
  8. package/dist/holidays/countries/au.js +79 -0
  9. package/dist/holidays/countries/au.js.map +1 -0
  10. package/dist/holidays/countries/cn.d.ts +6 -0
  11. package/dist/holidays/countries/cn.js +57 -0
  12. package/dist/holidays/countries/cn.js.map +1 -0
  13. package/dist/holidays/countries/de.d.ts +6 -0
  14. package/dist/holidays/countries/de.js +81 -0
  15. package/dist/holidays/countries/de.js.map +1 -0
  16. package/dist/holidays/countries/fr.d.ts +6 -0
  17. package/dist/holidays/countries/fr.js +97 -0
  18. package/dist/holidays/countries/fr.js.map +1 -0
  19. package/dist/holidays/countries/gb.d.ts +6 -0
  20. package/dist/holidays/countries/gb.js +80 -0
  21. package/dist/holidays/countries/gb.js.map +1 -0
  22. package/dist/holidays/countries/hk.d.ts +6 -0
  23. package/dist/holidays/countries/hk.js +87 -0
  24. package/dist/holidays/countries/hk.js.map +1 -0
  25. package/dist/holidays/countries/in.d.ts +6 -0
  26. package/dist/holidays/countries/in.js +52 -0
  27. package/dist/holidays/countries/in.js.map +1 -0
  28. package/dist/holidays/countries/index.d.ts +18 -0
  29. package/dist/holidays/countries/index.js +34 -0
  30. package/dist/holidays/countries/index.js.map +1 -0
  31. package/dist/holidays/countries/ir.d.ts +7 -0
  32. package/dist/holidays/countries/ir.js +93 -0
  33. package/dist/holidays/countries/ir.js.map +1 -0
  34. package/dist/holidays/countries/jp.d.ts +6 -0
  35. package/dist/holidays/countries/jp.js +140 -0
  36. package/dist/holidays/countries/jp.js.map +1 -0
  37. package/dist/holidays/countries/sa.d.ts +7 -0
  38. package/dist/holidays/countries/sa.js +56 -0
  39. package/dist/holidays/countries/sa.js.map +1 -0
  40. package/dist/holidays/countries/sg.d.ts +6 -0
  41. package/dist/holidays/countries/sg.js +71 -0
  42. package/dist/holidays/countries/sg.js.map +1 -0
  43. package/dist/holidays/countries/th.d.ts +6 -0
  44. package/dist/holidays/countries/th.js +151 -0
  45. package/dist/holidays/countries/th.js.map +1 -0
  46. package/dist/holidays/countries/tw.d.ts +6 -0
  47. package/dist/holidays/countries/tw.js +57 -0
  48. package/dist/holidays/countries/tw.js.map +1 -0
  49. package/dist/holidays/countries/us.d.ts +6 -0
  50. package/dist/holidays/countries/us.js +112 -0
  51. package/dist/holidays/countries/us.js.map +1 -0
  52. package/dist/holidays/easter.d.ts +23 -0
  53. package/dist/holidays/easter.js +49 -0
  54. package/dist/holidays/easter.js.map +1 -0
  55. package/dist/holidays/index.d.ts +5 -0
  56. package/dist/holidays/index.js +6 -0
  57. package/dist/holidays/index.js.map +1 -0
  58. package/dist/holidays/registry.d.ts +14 -0
  59. package/dist/holidays/registry.js +79 -0
  60. package/dist/holidays/registry.js.map +1 -0
  61. package/dist/holidays/rules/easter-rule.d.ts +5 -0
  62. package/dist/holidays/rules/easter-rule.js +26 -0
  63. package/dist/holidays/rules/easter-rule.js.map +1 -0
  64. package/dist/holidays/rules/fixed-rule.d.ts +5 -0
  65. package/dist/holidays/rules/fixed-rule.js +72 -0
  66. package/dist/holidays/rules/fixed-rule.js.map +1 -0
  67. package/dist/holidays/rules/floating-rule.d.ts +6 -0
  68. package/dist/holidays/rules/floating-rule.js +53 -0
  69. package/dist/holidays/rules/floating-rule.js.map +1 -0
  70. package/dist/holidays/rules/hijri-rule.d.ts +6 -0
  71. package/dist/holidays/rules/hijri-rule.js +44 -0
  72. package/dist/holidays/rules/hijri-rule.js.map +1 -0
  73. package/dist/holidays/rules/index.d.ts +9 -0
  74. package/dist/holidays/rules/index.js +26 -0
  75. package/dist/holidays/rules/index.js.map +1 -0
  76. package/dist/holidays/types.d.ts +106 -0
  77. package/dist/holidays/types.js +2 -0
  78. package/dist/holidays/types.js.map +1 -0
  79. package/dist/index.d.ts +8 -2
  80. package/dist/index.js +5 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/operations/business-days.d.ts +30 -14
  83. package/dist/operations/business-days.js +115 -37
  84. package/dist/operations/business-days.js.map +1 -1
  85. package/dist/operations/business-hours.d.ts +137 -0
  86. package/dist/operations/business-hours.js +488 -0
  87. package/dist/operations/business-hours.js.map +1 -0
  88. package/dist/operations/holidays.d.ts +15 -0
  89. package/dist/operations/holidays.js +36 -0
  90. package/dist/operations/holidays.js.map +1 -0
  91. package/package.json +10 -1
@@ -1,28 +1,82 @@
1
1
  import { getIsoDayOfWeek } from "../core/iso-week.js";
2
+ import { localDate } from "../core/local-date.js";
3
+ import { gregorianFromAbsoluteDay } from "../calendar/gregory/absolute-day.js";
2
4
  import { ChroneraError } from "../errors/errors.js";
3
5
  import { addDays, getAbsoluteDay } from "./convenience.js";
6
+ import { isPublicHoliday } from "./holidays.js";
7
+ import { getHolidayCalendar } from "../holidays/registry.js";
4
8
  /**
5
- * Returns true if the specified date falls on a weekend (Saturday or Sunday).
6
- * Works across all calendars (Gregorian, Thai Buddhist, Japanese, Hijri, etc.).
9
+ * Checks whether a given absolute day is a weekend according to options or country defaults.
7
10
  */
8
- export function isWeekend(date) {
9
- const abs = getAbsoluteDay(date);
11
+ function isWeekendForOptions(abs, options) {
10
12
  const dow = getIsoDayOfWeek(abs);
13
+ if (options?.weekendDays && options.weekendDays.length > 0) {
14
+ return options.weekendDays.includes(dow);
15
+ }
16
+ if (typeof options?.holidays === "string" ||
17
+ (typeof options?.holidays === "object" &&
18
+ options?.holidays !== null &&
19
+ "country" in options.holidays)) {
20
+ try {
21
+ const cal = getHolidayCalendar(options.holidays);
22
+ if (cal.defaultWeekendDays) {
23
+ return cal.defaultWeekendDays.includes(dow);
24
+ }
25
+ }
26
+ catch {
27
+ // Fallback to standard Saturday & Sunday
28
+ }
29
+ }
11
30
  return dow === 6 || dow === 7;
12
31
  }
13
32
  /**
14
- * Returns true if the specified date falls on a weekday (Monday through Friday).
15
- * Works across all calendars (Gregorian, Thai Buddhist, Japanese, Hijri, etc.).
33
+ * Returns true if the specified absolute day is a non-working day (weekend or holiday).
34
+ */
35
+ function isNonWorkingDay(abs, options) {
36
+ if (isWeekendForOptions(abs, options)) {
37
+ return true;
38
+ }
39
+ const holidays = options?.holidays;
40
+ if (!holidays) {
41
+ return false;
42
+ }
43
+ const gFields = gregorianFromAbsoluteDay(abs);
44
+ const gDate = localDate(gFields.year, gFields.month, gFields.day);
45
+ if (typeof holidays === "function") {
46
+ return holidays(gDate);
47
+ }
48
+ if (typeof holidays === "string" || "rules" in holidays) {
49
+ return isPublicHoliday(gDate, holidays);
50
+ }
51
+ return holidays.some((hDate) => getAbsoluteDay(hDate) === abs);
52
+ }
53
+ /**
54
+ * Returns true if the specified date falls on a weekend.
55
+ * Supports custom weekend definitions per country (e.g. Thursday & Friday for Iran).
56
+ */
57
+ export function isWeekend(date, options) {
58
+ const abs = getAbsoluteDay(date);
59
+ return isWeekendForOptions(abs, options);
60
+ }
61
+ /**
62
+ * Returns true if the specified date falls on a weekday.
63
+ */
64
+ export function isWeekday(date, options) {
65
+ return !isWeekend(date, options);
66
+ }
67
+ /**
68
+ * Returns true if the specified date is a business day (neither weekend nor public holiday).
16
69
  */
17
- export function isWeekday(date) {
18
- return !isWeekend(date);
70
+ export function isBusinessDay(date, options) {
71
+ const abs = getAbsoluteDay(date);
72
+ return !isNonWorkingDay(abs, options);
19
73
  }
20
74
  /**
21
- * Adds business days to a LocalDate or CalendarDate, skipping Saturdays and Sundays.
75
+ * Adds business days to a LocalDate or CalendarDate, skipping weekends and optionally public holidays.
22
76
  * Supports positive, negative, and zero day additions.
23
- * Uses O(1) mathematical week advancement for large intervals while preserving exact calendar semantics.
77
+ * Uses O(1) mathematical week advancement when no holidays are provided, or iterative evaluation with holiday constraints.
24
78
  */
25
- export function addBusinessDays(date, amount) {
79
+ export function addBusinessDays(date, amount, options) {
26
80
  if (!Number.isFinite(amount)) {
27
81
  throw new ChroneraError("CHRONERA_OUT_OF_RANGE", "Amount must be a finite integer.");
28
82
  }
@@ -34,17 +88,31 @@ export function addBusinessDays(date, amount) {
34
88
  let currentAbs = startAbs;
35
89
  let remaining = intAmount;
36
90
  const step = remaining > 0 ? 1 : -1;
37
- while (remaining !== 0) {
38
- const currentDow = getIsoDayOfWeek(currentAbs);
39
- if (currentDow !== 6 && currentDow !== 7 && Math.abs(remaining) >= 5) {
40
- const fullWeeks = Math.trunc(remaining / 5);
41
- currentAbs += fullWeeks * 7;
42
- remaining %= 5;
91
+ // Fast-path: no holidays or custom weekends
92
+ const hasHolidays = Boolean(options?.holidays);
93
+ const hasCustomWeekends = Boolean(options?.weekendDays);
94
+ if (!hasHolidays && !hasCustomWeekends) {
95
+ while (remaining !== 0) {
96
+ const currentDow = getIsoDayOfWeek(currentAbs);
97
+ if (currentDow !== 6 && currentDow !== 7 && Math.abs(remaining) >= 5) {
98
+ const fullWeeks = Math.trunc(remaining / 5);
99
+ currentAbs += fullWeeks * 7;
100
+ remaining %= 5;
101
+ }
102
+ else {
103
+ currentAbs += step;
104
+ const dow = getIsoDayOfWeek(currentAbs);
105
+ if (dow !== 6 && dow !== 7) {
106
+ remaining -= step;
107
+ }
108
+ }
43
109
  }
44
- else {
110
+ }
111
+ else {
112
+ // Exact evaluation with holiday and custom weekend constraints
113
+ while (remaining !== 0) {
45
114
  currentAbs += step;
46
- const dow = getIsoDayOfWeek(currentAbs);
47
- if (dow !== 6 && dow !== 7) {
115
+ if (!isNonWorkingDay(currentAbs, options)) {
48
116
  remaining -= step;
49
117
  }
50
118
  }
@@ -53,39 +121,49 @@ export function addBusinessDays(date, amount) {
53
121
  return addDays(date, deltaDays);
54
122
  }
55
123
  /**
56
- * Subtracts business days from a LocalDate or CalendarDate, skipping weekends.
124
+ * Subtracts business days from a LocalDate or CalendarDate, skipping weekends and optional holidays.
57
125
  */
58
- export function subtractBusinessDays(date, amount) {
59
- return addBusinessDays(date, -amount);
126
+ export function subtractBusinessDays(date, amount, options) {
127
+ return addBusinessDays(date, -amount, options);
60
128
  }
61
129
  /**
62
130
  * Returns the signed count of business days between two dates (left - right).
63
131
  * Positive if left is after right, negative if left is before right, 0 if identical.
64
- * Works across all calendar systems with O(1) mathematical week acceleration.
65
132
  */
66
- export function diffInBusinessDays(left, right) {
133
+ export function diffInBusinessDays(left, right, options) {
67
134
  const absLeft = getAbsoluteDay(left);
68
135
  const absRight = getAbsoluteDay(right);
69
136
  if (absLeft === absRight) {
70
137
  return 0;
71
138
  }
72
139
  if (absLeft < absRight) {
73
- return -diffInBusinessDays(right, left);
140
+ return -diffInBusinessDays(right, left, options);
74
141
  }
75
142
  let count = 0;
76
143
  let current = absRight;
77
- // O(1) acceleration: any consecutive 7 calendar days contains exactly 5 business days
78
- const fullWeeks = Math.floor((absLeft - current) / 7);
79
- if (fullWeeks > 0) {
80
- count += fullWeeks * 5;
81
- current += fullWeeks * 7;
144
+ const hasHolidays = Boolean(options?.holidays);
145
+ const hasCustomWeekends = Boolean(options?.weekendDays);
146
+ if (!hasHolidays && !hasCustomWeekends) {
147
+ // Fast-path O(1) acceleration: any consecutive 7 calendar days contains exactly 5 business days
148
+ const fullWeeks = Math.floor((absLeft - current) / 7);
149
+ if (fullWeeks > 0) {
150
+ count += fullWeeks * 5;
151
+ current += fullWeeks * 7;
152
+ }
153
+ while (current < absLeft) {
154
+ current++;
155
+ const dow = getIsoDayOfWeek(current);
156
+ if (dow !== 6 && dow !== 7) {
157
+ count++;
158
+ }
159
+ }
82
160
  }
83
- // Step through remainder (at most 6 days)
84
- while (current < absLeft) {
85
- current++;
86
- const dow = getIsoDayOfWeek(current);
87
- if (dow !== 6 && dow !== 7) {
88
- count++;
161
+ else {
162
+ while (current < absLeft) {
163
+ current++;
164
+ if (!isNonWorkingDay(current, options)) {
165
+ count++;
166
+ }
89
167
  }
90
168
  }
91
169
  return count;
@@ -1 +1 @@
1
- {"version":3,"file":"business-days.js","sourceRoot":"","sources":["../../src/operations/business-days.ts"],"names":[],"mappings":"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","sourcesContent":["import { getIsoDayOfWeek } from \"../core/iso-week.js\";\nimport { ChroneraError } from \"../errors/errors.js\";\nimport { addDays, getAbsoluteDay } from \"./convenience.js\";\n\nimport type { DateOrCalendarDate } from \"../public-types.js\";\n\n/**\n * Returns true if the specified date falls on a weekend (Saturday or Sunday).\n * Works across all calendars (Gregorian, Thai Buddhist, Japanese, Hijri, etc.).\n */\nexport function isWeekend(date: DateOrCalendarDate): boolean {\n const abs = getAbsoluteDay(date);\n const dow = getIsoDayOfWeek(abs);\n return dow === 6 || dow === 7;\n}\n\n/**\n * Returns true if the specified date falls on a weekday (Monday through Friday).\n * Works across all calendars (Gregorian, Thai Buddhist, Japanese, Hijri, etc.).\n */\nexport function isWeekday(date: DateOrCalendarDate): boolean {\n return !isWeekend(date);\n}\n\n/**\n * Adds business days to a LocalDate or CalendarDate, skipping Saturdays and Sundays.\n * Supports positive, negative, and zero day additions.\n * Uses O(1) mathematical week advancement for large intervals while preserving exact calendar semantics.\n */\nexport function addBusinessDays<T extends DateOrCalendarDate>(\n date: T,\n amount: number,\n): T {\n if (!Number.isFinite(amount)) {\n throw new ChroneraError(\n \"CHRONERA_OUT_OF_RANGE\",\n \"Amount must be a finite integer.\",\n );\n }\n\n const intAmount = Math.trunc(amount);\n if (intAmount === 0) {\n return date;\n }\n\n const startAbs = getAbsoluteDay(date);\n let currentAbs = startAbs;\n let remaining = intAmount;\n const step = remaining > 0 ? 1 : -1;\n\n while (remaining !== 0) {\n const currentDow = getIsoDayOfWeek(currentAbs);\n if (currentDow !== 6 && currentDow !== 7 && Math.abs(remaining) >= 5) {\n const fullWeeks = Math.trunc(remaining / 5);\n currentAbs += fullWeeks * 7;\n remaining %= 5;\n } else {\n currentAbs += step;\n const dow = getIsoDayOfWeek(currentAbs);\n if (dow !== 6 && dow !== 7) {\n remaining -= step;\n }\n }\n }\n\n const deltaDays = currentAbs - startAbs;\n return addDays(date, deltaDays);\n}\n\n/**\n * Subtracts business days from a LocalDate or CalendarDate, skipping weekends.\n */\nexport function subtractBusinessDays<T extends DateOrCalendarDate>(\n date: T,\n amount: number,\n): T {\n return addBusinessDays(date, -amount);\n}\n\n/**\n * Returns the signed count of business days between two dates (left - right).\n * Positive if left is after right, negative if left is before right, 0 if identical.\n * Works across all calendar systems with O(1) mathematical week acceleration.\n */\nexport function diffInBusinessDays(\n left: DateOrCalendarDate,\n right: DateOrCalendarDate,\n): number {\n const absLeft = getAbsoluteDay(left);\n const absRight = getAbsoluteDay(right);\n\n if (absLeft === absRight) {\n return 0;\n }\n\n if (absLeft < absRight) {\n return -diffInBusinessDays(right, left);\n }\n\n let count = 0;\n let current = absRight;\n\n // O(1) acceleration: any consecutive 7 calendar days contains exactly 5 business days\n const fullWeeks = Math.floor((absLeft - current) / 7);\n if (fullWeeks > 0) {\n count += fullWeeks * 5;\n current += fullWeeks * 7;\n }\n\n // Step through remainder (at most 6 days)\n while (current < absLeft) {\n current++;\n const dow = getIsoDayOfWeek(current);\n if (dow !== 6 && dow !== 7) {\n count++;\n }\n }\n\n return count;\n}\n"]}
1
+ {"version":3,"file":"business-days.js","sourceRoot":"","sources":["../../src/operations/business-days.ts"],"names":[],"mappings":"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","sourcesContent":["import { getIsoDayOfWeek } from \"../core/iso-week.js\";\nimport { localDate } from \"../core/local-date.js\";\nimport { gregorianFromAbsoluteDay } from \"../calendar/gregory/absolute-day.js\";\nimport { ChroneraError } from \"../errors/errors.js\";\nimport { addDays, getAbsoluteDay } from \"./convenience.js\";\nimport { isPublicHoliday } from \"./holidays.js\";\nimport { getHolidayCalendar } from \"../holidays/registry.js\";\n\nimport type { CountryCode, HolidayCalendar } from \"../holidays/types.js\";\nimport type { DateOrCalendarDate, LocalDate } from \"../public-types.js\";\n\n/**\n * Options for business days arithmetic and queries.\n */\nexport interface BusinessDaysOptions {\n /**\n * Country code (e.g. \"TH\", \"JP\", \"US\") or custom HolidayCalendar or holiday date list or holiday predicate.\n */\n readonly holidays?:\n | CountryCode\n | string\n | HolidayCalendar\n | readonly DateOrCalendarDate[]\n | ((date: LocalDate) => boolean);\n /**\n * Custom weekend days (defaults to [6, 7], or [4, 5] if country is Iran, etc.).\n */\n readonly weekendDays?: readonly number[];\n}\n\n/**\n * Checks whether a given absolute day is a weekend according to options or country defaults.\n */\nfunction isWeekendForOptions(\n abs: number,\n options?: BusinessDaysOptions,\n): boolean {\n const dow = getIsoDayOfWeek(abs);\n if (options?.weekendDays && options.weekendDays.length > 0) {\n return options.weekendDays.includes(dow);\n }\n\n if (\n typeof options?.holidays === \"string\" ||\n (typeof options?.holidays === \"object\" &&\n options?.holidays !== null &&\n \"country\" in options.holidays)\n ) {\n try {\n const cal = getHolidayCalendar(options.holidays);\n if (cal.defaultWeekendDays) {\n return cal.defaultWeekendDays.includes(dow);\n }\n } catch {\n // Fallback to standard Saturday & Sunday\n }\n }\n\n return dow === 6 || dow === 7;\n}\n\n/**\n * Returns true if the specified absolute day is a non-working day (weekend or holiday).\n */\nfunction isNonWorkingDay(abs: number, options?: BusinessDaysOptions): boolean {\n if (isWeekendForOptions(abs, options)) {\n return true;\n }\n\n const holidays = options?.holidays;\n if (!holidays) {\n return false;\n }\n\n const gFields = gregorianFromAbsoluteDay(abs);\n const gDate = localDate(gFields.year, gFields.month, gFields.day);\n\n if (typeof holidays === \"function\") {\n return holidays(gDate);\n }\n\n if (typeof holidays === \"string\" || \"rules\" in holidays) {\n return isPublicHoliday(gDate, holidays);\n }\n\n return holidays.some((hDate) => getAbsoluteDay(hDate) === abs);\n}\n\n/**\n * Returns true if the specified date falls on a weekend.\n * Supports custom weekend definitions per country (e.g. Thursday & Friday for Iran).\n */\nexport function isWeekend(\n date: DateOrCalendarDate,\n options?: Pick<BusinessDaysOptions, \"weekendDays\" | \"holidays\">,\n): boolean {\n const abs = getAbsoluteDay(date);\n return isWeekendForOptions(abs, options);\n}\n\n/**\n * Returns true if the specified date falls on a weekday.\n */\nexport function isWeekday(\n date: DateOrCalendarDate,\n options?: Pick<BusinessDaysOptions, \"weekendDays\" | \"holidays\">,\n): boolean {\n return !isWeekend(date, options);\n}\n\n/**\n * Returns true if the specified date is a business day (neither weekend nor public holiday).\n */\nexport function isBusinessDay(\n date: DateOrCalendarDate,\n options?: BusinessDaysOptions,\n): boolean {\n const abs = getAbsoluteDay(date);\n return !isNonWorkingDay(abs, options);\n}\n\n/**\n * Adds business days to a LocalDate or CalendarDate, skipping weekends and optionally public holidays.\n * Supports positive, negative, and zero day additions.\n * Uses O(1) mathematical week advancement when no holidays are provided, or iterative evaluation with holiday constraints.\n */\nexport function addBusinessDays<T extends DateOrCalendarDate>(\n date: T,\n amount: number,\n options?: BusinessDaysOptions,\n): T {\n if (!Number.isFinite(amount)) {\n throw new ChroneraError(\n \"CHRONERA_OUT_OF_RANGE\",\n \"Amount must be a finite integer.\",\n );\n }\n\n const intAmount = Math.trunc(amount);\n if (intAmount === 0) {\n return date;\n }\n\n const startAbs = getAbsoluteDay(date);\n let currentAbs = startAbs;\n let remaining = intAmount;\n const step = remaining > 0 ? 1 : -1;\n\n // Fast-path: no holidays or custom weekends\n const hasHolidays = Boolean(options?.holidays);\n const hasCustomWeekends = Boolean(options?.weekendDays);\n\n if (!hasHolidays && !hasCustomWeekends) {\n while (remaining !== 0) {\n const currentDow = getIsoDayOfWeek(currentAbs);\n if (currentDow !== 6 && currentDow !== 7 && Math.abs(remaining) >= 5) {\n const fullWeeks = Math.trunc(remaining / 5);\n currentAbs += fullWeeks * 7;\n remaining %= 5;\n } else {\n currentAbs += step;\n const dow = getIsoDayOfWeek(currentAbs);\n if (dow !== 6 && dow !== 7) {\n remaining -= step;\n }\n }\n }\n } else {\n // Exact evaluation with holiday and custom weekend constraints\n while (remaining !== 0) {\n currentAbs += step;\n if (!isNonWorkingDay(currentAbs, options)) {\n remaining -= step;\n }\n }\n }\n\n const deltaDays = currentAbs - startAbs;\n return addDays(date, deltaDays);\n}\n\n/**\n * Subtracts business days from a LocalDate or CalendarDate, skipping weekends and optional holidays.\n */\nexport function subtractBusinessDays<T extends DateOrCalendarDate>(\n date: T,\n amount: number,\n options?: BusinessDaysOptions,\n): T {\n return addBusinessDays(date, -amount, options);\n}\n\n/**\n * Returns the signed count of business days between two dates (left - right).\n * Positive if left is after right, negative if left is before right, 0 if identical.\n */\nexport function diffInBusinessDays(\n left: DateOrCalendarDate,\n right: DateOrCalendarDate,\n options?: BusinessDaysOptions,\n): number {\n const absLeft = getAbsoluteDay(left);\n const absRight = getAbsoluteDay(right);\n\n if (absLeft === absRight) {\n return 0;\n }\n\n if (absLeft < absRight) {\n return -diffInBusinessDays(right, left, options);\n }\n\n let count = 0;\n let current = absRight;\n\n const hasHolidays = Boolean(options?.holidays);\n const hasCustomWeekends = Boolean(options?.weekendDays);\n\n if (!hasHolidays && !hasCustomWeekends) {\n // Fast-path O(1) acceleration: any consecutive 7 calendar days contains exactly 5 business days\n const fullWeeks = Math.floor((absLeft - current) / 7);\n if (fullWeeks > 0) {\n count += fullWeeks * 5;\n current += fullWeeks * 7;\n }\n\n while (current < absLeft) {\n current++;\n const dow = getIsoDayOfWeek(current);\n if (dow !== 6 && dow !== 7) {\n count++;\n }\n }\n } else {\n while (current < absLeft) {\n current++;\n if (!isNonWorkingDay(current, options)) {\n count++;\n }\n }\n }\n\n return count;\n}\n"]}
@@ -0,0 +1,137 @@
1
+ import type { CountryCode, HolidayCalendar } from "../holidays/types.js";
2
+ import type { BusinessDaysOptions } from "./business-days.js";
3
+ import type { Instant, LocalDate, LocalDateTime, LocalTime, TimeZoneId } from "../public-types.js";
4
+ /**
5
+ * Defines an active working shift window within a day.
6
+ */
7
+ export interface ShiftWindow {
8
+ /**
9
+ * Shift start time as "HH:mm", "HH:mm:ss", or a LocalTime object (e.g. "08:30").
10
+ */
11
+ readonly start: string | LocalTime;
12
+ /**
13
+ * Shift end time as "HH:mm", "HH:mm:ss", or a LocalTime object (e.g. "17:30").
14
+ */
15
+ readonly end: string | LocalTime;
16
+ }
17
+ /**
18
+ * Business schedule configuration for working hours, shifts, weekends, and holidays.
19
+ */
20
+ export interface BusinessSchedule {
21
+ /**
22
+ * Working days of the week as ISO weekday numbers (1 = Monday, 7 = Sunday).
23
+ * Defaults to [1, 2, 3, 4, 5] (Monday to Friday), or country weekend defaults.
24
+ */
25
+ readonly workDays?: readonly number[];
26
+ /**
27
+ * Standard daily shifts. Defaults to a single shift 09:00 - 17:00.
28
+ */
29
+ readonly shifts?: readonly ShiftWindow[];
30
+ /**
31
+ * Day-of-week specific shift overrides (e.g. `{ 5: [{ start: "08:30", end: "12:00" }] }` for Friday half-day).
32
+ */
33
+ readonly dayShifts?: Partial<Record<number, readonly ShiftWindow[]>>;
34
+ /**
35
+ * Country code (e.g. "th", "jp", "us", "gb", "de") or custom HolidayCalendar for automatic holiday exclusion.
36
+ */
37
+ readonly country?: CountryCode | string | HolidayCalendar;
38
+ /**
39
+ * Custom holiday list, holiday predicate, or holiday calendar.
40
+ */
41
+ readonly holidays?: BusinessDaysOptions["holidays"];
42
+ /**
43
+ * Custom weekend days (e.g. [6, 7] for Sat-Sun, or [4, 5] for Iran).
44
+ */
45
+ readonly weekendDays?: readonly number[];
46
+ /**
47
+ * TimeZone identifier (e.g. "Asia/Bangkok", "America/New_York", "UTC").
48
+ * Required when evaluating Instant or Date across specific local working hours.
49
+ */
50
+ readonly timeZone?: TimeZoneId;
51
+ }
52
+ /**
53
+ * Normalized shift representation in milliseconds from midnight [0, 86_400_000].
54
+ */
55
+ export interface NormalizedShift {
56
+ readonly startMs: number;
57
+ readonly endMs: number;
58
+ readonly durationMs: number;
59
+ }
60
+ /**
61
+ * Result representing an upcoming or active business shift window.
62
+ */
63
+ export interface NextBusinessShiftResult<T> {
64
+ readonly start: T;
65
+ readonly end: T;
66
+ }
67
+ export type BusinessTimeInput = LocalDateTime | Instant | Date | LocalDate;
68
+ /**
69
+ * Checks whether the given timestamp falls inside an active working shift on a working day.
70
+ * Returns false during non-working days, holidays, weekends, lunch breaks, or outside shift hours.
71
+ *
72
+ * @param input - LocalDateTime, Instant, Date, or LocalDate to check.
73
+ * @param schedule - Working schedule, shift windows, and holiday configuration.
74
+ */
75
+ export declare function isBusinessHour(input: BusinessTimeInput, schedule?: BusinessSchedule): boolean;
76
+ /**
77
+ * Adds business hours to a timestamp, skipping non-working hours, breaks, weekends, and statutory holidays.
78
+ * If the starting timestamp falls outside business hours (e.g. at night or during lunch),
79
+ * the SLA calculation automatically starts ticking from the start of the next active shift.
80
+ *
81
+ * @param input - The starting LocalDateTime, Instant, Date, or LocalDate.
82
+ * @param hours - Number of business hours to add (supports fractional hours like 2.5).
83
+ * @param schedule - Business schedule defining shift windows, weekends, holidays, and timezone.
84
+ * @returns The resulting deadline in the same type as the input.
85
+ */
86
+ export declare function addBusinessHours(input: LocalDate, hours: number, schedule?: BusinessSchedule): LocalDateTime;
87
+ export declare function addBusinessHours<T extends LocalDateTime | Instant | Date>(input: T, hours: number, schedule?: BusinessSchedule): T;
88
+ export declare function addBusinessHours(input: BusinessTimeInput, hours: number, schedule?: BusinessSchedule): LocalDateTime | Instant | Date;
89
+ /**
90
+ * Subtracts business hours from a timestamp backwards, skipping non-working hours, breaks, weekends, and holidays.
91
+ *
92
+ * @param input - The starting LocalDateTime, Instant, Date, or LocalDate.
93
+ * @param hours - Number of business hours to subtract (supports fractional hours).
94
+ * @param schedule - Business schedule configuration.
95
+ * @returns The resulting earlier timestamp in the same type as the input.
96
+ */
97
+ export declare function subtractBusinessHours(input: LocalDate, hours: number, schedule?: BusinessSchedule): LocalDateTime;
98
+ export declare function subtractBusinessHours<T extends LocalDateTime | Instant | Date>(input: T, hours: number, schedule?: BusinessSchedule): T;
99
+ export declare function subtractBusinessHours(input: BusinessTimeInput, hours: number, schedule?: BusinessSchedule): LocalDateTime | Instant | Date;
100
+ /**
101
+ * Computes the exact signed number of business hours elapsed between two timestamps (left - right).
102
+ * Non-working hours, lunch breaks, weekends, and statutory holidays are excluded.
103
+ *
104
+ * @param left - The later or earlier timestamp.
105
+ * @param right - The reference timestamp.
106
+ * @param schedule - Business schedule configuration.
107
+ * @returns Fractional business hours elapsed (positive if left > right, negative if left < right, 0 if equal).
108
+ */
109
+ export declare function diffInBusinessHours(left: BusinessTimeInput, right: BusinessTimeInput, schedule?: BusinessSchedule): number;
110
+ /**
111
+ * Options for resolving the next business shift window.
112
+ */
113
+ export interface NextBusinessShiftOptions {
114
+ /**
115
+ * If true (default), returns the currently ongoing shift if the timestamp falls inside it.
116
+ * If false, strictly returns the next upcoming shift after the timestamp.
117
+ */
118
+ readonly includeCurrent?: boolean;
119
+ }
120
+ /**
121
+ * Resolves the next upcoming (or currently active) business shift window.
122
+ *
123
+ * @param input - The reference timestamp.
124
+ * @param schedule - Business schedule configuration.
125
+ * @param options - Options for including the currently active shift.
126
+ */
127
+ export declare function nextBusinessShift(input: LocalDate, schedule?: BusinessSchedule, options?: NextBusinessShiftOptions): NextBusinessShiftResult<LocalDateTime>;
128
+ export declare function nextBusinessShift<T extends LocalDateTime | Instant | Date>(input: T, schedule?: BusinessSchedule, options?: NextBusinessShiftOptions): NextBusinessShiftResult<T>;
129
+ export declare function nextBusinessShift(input: BusinessTimeInput, schedule?: BusinessSchedule, options?: NextBusinessShiftOptions): NextBusinessShiftResult<LocalDateTime | Instant | Date>;
130
+ /**
131
+ * Returns the earliest shift start time for a given date as a LocalDateTime.
132
+ */
133
+ export declare function startOfBusinessDay(date: LocalDate | LocalDateTime | Date, schedule?: BusinessSchedule): LocalDateTime;
134
+ /**
135
+ * Returns the latest shift end time for a given date as a LocalDateTime.
136
+ */
137
+ export declare function endOfBusinessDay(date: LocalDate | LocalDateTime | Date, schedule?: BusinessSchedule): LocalDateTime;