@fluixi/utils 1.0.0-alpha.53

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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/index.cjs +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/lib/array/advanced.cjs +1 -0
  9. package/dist/lib/array/advanced.d.ts +1116 -0
  10. package/dist/lib/array/advanced.d.ts.map +1 -0
  11. package/dist/lib/array/advanced.js +2429 -0
  12. package/dist/lib/array/advanced.mjs +1 -0
  13. package/dist/lib/array/array.cjs +1 -0
  14. package/dist/lib/array/array.d.ts +6 -0
  15. package/dist/lib/array/array.d.ts.map +1 -0
  16. package/dist/lib/array/array.js +31 -0
  17. package/dist/lib/array/array.mjs +1 -0
  18. package/dist/lib/array/index.cjs +1 -0
  19. package/dist/lib/array/index.d.ts +4 -0
  20. package/dist/lib/array/index.d.ts.map +1 -0
  21. package/dist/lib/array/index.js +3 -0
  22. package/dist/lib/array/index.mjs +1 -0
  23. package/dist/lib/array/utils.cjs +1 -0
  24. package/dist/lib/array/utils.d.ts +100 -0
  25. package/dist/lib/array/utils.d.ts.map +1 -0
  26. package/dist/lib/array/utils.js +165 -0
  27. package/dist/lib/array/utils.mjs +1 -0
  28. package/dist/lib/color/color.cjs +1 -0
  29. package/dist/lib/color/color.d.ts +366 -0
  30. package/dist/lib/color/color.d.ts.map +1 -0
  31. package/dist/lib/color/color.js +722 -0
  32. package/dist/lib/color/color.mjs +1 -0
  33. package/dist/lib/color/constants.cjs +1 -0
  34. package/dist/lib/color/constants.d.ts +361 -0
  35. package/dist/lib/color/constants.d.ts.map +1 -0
  36. package/dist/lib/color/constants.js +358 -0
  37. package/dist/lib/color/constants.mjs +1 -0
  38. package/dist/lib/color/conversions.cjs +1 -0
  39. package/dist/lib/color/conversions.d.ts +166 -0
  40. package/dist/lib/color/conversions.d.ts.map +1 -0
  41. package/dist/lib/color/conversions.js +766 -0
  42. package/dist/lib/color/conversions.mjs +1 -0
  43. package/dist/lib/color/helpers.cjs +1 -0
  44. package/dist/lib/color/helpers.d.ts +75 -0
  45. package/dist/lib/color/helpers.d.ts.map +1 -0
  46. package/dist/lib/color/helpers.js +419 -0
  47. package/dist/lib/color/helpers.mjs +1 -0
  48. package/dist/lib/color/index.cjs +1 -0
  49. package/dist/lib/color/index.d.ts +12 -0
  50. package/dist/lib/color/index.d.ts.map +1 -0
  51. package/dist/lib/color/index.js +15 -0
  52. package/dist/lib/color/index.mjs +1 -0
  53. package/dist/lib/color/types.cjs +1 -0
  54. package/dist/lib/color/types.d.ts +179 -0
  55. package/dist/lib/color/types.d.ts.map +1 -0
  56. package/dist/lib/color/types.js +1 -0
  57. package/dist/lib/color/types.mjs +0 -0
  58. package/dist/lib/color/utils.cjs +1 -0
  59. package/dist/lib/color/utils.d.ts +199 -0
  60. package/dist/lib/color/utils.d.ts.map +1 -0
  61. package/dist/lib/color/utils.js +858 -0
  62. package/dist/lib/color/utils.mjs +1 -0
  63. package/dist/lib/compare/comparators.cjs +1 -0
  64. package/dist/lib/compare/comparators.d.ts +65 -0
  65. package/dist/lib/compare/comparators.d.ts.map +1 -0
  66. package/dist/lib/compare/comparators.js +210 -0
  67. package/dist/lib/compare/comparators.mjs +1 -0
  68. package/dist/lib/compare/index.cjs +1 -0
  69. package/dist/lib/compare/index.d.ts +2 -0
  70. package/dist/lib/compare/index.d.ts.map +1 -0
  71. package/dist/lib/compare/index.js +1 -0
  72. package/dist/lib/compare/index.mjs +1 -0
  73. package/dist/lib/crypto/hash.cjs +1 -0
  74. package/dist/lib/crypto/hash.d.ts +6 -0
  75. package/dist/lib/crypto/hash.d.ts.map +1 -0
  76. package/dist/lib/crypto/hash.js +53 -0
  77. package/dist/lib/crypto/hash.mjs +1 -0
  78. package/dist/lib/crypto/index.cjs +1 -0
  79. package/dist/lib/crypto/index.d.ts +2 -0
  80. package/dist/lib/crypto/index.d.ts.map +1 -0
  81. package/dist/lib/crypto/index.js +1 -0
  82. package/dist/lib/crypto/index.mjs +1 -0
  83. package/dist/lib/dom/events.cjs +1 -0
  84. package/dist/lib/dom/events.d.ts +159 -0
  85. package/dist/lib/dom/events.d.ts.map +1 -0
  86. package/dist/lib/dom/events.js +50 -0
  87. package/dist/lib/dom/events.mjs +1 -0
  88. package/dist/lib/dom/index.cjs +1 -0
  89. package/dist/lib/dom/index.d.ts +3 -0
  90. package/dist/lib/dom/index.d.ts.map +1 -0
  91. package/dist/lib/dom/index.js +2 -0
  92. package/dist/lib/dom/index.mjs +1 -0
  93. package/dist/lib/dom/utils.cjs +1 -0
  94. package/dist/lib/dom/utils.d.ts +6 -0
  95. package/dist/lib/dom/utils.d.ts.map +1 -0
  96. package/dist/lib/dom/utils.js +37 -0
  97. package/dist/lib/dom/utils.mjs +1 -0
  98. package/dist/lib/functions/functions.cjs +1 -0
  99. package/dist/lib/functions/functions.d.ts +329 -0
  100. package/dist/lib/functions/functions.d.ts.map +1 -0
  101. package/dist/lib/functions/functions.js +585 -0
  102. package/dist/lib/functions/functions.mjs +1 -0
  103. package/dist/lib/functions/index.cjs +1 -0
  104. package/dist/lib/functions/index.d.ts +3 -0
  105. package/dist/lib/functions/index.d.ts.map +1 -0
  106. package/dist/lib/functions/index.js +1 -0
  107. package/dist/lib/functions/index.mjs +1 -0
  108. package/dist/lib/functions/types.cjs +1 -0
  109. package/dist/lib/functions/types.d.ts +2 -0
  110. package/dist/lib/functions/types.d.ts.map +1 -0
  111. package/dist/lib/functions/types.js +1 -0
  112. package/dist/lib/functions/types.mjs +0 -0
  113. package/dist/lib/helpers.cjs +1 -0
  114. package/dist/lib/helpers.d.ts +13 -0
  115. package/dist/lib/helpers.d.ts.map +1 -0
  116. package/dist/lib/helpers.js +66 -0
  117. package/dist/lib/helpers.mjs +1 -0
  118. package/dist/lib/index.cjs +1 -0
  119. package/dist/lib/index.d.ts +6 -0
  120. package/dist/lib/index.d.ts.map +1 -0
  121. package/dist/lib/index.js +8 -0
  122. package/dist/lib/index.mjs +1 -0
  123. package/dist/lib/maths/constants.cjs +1 -0
  124. package/dist/lib/maths/constants.d.ts +84 -0
  125. package/dist/lib/maths/constants.d.ts.map +1 -0
  126. package/dist/lib/maths/constants.js +79 -0
  127. package/dist/lib/maths/constants.mjs +1 -0
  128. package/dist/lib/maths/fn/advanced.cjs +1 -0
  129. package/dist/lib/maths/fn/advanced.d.ts +149 -0
  130. package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
  131. package/dist/lib/maths/fn/advanced.js +264 -0
  132. package/dist/lib/maths/fn/advanced.mjs +1 -0
  133. package/dist/lib/maths/fn/basic.cjs +1 -0
  134. package/dist/lib/maths/fn/basic.d.ts +182 -0
  135. package/dist/lib/maths/fn/basic.d.ts.map +1 -0
  136. package/dist/lib/maths/fn/basic.js +343 -0
  137. package/dist/lib/maths/fn/basic.mjs +1 -0
  138. package/dist/lib/maths/fn/complex.cjs +1 -0
  139. package/dist/lib/maths/fn/complex.d.ts +153 -0
  140. package/dist/lib/maths/fn/complex.d.ts.map +1 -0
  141. package/dist/lib/maths/fn/complex.js +383 -0
  142. package/dist/lib/maths/fn/complex.mjs +1 -0
  143. package/dist/lib/maths/fn/index.cjs +1 -0
  144. package/dist/lib/maths/fn/index.d.ts +10 -0
  145. package/dist/lib/maths/fn/index.d.ts.map +1 -0
  146. package/dist/lib/maths/fn/index.js +9 -0
  147. package/dist/lib/maths/fn/index.mjs +1 -0
  148. package/dist/lib/maths/fn/trigo.cjs +1 -0
  149. package/dist/lib/maths/fn/trigo.d.ts +147 -0
  150. package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
  151. package/dist/lib/maths/fn/trigo.js +227 -0
  152. package/dist/lib/maths/fn/trigo.mjs +1 -0
  153. package/dist/lib/maths/fn/vector.cjs +1 -0
  154. package/dist/lib/maths/fn/vector.d.ts +512 -0
  155. package/dist/lib/maths/fn/vector.d.ts.map +1 -0
  156. package/dist/lib/maths/fn/vector.js +1136 -0
  157. package/dist/lib/maths/fn/vector.mjs +1 -0
  158. package/dist/lib/maths/formatter.cjs +1 -0
  159. package/dist/lib/maths/formatter.d.ts +35 -0
  160. package/dist/lib/maths/formatter.d.ts.map +1 -0
  161. package/dist/lib/maths/formatter.js +56 -0
  162. package/dist/lib/maths/formatter.mjs +1 -0
  163. package/dist/lib/maths/guards.cjs +1 -0
  164. package/dist/lib/maths/guards.d.ts +166 -0
  165. package/dist/lib/maths/guards.d.ts.map +1 -0
  166. package/dist/lib/maths/guards.js +315 -0
  167. package/dist/lib/maths/guards.mjs +1 -0
  168. package/dist/lib/maths/index.cjs +1 -0
  169. package/dist/lib/maths/index.d.ts +24 -0
  170. package/dist/lib/maths/index.d.ts.map +1 -0
  171. package/dist/lib/maths/index.js +29 -0
  172. package/dist/lib/maths/index.mjs +1 -0
  173. package/dist/lib/maths/parser.cjs +1 -0
  174. package/dist/lib/maths/parser.d.ts +20 -0
  175. package/dist/lib/maths/parser.d.ts.map +1 -0
  176. package/dist/lib/maths/parser.js +78 -0
  177. package/dist/lib/maths/parser.mjs +1 -0
  178. package/dist/lib/maths/random.cjs +1 -0
  179. package/dist/lib/maths/random.d.ts +27 -0
  180. package/dist/lib/maths/random.d.ts.map +1 -0
  181. package/dist/lib/maths/random.js +49 -0
  182. package/dist/lib/maths/random.mjs +1 -0
  183. package/dist/lib/maths/types/advanced.cjs +1 -0
  184. package/dist/lib/maths/types/advanced.d.ts +357 -0
  185. package/dist/lib/maths/types/advanced.d.ts.map +1 -0
  186. package/dist/lib/maths/types/advanced.js +5 -0
  187. package/dist/lib/maths/types/advanced.mjs +0 -0
  188. package/dist/lib/maths/types/basic.cjs +1 -0
  189. package/dist/lib/maths/types/basic.d.ts +95 -0
  190. package/dist/lib/maths/types/basic.d.ts.map +1 -0
  191. package/dist/lib/maths/types/basic.js +5 -0
  192. package/dist/lib/maths/types/basic.mjs +0 -0
  193. package/dist/lib/maths/types/index.cjs +1 -0
  194. package/dist/lib/maths/types/index.d.ts +4 -0
  195. package/dist/lib/maths/types/index.d.ts.map +1 -0
  196. package/dist/lib/maths/types/index.js +3 -0
  197. package/dist/lib/maths/types/index.mjs +0 -0
  198. package/dist/lib/maths/types/negative.cjs +1 -0
  199. package/dist/lib/maths/types/negative.d.ts +51 -0
  200. package/dist/lib/maths/types/negative.d.ts.map +1 -0
  201. package/dist/lib/maths/types/negative.js +5 -0
  202. package/dist/lib/maths/types/negative.mjs +0 -0
  203. package/dist/lib/maths/types/operations.cjs +1 -0
  204. package/dist/lib/maths/types/operations.d.ts +188 -0
  205. package/dist/lib/maths/types/operations.d.ts.map +1 -0
  206. package/dist/lib/maths/types/operations.js +6 -0
  207. package/dist/lib/maths/types/operations.mjs +0 -0
  208. package/dist/lib/object/clone.cjs +1 -0
  209. package/dist/lib/object/clone.d.ts +46 -0
  210. package/dist/lib/object/clone.d.ts.map +1 -0
  211. package/dist/lib/object/clone.js +104 -0
  212. package/dist/lib/object/clone.mjs +1 -0
  213. package/dist/lib/object/diff.cjs +1 -0
  214. package/dist/lib/object/diff.d.ts +203 -0
  215. package/dist/lib/object/diff.d.ts.map +1 -0
  216. package/dist/lib/object/diff.js +805 -0
  217. package/dist/lib/object/diff.mjs +1 -0
  218. package/dist/lib/object/getter-setter.cjs +1 -0
  219. package/dist/lib/object/getter-setter.d.ts +361 -0
  220. package/dist/lib/object/getter-setter.d.ts.map +1 -0
  221. package/dist/lib/object/getter-setter.js +1771 -0
  222. package/dist/lib/object/getter-setter.mjs +1 -0
  223. package/dist/lib/object/index.cjs +6 -0
  224. package/dist/lib/object/index.d.ts +13 -0
  225. package/dist/lib/object/index.d.ts.map +1 -0
  226. package/dist/lib/object/index.js +12 -0
  227. package/dist/lib/object/index.mjs +6 -0
  228. package/dist/lib/object/merge.cjs +1 -0
  229. package/dist/lib/object/merge.d.ts +93 -0
  230. package/dist/lib/object/merge.d.ts.map +1 -0
  231. package/dist/lib/object/merge.js +474 -0
  232. package/dist/lib/object/merge.mjs +1 -0
  233. package/dist/lib/object/object.cjs +1 -0
  234. package/dist/lib/object/object.d.ts +128 -0
  235. package/dist/lib/object/object.d.ts.map +1 -0
  236. package/dist/lib/object/object.js +432 -0
  237. package/dist/lib/object/object.mjs +1 -0
  238. package/dist/lib/object/omit.cjs +1 -0
  239. package/dist/lib/object/omit.d.ts +351 -0
  240. package/dist/lib/object/omit.d.ts.map +1 -0
  241. package/dist/lib/object/omit.js +656 -0
  242. package/dist/lib/object/omit.mjs +1 -0
  243. package/dist/lib/object/reconcile.cjs +1 -0
  244. package/dist/lib/object/reconcile.d.ts +7 -0
  245. package/dist/lib/object/reconcile.d.ts.map +1 -0
  246. package/dist/lib/object/reconcile.js +122 -0
  247. package/dist/lib/object/reconcile.mjs +1 -0
  248. package/dist/lib/object/selector.cjs +1 -0
  249. package/dist/lib/object/selector.d.ts +50 -0
  250. package/dist/lib/object/selector.d.ts.map +1 -0
  251. package/dist/lib/object/selector.js +371 -0
  252. package/dist/lib/object/selector.mjs +1 -0
  253. package/dist/lib/object/sort.cjs +1 -0
  254. package/dist/lib/object/sort.d.ts +149 -0
  255. package/dist/lib/object/sort.d.ts.map +1 -0
  256. package/dist/lib/object/sort.js +298 -0
  257. package/dist/lib/object/sort.mjs +1 -0
  258. package/dist/lib/object/transform.cjs +1 -0
  259. package/dist/lib/object/transform.d.ts +620 -0
  260. package/dist/lib/object/transform.d.ts.map +1 -0
  261. package/dist/lib/object/transform.js +1058 -0
  262. package/dist/lib/object/transform.mjs +1 -0
  263. package/dist/lib/object/types.cjs +1 -0
  264. package/dist/lib/object/types.d.ts +250 -0
  265. package/dist/lib/object/types.d.ts.map +1 -0
  266. package/dist/lib/object/types.js +1 -0
  267. package/dist/lib/object/types.mjs +0 -0
  268. package/dist/lib/object/utils.cjs +6 -0
  269. package/dist/lib/object/utils.d.ts +165 -0
  270. package/dist/lib/object/utils.d.ts.map +1 -0
  271. package/dist/lib/object/utils.js +444 -0
  272. package/dist/lib/object/utils.mjs +6 -0
  273. package/dist/lib/primitive/boolean/boolean.cjs +1 -0
  274. package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
  275. package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
  276. package/dist/lib/primitive/boolean/boolean.js +615 -0
  277. package/dist/lib/primitive/boolean/boolean.mjs +1 -0
  278. package/dist/lib/primitive/boolean/index.cjs +1 -0
  279. package/dist/lib/primitive/boolean/index.d.ts +3 -0
  280. package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
  281. package/dist/lib/primitive/boolean/index.js +2 -0
  282. package/dist/lib/primitive/boolean/index.mjs +1 -0
  283. package/dist/lib/primitive/boolean/types.cjs +1 -0
  284. package/dist/lib/primitive/boolean/types.d.ts +14 -0
  285. package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
  286. package/dist/lib/primitive/boolean/types.js +1 -0
  287. package/dist/lib/primitive/boolean/types.mjs +0 -0
  288. package/dist/lib/primitive/date/date.cjs +1 -0
  289. package/dist/lib/primitive/date/date.d.ts +641 -0
  290. package/dist/lib/primitive/date/date.d.ts.map +1 -0
  291. package/dist/lib/primitive/date/date.js +1998 -0
  292. package/dist/lib/primitive/date/date.mjs +1 -0
  293. package/dist/lib/primitive/date/index.cjs +1 -0
  294. package/dist/lib/primitive/date/index.d.ts +3 -0
  295. package/dist/lib/primitive/date/index.d.ts.map +1 -0
  296. package/dist/lib/primitive/date/index.js +2 -0
  297. package/dist/lib/primitive/date/index.mjs +1 -0
  298. package/dist/lib/primitive/date/types.cjs +1 -0
  299. package/dist/lib/primitive/date/types.d.ts +205 -0
  300. package/dist/lib/primitive/date/types.d.ts.map +1 -0
  301. package/dist/lib/primitive/date/types.js +7 -0
  302. package/dist/lib/primitive/date/types.mjs +0 -0
  303. package/dist/lib/primitive/index.cjs +5 -0
  304. package/dist/lib/primitive/index.d.ts +5 -0
  305. package/dist/lib/primitive/index.d.ts.map +1 -0
  306. package/dist/lib/primitive/index.js +4 -0
  307. package/dist/lib/primitive/index.mjs +5 -0
  308. package/dist/lib/primitive/string/index.cjs +5 -0
  309. package/dist/lib/primitive/string/index.d.ts +3 -0
  310. package/dist/lib/primitive/string/index.d.ts.map +1 -0
  311. package/dist/lib/primitive/string/index.js +2 -0
  312. package/dist/lib/primitive/string/index.mjs +5 -0
  313. package/dist/lib/primitive/string/parser-2.cjs +8 -0
  314. package/dist/lib/primitive/string/parser-2.d.ts +17 -0
  315. package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
  316. package/dist/lib/primitive/string/parser-2.js +656 -0
  317. package/dist/lib/primitive/string/parser-2.mjs +8 -0
  318. package/dist/lib/primitive/string/parser.cjs +3 -0
  319. package/dist/lib/primitive/string/parser.d.ts +9 -0
  320. package/dist/lib/primitive/string/parser.d.ts.map +1 -0
  321. package/dist/lib/primitive/string/parser.js +775 -0
  322. package/dist/lib/primitive/string/parser.mjs +3 -0
  323. package/dist/lib/primitive/string/string.cjs +5 -0
  324. package/dist/lib/primitive/string/string.d.ts +535 -0
  325. package/dist/lib/primitive/string/string.d.ts.map +1 -0
  326. package/dist/lib/primitive/string/string.js +1301 -0
  327. package/dist/lib/primitive/string/string.mjs +5 -0
  328. package/dist/lib/primitive/string/types.cjs +1 -0
  329. package/dist/lib/primitive/string/types.d.ts +318 -0
  330. package/dist/lib/primitive/string/types.d.ts.map +1 -0
  331. package/dist/lib/primitive/string/types.js +5 -0
  332. package/dist/lib/primitive/string/types.mjs +0 -0
  333. package/dist/lib/primitive/types.cjs +1 -0
  334. package/dist/lib/primitive/types.d.ts +27 -0
  335. package/dist/lib/primitive/types.d.ts.map +1 -0
  336. package/dist/lib/primitive/types.js +1 -0
  337. package/dist/lib/primitive/types.mjs +0 -0
  338. package/dist/lib/url/index.cjs +1 -0
  339. package/dist/lib/url/index.d.ts +3 -0
  340. package/dist/lib/url/index.d.ts.map +1 -0
  341. package/dist/lib/url/index.js +2 -0
  342. package/dist/lib/url/index.mjs +1 -0
  343. package/dist/lib/url/parser.cjs +1 -0
  344. package/dist/lib/url/parser.d.ts +216 -0
  345. package/dist/lib/url/parser.d.ts.map +1 -0
  346. package/dist/lib/url/parser.js +488 -0
  347. package/dist/lib/url/parser.mjs +1 -0
  348. package/dist/lib/url/types.cjs +1 -0
  349. package/dist/lib/url/types.d.ts +18 -0
  350. package/dist/lib/url/types.d.ts.map +1 -0
  351. package/dist/lib/url/types.js +1 -0
  352. package/dist/lib/url/types.mjs +0 -0
  353. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  354. package/package.json +150 -0
@@ -0,0 +1,1998 @@
1
+ // Constants
2
+ const MILLISECONDS_IN_SECOND = 1000;
3
+ const SECONDS_IN_MINUTE = 60;
4
+ const MINUTES_IN_HOUR = 60;
5
+ const HOURS_IN_DAY = 24;
6
+ const DAYS_IN_WEEK = 7;
7
+ const MONTHS_IN_YEAR = 12;
8
+ const QUARTERS_IN_YEAR = 4;
9
+ const WEEKDAY_NAMES = [
10
+ 'Sunday',
11
+ 'Monday',
12
+ 'Tuesday',
13
+ 'Wednesday',
14
+ 'Thursday',
15
+ 'Friday',
16
+ 'Saturday',
17
+ ];
18
+ const WEEKDAY_NAMES_SHORT = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
19
+ const MONTH_NAMES = [
20
+ 'January',
21
+ 'February',
22
+ 'March',
23
+ 'April',
24
+ 'May',
25
+ 'June',
26
+ 'July',
27
+ 'August',
28
+ 'September',
29
+ 'October',
30
+ 'November',
31
+ 'December',
32
+ ];
33
+ const MONTH_NAMES_SHORT = [
34
+ 'Jan',
35
+ 'Feb',
36
+ 'Mar',
37
+ 'Apr',
38
+ 'May',
39
+ 'Jun',
40
+ 'Jul',
41
+ 'Aug',
42
+ 'Sep',
43
+ 'Oct',
44
+ 'Nov',
45
+ 'Dec',
46
+ ];
47
+ // Default configuration
48
+ const DEFAULT_CONFIG = {
49
+ defaultTimezone: 'UTC',
50
+ defaultLocale: 'en-US',
51
+ weekStartsOn: 0, // Sunday
52
+ businessDays: {
53
+ businessDays: [1, 2, 3, 4, 5], // Monday to Friday
54
+ holidays: [],
55
+ holidayCheck: () => false,
56
+ workStartHour: 9,
57
+ workEndHour: 17,
58
+ },
59
+ };
60
+ let config = { ...DEFAULT_CONFIG };
61
+ /**
62
+ * Configure date utility settings globally
63
+ * @function configureDateUtils
64
+ * @param {Partial<DateConfig>} newConfig - New configuration options
65
+ * @returns {void}
66
+ * @example
67
+ * // Configure with French locale and week starting on Monday
68
+ * configureDateUtils({
69
+ * defaultLocale: 'fr-FR',
70
+ * weekStartsOn: 1,
71
+ * });
72
+ */
73
+ export function configureDateUtils(newConfig) {
74
+ config = {
75
+ ...config,
76
+ ...newConfig,
77
+ businessDays: {
78
+ ...config.businessDays,
79
+ ...(newConfig.businessDays || {}),
80
+ },
81
+ };
82
+ }
83
+ /**
84
+ * Reset configuration to defaults
85
+ * @function resetDateConfig
86
+ * @returns {void}
87
+ * @example
88
+ * resetDateConfig(); // Reset all settings to defaults
89
+ */
90
+ export function resetDateConfig() {
91
+ config = { ...DEFAULT_CONFIG };
92
+ }
93
+ /**
94
+ * Parse a date from various input types with flexible formatting options
95
+ * @function parseDate
96
+ * @param {Date | string | number} input - Date to parse (Date object, string, or timestamp)
97
+ * @param {ParseOptions} [options={}] - Parsing options
98
+ * @returns {Date} Parsed Date object
99
+ * @throws {Error} If date cannot be parsed and strict mode is enabled
100
+ * @example
101
+ * // Parse ISO date
102
+ * parseDate('2024-01-15T10:30:00Z');
103
+ *
104
+ * // Parse US date
105
+ * parseDate('01/15/2024', { format: 'US' });
106
+ *
107
+ * // Parse with custom format
108
+ * parseDate('2024-15-01', { format: 'CUSTOM', customFormat: 'YYYY-DD-MM' });
109
+ */
110
+ export function parseDate(input, options = {}) {
111
+ const { format = 'ISO', customFormat = '', timezone = config.defaultTimezone, strict = true, locale = config.defaultLocale, } = options;
112
+ // If already a valid Date, return it
113
+ if (input instanceof Date && isDate(input)) {
114
+ return input;
115
+ }
116
+ let parsedDate = null;
117
+ // Handle number input (timestamp)
118
+ if (typeof input === 'number') {
119
+ parsedDate = new Date(input);
120
+ if (!isDate(parsedDate)) {
121
+ throw new Error(`Invalid timestamp: ${input}`);
122
+ }
123
+ return applyTimezone(parsedDate, timezone);
124
+ }
125
+ // Handle string input
126
+ if (typeof input !== 'string') {
127
+ throw new Error(`Invalid input type: ${typeof input}`);
128
+ }
129
+ const trimmedInput = input.trim();
130
+ if (!trimmedInput) {
131
+ throw new Error('Empty date string');
132
+ }
133
+ try {
134
+ switch (format) {
135
+ case 'ISO':
136
+ case 'ISO_DATE':
137
+ case 'ISO_TIME':
138
+ parsedDate = parseISODate(trimmedInput, format);
139
+ break;
140
+ case 'US':
141
+ parsedDate = parseUSDate(trimmedInput);
142
+ break;
143
+ case 'EU':
144
+ parsedDate = parseEUDate(trimmedInput);
145
+ break;
146
+ case 'CUSTOM':
147
+ parsedDate = parseCustomDate(trimmedInput, customFormat);
148
+ break;
149
+ case 'RFC_2822':
150
+ parsedDate = parseRFC2822Date(trimmedInput);
151
+ break;
152
+ case 'TIMESTAMP':
153
+ parsedDate = parseTimestamp(trimmedInput);
154
+ break;
155
+ default:
156
+ // Try locale-aware parsing as fallback
157
+ parsedDate = parseLocaleDate(trimmedInput, locale, strict);
158
+ }
159
+ if (!parsedDate || !isDate(parsedDate)) {
160
+ if (strict) {
161
+ throw new Error(`Parsing resulted in invalid date from input: "${trimmedInput}"`);
162
+ }
163
+ // In non-strict mode, try generic parsing
164
+ parsedDate = new Date(trimmedInput);
165
+ if (!isDate(parsedDate)) {
166
+ throw new Error(`Invalid date format: ${trimmedInput}`);
167
+ }
168
+ }
169
+ // Apply timezone if specified
170
+ if (timezone && timezone !== 'UTC') {
171
+ parsedDate = convertTimezone(parsedDate, timezone);
172
+ }
173
+ if (!parsedDate) {
174
+ throw new Error(`Invalid date format: ${trimmedInput}`);
175
+ }
176
+ return parsedDate;
177
+ }
178
+ catch (error) {
179
+ if (strict) {
180
+ throw new Error(`Failed to parse date "${trimmedInput}" with format ${format}: ${error instanceof Error ? error.message : String(error)}`);
181
+ }
182
+ // Fallback to Date constructor in non-strict mode
183
+ const fallbackDate = new Date(trimmedInput);
184
+ if (isDate(fallbackDate)) {
185
+ const t = applyTimezone(fallbackDate, timezone);
186
+ if (!t) {
187
+ throw new Error(`Invalid date format: ${trimmedInput}`);
188
+ }
189
+ return t;
190
+ }
191
+ throw new Error(`Invalid date format: ${trimmedInput}`);
192
+ }
193
+ }
194
+ // ============================================================================
195
+ // Fixed Internal Helper Functions
196
+ // ============================================================================
197
+ /**
198
+ * Parse ISO date string
199
+ * @private
200
+ * @param {string} input - ISO date string
201
+ * @param {DateFormat} format - ISO format type
202
+ * @returns {Date} Parsed date
203
+ */
204
+ function parseISODate(input, format) {
205
+ try {
206
+ if (format === 'ISO_DATE') {
207
+ // ISO date without time: YYYY-MM-DD
208
+ // Ensure we have a complete date
209
+ const parts = input.split('-').map((part) => parseInt(part, 10));
210
+ if (parts.length < 3 || parts.some(isNaN)) {
211
+ throw new Error('Invalid ISO date format');
212
+ }
213
+ // Create date at midnight UTC
214
+ const [year, month, day] = parts;
215
+ return new Date(Date.UTC(year, month - 1, day));
216
+ }
217
+ if (format === 'ISO_TIME') {
218
+ // ISO time only: HH:MM:SS.mmm
219
+ const today = new Date();
220
+ const datePart = today.toISOString().split('T')[0];
221
+ return new Date(`${datePart}T${input}Z`);
222
+ }
223
+ // Full ISO format
224
+ const date = new Date(input);
225
+ if (!isDate(date)) {
226
+ throw new Error('Invalid ISO date');
227
+ }
228
+ return date;
229
+ }
230
+ catch {
231
+ throw new Error(`Invalid ISO ${format} format: ${input}`);
232
+ }
233
+ }
234
+ /**
235
+ * Parse US date string (MM/DD/YYYY or MM/DD/YY)
236
+ * @private
237
+ * @param {string} input - US date string
238
+ * @returns {Date} Parsed date
239
+ */
240
+ function parseUSDate(input) {
241
+ try {
242
+ // Handle various US date formats: MM/DD/YYYY, MM/DD/YY, M/D/YYYY, etc.
243
+ const parts = input.split('/');
244
+ if (parts.length !== 3) {
245
+ throw new Error('Invalid US date format - expected MM/DD/YYYY');
246
+ }
247
+ let month = parseInt(parts[0], 10) - 1; // Convert to 0-based
248
+ let day = parseInt(parts[1], 10);
249
+ let year = parseInt(parts[2], 10);
250
+ if (isNaN(month) || isNaN(day) || isNaN(year)) {
251
+ throw new Error('Invalid US date format - non-numeric parts');
252
+ }
253
+ // Handle 2-digit years
254
+ if (year < 100) {
255
+ const currentYear = new Date().getFullYear();
256
+ const century = Math.floor(currentYear / 100) * 100;
257
+ year += year < 50 ? century : century - 100;
258
+ }
259
+ // Validate date parts
260
+ if (month < 0 || month > 11) {
261
+ throw new Error(`Month must be between 1 and 12, got ${month + 1}`);
262
+ }
263
+ const date = new Date(year, month, day);
264
+ if (!isDate(date)) {
265
+ throw new Error('Invalid date');
266
+ }
267
+ // Check if parsed date matches input (handles invalid dates like February 30)
268
+ if (date.getFullYear() !== year ||
269
+ date.getMonth() !== month ||
270
+ date.getDate() !== day) {
271
+ throw new Error('Invalid date');
272
+ }
273
+ return date;
274
+ }
275
+ catch (error) {
276
+ throw new Error(`Invalid US date format: ${input} - ${error instanceof Error ? error.message : String(error)}`);
277
+ }
278
+ }
279
+ /**
280
+ * Parse EU date string (DD/MM/YYYY or DD/MM/YY)
281
+ * @private
282
+ * @param {string} input - EU date string
283
+ * @returns {Date} Parsed date
284
+ */
285
+ function parseEUDate(input) {
286
+ try {
287
+ // Handle various EU date formats: DD/MM/YYYY, DD/MM/YY, D/M/YYYY, etc.
288
+ const parts = input.split('/');
289
+ if (parts.length !== 3) {
290
+ throw new Error('Invalid EU date format - expected DD/MM/YYYY');
291
+ }
292
+ let day = parseInt(parts[0], 10);
293
+ let month = parseInt(parts[1], 10) - 1; // Convert to 0-based
294
+ let year = parseInt(parts[2], 10);
295
+ if (isNaN(day) || isNaN(month) || isNaN(year)) {
296
+ throw new Error('Invalid EU date format - non-numeric parts');
297
+ }
298
+ // Handle 2-digit years
299
+ if (year < 100) {
300
+ const currentYear = new Date().getFullYear();
301
+ const century = Math.floor(currentYear / 100) * 100;
302
+ year += year < 50 ? century : century - 100;
303
+ }
304
+ // Validate date parts
305
+ if (month < 0 || month > 11) {
306
+ throw new Error(`Month must be between 1 and 12, got ${month + 1}`);
307
+ }
308
+ const date = new Date(year, month, day);
309
+ if (!isDate(date)) {
310
+ throw new Error('Invalid date');
311
+ }
312
+ // Check if parsed date matches input
313
+ if (date.getFullYear() !== year ||
314
+ date.getMonth() !== month ||
315
+ date.getDate() !== day) {
316
+ throw new Error('Invalid date');
317
+ }
318
+ return date;
319
+ }
320
+ catch (error) {
321
+ throw new Error(`Invalid EU date format: ${input} - ${error instanceof Error ? error.message : String(error)}`);
322
+ }
323
+ }
324
+ /**
325
+ * Parse date with custom format string
326
+ * @private
327
+ * @param {string} input - Date string
328
+ * @param {string} format - Custom format string
329
+ * @returns {Date} Parsed date
330
+ */
331
+ function parseCustomDate(input, format) {
332
+ try {
333
+ if (!format) {
334
+ throw new Error('Custom format string is required');
335
+ }
336
+ // Simple custom format parsing
337
+ // Supported tokens: YYYY, YY, MM, M, DD, D, HH, H, hh, h, mm, m, ss, s
338
+ const formatPattern = format
339
+ .replace(/YYYY/g, '(\\d{4})')
340
+ .replace(/YY/g, '(\\d{2})')
341
+ .replace(/MM/g, '(\\d{2})')
342
+ .replace(/M/g, '(\\d{1,2})')
343
+ .replace(/DD/g, '(\\d{2})')
344
+ .replace(/D/g, '(\\d{1,2})')
345
+ .replace(/HH/g, '(\\d{2})')
346
+ .replace(/H/g, '(\\d{1,2})')
347
+ .replace(/hh/g, '(\\d{2})')
348
+ .replace(/h/g, '(\\d{1,2})')
349
+ .replace(/mm/g, '(\\d{2})')
350
+ .replace(/m/g, '(\\d{1,2})')
351
+ .replace(/ss/g, '(\\d{2})')
352
+ .replace(/s/g, '(\\d{1,2})');
353
+ const regex = new RegExp(`^${formatPattern}$`);
354
+ const match = input.match(regex);
355
+ if (!match) {
356
+ throw new Error(`Input does not match custom format: ${format}`);
357
+ }
358
+ // Extract values based on format
359
+ let year = 0, month = 0, day = 1, hour = 0, minute = 0, second = 0;
360
+ let matchIndex = 1;
361
+ const formatTokens = format.match(/YYYY|YY|MM|M|DD|D|HH|H|hh|h|mm|m|ss|s/g) || [];
362
+ for (const token of formatTokens) {
363
+ const value = parseInt(match[matchIndex], 10);
364
+ if (isNaN(value)) {
365
+ throw new Error(`Invalid numeric value for token ${token}`);
366
+ }
367
+ switch (token) {
368
+ case 'YYYY':
369
+ year = value;
370
+ break;
371
+ case 'YY':
372
+ year = value;
373
+ // Handle 2-digit years
374
+ if (year < 100) {
375
+ const currentYear = new Date().getFullYear();
376
+ const century = Math.floor(currentYear / 100) * 100;
377
+ year += year < 50 ? century : century - 100;
378
+ }
379
+ break;
380
+ case 'MM':
381
+ case 'M':
382
+ month = value - 1; // Convert to 0-based
383
+ break;
384
+ case 'DD':
385
+ case 'D':
386
+ day = value;
387
+ break;
388
+ case 'HH':
389
+ case 'H':
390
+ hour = value;
391
+ break;
392
+ case 'hh':
393
+ case 'h':
394
+ hour = value;
395
+ // Handle 12-hour format (needs AM/PM indicator - not implemented)
396
+ break;
397
+ case 'mm':
398
+ case 'm':
399
+ minute = value;
400
+ break;
401
+ case 'ss':
402
+ case 's':
403
+ second = value;
404
+ break;
405
+ }
406
+ matchIndex++;
407
+ }
408
+ // Validate date parts
409
+ if (year < 1000 || year > 9999) {
410
+ throw new Error(`Year must be between 1000 and 9999: ${year}`);
411
+ }
412
+ if (month < 0 || month > 11) {
413
+ throw new Error(`Month must be between 1 and 12: ${month + 1}`);
414
+ }
415
+ const date = new Date(year, month, day, hour, minute, second);
416
+ if (!isDate(date)) {
417
+ throw new Error('Invalid date');
418
+ }
419
+ // Check if parsed date matches input
420
+ if (date.getFullYear() !== year ||
421
+ date.getMonth() !== month ||
422
+ date.getDate() !== day) {
423
+ throw new Error('Invalid date');
424
+ }
425
+ return date;
426
+ }
427
+ catch (error) {
428
+ throw new Error(`Failed to parse with custom format "${format}": ${error instanceof Error ? error.message : String(error)}`);
429
+ }
430
+ }
431
+ /**
432
+ * Parse RFC 2822 date string
433
+ * @private
434
+ * @param {string} input - RFC 2822 date string
435
+ * @returns {Date} Parsed date
436
+ */
437
+ function parseRFC2822Date(input) {
438
+ try {
439
+ const date = new Date(input);
440
+ if (!isDate(date)) {
441
+ throw new Error('Invalid RFC 2822 date');
442
+ }
443
+ return date;
444
+ }
445
+ catch {
446
+ throw new Error(`Invalid RFC 2822 date format: ${input}`);
447
+ }
448
+ }
449
+ /**
450
+ * Parse timestamp string (YYYYMMDDHHMMSS or YYYYMMDD)
451
+ * @private
452
+ * @param {string} input - Timestamp string
453
+ * @returns {Date} Parsed date
454
+ */
455
+ function parseTimestamp(input) {
456
+ try {
457
+ // Handle YYYYMMDDHHMMSS or YYYYMMDD
458
+ if (!/^\d{8}(\d{6})?$/.test(input)) {
459
+ throw new Error('Invalid timestamp format - expected YYYYMMDD or YYYYMMDDHHMMSS');
460
+ }
461
+ const year = parseInt(input.substring(0, 4), 10);
462
+ const month = parseInt(input.substring(4, 6), 10) - 1;
463
+ const day = parseInt(input.substring(6, 8), 10);
464
+ let hour = 0, minute = 0, second = 0;
465
+ if (input.length === 14) {
466
+ hour = parseInt(input.substring(8, 10), 10);
467
+ minute = parseInt(input.substring(10, 12), 10);
468
+ second = parseInt(input.substring(12, 14), 10);
469
+ }
470
+ // Validate date parts
471
+ if (month < 0 || month > 11) {
472
+ throw new Error(`Month must be between 1 and 12: ${month + 1}`);
473
+ }
474
+ const date = new Date(year, month, day, hour, minute, second);
475
+ if (!isDate(date)) {
476
+ throw new Error('Invalid timestamp');
477
+ }
478
+ return date;
479
+ }
480
+ catch (error) {
481
+ throw new Error(`Invalid timestamp format: ${input} - ${error instanceof Error ? error.message : String(error)}`);
482
+ }
483
+ }
484
+ /**
485
+ * Parse date using locale-specific formatting
486
+ * @private
487
+ * @param {string} input - Date string
488
+ * @param {string} locale - Locale to use for parsing
489
+ * @param {boolean} strict - Whether to throw errors
490
+ * @returns {Date} Parsed date
491
+ */
492
+ function parseLocaleDate(input, locale, strict) {
493
+ try {
494
+ // Try to parse with Date constructor first (handles many common formats)
495
+ const date = new Date(input);
496
+ if (isDate(date)) {
497
+ return date;
498
+ }
499
+ // Try using Intl.DateTimeFormat for locale-aware parsing
500
+ const formatter = new Intl.DateTimeFormat(locale, {
501
+ year: 'numeric',
502
+ month: 'numeric',
503
+ day: 'numeric',
504
+ hour: 'numeric',
505
+ minute: 'numeric',
506
+ second: 'numeric',
507
+ });
508
+ // This is a simplification - in practice, you'd need a proper parser
509
+ // For now, fall back to Date constructor
510
+ const fallback = new Date(input);
511
+ if (isDate(fallback)) {
512
+ return fallback;
513
+ }
514
+ if (strict) {
515
+ throw new Error(`Could not parse date with locale ${locale}`);
516
+ }
517
+ return new Date(NaN); // Return invalid date
518
+ }
519
+ catch (error) {
520
+ if (strict) {
521
+ throw new Error(`Failed to parse locale date: ${error instanceof Error ? error.message : String(error)}`);
522
+ }
523
+ return new Date(NaN);
524
+ }
525
+ }
526
+ /**
527
+ * Apply timezone to a date
528
+ * @private
529
+ * @param {Date} date - Date to adjust
530
+ * @param {string} timezone - Target timezone
531
+ * @returns {Date} Date with timezone applied
532
+ */
533
+ function applyTimezone(date, timezone) {
534
+ if (!timezone || timezone === 'UTC') {
535
+ return date;
536
+ }
537
+ // For now, return the date as-is
538
+ // In a production environment, you'd use a library like date-fns-tz or moment-timezone
539
+ return date;
540
+ }
541
+ /**
542
+ * Validate if a value is a valid Date object
543
+ * @function isDate
544
+ * @param {unknown} date - Value to validate
545
+ * @returns {date is Date} True if value is a valid Date object
546
+ * @example
547
+ * isDate(new Date()); // true
548
+ * isDate('invalid'); // false
549
+ * isDate(new Date('invalid')); // false
550
+ */
551
+ export function isDate(date) {
552
+ // More robust validation that doesn't rely solely on instanceof
553
+ if (!date || typeof date !== 'object') {
554
+ return false;
555
+ }
556
+ // Check if it has Date-like properties
557
+ const d = date;
558
+ if (typeof d.getTime !== 'function' || typeof d.getFullYear !== 'function') {
559
+ return false;
560
+ }
561
+ // Check if the internal time value is valid
562
+ const time = d.getTime();
563
+ return !isNaN(time) && typeof time === 'number' && isFinite(time);
564
+ }
565
+ /**
566
+ * Format a date to string with various formatting options
567
+ * @function formatDate
568
+ * @param {Date | string | number} date - Date to format
569
+ * @param {FormatOptions} [options={}] - Formatting options
570
+ * @returns {string} Formatted date string
571
+ * @example
572
+ * // Format as ISO
573
+ * formatDate(new Date(), { format: 'ISO' });
574
+ *
575
+ * // Format as US date
576
+ * formatDate('2024-01-15', { format: 'US' });
577
+ *
578
+ * // Format with custom format
579
+ * formatDate(new Date(), { format: 'CUSTOM', customFormat: 'YYYY-MM-DD HH:mm:ss' });
580
+ */
581
+ export function formatDate(date, options = {}) {
582
+ const parsedDate = parseDate(date);
583
+ const { format = 'ISO', customFormat = '', timezone = config.defaultTimezone, locale = config.defaultLocale, includeTime = true, includeSeconds = false, includeMilliseconds = false, } = options;
584
+ // Convert to target timezone if needed
585
+ let targetDate = parsedDate;
586
+ if (timezone && timezone !== 'UTC') {
587
+ targetDate = convertTimezone(parsedDate, timezone);
588
+ }
589
+ switch (format) {
590
+ case 'ISO':
591
+ return targetDate.toISOString();
592
+ case 'ISO_DATE':
593
+ return targetDate.toISOString().split('T')[0];
594
+ case 'ISO_TIME': {
595
+ const timePart = targetDate.toISOString().split('T')[1];
596
+ return includeMilliseconds ? timePart : timePart.split('.')[0];
597
+ }
598
+ case 'US':
599
+ return formatUSDate(targetDate);
600
+ case 'EU':
601
+ return formatEUDate(targetDate);
602
+ case 'FULL':
603
+ return formatFullDate(targetDate, locale);
604
+ case 'LONG':
605
+ return formatLongDate(targetDate, locale);
606
+ case 'MEDIUM':
607
+ return formatMediumDate(targetDate, locale);
608
+ case 'SHORT':
609
+ return formatShortDate(targetDate, locale);
610
+ case 'TIME':
611
+ return formatTime(targetDate, locale, includeSeconds);
612
+ case 'TIME_24':
613
+ return formatTime24(targetDate, includeSeconds);
614
+ case 'DATETIME':
615
+ return `${formatMediumDate(targetDate, locale)}, ${formatTime(targetDate, locale, includeSeconds)}`;
616
+ case 'TIMESTAMP':
617
+ return formatTimestamp(targetDate);
618
+ case 'RFC_2822':
619
+ return formatRFC2822(targetDate);
620
+ case 'CUSTOM':
621
+ return formatCustomDate(targetDate, customFormat);
622
+ default:
623
+ return targetDate.toISOString();
624
+ }
625
+ }
626
+ /**
627
+ * Get current date and time
628
+ * @function now
629
+ * @returns {Date} Current date and time
630
+ * @example
631
+ * const current = now(); // Returns current Date object
632
+ */
633
+ export function now() {
634
+ return new Date();
635
+ }
636
+ /**
637
+ * Get today's date at midnight (00:00:00.000)
638
+ * @function today
639
+ * @returns {Date} Today's date at midnight
640
+ * @example
641
+ * const todayDate = today(); // Returns today at midnight
642
+ */
643
+ export function today() {
644
+ return startOfDay(new Date());
645
+ }
646
+ /**
647
+ * Get tomorrow's date at midnight
648
+ * @function tomorrow
649
+ * @returns {Date} Tomorrow's date at midnight
650
+ * @example
651
+ * const tomorrowDate = tomorrow(); // Returns tomorrow at midnight
652
+ */
653
+ export function tomorrow() {
654
+ return addDays(today(), 1);
655
+ }
656
+ /**
657
+ * Get yesterday's date at midnight
658
+ * @function yesterday
659
+ * @returns {Date} Yesterday's date at midnight
660
+ * @example
661
+ * const yesterdayDate = yesterday(); // Returns yesterday at midnight
662
+ */
663
+ export function yesterday() {
664
+ return subtractDays(today(), 1);
665
+ }
666
+ /**
667
+ * Create a date from individual components
668
+ * @function createDate
669
+ * @param {number} year - Year (e.g., 2024)
670
+ * @param {Month} month - Month (0-11, where 0 = January)
671
+ * @param {number} day - Day of month (1-31)
672
+ * @param {number} [hour=0] - Hour (0-23)
673
+ * @param {number} [minute=0] - Minute (0-59)
674
+ * @param {number} [second=0] - Second (0-59)
675
+ * @param {number} [millisecond=0] - Millisecond (0-999)
676
+ * @returns {Date} Created Date object
677
+ * @throws {Error} If any component is out of valid range
678
+ * @example
679
+ * // Create date for January 15, 2024, 10:30:45
680
+ * createDate(2024, 0, 15, 10, 30, 45);
681
+ */
682
+ export function createDate(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
683
+ validateDateParts(year, month, day, hour, minute, second, millisecond);
684
+ return new Date(year, month, day, hour, minute, second, millisecond);
685
+ }
686
+ /**
687
+ * Add time to a date
688
+ * @function add
689
+ * @param {Date | string | number} date - Base date
690
+ * @param {number} amount - Amount to add (can be negative to subtract)
691
+ * @param {DateUnit} [unit='day'] - Unit of time to add
692
+ * @returns {Date} New date after addition
693
+ * @example
694
+ * // Add 5 days
695
+ * add(new Date(), 5, 'day');
696
+ *
697
+ * // Add 2 months
698
+ * add('2024-01-15', 2, 'month');
699
+ *
700
+ * // Subtract 3 hours
701
+ * add(new Date(), -3, 'hour');
702
+ */
703
+ export function add(date, amount, unit = 'day') {
704
+ const parsedDate = parseDate(date);
705
+ const result = new Date(parsedDate);
706
+ switch (unit) {
707
+ case 'millisecond':
708
+ result.setMilliseconds(result.getMilliseconds() + amount);
709
+ break;
710
+ case 'second':
711
+ result.setSeconds(result.getSeconds() + amount);
712
+ break;
713
+ case 'minute':
714
+ result.setMinutes(result.getMinutes() + amount);
715
+ break;
716
+ case 'hour':
717
+ result.setHours(result.getHours() + amount);
718
+ break;
719
+ case 'day':
720
+ result.setDate(result.getDate() + amount);
721
+ break;
722
+ case 'week':
723
+ result.setDate(result.getDate() + amount * DAYS_IN_WEEK);
724
+ break;
725
+ case 'month':
726
+ result.setMonth(result.getMonth() + amount);
727
+ break;
728
+ case 'quarter':
729
+ result.setMonth(result.getMonth() + amount * 3);
730
+ break;
731
+ case 'year':
732
+ result.setFullYear(result.getFullYear() + amount);
733
+ break;
734
+ default:
735
+ throw new Error(`Unsupported date unit: ${unit}`);
736
+ }
737
+ return result;
738
+ }
739
+ /**
740
+ * Subtract time from a date
741
+ * @function subtract
742
+ * @param {Date | string | number} date - Base date
743
+ * @param {number} amount - Amount to subtract (can be negative to add)
744
+ * @param {DateUnit} [unit='day'] - Unit of time to subtract
745
+ * @returns {Date} New date after subtraction
746
+ * @example
747
+ * // Subtract 5 days
748
+ * subtract(new Date(), 5, 'day');
749
+ *
750
+ * // Subtract 2 months
751
+ * subtract('2024-01-15', 2, 'month');
752
+ */
753
+ export function subtract(date, amount, unit = 'day') {
754
+ return add(date, -amount, unit);
755
+ }
756
+ /**
757
+ * Add days to a date
758
+ * @function addDays
759
+ * @param {Date | string | number} date - Base date
760
+ * @param {number} days - Number of days to add (can be negative)
761
+ * @returns {Date} New date after adding days
762
+ * @example
763
+ * // Add 5 days
764
+ * addDays(new Date(), 5);
765
+ *
766
+ * // Subtract 3 days
767
+ * addDays('2024-01-15', -3);
768
+ */
769
+ export function addDays(date, days) {
770
+ return add(date, days, 'day');
771
+ }
772
+ /**
773
+ * Subtract days from a date
774
+ * @function subtractDays
775
+ * @param {Date | string | number} date - Base date
776
+ * @param {number} days - Number of days to subtract
777
+ * @returns {Date} New date after subtracting days
778
+ * @example
779
+ * // Subtract 5 days
780
+ * subtractDays(new Date(), 5);
781
+ */
782
+ export function subtractDays(date, days) {
783
+ return subtract(date, days, 'day');
784
+ }
785
+ /**
786
+ * Add months to a date
787
+ * @function addMonths
788
+ * @param {Date | string | number} date - Base date
789
+ * @param {number} months - Number of months to add (can be negative)
790
+ * @returns {Date} New date after adding months
791
+ * @example
792
+ * // Add 2 months
793
+ * addMonths(new Date(), 2);
794
+ *
795
+ * // Subtract 3 months
796
+ * addMonths('2024-01-15', -3);
797
+ */
798
+ export function addMonths(date, months) {
799
+ return add(date, months, 'month');
800
+ }
801
+ /**
802
+ * Subtract months from a date
803
+ * @function subtractMonths
804
+ * @param {Date | string | number} date - Base date
805
+ * @param {number} months - Number of months to subtract
806
+ * @returns {Date} New date after subtracting months
807
+ * @example
808
+ * // Subtract 2 months
809
+ * subtractMonths(new Date(), 2);
810
+ */
811
+ export function subtractMonths(date, months) {
812
+ return subtract(date, months, 'month');
813
+ }
814
+ /**
815
+ * Add years to a date
816
+ * @function addYears
817
+ * @param {Date | string | number} date - Base date
818
+ * @param {number} years - Number of years to add (can be negative)
819
+ * @returns {Date} New date after adding years
820
+ * @example
821
+ * // Add 1 year
822
+ * addYears(new Date(), 1);
823
+ *
824
+ * // Subtract 5 years
825
+ * addYears('2024-01-15', -5);
826
+ */
827
+ export function addYears(date, years) {
828
+ return add(date, years, 'year');
829
+ }
830
+ /**
831
+ * Subtract years from a date
832
+ * @function subtractYears
833
+ * @param {Date | string | number} date - Base date
834
+ * @param {number} years - Number of years to subtract
835
+ * @returns {Date} New date after subtracting years
836
+ * @example
837
+ * // Subtract 10 years
838
+ * subtractYears(new Date(), 10);
839
+ */
840
+ export function subtractYears(date, years) {
841
+ return subtract(date, years, 'year');
842
+ }
843
+ /**
844
+ * Get the start of a time unit
845
+ * @function startOf
846
+ * @param {Date | string | number} date - Base date
847
+ * @param {DateUnit} unit - Time unit to get start of
848
+ * @returns {Date} Date representing the start of the specified unit
849
+ * @example
850
+ * // Start of month
851
+ * startOf(new Date(), 'month');
852
+ *
853
+ * // Start of year
854
+ * startOf('2024-06-15', 'year');
855
+ *
856
+ * // Start of week (configurable via weekStartsOn)
857
+ * startOf(new Date(), 'week');
858
+ */
859
+ export function startOf(date, unit) {
860
+ const parsedDate = parseDate(date);
861
+ const result = new Date(parsedDate);
862
+ switch (unit) {
863
+ case 'year':
864
+ result.setMonth(0, 1);
865
+ result.setHours(0, 0, 0, 0);
866
+ break;
867
+ case 'month':
868
+ result.setDate(1);
869
+ result.setHours(0, 0, 0, 0);
870
+ break;
871
+ case 'day':
872
+ result.setHours(0, 0, 0, 0);
873
+ break;
874
+ case 'hour':
875
+ result.setMinutes(0, 0, 0);
876
+ break;
877
+ case 'minute':
878
+ result.setSeconds(0, 0);
879
+ break;
880
+ case 'second':
881
+ result.setMilliseconds(0);
882
+ break;
883
+ case 'week': {
884
+ const dayOfWeek = result.getDay();
885
+ const diff = (dayOfWeek - config.weekStartsOn + DAYS_IN_WEEK) % DAYS_IN_WEEK;
886
+ result.setDate(result.getDate() - diff);
887
+ result.setHours(0, 0, 0, 0);
888
+ break;
889
+ }
890
+ case 'quarter': {
891
+ const quarter = Math.floor(result.getMonth() / 3);
892
+ result.setMonth(quarter * 3, 1);
893
+ result.setHours(0, 0, 0, 0);
894
+ break;
895
+ }
896
+ default:
897
+ throw new Error(`Unsupported unit for startOf: ${unit}`);
898
+ }
899
+ return result;
900
+ }
901
+ /**
902
+ * Get the end of a time unit
903
+ * @function endOf
904
+ * @param {Date | string | number} date - Base date
905
+ * @param {DateUnit} unit - Time unit to get end of
906
+ * @returns {Date} Date representing the end of the specified unit
907
+ * @example
908
+ * // End of month
909
+ * endOf(new Date(), 'month');
910
+ *
911
+ * // End of day (23:59:59.999)
912
+ * endOf('2024-01-15', 'day');
913
+ */
914
+ export function endOf(date, unit) {
915
+ const start = startOf(date, unit);
916
+ const result = new Date(start);
917
+ switch (unit) {
918
+ case 'year':
919
+ result.setFullYear(result.getFullYear() + 1);
920
+ break;
921
+ case 'quarter':
922
+ result.setMonth(result.getMonth() + 3);
923
+ break;
924
+ case 'month':
925
+ result.setMonth(result.getMonth() + 1);
926
+ break;
927
+ case 'week':
928
+ result.setDate(result.getDate() + DAYS_IN_WEEK);
929
+ break;
930
+ case 'day':
931
+ result.setDate(result.getDate() + 1);
932
+ break;
933
+ case 'hour':
934
+ result.setHours(result.getHours() + 1);
935
+ break;
936
+ case 'minute':
937
+ result.setMinutes(result.getMinutes() + 1);
938
+ break;
939
+ case 'second':
940
+ result.setSeconds(result.getSeconds() + 1);
941
+ break;
942
+ default:
943
+ throw new Error(`Unsupported unit for endOf: ${unit}`);
944
+ }
945
+ result.setMilliseconds(result.getMilliseconds() - 1);
946
+ return result;
947
+ }
948
+ /**
949
+ * Get start of day (midnight)
950
+ * @function startOfDay
951
+ * @param {Date | string | number} date - Base date
952
+ * @returns {Date} Date at 00:00:00.000
953
+ * @example
954
+ * startOfDay(new Date()); // Returns today at midnight
955
+ */
956
+ export function startOfDay(date) {
957
+ return startOf(date, 'day');
958
+ }
959
+ /**
960
+ * Get end of day (last millisecond)
961
+ * @function endOfDay
962
+ * @param {Date | string | number} date - Base date
963
+ * @returns {Date} Date at 23:59:59.999
964
+ * @example
965
+ * endOfDay(new Date()); // Returns today at 23:59:59.999
966
+ */
967
+ export function endOfDay(date) {
968
+ return endOf(date, 'day');
969
+ }
970
+ /**
971
+ * Get start of month
972
+ * @function startOfMonth
973
+ * @param {Date | string | number} date - Base date
974
+ * @returns {Date} First day of month at 00:00:00.000
975
+ * @example
976
+ * startOfMonth(new Date()); // Returns first day of current month
977
+ */
978
+ export function startOfMonth(date) {
979
+ return startOf(date, 'month');
980
+ }
981
+ /**
982
+ * Get end of month
983
+ * @function endOfMonth
984
+ * @param {Date | string | number} date - Base date
985
+ * @returns {Date} Last day of month at 23:59:59.999
986
+ * @example
987
+ * endOfMonth(new Date()); // Returns last day of current month at 23:59:59.999
988
+ */
989
+ export function endOfMonth(date) {
990
+ return endOf(date, 'month');
991
+ }
992
+ /**
993
+ * Get start of year
994
+ * @function startOfYear
995
+ * @param {Date | string | number} date - Base date
996
+ * @returns {Date} January 1st at 00:00:00.000
997
+ * @example
998
+ * startOfYear(new Date()); // Returns January 1st of current year
999
+ */
1000
+ export function startOfYear(date) {
1001
+ return startOf(date, 'year');
1002
+ }
1003
+ /**
1004
+ * Get end of year
1005
+ * @function endOfYear
1006
+ * @param {Date | string | number} date - Base date
1007
+ * @returns {Date} December 31st at 23:59:59.999
1008
+ * @example
1009
+ * endOfYear(new Date()); // Returns December 31st of current year at 23:59:59.999
1010
+ */
1011
+ export function endOfYear(date) {
1012
+ return endOf(date, 'year');
1013
+ }
1014
+ /**
1015
+ * Get start of week
1016
+ * @function startOfWeek
1017
+ * @param {Date | string | number} date - Base date
1018
+ * @returns {Date} Start of week (configurable via weekStartsOn) at 00:00:00.000
1019
+ * @example
1020
+ * // With weekStartsOn = 0 (Sunday)
1021
+ * startOfWeek(new Date('2024-01-15')); // Returns Sunday, January 14, 2024
1022
+ */
1023
+ export function startOfWeek(date) {
1024
+ return startOf(date, 'week');
1025
+ }
1026
+ /**
1027
+ * Get end of week
1028
+ * @function endOfWeek
1029
+ * @param {Date | string | number} date - Base date
1030
+ * @returns {Date} End of week (configurable via weekStartsOn) at 23:59:59.999
1031
+ * @example
1032
+ * // With weekStartsOn = 0 (Sunday)
1033
+ * endOfWeek(new Date('2024-01-15')); // Returns Saturday, January 20, 2024
1034
+ */
1035
+ export function endOfWeek(date) {
1036
+ return endOf(date, 'week');
1037
+ }
1038
+ /**
1039
+ * Check if a date is between two other dates
1040
+ * @function isBetween
1041
+ * @param {Date | string | number} date - Date to check
1042
+ * @param {Date | string | number} start - Start of range
1043
+ * @param {Date | string | number} end - End of range
1044
+ * @param {boolean} [inclusive=true] - Whether to include boundaries
1045
+ * @returns {boolean} True if date is between start and end
1046
+ * @example
1047
+ * // Check if date is in January 2024
1048
+ * isBetween('2024-01-15', '2024-01-01', '2024-01-31');
1049
+ *
1050
+ * // Exclusive check
1051
+ * isBetween('2024-01-01', '2024-01-01', '2024-01-31', false); // false
1052
+ */
1053
+ export function isBetween(date, start, end, inclusive = true) {
1054
+ const parsedDate = parseDate(date);
1055
+ const parsedStart = parseDate(start);
1056
+ const parsedEnd = parseDate(end);
1057
+ if (inclusive) {
1058
+ return parsedDate >= parsedStart && parsedDate <= parsedEnd;
1059
+ }
1060
+ return parsedDate > parsedStart && parsedDate < parsedEnd;
1061
+ }
1062
+ /**
1063
+ * Check if a date is before another date
1064
+ * @function isBefore
1065
+ * @param {Date | string | number} date - Date to check
1066
+ * @param {Date | string | number} compareTo - Date to compare against
1067
+ * @returns {boolean} True if date is before compareTo
1068
+ * @example
1069
+ * isBefore('2024-01-15', '2024-01-20'); // true
1070
+ * isBefore('2024-01-20', '2024-01-15'); // false
1071
+ */
1072
+ export function isBefore(date, compareTo) {
1073
+ return parseDate(date) < parseDate(compareTo);
1074
+ }
1075
+ /**
1076
+ * Check if a date is after another date
1077
+ * @function isAfter
1078
+ * @param {Date | string | number} date - Date to check
1079
+ * @param {Date | string | number} compareTo - Date to compare against
1080
+ * @returns {boolean} True if date is after compareTo
1081
+ * @example
1082
+ * isAfter('2024-01-20', '2024-01-15'); // true
1083
+ * isAfter('2024-01-15', '2024-01-20'); // false
1084
+ */
1085
+ export function isAfter(date, compareTo) {
1086
+ return parseDate(date) > parseDate(compareTo);
1087
+ }
1088
+ /**
1089
+ * Check if two dates are the same (optionally by specific unit)
1090
+ * @function isSame
1091
+ * @param {Date | string | number} date1 - First date
1092
+ * @param {Date | string | number} date2 - Second date
1093
+ * @param {DateUnit} [unit] - Unit to compare (if not provided, compares exact timestamps)
1094
+ * @returns {boolean} True if dates are the same
1095
+ * @example
1096
+ * // Exact timestamp comparison
1097
+ * isSame(new Date('2024-01-15T10:30:00'), new Date('2024-01-15T10:30:00')); // true
1098
+ *
1099
+ * // Same day comparison
1100
+ * isSame('2024-01-15T10:30:00', '2024-01-15T15:45:00', 'day'); // true
1101
+ *
1102
+ * // Same month comparison
1103
+ * isSame('2024-01-15', '2024-01-31', 'month'); // true
1104
+ */
1105
+ export function isSame(date1, date2, unit) {
1106
+ const d1 = parseDate(date1);
1107
+ const d2 = parseDate(date2);
1108
+ if (!unit) {
1109
+ return d1.getTime() === d2.getTime();
1110
+ }
1111
+ const start1 = startOf(d1, unit);
1112
+ const start2 = startOf(d2, unit);
1113
+ return start1.getTime() === start2.getTime();
1114
+ }
1115
+ /**
1116
+ * Check if a date is today
1117
+ * @function isToday
1118
+ * @param {Date | string | number} date - Date to check
1119
+ * @returns {boolean} True if date is today
1120
+ * @example
1121
+ * isToday(new Date()); // true
1122
+ * isToday('2024-01-15'); // depends on current date
1123
+ */
1124
+ export function isToday(date) {
1125
+ return isSame(date, today(), 'day');
1126
+ }
1127
+ /**
1128
+ * Check if a date is in the past
1129
+ * @function isPast
1130
+ * @param {Date | string | number} date - Date to check
1131
+ * @returns {boolean} True if date is before now
1132
+ * @example
1133
+ * isPast('2020-01-01'); // true
1134
+ * isPast('2030-01-01'); // false
1135
+ */
1136
+ export function isPast(date) {
1137
+ return parseDate(date) < now();
1138
+ }
1139
+ /**
1140
+ * Check if a date is in the future
1141
+ * @function isFuture
1142
+ * @param {Date | string | number} date - Date to check
1143
+ * @returns {boolean} True if date is after now
1144
+ * @example
1145
+ * isFuture('2030-01-01'); // true
1146
+ * isFuture('2020-01-01'); // false
1147
+ */
1148
+ export function isFuture(date) {
1149
+ return parseDate(date) > now();
1150
+ }
1151
+ /**
1152
+ * Calculate the difference between two dates in multiple units
1153
+ * @function difference
1154
+ * @param {Date | string | number} date1 - First date
1155
+ * @param {Date | string | number} date2 - Second date
1156
+ * @returns {DateDifference} Object containing differences in various units
1157
+ * @example
1158
+ * const diff = difference('2024-01-10', '2024-01-15');
1159
+ * console.log(diff.days); // 5
1160
+ * console.log(diff.totalHours); // 120
1161
+ */
1162
+ export function difference(date1, date2) {
1163
+ const d1 = parseDate(date1);
1164
+ const d2 = parseDate(date2);
1165
+ const diffMs = Math.abs(d2.getTime() - d1.getTime());
1166
+ const totalSeconds = Math.floor(diffMs / MILLISECONDS_IN_SECOND);
1167
+ const totalMinutes = Math.floor(totalSeconds / SECONDS_IN_MINUTE);
1168
+ const totalHours = Math.floor(totalMinutes / MINUTES_IN_HOUR);
1169
+ const totalDays = Math.floor(totalHours / HOURS_IN_DAY);
1170
+ return {
1171
+ years: Math.floor(totalDays / 365),
1172
+ months: Math.floor(totalDays / 30),
1173
+ weeks: Math.floor(totalDays / DAYS_IN_WEEK),
1174
+ days: totalDays,
1175
+ hours: totalHours,
1176
+ minutes: totalMinutes,
1177
+ seconds: totalSeconds,
1178
+ milliseconds: diffMs,
1179
+ totalDays,
1180
+ totalHours,
1181
+ totalMinutes,
1182
+ totalSeconds,
1183
+ totalMilliseconds: diffMs,
1184
+ };
1185
+ }
1186
+ /**
1187
+ * Get human-readable description of date difference
1188
+ * @function humanizeDifference
1189
+ * @param {Date | string | number} date1 - First date
1190
+ * @param {Date | string | number} [date2=now()] - Second date (defaults to now)
1191
+ * @param {boolean} [detailed=false] - Whether to use detailed descriptions
1192
+ * @returns {string} Human-readable difference
1193
+ * @example
1194
+ * // Returns something like "2 days ago" or "in 3 hours"
1195
+ * humanizeDifference('2024-01-13');
1196
+ *
1197
+ * // Detailed format
1198
+ * humanizeDifference('2024-01-13', new Date(), true); // "2 days ago"
1199
+ */
1200
+ export function humanizeDifference(date1, date2 = now(), detailed = false) {
1201
+ const diff = difference(date1, date2);
1202
+ const isPast = parseDate(date1) < parseDate(date2);
1203
+ const prefix = isPast ? 'ago' : 'in';
1204
+ if (diff.years > 0) {
1205
+ return detailed
1206
+ ? `${diff.years} year${diff.years === 1 ? '' : 's'} ${prefix}`
1207
+ : `${diff.years}y ${prefix}`;
1208
+ }
1209
+ if (diff.months > 0) {
1210
+ return detailed
1211
+ ? `${diff.months} month${diff.months === 1 ? '' : 's'} ${prefix}`
1212
+ : `${diff.months}mo ${prefix}`;
1213
+ }
1214
+ if (diff.weeks > 0) {
1215
+ return detailed
1216
+ ? `${diff.weeks} week${diff.weeks === 1 ? '' : 's'} ${prefix}`
1217
+ : `${diff.weeks}w ${prefix}`;
1218
+ }
1219
+ if (diff.days > 0) {
1220
+ return detailed
1221
+ ? `${diff.days} day${diff.days === 1 ? '' : 's'} ${prefix}`
1222
+ : `${diff.days}d ${prefix}`;
1223
+ }
1224
+ if (diff.hours > 0) {
1225
+ return detailed
1226
+ ? `${diff.hours} hour${diff.hours === 1 ? '' : 's'} ${prefix}`
1227
+ : `${diff.hours}h ${prefix}`;
1228
+ }
1229
+ if (diff.minutes > 0) {
1230
+ return detailed
1231
+ ? `${diff.minutes} minute${diff.minutes === 1 ? '' : 's'} ${prefix}`
1232
+ : `${diff.minutes}m ${prefix}`;
1233
+ }
1234
+ return detailed
1235
+ ? `${diff.seconds} second${diff.seconds === 1 ? '' : 's'} ${prefix}`
1236
+ : `${diff.seconds}s ${prefix}`;
1237
+ }
1238
+ /**
1239
+ * Check if a date falls on a weekend
1240
+ * @function isWeekend
1241
+ * @param {Date | string | number} date - Date to check
1242
+ * @returns {boolean} True if date is Saturday or Sunday
1243
+ * @example
1244
+ * isWeekend('2024-01-15'); // false (Monday)
1245
+ * isWeekend('2024-01-20'); // true (Saturday)
1246
+ */
1247
+ export function isWeekend(date) {
1248
+ const parsedDate = parseDate(date);
1249
+ const day = parsedDate.getDay();
1250
+ return day === 0 || day === 6; // Sunday or Saturday
1251
+ }
1252
+ /**
1253
+ * Check if a date falls on a weekday
1254
+ * @function isWeekday
1255
+ * @param {Date | string | number} date - Date to check
1256
+ * @returns {boolean} True if date is Monday through Friday
1257
+ * @example
1258
+ * isWeekday('2024-01-15'); // true (Monday)
1259
+ * isWeekday('2024-01-20'); // false (Saturday)
1260
+ */
1261
+ export function isWeekday(date) {
1262
+ return !isWeekend(date);
1263
+ }
1264
+ /**
1265
+ * Check if a date is a business day (considering weekends and holidays)
1266
+ * @function isBusinessDay
1267
+ * @param {Date | string | number} date - Date to check
1268
+ * @returns {boolean} True if date is a business day
1269
+ * @example
1270
+ * // Assuming Monday-Friday are business days
1271
+ * isBusinessDay('2024-01-15'); // true (Monday)
1272
+ * isBusinessDay('2024-01-20'); // false (Saturday)
1273
+ */
1274
+ export function isBusinessDay(date) {
1275
+ const parsedDate = parseDate(date);
1276
+ // Check if it's a weekend
1277
+ if (isWeekend(parsedDate)) {
1278
+ return false;
1279
+ }
1280
+ // Check if it's a configured business day
1281
+ if (!config.businessDays.businessDays.includes(parsedDate.getDay())) {
1282
+ return false;
1283
+ }
1284
+ // Check if it's a holiday
1285
+ const isHoliday = config.businessDays.holidays?.some((holiday) => isSame(holiday, parsedDate, 'day')) || config.businessDays.holidayCheck?.(parsedDate);
1286
+ return !isHoliday;
1287
+ }
1288
+ /**
1289
+ * Add business days to a date (skips weekends and holidays)
1290
+ * @function addBusinessDays
1291
+ * @param {Date | string | number} date - Base date
1292
+ * @param {number} days - Number of business days to add (can be negative)
1293
+ * @returns {Date} New date after adding business days
1294
+ * @example
1295
+ * // Add 3 business days to Friday
1296
+ * addBusinessDays('2024-01-12', 3); // Returns Wednesday, January 17
1297
+ */
1298
+ export function addBusinessDays(date, days) {
1299
+ let result = parseDate(date);
1300
+ let daysAdded = 0;
1301
+ const direction = days > 0 ? 1 : -1;
1302
+ while (daysAdded < Math.abs(days)) {
1303
+ result = addDays(result, direction);
1304
+ if (isBusinessDay(result)) {
1305
+ daysAdded++;
1306
+ }
1307
+ }
1308
+ return result;
1309
+ }
1310
+ /**
1311
+ * Calculate number of days between two dates
1312
+ * @function daysBetween
1313
+ * @param {Date | string | number} start - Start date
1314
+ * @param {Date | string | number} end - End date
1315
+ * @param {boolean} [inclusive=false] - Whether to include both start and end dates
1316
+ * @returns {number} Number of days between dates
1317
+ * @example
1318
+ * daysBetween('2024-01-10', '2024-01-15'); // 5
1319
+ * daysBetween('2024-01-10', '2024-01-15', true); // 6
1320
+ */
1321
+ export function daysBetween(start, end, inclusive = false) {
1322
+ const startDate = startOfDay(parseDate(start));
1323
+ const endDate = startOfDay(parseDate(end));
1324
+ const diffMs = Math.abs(endDate.getTime() - startDate.getTime());
1325
+ const days = Math.floor(diffMs /
1326
+ (MILLISECONDS_IN_SECOND *
1327
+ SECONDS_IN_MINUTE *
1328
+ MINUTES_IN_HOUR *
1329
+ HOURS_IN_DAY));
1330
+ return inclusive ? days + 1 : days;
1331
+ }
1332
+ /**
1333
+ * Calculate number of months between two dates
1334
+ * @function monthsBetween
1335
+ * @param {Date | string | number} start - Start date
1336
+ * @param {Date | string | number} end - End date
1337
+ * @returns {number} Number of months between dates
1338
+ * @example
1339
+ * monthsBetween('2024-01-15', '2024-03-15'); // 2
1340
+ * monthsBetween('2024-01-31', '2024-02-28'); // 1
1341
+ */
1342
+ export function monthsBetween(start, end) {
1343
+ const startDate = parseDate(start);
1344
+ const endDate = parseDate(end);
1345
+ const yearsDiff = endDate.getFullYear() - startDate.getFullYear();
1346
+ const monthsDiff = endDate.getMonth() - startDate.getMonth();
1347
+ return yearsDiff * 12 + monthsDiff;
1348
+ }
1349
+ /**
1350
+ * Calculate number of years between two dates
1351
+ * @function yearsBetween
1352
+ * @param {Date | string | number} start - Start date
1353
+ * @param {Date | string | number} end - End date
1354
+ * @returns {number} Number of years between dates
1355
+ * @example
1356
+ * yearsBetween('2020-06-15', '2024-01-15'); // 4
1357
+ */
1358
+ export function yearsBetween(start, end) {
1359
+ const startDate = parseDate(start);
1360
+ const endDate = parseDate(end);
1361
+ return endDate.getFullYear() - startDate.getFullYear();
1362
+ }
1363
+ /**
1364
+ * Calculate age from birth date
1365
+ * @function getAge
1366
+ * @param {Date | string | number} birthDate - Birth date
1367
+ * @param {Date | string | number} [asOfDate=now()] - Date to calculate age as of
1368
+ * @returns {number} Age in years
1369
+ * @example
1370
+ * getAge('1990-06-15', '2024-01-15'); // 33
1371
+ * getAge('2000-02-29', '2024-02-28'); // 23
1372
+ * getAge('2000-02-29', '2024-02-29'); // 24
1373
+ */
1374
+ export function getAge(birthDate, asOfDate = now()) {
1375
+ const birth = parseDate(birthDate);
1376
+ const asOf = parseDate(asOfDate);
1377
+ let age = asOf.getFullYear() - birth.getFullYear();
1378
+ const monthDiff = asOf.getMonth() - birth.getMonth();
1379
+ if (monthDiff < 0 || (monthDiff === 0 && asOf.getDate() < birth.getDate())) {
1380
+ age--;
1381
+ }
1382
+ return Math.max(0, age);
1383
+ }
1384
+ /**
1385
+ * Get ISO week number of year
1386
+ * @function getWeekNumber
1387
+ * @param {Date | string | number} date - Date to check
1388
+ * @returns {number} Week number (1-53)
1389
+ * @example
1390
+ * getWeekNumber('2024-01-01'); // 1
1391
+ * getWeekNumber('2024-01-15'); // 3
1392
+ */
1393
+ export function getWeekNumber(date) {
1394
+ const parsedDate = parseDate(date);
1395
+ const firstDayOfYear = new Date(parsedDate.getFullYear(), 0, 1);
1396
+ const pastDaysOfYear = (parsedDate.getTime() - firstDayOfYear.getTime()) /
1397
+ (MILLISECONDS_IN_SECOND *
1398
+ SECONDS_IN_MINUTE *
1399
+ MINUTES_IN_HOUR *
1400
+ HOURS_IN_DAY);
1401
+ return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / DAYS_IN_WEEK);
1402
+ }
1403
+ /**
1404
+ * Get quarter of year
1405
+ * @function getQuarter
1406
+ * @param {Date | string | number} date - Date to check
1407
+ * @returns {number} Quarter number (1-4)
1408
+ * @example
1409
+ * getQuarter('2024-01-15'); // 1
1410
+ * getQuarter('2024-04-15'); // 2
1411
+ * getQuarter('2024-07-15'); // 3
1412
+ * getQuarter('2024-10-15'); // 4
1413
+ */
1414
+ export function getQuarter(date) {
1415
+ const month = parseDate(date).getMonth();
1416
+ return Math.floor(month / 3) + 1;
1417
+ }
1418
+ /**
1419
+ * Get day name
1420
+ * @function getDayName
1421
+ * @param {Date | string | number} date - Date to check
1422
+ * @param {'full' | 'short'} [format='full'] - Format of day name
1423
+ * @returns {string} Day name
1424
+ * @example
1425
+ * getDayName('2024-01-15'); // "Monday"
1426
+ * getDayName('2024-01-15', 'short'); // "Mon"
1427
+ */
1428
+ export function getDayName(date, format = 'full') {
1429
+ const parsedDate = parseDate(date);
1430
+ const dayIndex = parsedDate.getDay();
1431
+ return format === 'full'
1432
+ ? WEEKDAY_NAMES[dayIndex]
1433
+ : WEEKDAY_NAMES_SHORT[dayIndex];
1434
+ }
1435
+ /**
1436
+ * Get month name
1437
+ * @function getMonthName
1438
+ * @param {Date | string | number} date - Date to check
1439
+ * @param {'full' | 'short'} [format='full'] - Format of month name
1440
+ * @returns {string} Month name
1441
+ * @example
1442
+ * getMonthName('2024-01-15'); // "January"
1443
+ * getMonthName('2024-01-15', 'short'); // "Jan"
1444
+ */
1445
+ export function getMonthName(date, format = 'full') {
1446
+ const parsedDate = parseDate(date);
1447
+ const monthIndex = parsedDate.getMonth();
1448
+ return format === 'full'
1449
+ ? MONTH_NAMES[monthIndex]
1450
+ : MONTH_NAMES_SHORT[monthIndex];
1451
+ }
1452
+ /**
1453
+ * Get number of days in month
1454
+ * @function getDaysInMonth
1455
+ * @param {Date | string | number} date - Date to check
1456
+ * @returns {number} Number of days in month
1457
+ * @example
1458
+ * getDaysInMonth('2024-01-15'); // 31
1459
+ * getDaysInMonth('2024-02-15'); // 29 (leap year)
1460
+ * getDaysInMonth('2023-02-15'); // 28
1461
+ */
1462
+ export function getDaysInMonth(date) {
1463
+ const parsedDate = parseDate(date);
1464
+ return new Date(parsedDate.getFullYear(), parsedDate.getMonth() + 1, 0).getDate();
1465
+ }
1466
+ /**
1467
+ * Check if year is a leap year
1468
+ * @function isLeapYear
1469
+ * @param {number} year - Year to check
1470
+ * @returns {boolean} True if year is a leap year
1471
+ * @example
1472
+ * isLeapYear(2024); // true
1473
+ * isLeapYear(2023); // false
1474
+ * isLeapYear(2000); // true (century divisible by 400)
1475
+ * isLeapYear(1900); // false (century not divisible by 400)
1476
+ */
1477
+ export function isLeapYear(year) {
1478
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
1479
+ }
1480
+ /**
1481
+ * Validate a date with constraints
1482
+ * @function validateDate
1483
+ * @param {Date | string | number} date - Date to validate
1484
+ * @param {ValidationOptions} [options={}] - Validation options
1485
+ * @returns {ValidationResult} Validation result with errors if any
1486
+ * @example
1487
+ * // Simple validation
1488
+ * validateDate('2024-01-15');
1489
+ *
1490
+ * // With constraints
1491
+ * validateDate('2024-01-15', {
1492
+ * min: '2024-01-01',
1493
+ * max: '2024-12-31',
1494
+ * allowPast: false,
1495
+ * });
1496
+ */
1497
+ export function validateDate(date, options = {}) {
1498
+ const errors = [];
1499
+ try {
1500
+ const parsedDate = parseDate(date);
1501
+ if (options.required && !parsedDate) {
1502
+ errors.push('Date is required');
1503
+ }
1504
+ if (options.min || options.minDate) {
1505
+ const minDate = parseDate(options.min || options.minDate);
1506
+ if (parsedDate < minDate) {
1507
+ errors.push(`Date must be on or after ${formatDate(minDate, {
1508
+ format: 'MEDIUM',
1509
+ })}`);
1510
+ }
1511
+ }
1512
+ if (options.max || options.maxDate) {
1513
+ const maxDate = parseDate(options.max || options.maxDate);
1514
+ if (parsedDate > maxDate) {
1515
+ errors.push(`Date must be on or before ${formatDate(maxDate, {
1516
+ format: 'MEDIUM',
1517
+ })}`);
1518
+ }
1519
+ }
1520
+ if (options.allowPast === false && isPast(parsedDate)) {
1521
+ errors.push('Date must be in the future');
1522
+ }
1523
+ if (options.allowFuture === false && isFuture(parsedDate)) {
1524
+ errors.push('Date must be in the past');
1525
+ }
1526
+ }
1527
+ catch (error) {
1528
+ errors.push(error instanceof Error ? error.message : 'Invalid date');
1529
+ }
1530
+ return {
1531
+ isValid: errors.length === 0,
1532
+ errors,
1533
+ };
1534
+ }
1535
+ /**
1536
+ * Get timezone information for a date
1537
+ * @function getTimezoneInfo
1538
+ * @param {Date | string | number} date - Date to get timezone info for
1539
+ * @returns {TimezoneInfo} Timezone information
1540
+ * @example
1541
+ * const info = getTimezoneInfo(new Date());
1542
+ * console.log(info.name); // "America/New_York"
1543
+ * console.log(info.offsetString); // "UTC-05:00"
1544
+ */
1545
+ export function getTimezoneInfo(date) {
1546
+ const parsedDate = parseDate(date);
1547
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
1548
+ const offset = -parsedDate.getTimezoneOffset();
1549
+ const hours = Math.floor(Math.abs(offset) / 60);
1550
+ const minutes = Math.abs(offset) % 60;
1551
+ const sign = offset >= 0 ? '+' : '-';
1552
+ return {
1553
+ abbreviation: getTimezoneAbbreviation(parsedDate),
1554
+ name: timezone,
1555
+ offset,
1556
+ offsetString: `UTC${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`,
1557
+ isDST: isDST(parsedDate),
1558
+ };
1559
+ }
1560
+ /**
1561
+ * Convert date to another timezone
1562
+ * @function convertTimezone
1563
+ * @param {Date | string | number} date - Date to convert
1564
+ * @param {string} targetTimezone - Target timezone (e.g., 'America/New_York', 'UTC')
1565
+ * @returns {Date} Date converted to target timezone
1566
+ * @example
1567
+ * // Convert to UTC
1568
+ * convertTimezone(new Date(), 'UTC');
1569
+ *
1570
+ * // Convert to New York time
1571
+ * convertTimezone('2024-01-15T10:30:00Z', 'America/New_York');
1572
+ */
1573
+ export function convertTimezone(date, targetTimezone) {
1574
+ const parsedDate = parseDate(date);
1575
+ if (targetTimezone === 'UTC') {
1576
+ return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
1577
+ }
1578
+ // For other timezones, use Intl.DateTimeFormat
1579
+ const formatter = new Intl.DateTimeFormat('en-US', {
1580
+ timeZone: targetTimezone,
1581
+ year: 'numeric',
1582
+ month: 'numeric',
1583
+ day: 'numeric',
1584
+ hour: 'numeric',
1585
+ minute: 'numeric',
1586
+ second: 'numeric',
1587
+ });
1588
+ const parts = formatter.formatToParts(parsedDate);
1589
+ const getPart = (type) => parts.find((p) => p.type === type)?.value;
1590
+ return createDate(parseInt(getPart('year')), (parseInt(getPart('month')) - 1), parseInt(getPart('day')), parseInt(getPart('hour')), parseInt(getPart('minute')), parseInt(getPart('second')));
1591
+ }
1592
+ // ============================================================================
1593
+ // Internal Helper Functions
1594
+ // ============================================================================
1595
+ // /**
1596
+ // * Parse ISO date string
1597
+ // * @private
1598
+ // * @param {string} input - ISO date string
1599
+ // * @param {DateFormat} format - ISO format type
1600
+ // * @returns {Date} Parsed date
1601
+ // */
1602
+ // function parseISODate(input: string, format: DateFormat): Date {
1603
+ // if (format === 'ISO_DATE') {
1604
+ // // Ensure we have a complete date
1605
+ // const parts = input.split('-');
1606
+ // if (parts.length < 3) {
1607
+ // throw new Error('Invalid ISO date format');
1608
+ // }
1609
+ // return new Date(`${input}T00:00:00.000Z`);
1610
+ // }
1611
+ // if (format === 'ISO_TIME') {
1612
+ // // Parse time only (use today's date)
1613
+ // const today = new Date().toISOString().split('T')[0];
1614
+ // return new Date(`${today}T${input}Z`);
1615
+ // }
1616
+ // // Full ISO format
1617
+ // return new Date(input);
1618
+ // }
1619
+ // /**
1620
+ // * Parse US date string (MM/DD/YYYY or MM/DD/YY)
1621
+ // * @private
1622
+ // * @param {string} input - US date string
1623
+ // * @returns {Date} Parsed date
1624
+ // */
1625
+ // function parseUSDate(input: string): Date {
1626
+ // // MM/DD/YYYY or MM/DD/YY
1627
+ // const parts = input.split('/');
1628
+ // if (parts.length !== 3) {
1629
+ // throw new Error('Invalid US date format');
1630
+ // }
1631
+ // let month = parseInt(parts[0]) - 1;
1632
+ // let day = parseInt(parts[1]);
1633
+ // let year = parseInt(parts[2]);
1634
+ // // Handle 2-digit years
1635
+ // if (year < 100) {
1636
+ // year += year < 50 ? 2000 : 1900;
1637
+ // }
1638
+ // validateDateParts(year, month as Month, day);
1639
+ // return new Date(year, month, day);
1640
+ // }
1641
+ // /**
1642
+ // * Parse EU date string (DD/MM/YYYY or DD/MM/YY)
1643
+ // * @private
1644
+ // * @param {string} input - EU date string
1645
+ // * @returns {Date} Parsed date
1646
+ // */
1647
+ // function parseEUDate(input: string): Date {
1648
+ // // DD/MM/YYYY or DD/MM/YY
1649
+ // const parts = input.split('/');
1650
+ // if (parts.length !== 3) {
1651
+ // throw new Error('Invalid EU date format');
1652
+ // }
1653
+ // let day = parseInt(parts[0]);
1654
+ // let month = parseInt(parts[1]) - 1;
1655
+ // let year = parseInt(parts[2]);
1656
+ // // Handle 2-digit years
1657
+ // if (year < 100) {
1658
+ // year += year < 50 ? 2000 : 1900;
1659
+ // }
1660
+ // validateDateParts(year, month as Month, day);
1661
+ // return new Date(year, month, day);
1662
+ // }
1663
+ // /**
1664
+ // * Parse date with custom format string
1665
+ // * @private
1666
+ // * @param {string} input - Date string
1667
+ // * @param {string} format - Custom format string
1668
+ // * @returns {Date} Parsed date
1669
+ // */
1670
+ // function parseCustomDate(input: string, format: string): Date {
1671
+ // // Simple custom format parsing
1672
+ // const formatChars = format.split('');
1673
+ // const inputChars = input.split('');
1674
+ // let year = 0,
1675
+ // month = 0,
1676
+ // day = 0,
1677
+ // hour = 0,
1678
+ // minute = 0,
1679
+ // second = 0;
1680
+ // for (let i = 0; i < formatChars.length; i++) {
1681
+ // const formatChar = formatChars[i];
1682
+ // const inputChar = inputChars[i];
1683
+ // switch (formatChar) {
1684
+ // case 'Y':
1685
+ // year = year * 10 + parseInt(inputChar);
1686
+ // break;
1687
+ // case 'M':
1688
+ // month = month * 10 + parseInt(inputChar);
1689
+ // break;
1690
+ // case 'D':
1691
+ // day = day * 10 + parseInt(inputChar);
1692
+ // break;
1693
+ // case 'h':
1694
+ // hour = hour * 10 + parseInt(inputChar);
1695
+ // break;
1696
+ // case 'm':
1697
+ // minute = minute * 10 + parseInt(inputChar);
1698
+ // break;
1699
+ // case 's':
1700
+ // second = second * 10 + parseInt(inputChar);
1701
+ // break;
1702
+ // default:
1703
+ // // Skip separator characters
1704
+ // break;
1705
+ // }
1706
+ // }
1707
+ // // Adjust month (format is 1-based, Date is 0-based)
1708
+ // month = Math.max(1, month) - 1;
1709
+ // validateDateParts(year, month as Month, day, hour, minute, second);
1710
+ // return new Date(year, month, day, hour, minute, second);
1711
+ // }
1712
+ // /**
1713
+ // * Parse RFC 2822 date string
1714
+ // * @private
1715
+ // * @param {string} input - RFC 2822 date string
1716
+ // * @returns {Date} Parsed date
1717
+ // */
1718
+ // function parseRFC2822Date(input: string): Date {
1719
+ // return new Date(input);
1720
+ // }
1721
+ // /**
1722
+ // * Parse timestamp string (YYYYMMDDHHMMSS)
1723
+ // * @private
1724
+ // * @param {string} input - Timestamp string
1725
+ // * @returns {Date} Parsed date
1726
+ // */
1727
+ // function parseTimestamp(input: string): Date {
1728
+ // // YYYYMMDDHHMMSS format
1729
+ // if (input.length !== 14) {
1730
+ // throw new Error('Invalid timestamp format');
1731
+ // }
1732
+ // const year = parseInt(input.substring(0, 4));
1733
+ // const month = parseInt(input.substring(4, 6)) - 1;
1734
+ // const day = parseInt(input.substring(6, 8));
1735
+ // const hour = parseInt(input.substring(8, 10));
1736
+ // const minute = parseInt(input.substring(10, 12));
1737
+ // const second = parseInt(input.substring(12, 14));
1738
+ // validateDateParts(year, month as Month, day, hour, minute, second);
1739
+ // return new Date(year, month, day, hour, minute, second);
1740
+ // }
1741
+ // /**
1742
+ // * Parse date using locale-specific formatting
1743
+ // * @private
1744
+ // * @param {string} input - Date string
1745
+ // * @param {string} locale - Locale to use for parsing
1746
+ // * @returns {Date} Parsed date
1747
+ // */
1748
+ // function parseLocaleDate(input: string, locale: string): Date {
1749
+ // // Try various common formats
1750
+ // const formats = [
1751
+ // new Intl.DateTimeFormat(locale, { dateStyle: 'short' }),
1752
+ // new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }),
1753
+ // new Intl.DateTimeFormat(locale, { dateStyle: 'long' }),
1754
+ // ];
1755
+ // for (const formatter of formats) {
1756
+ // try {
1757
+ // // Use Date's built-in parsing for now
1758
+ // const date = new Date(input);
1759
+ // if (isDate(date)) {
1760
+ // return date;
1761
+ // }
1762
+ // } catch {
1763
+ // // Continue to next format
1764
+ // }
1765
+ // }
1766
+ // throw new Error('Could not parse date with locale');
1767
+ // }
1768
+ /**
1769
+ * Format date as US format (MM/DD/YYYY)
1770
+ * @private
1771
+ * @param {Date} date - Date to format
1772
+ * @returns {string} Formatted date
1773
+ */
1774
+ function formatUSDate(date) {
1775
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
1776
+ const day = date.getDate().toString().padStart(2, '0');
1777
+ const year = date.getFullYear();
1778
+ return `${month}/${day}/${year}`;
1779
+ }
1780
+ /**
1781
+ * Format date as EU format (DD/MM/YYYY)
1782
+ * @private
1783
+ * @param {Date} date - Date to format
1784
+ * @returns {string} Formatted date
1785
+ */
1786
+ function formatEUDate(date) {
1787
+ const day = date.getDate().toString().padStart(2, '0');
1788
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
1789
+ const year = date.getFullYear();
1790
+ return `${day}/${month}/${year}`;
1791
+ }
1792
+ /**
1793
+ * Format date as full textual format
1794
+ * @private
1795
+ * @param {Date} date - Date to format
1796
+ * @param {string} locale - Locale for formatting
1797
+ * @returns {string} Formatted date
1798
+ */
1799
+ function formatFullDate(date, locale) {
1800
+ return new Intl.DateTimeFormat(locale, {
1801
+ weekday: 'long',
1802
+ year: 'numeric',
1803
+ month: 'long',
1804
+ day: 'numeric',
1805
+ }).format(date);
1806
+ }
1807
+ /**
1808
+ * Format date as long format
1809
+ * @private
1810
+ * @param {Date} date - Date to format
1811
+ * @param {string} locale - Locale for formatting
1812
+ * @returns {string} Formatted date
1813
+ */
1814
+ function formatLongDate(date, locale) {
1815
+ return new Intl.DateTimeFormat(locale, {
1816
+ year: 'numeric',
1817
+ month: 'long',
1818
+ day: 'numeric',
1819
+ }).format(date);
1820
+ }
1821
+ /**
1822
+ * Format date as medium format
1823
+ * @private
1824
+ * @param {Date} date - Date to format
1825
+ * @param {string} locale - Locale for formatting
1826
+ * @returns {string} Formatted date
1827
+ */
1828
+ function formatMediumDate(date, locale) {
1829
+ return new Intl.DateTimeFormat(locale, {
1830
+ year: 'numeric',
1831
+ month: 'short',
1832
+ day: 'numeric',
1833
+ }).format(date);
1834
+ }
1835
+ /**
1836
+ * Format date as short format
1837
+ * @private
1838
+ * @param {Date} date - Date to format
1839
+ * @param {string} locale - Locale for formatting
1840
+ * @returns {string} Formatted date
1841
+ */
1842
+ function formatShortDate(date, locale) {
1843
+ return new Intl.DateTimeFormat(locale, {
1844
+ year: '2-digit',
1845
+ month: 'numeric',
1846
+ day: 'numeric',
1847
+ }).format(date);
1848
+ }
1849
+ /**
1850
+ * Format time
1851
+ * @private
1852
+ * @param {Date} date - Date to format
1853
+ * @param {string} locale - Locale for formatting
1854
+ * @param {boolean} includeSeconds - Whether to include seconds
1855
+ * @returns {string} Formatted time
1856
+ */
1857
+ function formatTime(date, locale, includeSeconds) {
1858
+ return new Intl.DateTimeFormat(locale, {
1859
+ hour: 'numeric',
1860
+ minute: '2-digit',
1861
+ second: includeSeconds ? '2-digit' : undefined,
1862
+ hour12: true,
1863
+ }).format(date);
1864
+ }
1865
+ /**
1866
+ * Format time in 24-hour format
1867
+ * @private
1868
+ * @param {Date} date - Date to format
1869
+ * @param {boolean} includeSeconds - Whether to include seconds
1870
+ * @returns {string} Formatted time
1871
+ */
1872
+ function formatTime24(date, includeSeconds) {
1873
+ const hours = date.getHours().toString().padStart(2, '0');
1874
+ const minutes = date.getMinutes().toString().padStart(2, '0');
1875
+ const seconds = includeSeconds
1876
+ ? `:${date.getSeconds().toString().padStart(2, '0')}`
1877
+ : '';
1878
+ return `${hours}:${minutes}${seconds}`;
1879
+ }
1880
+ /**
1881
+ * Format date as timestamp (YYYYMMDDHHMMSS)
1882
+ * @private
1883
+ * @param {Date} date - Date to format
1884
+ * @returns {string} Formatted timestamp
1885
+ */
1886
+ function formatTimestamp(date) {
1887
+ const year = date.getFullYear();
1888
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
1889
+ const day = date.getDate().toString().padStart(2, '0');
1890
+ const hours = date.getHours().toString().padStart(2, '0');
1891
+ const minutes = date.getMinutes().toString().padStart(2, '0');
1892
+ const seconds = date.getSeconds().toString().padStart(2, '0');
1893
+ return `${year}${month}${day}${hours}${minutes}${seconds}`;
1894
+ }
1895
+ /**
1896
+ * Format date as RFC 2822 string
1897
+ * @private
1898
+ * @param {Date} date - Date to format
1899
+ * @returns {string} RFC 2822 formatted date
1900
+ */
1901
+ function formatRFC2822(date) {
1902
+ return date.toUTCString();
1903
+ }
1904
+ /**
1905
+ * Format date with custom format string
1906
+ * @private
1907
+ * @param {Date} date - Date to format
1908
+ * @param {string} format - Custom format string
1909
+ * @returns {string} Formatted date
1910
+ */
1911
+ function formatCustomDate(date, format) {
1912
+ const tokens = {
1913
+ YYYY: date.getFullYear().toString(),
1914
+ YY: date.getFullYear().toString().slice(-2),
1915
+ MMMM: MONTH_NAMES[date.getMonth()],
1916
+ MMM: MONTH_NAMES_SHORT[date.getMonth()],
1917
+ MM: (date.getMonth() + 1).toString().padStart(2, '0'),
1918
+ M: (date.getMonth() + 1).toString(),
1919
+ DDDD: WEEKDAY_NAMES[date.getDay()],
1920
+ DDD: WEEKDAY_NAMES_SHORT[date.getDay()],
1921
+ DD: date.getDate().toString().padStart(2, '0'),
1922
+ D: date.getDate().toString(),
1923
+ HH: date.getHours().toString().padStart(2, '0'),
1924
+ H: date.getHours().toString(),
1925
+ hh: (date.getHours() % 12 || 12).toString().padStart(2, '0'),
1926
+ h: (date.getHours() % 12 || 12).toString(),
1927
+ mm: date.getMinutes().toString().padStart(2, '0'),
1928
+ m: date.getMinutes().toString(),
1929
+ ss: date.getSeconds().toString().padStart(2, '0'),
1930
+ s: date.getSeconds().toString(),
1931
+ SSS: date.getMilliseconds().toString().padStart(3, '0'),
1932
+ A: date.getHours() < 12 ? 'AM' : 'PM',
1933
+ a: date.getHours() < 12 ? 'am' : 'pm',
1934
+ };
1935
+ return format.replace(/YYYY|YY|MMMM|MMM|MM|M|DDDD|DDD|DD|D|HH|H|hh|h|mm|m|ss|s|SSS|A|a/g, (match) => tokens[match] || match);
1936
+ }
1937
+ /**
1938
+ * Validate date component values
1939
+ * @private
1940
+ * @param {number} year - Year
1941
+ * @param {Month} month - Month (0-11)
1942
+ * @param {number} day - Day of month
1943
+ * @param {number} [hour=0] - Hour (0-23)
1944
+ * @param {number} [minute=0] - Minute (0-59)
1945
+ * @param {number} [second=0] - Second (0-59)
1946
+ * @param {number} [millisecond=0] - Millisecond (0-999)
1947
+ * @throws {Error} If any component is out of valid range
1948
+ */
1949
+ function validateDateParts(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
1950
+ if (year < 1000 || year > 9999) {
1951
+ throw new Error(`Year must be between 1000 and 9999: ${year}`);
1952
+ }
1953
+ if (month < 0 || month > 11) {
1954
+ throw new Error(`Month must be between 0 and 11: ${month}`);
1955
+ }
1956
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1957
+ if (day < 1 || day > daysInMonth) {
1958
+ throw new Error(`Day must be between 1 and ${daysInMonth} for month ${month}: ${day}`);
1959
+ }
1960
+ if (hour < 0 || hour > 23) {
1961
+ throw new Error(`Hour must be between 0 and 23: ${hour}`);
1962
+ }
1963
+ if (minute < 0 || minute > 59) {
1964
+ throw new Error(`Minute must be between 0 and 59: ${minute}`);
1965
+ }
1966
+ if (second < 0 || second > 59) {
1967
+ throw new Error(`Second must be between 0 and 59: ${second}`);
1968
+ }
1969
+ if (millisecond < 0 || millisecond > 999) {
1970
+ throw new Error(`Millisecond must be between 0 and 999: ${millisecond}`);
1971
+ }
1972
+ }
1973
+ /**
1974
+ * Get timezone abbreviation
1975
+ * @private
1976
+ * @param {Date} date - Date to get timezone for
1977
+ * @returns {string} Timezone abbreviation
1978
+ */
1979
+ function getTimezoneAbbreviation(date) {
1980
+ const formatter = new Intl.DateTimeFormat('en-US', {
1981
+ timeZoneName: 'short',
1982
+ });
1983
+ const parts = formatter.formatToParts(date);
1984
+ const timezonePart = parts.find((part) => part.type === 'timeZoneName');
1985
+ return timezonePart ? timezonePart.value : 'UTC';
1986
+ }
1987
+ /**
1988
+ * Check if Daylight Saving Time is active for a date
1989
+ * @private
1990
+ * @param {Date} date - Date to check
1991
+ * @returns {boolean} True if DST is active
1992
+ */
1993
+ function isDST(date) {
1994
+ const jan = new Date(date.getFullYear(), 0, 1);
1995
+ const jul = new Date(date.getFullYear(), 6, 1);
1996
+ const stdTimezoneOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
1997
+ return date.getTimezoneOffset() < stdTimezoneOffset;
1998
+ }