@odoo/o-spreadsheet 14.0.0 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.eslintrc.json +20 -0
  2. package/.husky/post-checkout +9 -0
  3. package/.husky/pre-commit +39 -0
  4. package/.prettierignore +1 -0
  5. package/astToFormula.patch +0 -0
  6. package/demo/data.js +255 -0
  7. package/demo/index.html +9 -0
  8. package/demo/libs/Chart.bundle.js +20755 -0
  9. package/demo/main.js +101 -0
  10. package/doc/readme.md +46 -0
  11. package/package.json +3 -17
  12. package/rollup.config.js +23 -0
  13. package/src/base_plugin.ts +102 -0
  14. package/src/components/autofill.ts +146 -0
  15. package/src/components/bottom_bar.ts +199 -0
  16. package/src/components/color_picker.ts +150 -0
  17. package/src/components/composer/autocomplete_dropdown.ts +135 -0
  18. package/src/components/composer/composer.ts +487 -0
  19. package/src/components/composer/content_editable_helper.ts +146 -0
  20. package/src/components/figures/chart.ts +70 -0
  21. package/src/components/figures/container.ts +268 -0
  22. package/src/components/figures/text_figure.ts +36 -0
  23. package/src/components/grid.ts +635 -0
  24. package/src/components/helpers/dom_helpers.ts +7 -0
  25. package/src/components/helpers/drag_and_drop.ts +17 -0
  26. package/src/components/icons.ts +36 -0
  27. package/src/components/index.ts +1 -0
  28. package/src/components/menu.ts +288 -0
  29. package/src/components/overlay.ts +457 -0
  30. package/src/components/scrollbar.ts +22 -0
  31. package/src/components/selection_input.ts +168 -0
  32. package/src/components/side_panel/cell_is_rule_editor.ts +169 -0
  33. package/src/components/side_panel/chart_panel.ts +104 -0
  34. package/src/components/side_panel/color_scale_rule_editor.ts +145 -0
  35. package/src/components/side_panel/conditional_formatting.ts +396 -0
  36. package/src/components/side_panel/side_panel.ts +127 -0
  37. package/src/components/side_panel/translations_terms.ts +45 -0
  38. package/src/components/spreadsheet.ts +198 -0
  39. package/src/components/top_bar.ts +535 -0
  40. package/src/constants.ts +26 -0
  41. package/src/data.ts +149 -0
  42. package/src/fonts.ts +41 -0
  43. package/src/formulas/compiler.ts +258 -0
  44. package/src/formulas/composer_tokenizer.ts +70 -0
  45. package/src/formulas/index.ts +15 -0
  46. package/src/formulas/normalize.ts +52 -0
  47. package/src/formulas/parser.ts +280 -0
  48. package/src/formulas/range_tokenizer.ts +97 -0
  49. package/src/formulas/tokenizer.ts +242 -0
  50. package/src/functions/arguments.ts +114 -0
  51. package/src/functions/dates.ts +302 -0
  52. package/src/functions/helpers.ts +592 -0
  53. package/src/functions/index.ts +52 -0
  54. package/src/functions/module_database.ts +282 -0
  55. package/src/functions/module_date.ts +743 -0
  56. package/src/functions/module_info.ts +69 -0
  57. package/src/functions/module_logical.ts +208 -0
  58. package/src/functions/module_lookup.ts +185 -0
  59. package/src/functions/module_math.ts +1252 -0
  60. package/src/functions/module_operators.ts +291 -0
  61. package/src/functions/module_statistical.ts +882 -0
  62. package/src/functions/module_text.ts +370 -0
  63. package/src/helpers/color.ts +30 -0
  64. package/src/helpers/coordinates.ts +71 -0
  65. package/src/helpers/grid_manipulation.ts +68 -0
  66. package/src/helpers/index.ts +7 -0
  67. package/src/helpers/misc.ts +76 -0
  68. package/src/helpers/numbers.ts +117 -0
  69. package/src/helpers/uuid.ts +19 -0
  70. package/src/helpers/zones.ts +263 -0
  71. package/src/history.ts +186 -0
  72. package/src/index.ts +84 -0
  73. package/src/model.ts +261 -0
  74. package/src/plugins/autofill.ts +412 -0
  75. package/src/plugins/chart.ts +404 -0
  76. package/src/plugins/clipboard.ts +461 -0
  77. package/src/plugins/conditional_format.ts +445 -0
  78. package/src/plugins/core.ts +1502 -0
  79. package/src/plugins/edition.ts +157 -0
  80. package/src/plugins/evaluation.ts +428 -0
  81. package/src/plugins/figures.ts +123 -0
  82. package/src/plugins/formatting.ts +630 -0
  83. package/src/plugins/highlight.ts +187 -0
  84. package/src/plugins/index.ts +32 -0
  85. package/src/plugins/merge.ts +524 -0
  86. package/src/plugins/renderer.ts +571 -0
  87. package/src/plugins/selection.ts +562 -0
  88. package/src/plugins/selection_inputs.ts +326 -0
  89. package/src/registries/autofill_modifiers.ts +76 -0
  90. package/src/registries/autofill_rules.ts +98 -0
  91. package/src/registries/figure_registry.ts +30 -0
  92. package/src/registries/index.ts +7 -0
  93. package/src/registries/menu_items_registry.ts +110 -0
  94. package/src/registries/menus/cell_menu_registry.ts +79 -0
  95. package/src/registries/menus/col_menu_registry.ts +63 -0
  96. package/src/registries/menus/index.ts +6 -0
  97. package/src/registries/menus/menu_items_actions.ts +425 -0
  98. package/src/registries/menus/row_menu_registry.ts +63 -0
  99. package/src/registries/menus/sheet_menu_registry.ts +75 -0
  100. package/src/registries/menus/topbar_menu_registry.ts +204 -0
  101. package/src/registries/side_panel_registry.ts +26 -0
  102. package/src/registries/topbar_component_registry.ts +12 -0
  103. package/src/registry.ts +51 -0
  104. package/src/translation.ts +44 -0
  105. package/src/types/autofill.ts +48 -0
  106. package/src/types/chart.ts +26 -0
  107. package/src/types/commands.ts +716 -0
  108. package/src/types/conditional_formatting.ts +124 -0
  109. package/src/types/env.ts +13 -0
  110. package/src/types/functions.ts +35 -0
  111. package/src/types/getters.ts +103 -0
  112. package/src/types/index.ts +20 -0
  113. package/src/types/misc.ts +143 -0
  114. package/src/types/rendering.ts +41 -0
  115. package/src/types/workbook_data.ts +62 -0
  116. package/tests/__mocks__/uuid.ts +7 -0
  117. package/tests/canvas.mock.ts +34 -0
  118. package/tests/components/__mocks__/content_editable_helper.ts +79 -0
  119. package/tests/components/__mocks__/scrollbar.ts +18 -0
  120. package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +29 -0
  121. package/tests/components/__snapshots__/bottom_bar_test.ts.snap +75 -0
  122. package/tests/components/__snapshots__/context_menu_test.ts.snap +88 -0
  123. package/tests/components/__snapshots__/grid_test.ts.snap +60 -0
  124. package/tests/components/__snapshots__/spreadsheet_test.ts.snap +455 -0
  125. package/tests/components/__snapshots__/top_bar_test.ts.snap +705 -0
  126. package/tests/components/autocomplete_dropdown_test.ts +315 -0
  127. package/tests/components/autofill_test.ts +167 -0
  128. package/tests/components/bottom_bar_test.ts +262 -0
  129. package/tests/components/chart_side_panel_test.ts +123 -0
  130. package/tests/components/composer_test.ts +400 -0
  131. package/tests/components/context_menu_test.ts +825 -0
  132. package/tests/components/grid_manipulation_test.ts +163 -0
  133. package/tests/components/grid_test.ts +581 -0
  134. package/tests/components/overlay_test.ts +541 -0
  135. package/tests/components/selection_input_test.ts +259 -0
  136. package/tests/components/side_panel_test.ts +148 -0
  137. package/tests/components/spreadsheet_test.ts +194 -0
  138. package/tests/components/top_bar_test.ts +427 -0
  139. package/tests/data_test.ts +175 -0
  140. package/tests/dom_helper.ts +42 -0
  141. package/tests/formulas/__snapshots__/compiler_test.ts.snap +302 -0
  142. package/tests/formulas/compiler_test.ts +266 -0
  143. package/tests/formulas/composer_tokenizer_test.ts +137 -0
  144. package/tests/formulas/expressions_test.ts +57 -0
  145. package/tests/formulas/formulas_test.ts +80 -0
  146. package/tests/formulas/normalize_test.ts +82 -0
  147. package/tests/formulas/parser_test.ts +224 -0
  148. package/tests/formulas/range_tokenizer_test.ts +187 -0
  149. package/tests/formulas/tokenizer_test.ts +164 -0
  150. package/tests/functions/arguments_test.ts +141 -0
  151. package/tests/functions/dates_test.ts +1081 -0
  152. package/tests/functions/functions_test.ts +53 -0
  153. package/tests/functions/helper_test.ts +98 -0
  154. package/tests/functions/module_database_test.ts +311 -0
  155. package/tests/functions/module_date_test.ts +941 -0
  156. package/tests/functions/module_info_test.ts +139 -0
  157. package/tests/functions/module_logical_test.ts +369 -0
  158. package/tests/functions/module_lookup_test.ts +541 -0
  159. package/tests/functions/module_math_test.ts +2482 -0
  160. package/tests/functions/module_operators_test.ts +955 -0
  161. package/tests/functions/module_statistical_test.ts +2014 -0
  162. package/tests/functions/module_text_test.ts +667 -0
  163. package/tests/helpers/coordinates_test.ts +25 -0
  164. package/tests/helpers/misc_test.ts +95 -0
  165. package/tests/helpers/numbers_test.ts +196 -0
  166. package/tests/helpers/zones_test.ts +97 -0
  167. package/tests/helpers.ts +384 -0
  168. package/tests/menu_items_registry_test.ts +540 -0
  169. package/tests/model/plugins/__snapshots__/chart_test.ts.snap +90 -0
  170. package/tests/model/plugins/chart_test.ts +562 -0
  171. package/tests/model_test.ts +82 -0
  172. package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +131 -0
  173. package/tests/plugins/__snapshots__/renderer_test.ts.snap +371 -0
  174. package/tests/plugins/autofill_test.ts +457 -0
  175. package/tests/plugins/borders_test.ts +295 -0
  176. package/tests/plugins/clipboard_test.ts +1180 -0
  177. package/tests/plugins/conditional_formatting_test.ts +1253 -0
  178. package/tests/plugins/core_test.ts +614 -0
  179. package/tests/plugins/edition_test.ts +100 -0
  180. package/tests/plugins/evaluation_async_test.ts +332 -0
  181. package/tests/plugins/evaluation_dates_test.ts +79 -0
  182. package/tests/plugins/evaluation_test.ts +901 -0
  183. package/tests/plugins/figures_test.ts +327 -0
  184. package/tests/plugins/formatting_test.ts +391 -0
  185. package/tests/plugins/grid_manipulation_test.ts +1366 -0
  186. package/tests/plugins/highlight_test.ts +413 -0
  187. package/tests/plugins/history_test.ts +142 -0
  188. package/tests/plugins/import_export_test.ts +276 -0
  189. package/tests/plugins/merges_test.ts +490 -0
  190. package/tests/plugins/navigation_test.ts +181 -0
  191. package/tests/plugins/plugins/evaluation_test.ts +111 -0
  192. package/tests/plugins/renderer_test.ts +598 -0
  193. package/tests/plugins/resizing_test.ts +152 -0
  194. package/tests/plugins/selection_input_test.ts +409 -0
  195. package/tests/plugins/selection_test.ts +442 -0
  196. package/tests/plugins/sheets_test.ts +643 -0
  197. package/tests/plugins/style_test.ts +90 -0
  198. package/tsconfig.json +22 -0
@@ -0,0 +1,1081 @@
1
+ import { formatDateTime, parseDateTime, toNativeDate } from "../../src/functions/dates";
2
+
3
+ const CURRENT_YEAR = new Date().getFullYear();
4
+
5
+ // -----------------------------------------------------------------------------
6
+ // Test on date
7
+ // -----------------------------------------------------------------------------
8
+
9
+ describe("date helpers: can detect and parse various dates", () => {
10
+ test("can properly convert various dates in a large time span", () => {
11
+ expect(parseDateTime("1/1/1000")!.value).toBe(-328716);
12
+ expect(parseDateTime("1/1/2000")!.value).toBe(36526);
13
+ expect(parseDateTime("1/1/3000")!.value).toBe(401769);
14
+ expect(parseDateTime("1/1/4000")!.value).toBe(767011);
15
+ });
16
+
17
+ test("parse various dates in m/d/yyyy format", () => {
18
+ expect(parseDateTime("12/30/1899")).toEqual({
19
+ value: 0,
20
+ format: "m/d/yyyy",
21
+ jsDate: new Date(1899, 11, 30),
22
+ });
23
+ expect(parseDateTime("12/31/1899")).toEqual({
24
+ value: 1,
25
+ format: "m/d/yyyy",
26
+ jsDate: new Date(1899, 11, 31),
27
+ });
28
+ expect(parseDateTime("1/1/1900")).toEqual({
29
+ value: 2,
30
+ format: "m/d/yyyy",
31
+ jsDate: new Date(1900, 0, 1),
32
+ });
33
+ expect(parseDateTime("4/20/2020")).toEqual({
34
+ value: 43941,
35
+ format: "m/d/yyyy",
36
+ jsDate: new Date(2020, 3, 20),
37
+ });
38
+ });
39
+
40
+ test("parse various dates in yyyy/m/d format", () => {
41
+ expect(parseDateTime("1899/12/30")).toEqual({
42
+ value: 0,
43
+ format: "yyyy/m/d",
44
+ jsDate: new Date(1899, 11, 30),
45
+ });
46
+ expect(parseDateTime("1899/12/31")).toEqual({
47
+ value: 1,
48
+ format: "yyyy/m/d",
49
+ jsDate: new Date(1899, 11, 31),
50
+ });
51
+ expect(parseDateTime("1900/1/1")).toEqual({
52
+ value: 2,
53
+ format: "yyyy/m/d",
54
+ jsDate: new Date(1900, 0, 1),
55
+ });
56
+ expect(parseDateTime("2020/4/20")).toEqual({
57
+ value: 43941,
58
+ format: "yyyy/m/d",
59
+ jsDate: new Date(2020, 3, 20),
60
+ });
61
+ });
62
+
63
+ test("can detect and parse mm/dd/yyyy dates ", () => {
64
+ expect(parseDateTime("03/20/2010")).toEqual({
65
+ value: 40257,
66
+ format: "mm/dd/yyyy",
67
+ jsDate: new Date(2010, 2, 20),
68
+ });
69
+ expect(parseDateTime("10/03/2010")).toEqual({
70
+ value: 40454,
71
+ format: "mm/dd/yyyy",
72
+ jsDate: new Date(2010, 9, 3),
73
+ });
74
+ });
75
+
76
+ test("can detect and parse yyyy/mm/dd dates ", () => {
77
+ expect(parseDateTime("2010/03/20")).toEqual({
78
+ value: 40257,
79
+ format: "yyyy/mm/dd",
80
+ jsDate: new Date(2010, 2, 20),
81
+ });
82
+ expect(parseDateTime("2010/10/03")).toEqual({
83
+ value: 40454,
84
+ format: "yyyy/mm/dd",
85
+ jsDate: new Date(2010, 9, 3),
86
+ });
87
+ });
88
+
89
+ test("can detect and parse m/d and mm/dd dates ", () => {
90
+ const d1 = parseDateTime("1/3")!;
91
+ expect(d1.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
92
+ expect(d1.format).toBe("m/d");
93
+
94
+ const d2 = parseDateTime("1/03")!;
95
+ expect(d2.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
96
+ expect(d2.format).toBe("mm/dd");
97
+ });
98
+
99
+ test("can detect and parse m-d-yyyy dates ", () => {
100
+ expect(parseDateTime("3-2-2010")).toEqual({
101
+ value: 40239,
102
+ format: "m-d-yyyy",
103
+ jsDate: new Date(2010, 2, 2),
104
+ });
105
+ expect(parseDateTime("10-23-2010")).toEqual({
106
+ value: 40474,
107
+ format: "m-d-yyyy",
108
+ jsDate: new Date(2010, 9, 23),
109
+ });
110
+ });
111
+
112
+ test("can detect and parse yyyy-m-d dates ", () => {
113
+ expect(parseDateTime("2010-3-2")).toEqual({
114
+ value: 40239,
115
+ format: "yyyy-m-d",
116
+ jsDate: new Date(2010, 2, 2),
117
+ });
118
+ expect(parseDateTime("2010-10-23")).toEqual({
119
+ value: 40474,
120
+ format: "yyyy-m-d",
121
+ jsDate: new Date(2010, 9, 23),
122
+ });
123
+ });
124
+
125
+ test("can detect and parse yyyy-mm-dd dates ", () => {
126
+ expect(parseDateTime("2010-03-20")).toEqual({
127
+ value: 40257,
128
+ format: "yyyy-mm-dd",
129
+ jsDate: new Date(2010, 2, 20),
130
+ });
131
+ expect(parseDateTime("2010-10-03")).toEqual({
132
+ value: 40454,
133
+ format: "yyyy-mm-dd",
134
+ jsDate: new Date(2010, 9, 3),
135
+ });
136
+ });
137
+
138
+ test("can detect and parse m-d and mm-dd dates ", () => {
139
+ const d1 = parseDateTime("1-3")!;
140
+ expect(d1.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
141
+ expect(d1.format).toBe("m-d");
142
+
143
+ const d2 = parseDateTime("1-03")!;
144
+ expect(d2.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
145
+ expect(d2.format).toBe("mm-dd");
146
+ });
147
+
148
+ test('can detect and parse "mm dd yyyy" dates ', () => {
149
+ expect(parseDateTime("03 20 2010")).toEqual({
150
+ value: 40257,
151
+ format: "mm dd yyyy",
152
+ jsDate: new Date(2010, 2, 20),
153
+ });
154
+ expect(parseDateTime("10 03 2010")).toEqual({
155
+ value: 40454,
156
+ format: "mm dd yyyy",
157
+ jsDate: new Date(2010, 9, 3),
158
+ });
159
+ });
160
+
161
+ test('can detect and parse "yyyy mm dd" dates ', () => {
162
+ expect(parseDateTime("2010 03 20")).toEqual({
163
+ value: 40257,
164
+ format: "yyyy mm dd",
165
+ jsDate: new Date(2010, 2, 20),
166
+ });
167
+ expect(parseDateTime("2010 10 03")).toEqual({
168
+ value: 40454,
169
+ format: "yyyy mm dd",
170
+ jsDate: new Date(2010, 9, 3),
171
+ });
172
+ });
173
+
174
+ test("can detect and parse 'm d' and 'mm dd' dates ", () => {
175
+ const d1 = parseDateTime("1 3")!;
176
+ expect(d1.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
177
+ expect(d1.format).toBe("m d");
178
+
179
+ const d2 = parseDateTime("1 03")!;
180
+ expect(d2.jsDate!).toEqual(new Date(CURRENT_YEAR, 0, 3));
181
+ expect(d2.format).toBe("mm dd");
182
+ });
183
+
184
+ test("does not parse invalid dates", () => {
185
+ expect(parseDateTime("100/100/2099")).toBeNull();
186
+ expect(parseDateTime("20/10/2020")).toBeNull(); // 20 is not a valid month
187
+ expect(parseDateTime("02/29/2020")).not.toBeNull();
188
+ expect(parseDateTime("02/30/2020")).toBeNull();
189
+ expect(parseDateTime("02/28/2021")).not.toBeNull();
190
+ expect(parseDateTime("02/29/2021")).toBeNull();
191
+ expect(parseDateTime("01/33/2021")).toBeNull();
192
+ });
193
+
194
+ test("can infer a year if not given completely", () => {
195
+ const getYear = (str) => toNativeDate(parseDateTime(str)!).getFullYear();
196
+
197
+ expect(getYear("1/1/0")).toBe(2000);
198
+ expect(getYear("1/1/8")).toBe(2008);
199
+ expect(getYear("1/1/12")).toBe(2012);
200
+
201
+ expect(getYear(`1/1/${CURRENT_YEAR - 2000 + 9}`)).toBe(CURRENT_YEAR + 9);
202
+ expect(getYear(`1/1/${CURRENT_YEAR - 2000 + 10}`)).toBe(CURRENT_YEAR + 10);
203
+ expect(getYear(`1/1/${CURRENT_YEAR - 2000 + 11}`)).toBe(CURRENT_YEAR + 11 - 100);
204
+ });
205
+
206
+ test("refuse various strings", () => {
207
+ expect(parseDateTime("1/1/1920/3")).toBe(null);
208
+ expect(parseDateTime("1.2/10/2020")).toBe(null);
209
+ });
210
+ });
211
+
212
+ describe("formatDateTime", () => {
213
+ test("month day year, with / as separator", () => {
214
+ expect(formatDateTime(parseDateTime("01/02/1954")!)).toBe("01/02/1954");
215
+ expect(formatDateTime(parseDateTime("1/2/3")!)).toBe("1/2/2003");
216
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m/d/yyyy")).toBe("1/2/1954");
217
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm/dd/yyyy")).toBe("01/02/1954");
218
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm/dd")).toBe("01/02");
219
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m/d")).toBe("1/2");
220
+ });
221
+
222
+ test("month day year, with - as separator", () => {
223
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m-d-yyyy")).toBe("1-2-1954");
224
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm-dd-yyyy")).toBe("01-02-1954");
225
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm-dd")).toBe("01-02");
226
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m-d")).toBe("1-2");
227
+ });
228
+
229
+ test("month day year, with ' ' as separator", () => {
230
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m d yyyy")).toBe("1 2 1954");
231
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm dd yyyy")).toBe("01 02 1954");
232
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "mm dd")).toBe("01 02");
233
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "m d")).toBe("1 2");
234
+ });
235
+
236
+ test("year month day, with / as separator", () => {
237
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy/m/d")).toBe("1954/1/2");
238
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy/mm/dd")).toBe("1954/01/02");
239
+ });
240
+
241
+ test("year month day, with - as separator", () => {
242
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy-m-d")).toBe("1954-1-2");
243
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy-mm-dd")).toBe("1954-01-02");
244
+ });
245
+
246
+ test("year month day, with ' ' as separator", () => {
247
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy m d")).toBe("1954 1 2");
248
+ expect(formatDateTime(parseDateTime("01/02/1954")!, "yyyy mm dd")).toBe("1954 01 02");
249
+ });
250
+ });
251
+
252
+ // -----------------------------------------------------------------------------
253
+ // Test on time
254
+ // -----------------------------------------------------------------------------
255
+
256
+ describe("date helpers: can detect and parse various times", () => {
257
+ test("can detect and parse 'hh:mm' times", () => {
258
+ expect(parseDateTime("00:00")).toEqual({
259
+ value: 0,
260
+ format: "hh:mm",
261
+ jsDate: new Date(1899, 11, 30, 0, 0, 0),
262
+ });
263
+ expect(parseDateTime("6:00")).toEqual({
264
+ value: 0.25,
265
+ format: "hh:mm",
266
+ jsDate: new Date(1899, 11, 30, 6, 0, 0),
267
+ });
268
+ expect(parseDateTime("12:09")).toEqual({
269
+ value: 0.50625,
270
+ format: "hh:mm",
271
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
272
+ });
273
+ expect(parseDateTime("12:9")).toEqual({
274
+ // @compatibility: on google sheets, return string
275
+ value: 0.50625,
276
+ format: "hh:mm",
277
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
278
+ });
279
+ expect(parseDateTime("00012:09")).toEqual({
280
+ value: 0.50625,
281
+ format: "hh:mm",
282
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
283
+ });
284
+ expect(parseDateTime("12:00000009")).toEqual({
285
+ value: 0.50625,
286
+ format: "hh:mm",
287
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
288
+ });
289
+ expect(parseDateTime("11:69")).toEqual({
290
+ value: 0.50625,
291
+ format: "hh:mm",
292
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
293
+ });
294
+ });
295
+
296
+ test("can detect and parse 'hh:mm:ss' times", () => {
297
+ expect(parseDateTime("12:00:00")).toEqual({
298
+ value: 0.5,
299
+ format: "hh:mm:ss",
300
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
301
+ });
302
+ expect(parseDateTime("12:08:06")).toEqual({
303
+ value: 0.505625,
304
+ format: "hh:mm:ss",
305
+ jsDate: new Date(1899, 11, 30, 12, 8, 6),
306
+ });
307
+ expect(parseDateTime("12:8:6")).toEqual({
308
+ value: 0.505625,
309
+ format: "hh:mm:ss",
310
+ jsDate: new Date(1899, 11, 30, 12, 8, 6),
311
+ });
312
+ expect(parseDateTime("12:008:006")).toEqual({
313
+ value: 0.505625,
314
+ format: "hh:mm:ss",
315
+ jsDate: new Date(1899, 11, 30, 12, 8, 6),
316
+ });
317
+ expect(parseDateTime("11:59:546")).toEqual({
318
+ value: 0.505625,
319
+ format: "hh:mm:ss",
320
+ jsDate: new Date(1899, 11, 30, 12, 8, 6),
321
+ });
322
+ });
323
+
324
+ test("can detect and parse 'hh:mm a' times", () => {
325
+ expect(parseDateTime("0 AM")).toEqual({
326
+ value: 0,
327
+ format: "hh:mm a",
328
+ jsDate: new Date(1899, 11, 30, 0, 0, 0),
329
+ });
330
+ expect(parseDateTime("12 AM")).toEqual({
331
+ value: 0,
332
+ format: "hh:mm a",
333
+ jsDate: new Date(1899, 11, 30, 0, 0, 0),
334
+ });
335
+ expect(parseDateTime("24 AM")).toEqual({
336
+ // @compatibility: on google sheets, return string
337
+ value: 0.5,
338
+ format: "hh:mm a",
339
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
340
+ });
341
+ expect(parseDateTime("6AM")).toEqual({
342
+ value: 0.25,
343
+ format: "hh:mm a",
344
+ jsDate: new Date(1899, 11, 30, 6, 0, 0),
345
+ });
346
+ expect(parseDateTime("6 AM")).toEqual({
347
+ value: 0.25,
348
+ format: "hh:mm a",
349
+ jsDate: new Date(1899, 11, 30, 6, 0, 0),
350
+ });
351
+
352
+ expect(parseDateTime("0 PM")).toEqual({
353
+ value: 0.5,
354
+ format: "hh:mm a",
355
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
356
+ });
357
+ expect(parseDateTime("12 PM")).toEqual({
358
+ value: 0.5,
359
+ format: "hh:mm a",
360
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
361
+ });
362
+ expect(parseDateTime("6PM")).toEqual({
363
+ value: 0.75,
364
+ format: "hh:mm a",
365
+ jsDate: new Date(1899, 11, 30, 18, 0, 0),
366
+ });
367
+ expect(parseDateTime("6 PM")).toEqual({
368
+ value: 0.75,
369
+ format: "hh:mm a",
370
+ jsDate: new Date(1899, 11, 30, 18, 0, 0),
371
+ });
372
+
373
+ expect(parseDateTime("0:09 AM")).toEqual({
374
+ value: 0.00625,
375
+ format: "hh:mm a",
376
+ jsDate: new Date(1899, 11, 30, 0, 9, 0),
377
+ });
378
+ expect(parseDateTime("12:09 AM")).toEqual({
379
+ value: 0.00625,
380
+ format: "hh:mm a",
381
+ jsDate: new Date(1899, 11, 30, 0, 9, 0),
382
+ });
383
+ expect(parseDateTime("00012:00000009 AM")).toEqual({
384
+ value: 0.00625,
385
+ format: "hh:mm a",
386
+ jsDate: new Date(1899, 11, 30, 0, 9, 0),
387
+ });
388
+
389
+ expect(parseDateTime("11:69 AM")).toEqual({
390
+ value: 0.50625,
391
+ format: "hh:mm a",
392
+ jsDate: new Date(1899, 11, 30, 12, 9, 0),
393
+ });
394
+ expect(parseDateTime("18:00 AM")).toEqual({
395
+ value: 0.25,
396
+ format: "hh:mm a",
397
+ jsDate: new Date(1899, 11, 30, 6, 0, 0),
398
+ });
399
+ });
400
+
401
+ test("can detect and parse 'hh:mm:ss a' times", () => {
402
+ expect(parseDateTime("12:00:00 AM")).toEqual({
403
+ value: 0,
404
+ format: "hh:mm:ss a",
405
+ jsDate: new Date(1899, 11, 30, 0, 0, 0),
406
+ });
407
+ expect(parseDateTime("00:00:00 AM")).toEqual({
408
+ value: 0,
409
+ format: "hh:mm:ss a",
410
+ jsDate: new Date(1899, 11, 30, 0, 0, 0),
411
+ });
412
+ expect(parseDateTime("12:00:00 PM")).toEqual({
413
+ value: 0.5,
414
+ format: "hh:mm:ss a",
415
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
416
+ });
417
+ expect(parseDateTime("0:00:00 PM")).toEqual({
418
+ value: 0.5,
419
+ format: "hh:mm:ss a",
420
+ jsDate: new Date(1899, 11, 30, 12, 0, 0),
421
+ });
422
+ expect(parseDateTime("12:08:06 AM")).toEqual({
423
+ value: 0.005625,
424
+ format: "hh:mm:ss a",
425
+ jsDate: new Date(1899, 11, 30, 0, 8, 6),
426
+ });
427
+ expect(parseDateTime("12:8:6 AM")).toEqual({
428
+ value: 0.005625,
429
+ format: "hh:mm:ss a",
430
+ jsDate: new Date(1899, 11, 30, 0, 8, 6),
431
+ });
432
+ expect(parseDateTime("12:008:006 AM")).toEqual({
433
+ value: 0.005625,
434
+ format: "hh:mm:ss a",
435
+ jsDate: new Date(1899, 11, 30, 0, 8, 6),
436
+ });
437
+ expect(parseDateTime("11:59:546 AM")).toEqual({
438
+ value: 0.505625,
439
+ format: "hh:mm:ss a",
440
+ jsDate: new Date(1899, 11, 30, 12, 8, 6),
441
+ });
442
+ });
443
+
444
+ test("can detect and parse 'hhhh:mm:ss' times", () => {
445
+ expect(parseDateTime("30:00")).toEqual({
446
+ value: 1.25,
447
+ format: "hhhh:mm:ss",
448
+ jsDate: new Date(1899, 11, 31, 6, 0, 0),
449
+ });
450
+ expect(parseDateTime("24:08:06")).toEqual({
451
+ value: 1.005625,
452
+ format: "hhhh:mm:ss",
453
+ jsDate: new Date(1899, 11, 31, 0, 8, 6),
454
+ });
455
+ expect(parseDateTime("36 AM")).toEqual({
456
+ value: 1,
457
+ format: "hhhh:mm:ss",
458
+ jsDate: new Date(1899, 11, 31, 0, 0, 0),
459
+ });
460
+ expect(parseDateTime("24 PM")).toEqual({
461
+ value: 1,
462
+ format: "hhhh:mm:ss",
463
+ jsDate: new Date(1899, 11, 31, 0, 0, 0),
464
+ });
465
+ expect(parseDateTime("36:09 AM")).toEqual({
466
+ value: 1.00625,
467
+ format: "hhhh:mm:ss",
468
+ jsDate: new Date(1899, 11, 31, 0, 9, 0),
469
+ });
470
+ expect(parseDateTime("23:59:60 PM")).toEqual({
471
+ value: 1,
472
+ format: "hhhh:mm:ss",
473
+ jsDate: new Date(1899, 11, 31, 0, 0, 0),
474
+ });
475
+ });
476
+ });
477
+
478
+ describe("formatDateTime", () => {
479
+ test("hours minutes 'hh:mm'", () => {
480
+ expect(formatDateTime(parseDateTime("0:0")!)).toBe("00:00");
481
+ expect(formatDateTime(parseDateTime("6:0")!)).toBe("06:00");
482
+ expect(formatDateTime(parseDateTime("12:9")!)).toBe("12:09");
483
+ expect(formatDateTime(parseDateTime("00012:09")!)).toBe("12:09");
484
+ expect(formatDateTime(parseDateTime("12:00000009")!)).toBe("12:09");
485
+ expect(formatDateTime(parseDateTime("11:69")!)).toBe("12:09");
486
+
487
+ expect(formatDateTime(parseDateTime("12:08:06")!, "hh:mm")).toBe("12:08");
488
+ expect(formatDateTime(parseDateTime("05:09 PM")!, "hh:mm")).toBe("17:09");
489
+ expect(formatDateTime(parseDateTime("012:008:006 AM")!, "hh:mm")).toBe("00:08");
490
+ expect(formatDateTime(parseDateTime("30:00:00")!, "hh:mm")).toBe("06:00");
491
+ });
492
+
493
+ test("hours minutes seconds 'hh:mm:ss'", () => {
494
+ expect(formatDateTime(parseDateTime("12:0:0")!)).toBe("12:00:00");
495
+ expect(formatDateTime(parseDateTime("12:8:6")!)).toBe("12:08:06");
496
+ expect(formatDateTime(parseDateTime("0012:008:006")!)).toBe("12:08:06");
497
+ expect(formatDateTime(parseDateTime("11:59:546")!)).toBe("12:08:06");
498
+
499
+ expect(formatDateTime(parseDateTime("12:08")!, "hh:mm:ss")).toBe("12:08:00");
500
+ expect(formatDateTime(parseDateTime("05:09 PM")!, "hh:mm:ss")).toBe("17:09:00");
501
+ expect(formatDateTime(parseDateTime("012:008:006 AM")!, "hh:mm:ss")).toBe("00:08:06");
502
+ expect(formatDateTime(parseDateTime("30:00:00")!, "hh:mm:ss")).toBe("06:00:00");
503
+ });
504
+
505
+ test("hours minutes meridian 'hh:mm a", () => {
506
+ expect(formatDateTime(parseDateTime("0 AM")!)).toBe("12:00 AM");
507
+ expect(formatDateTime(parseDateTime("0 PM")!)).toBe("12:00 PM");
508
+ expect(formatDateTime(parseDateTime("6AM")!)).toBe("06:00 AM");
509
+ expect(formatDateTime(parseDateTime("6 AM")!)).toBe("06:00 AM");
510
+ expect(formatDateTime(parseDateTime("7PM")!)).toBe("07:00 PM");
511
+ expect(formatDateTime(parseDateTime("7 PM")!)).toBe("07:00 PM");
512
+ expect(formatDateTime(parseDateTime("12 AM")!)).toBe("12:00 AM");
513
+ expect(formatDateTime(parseDateTime("12 PM")!)).toBe("12:00 PM");
514
+ expect(formatDateTime(parseDateTime("13 AM")!)).toBe("01:00 AM"); // @compatibility: on google sheets, parsing impposible
515
+ expect(formatDateTime(parseDateTime("13 PM")!)).toBe("01:00 PM"); // @compatibility: on google sheets, parsing impposible
516
+ expect(formatDateTime(parseDateTime("24 AM")!)).toBe("12:00 PM"); // @compatibility: on google sheets, parsing impposible
517
+ expect(formatDateTime(parseDateTime("0:09 AM")!)).toBe("12:09 AM");
518
+ expect(formatDateTime(parseDateTime("12:9 AM")!)).toBe("12:09 AM");
519
+ expect(formatDateTime(parseDateTime("00012:0009 AM")!)).toBe("12:09 AM");
520
+ expect(formatDateTime(parseDateTime("11:69 AM")!)).toBe("12:09 PM");
521
+ expect(formatDateTime(parseDateTime("18:00 AM")!)).toBe("06:00 AM");
522
+
523
+ expect(formatDateTime(parseDateTime("12:08")!, "hh:mm a")).toBe("12:08 PM");
524
+ expect(formatDateTime(parseDateTime("12:08:06")!, "hh:mm a")).toBe("12:08 PM");
525
+ expect(formatDateTime(parseDateTime("012:008:006 AM")!, "hh:mm a")).toBe("12:08 AM");
526
+ expect(formatDateTime(parseDateTime("30:00:00")!, "hh:mm a")).toBe("06:00 AM");
527
+ });
528
+
529
+ test("hours minutes seconds meridian 'hh:mm:ss a", () => {
530
+ expect(formatDateTime(parseDateTime("00:00:00 AM")!)).toBe("12:00:00 AM");
531
+ expect(formatDateTime(parseDateTime("00:00:00 PM")!)).toBe("12:00:00 PM");
532
+ expect(formatDateTime(parseDateTime("12:00:00 AM")!)).toBe("12:00:00 AM");
533
+ expect(formatDateTime(parseDateTime("012:008:006 AM")!)).toBe("12:08:06 AM");
534
+ expect(formatDateTime(parseDateTime("11:59:546 AM")!)).toBe("12:08:06 PM");
535
+
536
+ expect(formatDateTime(parseDateTime("12:08")!, "hh:mm:ss a")).toBe("12:08:00 PM");
537
+ expect(formatDateTime(parseDateTime("12:08:06")!, "hh:mm:ss a")).toBe("12:08:06 PM");
538
+ expect(formatDateTime(parseDateTime("05:09 PM")!, "hh:mm:ss a")).toBe("05:09:00 PM");
539
+ expect(formatDateTime(parseDateTime("30:00:00")!, "hh:mm:ss a")).toBe("06:00:00 AM");
540
+ });
541
+
542
+ test("duration 'hhhh:mm:ss", () => {
543
+ expect(formatDateTime(parseDateTime("30:00")!)).toBe("30:00:00");
544
+ expect(formatDateTime(parseDateTime("24:08:06")!)).toBe("24:08:06");
545
+ expect(formatDateTime(parseDateTime("36:09 AM")!)).toBe("24:09:00"); // @compatibility: on google sheets, parsing impposible
546
+ expect(formatDateTime(parseDateTime("24 PM")!)).toBe("24:00:00"); // @compatibility: on google sheets, parsing impposible
547
+ expect(formatDateTime(parseDateTime("11:59:546 PM")!)).toBe("24:08:06");
548
+
549
+ expect(formatDateTime(parseDateTime("12:08")!, "hhhh:mm:ss")).toBe("12:08:00");
550
+ expect(formatDateTime(parseDateTime("12:08:06")!, "hhhh:mm:ss")).toBe("12:08:06");
551
+ expect(formatDateTime(parseDateTime("05:09 PM")!, "hhhh:mm:ss")).toBe("17:09:00");
552
+ expect(formatDateTime(parseDateTime("012:008:006 AM")!, "hhhh:mm:ss")).toBe("0:08:06");
553
+ });
554
+ });
555
+
556
+ // -----------------------------------------------------------------------------
557
+ // Test on date and time
558
+ // -----------------------------------------------------------------------------
559
+
560
+ describe("date helpers: can detect and parse various datetimes (with '/' separator)", () => {
561
+ // m/d/yyyy
562
+
563
+ test("can detect and parse 'm/d/yyyy hh:mm' datetime", () => {
564
+ expect(parseDateTime("4/20/2020 12:09")).toEqual({
565
+ value: 43941.50625,
566
+ format: "m/d/yyyy hh:mm",
567
+ jsDate: new Date(2020, 3, 20, 12, 9, 0),
568
+ });
569
+ });
570
+
571
+ test("can detect and parse 'm/d/yyyy hh:mm:ss' datetime", () => {
572
+ expect(parseDateTime("4/20/2020 12:08:06")).toEqual({
573
+ value: 43941.505625,
574
+ format: "m/d/yyyy hh:mm:ss",
575
+ jsDate: new Date(2020, 3, 20, 12, 8, 6),
576
+ });
577
+ });
578
+
579
+ test("can detect and parse 'm/d/yyyy hh:mm a' datetime", () => {
580
+ expect(parseDateTime("4/20/2020 12:09 AM")).toEqual({
581
+ value: 43941.00625,
582
+ format: "m/d/yyyy hh:mm a",
583
+ jsDate: new Date(2020, 3, 20, 0, 9, 0),
584
+ });
585
+ expect(parseDateTime("4/20/2020 6 PM")).toEqual({
586
+ value: 43941.75,
587
+ format: "m/d/yyyy hh:mm a",
588
+ jsDate: new Date(2020, 3, 20, 18, 0, 0),
589
+ });
590
+ });
591
+
592
+ test("can detect and parse 'm/d/yyyy hh:mm:ss a' datetime", () => {
593
+ expect(parseDateTime("4/20/2020 12:08:06 AM")).toEqual({
594
+ value: 43941.005625,
595
+ format: "m/d/yyyy hh:mm:ss a",
596
+ jsDate: new Date(2020, 3, 20, 0, 8, 6),
597
+ });
598
+ });
599
+
600
+ // yyyy/m/d
601
+
602
+ test("can detect and parse 'yyyy/m/d hh:mm' datetime", () => {
603
+ expect(parseDateTime("2020/4/20 12:09")).toEqual({
604
+ value: 43941.50625,
605
+ format: "yyyy/m/d hh:mm",
606
+ jsDate: new Date(2020, 3, 20, 12, 9, 0),
607
+ });
608
+ });
609
+
610
+ test("can detect and parse 'yyyy/m/d hh:mm:ss' datetime", () => {
611
+ expect(parseDateTime("2020/4/20 12:08:06")).toEqual({
612
+ value: 43941.505625,
613
+ format: "yyyy/m/d hh:mm:ss",
614
+ jsDate: new Date(2020, 3, 20, 12, 8, 6),
615
+ });
616
+ });
617
+
618
+ test("can detect and parse 'yyyy/m/d hh:mm a' datetime", () => {
619
+ expect(parseDateTime("2020/4/20 12:09 AM")).toEqual({
620
+ value: 43941.00625,
621
+ format: "yyyy/m/d hh:mm a",
622
+ jsDate: new Date(2020, 3, 20, 0, 9, 0),
623
+ });
624
+ expect(parseDateTime("2020/4/20 6 PM")).toEqual({
625
+ value: 43941.75,
626
+ format: "yyyy/m/d hh:mm a",
627
+ jsDate: new Date(2020, 3, 20, 18, 0, 0),
628
+ });
629
+ });
630
+
631
+ test("can detect and parse 'yyyy/m/d hh:mm:ss a' datetime", () => {
632
+ expect(parseDateTime("2020/4/20 12:08:06 AM")).toEqual({
633
+ value: 43941.005625,
634
+ format: "yyyy/m/d hh:mm:ss a",
635
+ jsDate: new Date(2020, 3, 20, 0, 8, 6),
636
+ });
637
+ });
638
+
639
+ // mm/dd/yyyy
640
+
641
+ test("can detect and parse 'mm/dd/yyyy hh:mm' datetime", () => {
642
+ expect(parseDateTime("03/20/2010 12:09")).toEqual({
643
+ value: 40257.50625,
644
+ format: "mm/dd/yyyy hh:mm",
645
+ jsDate: new Date(2010, 2, 20, 12, 9, 0),
646
+ });
647
+ });
648
+
649
+ test("can detect and parse 'mm/dd/yyyy hh:mm:ss' datetime", () => {
650
+ expect(parseDateTime("03/20/2010 12:08:06")).toEqual({
651
+ value: 40257.505625,
652
+ format: "mm/dd/yyyy hh:mm:ss",
653
+ jsDate: new Date(2010, 2, 20, 12, 8, 6),
654
+ });
655
+ });
656
+
657
+ test("can detect and parse 'mm/dd/yyyy hh:mm a' datetime", () => {
658
+ expect(parseDateTime("03/20/2010 12:09 AM")).toEqual({
659
+ value: 40257.00625,
660
+ format: "mm/dd/yyyy hh:mm a",
661
+ jsDate: new Date(2010, 2, 20, 0, 9, 0),
662
+ });
663
+ expect(parseDateTime("03/20/2010 6 PM")).toEqual({
664
+ value: 40257.75,
665
+
666
+ format: "mm/dd/yyyy hh:mm a",
667
+ jsDate: new Date(2010, 2, 20, 18, 0, 0),
668
+ });
669
+ });
670
+
671
+ test("can detect and parse 'mm/dd/yyyy hh:mm:ss a' datetime", () => {
672
+ expect(parseDateTime("03/20/2010 12:08:06 AM")).toEqual({
673
+ value: 40257.005625,
674
+ format: "mm/dd/yyyy hh:mm:ss a",
675
+ jsDate: new Date(2010, 2, 20, 0, 8, 6),
676
+ });
677
+ });
678
+
679
+ // yyyy/mm/dd
680
+
681
+ test("can detect and parse 'yyyy/mm/dd hh:mm' datetime", () => {
682
+ expect(parseDateTime("2010/03/20 12:09")).toEqual({
683
+ value: 40257.50625,
684
+ format: "yyyy/mm/dd hh:mm",
685
+ jsDate: new Date(2010, 2, 20, 12, 9, 0),
686
+ });
687
+ });
688
+
689
+ test("can detect and parse 'yyyy/mm/dd hh:mm:ss' datetime", () => {
690
+ expect(parseDateTime("2010/03/20 12:08:06")).toEqual({
691
+ value: 40257.505625,
692
+ format: "yyyy/mm/dd hh:mm:ss",
693
+ jsDate: new Date(2010, 2, 20, 12, 8, 6),
694
+ });
695
+ });
696
+
697
+ test("can detect and parse 'yyyy/mm/dd hh:mm a' datetime", () => {
698
+ expect(parseDateTime("2010/03/20 12:09 AM")).toEqual({
699
+ value: 40257.00625,
700
+ format: "yyyy/mm/dd hh:mm a",
701
+ jsDate: new Date(2010, 2, 20, 0, 9, 0),
702
+ });
703
+ expect(parseDateTime("2010/03/20 6 PM")).toEqual({
704
+ value: 40257.75,
705
+ format: "yyyy/mm/dd hh:mm a",
706
+ jsDate: new Date(2010, 2, 20, 18, 0, 0),
707
+ });
708
+ });
709
+
710
+ test("can detect and parse 'yyyy/mm/dd hh:mm:ss a' datetime", () => {
711
+ expect(parseDateTime("2010/03/20 12:08:06 AM")).toEqual({
712
+ value: 40257.005625,
713
+ format: "yyyy/mm/dd hh:mm:ss a",
714
+ jsDate: new Date(2010, 2, 20, 0, 8, 6),
715
+ });
716
+ });
717
+
718
+ // m/d
719
+
720
+ test("can detect and parse 'm/d hh:mm' datetime", () => {
721
+ const d = parseDateTime("1/3 12:09")!;
722
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 9, 0));
723
+ expect(d.format).toBe("m/d hh:mm");
724
+ });
725
+
726
+ test("can detect and parse 'm/d hh:mm:ss' datetime", () => {
727
+ const d = parseDateTime("1/3 12:08:06")!;
728
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 8, 6));
729
+ expect(d.format).toBe("m/d hh:mm:ss");
730
+ });
731
+
732
+ test("can detect and parse 'm/d hh:mm a' datetime", () => {
733
+ const d1 = parseDateTime("1/3 12:09 AM")!;
734
+ expect(d1.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 9, 0));
735
+ expect(d1.format).toBe("m/d hh:mm a");
736
+
737
+ const d2 = parseDateTime("1/3 6 PM")!;
738
+ expect(d2.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 18, 0, 0));
739
+ expect(d2.format).toBe("m/d hh:mm a");
740
+ });
741
+
742
+ test("can detect and parse 'm/d hh:mm:ss a' datetime", () => {
743
+ const d = parseDateTime("1/3 12:08:06 AM")!;
744
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 8, 6));
745
+ expect(d.format).toBe("m/d hh:mm:ss a");
746
+ });
747
+
748
+ // mm/dd
749
+
750
+ test("can detect and parse 'mm/dd hh:mm' datetime", () => {
751
+ const d = parseDateTime("1/03 12:09")!;
752
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 9, 0));
753
+ expect(d.format).toBe("mm/dd hh:mm");
754
+ });
755
+
756
+ test("can detect and parse 'mm/dd hh:mm:ss' datetime", () => {
757
+ const d = parseDateTime("1/03 12:08:06")!;
758
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 8, 6));
759
+ expect(d.format).toBe("mm/dd hh:mm:ss");
760
+ });
761
+
762
+ test("can detect and parse 'mm/dd hh:mm a' datetime", () => {
763
+ const d1 = parseDateTime("1/03 12:09 AM")!;
764
+ expect(d1.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 9, 0));
765
+ expect(d1.format).toBe("mm/dd hh:mm a");
766
+
767
+ const d2 = parseDateTime("1/03 6 PM")!;
768
+ expect(d2.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 18, 0, 0));
769
+ expect(d2.format).toBe("mm/dd hh:mm a");
770
+ });
771
+
772
+ test("can detect and parse 'mm/dd hh:mm:ss a' datetime", () => {
773
+ const d = parseDateTime("1/03 12:08:06 AM")!;
774
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 8, 6));
775
+ expect(d.format).toBe("mm/dd hh:mm:ss a");
776
+ });
777
+ });
778
+
779
+ describe("date helpers: can detect and parse various datetime (with ' ' separator)", () => {
780
+ // m d yyyy
781
+
782
+ test("can detect and parse 'm d yyyy hh:mm' datetime", () => {
783
+ expect(parseDateTime("4 20 2020 12:09")).toEqual({
784
+ value: 43941.50625,
785
+ format: "m d yyyy hh:mm",
786
+ jsDate: new Date(2020, 3, 20, 12, 9, 0),
787
+ });
788
+ });
789
+
790
+ test("can detect and parse 'm d yyyy hh:mm:ss' datetime", () => {
791
+ expect(parseDateTime("4 20 2020 12:08:06")).toEqual({
792
+ value: 43941.505625,
793
+ format: "m d yyyy hh:mm:ss",
794
+ jsDate: new Date(2020, 3, 20, 12, 8, 6),
795
+ });
796
+ });
797
+
798
+ test("can detect and parse 'm d yyyy hh:mm a' datetime", () => {
799
+ expect(parseDateTime("4 20 2020 12:09 AM")).toEqual({
800
+ value: 43941.00625,
801
+ format: "m d yyyy hh:mm a",
802
+ jsDate: new Date(2020, 3, 20, 0, 9, 0),
803
+ });
804
+ expect(parseDateTime("4 20 2020 6 PM")).toEqual({
805
+ value: 43941.75,
806
+ format: "m d yyyy hh:mm a",
807
+ jsDate: new Date(2020, 3, 20, 18, 0, 0),
808
+ });
809
+ });
810
+
811
+ test("can detect and parse 'm d yyyy hh:mm:ss a' datetime", () => {
812
+ expect(parseDateTime("4 20 2020 12:08:06 AM")).toEqual({
813
+ value: 43941.005625,
814
+ format: "m d yyyy hh:mm:ss a",
815
+ jsDate: new Date(2020, 3, 20, 0, 8, 6),
816
+ });
817
+ });
818
+
819
+ // yyyy m d
820
+
821
+ test("can detect and parse 'yyyy m d hh:mm' datetime", () => {
822
+ expect(parseDateTime("2020 4 20 12:09")).toEqual({
823
+ value: 43941.50625,
824
+ format: "yyyy m d hh:mm",
825
+ jsDate: new Date(2020, 3, 20, 12, 9, 0),
826
+ });
827
+ });
828
+
829
+ test("can detect and parse 'yyyy m d hh:mm:ss' datetime", () => {
830
+ expect(parseDateTime("2020 4 20 12:08:06")).toEqual({
831
+ value: 43941.505625,
832
+ format: "yyyy m d hh:mm:ss",
833
+ jsDate: new Date(2020, 3, 20, 12, 8, 6),
834
+ });
835
+ });
836
+
837
+ test("can detect and parse 'yyyy m d hh:mm a' datetime", () => {
838
+ expect(parseDateTime("2020 4 20 12:09 AM")).toEqual({
839
+ value: 43941.00625,
840
+ format: "yyyy m d hh:mm a",
841
+ jsDate: new Date(2020, 3, 20, 0, 9, 0),
842
+ });
843
+ expect(parseDateTime("2020 4 20 6 PM")).toEqual({
844
+ value: 43941.75,
845
+ format: "yyyy m d hh:mm a",
846
+ jsDate: new Date(2020, 3, 20, 18, 0, 0),
847
+ });
848
+ });
849
+
850
+ test("can detect and parse 'yyyy m d hh:mm:ss a' datetime", () => {
851
+ expect(parseDateTime("2020 4 20 12:08:06 AM")).toEqual({
852
+ value: 43941.005625,
853
+ format: "yyyy m d hh:mm:ss a",
854
+ jsDate: new Date(2020, 3, 20, 0, 8, 6),
855
+ });
856
+ });
857
+
858
+ // mm dd yyyy
859
+
860
+ test("can detect and parse 'mm dd yyyy hh:mm' datetime", () => {
861
+ expect(parseDateTime("03 20 2010 12:09")).toEqual({
862
+ value: 40257.50625,
863
+ format: "mm dd yyyy hh:mm",
864
+ jsDate: new Date(2010, 2, 20, 12, 9, 0),
865
+ });
866
+ });
867
+
868
+ test("can detect and parse 'mm dd yyyy hh:mm:ss' datetime", () => {
869
+ expect(parseDateTime("03 20 2010 12:08:06")).toEqual({
870
+ value: 40257.505625,
871
+ format: "mm dd yyyy hh:mm:ss",
872
+ jsDate: new Date(2010, 2, 20, 12, 8, 6),
873
+ });
874
+ });
875
+
876
+ test("can detect and parse 'mm dd yyyy hh:mm a' datetime", () => {
877
+ expect(parseDateTime("03 20 2010 12:09 AM")).toEqual({
878
+ value: 40257.00625,
879
+ format: "mm dd yyyy hh:mm a",
880
+ jsDate: new Date(2010, 2, 20, 0, 9, 0),
881
+ });
882
+ expect(parseDateTime("03 20 2010 6 PM")).toEqual({
883
+ value: 40257.75,
884
+ format: "mm dd yyyy hh:mm a",
885
+ jsDate: new Date(2010, 2, 20, 18, 0, 0),
886
+ });
887
+ });
888
+
889
+ test("can detect and parse 'mm dd yyyy hh:mm:ss a' datetime", () => {
890
+ expect(parseDateTime("03 20 2010 12:08:06 AM")).toEqual({
891
+ value: 40257.005625,
892
+ format: "mm dd yyyy hh:mm:ss a",
893
+ jsDate: new Date(2010, 2, 20, 0, 8, 6),
894
+ });
895
+ });
896
+
897
+ // yyyy mm dd
898
+
899
+ test("can detect and parse 'yyyy mm dd hh:mm' datetime", () => {
900
+ expect(parseDateTime("2010 03 20 12:09")).toEqual({
901
+ value: 40257.50625,
902
+ format: "yyyy mm dd hh:mm",
903
+ jsDate: new Date(2010, 2, 20, 12, 9, 0),
904
+ });
905
+ });
906
+
907
+ test("can detect and parse 'yyyy mm dd hh:mm:ss' datetime", () => {
908
+ expect(parseDateTime("2010 03 20 12:08:06")).toEqual({
909
+ value: 40257.505625,
910
+ format: "yyyy mm dd hh:mm:ss",
911
+ jsDate: new Date(2010, 2, 20, 12, 8, 6),
912
+ });
913
+ });
914
+
915
+ test("can detect and parse 'yyyy mm dd hh:mm a' datetime", () => {
916
+ expect(parseDateTime("2010 03 20 12:09 AM")).toEqual({
917
+ value: 40257.00625,
918
+ format: "yyyy mm dd hh:mm a",
919
+ jsDate: new Date(2010, 2, 20, 0, 9, 0),
920
+ });
921
+ expect(parseDateTime("2010 03 20 6 PM")).toEqual({
922
+ value: 40257.75,
923
+ format: "yyyy mm dd hh:mm a",
924
+ jsDate: new Date(2010, 2, 20, 18, 0, 0),
925
+ });
926
+ });
927
+
928
+ test("can detect and parse 'yyyy mm dd hh:mm:ss a' datetime", () => {
929
+ expect(parseDateTime("2010 03 20 12:08:06 AM")).toEqual({
930
+ value: 40257.005625,
931
+ format: "yyyy mm dd hh:mm:ss a",
932
+ jsDate: new Date(2010, 2, 20, 0, 8, 6),
933
+ });
934
+ });
935
+
936
+ // m d
937
+
938
+ test("can detect and parse 'm d hh:mm' datetime", () => {
939
+ const d = parseDateTime("1 3 12:09")!;
940
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 9, 0));
941
+ expect(d.format).toBe("m d hh:mm");
942
+ });
943
+
944
+ test("can detect and parse 'm d hh:mm:ss' datetime", () => {
945
+ const d = parseDateTime("1 3 12:08:06")!;
946
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 8, 6));
947
+ expect(d.format).toBe("m d hh:mm:ss");
948
+ });
949
+
950
+ test("can detect and parse 'm d hh:mm a' datetime", () => {
951
+ const d1 = parseDateTime("1 3 12:09 AM")!;
952
+ expect(d1.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 9, 0));
953
+ expect(d1.format).toBe("m d hh:mm a");
954
+
955
+ const d2 = parseDateTime("1 3 6 PM")!;
956
+ expect(d2.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 18, 0, 0));
957
+ expect(d2.format).toBe("m d hh:mm a");
958
+ });
959
+
960
+ test("can detect and parse 'm d hh:mm:ss a' datetime", () => {
961
+ const d = parseDateTime("1 3 12:08:06 AM")!;
962
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 8, 6));
963
+ expect(d.format).toBe("m d hh:mm:ss a");
964
+ });
965
+
966
+ // mm dd
967
+
968
+ test("can detect and parse 'mm dd hh:mm' datetime", () => {
969
+ const d = parseDateTime("1 03 12:09")!;
970
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 9, 0));
971
+ expect(d.format).toBe("mm dd hh:mm");
972
+ });
973
+
974
+ test("can detect and parse 'mm dd hh:mm:ss' datetime", () => {
975
+ const d = parseDateTime("1 03 12:08:06")!;
976
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 12, 8, 6));
977
+ expect(d.format).toBe("mm dd hh:mm:ss");
978
+ });
979
+
980
+ test("can detect and parse 'mm dd hh:mm a' datetime", () => {
981
+ const d1 = parseDateTime("1 03 12:09 AM")!;
982
+ expect(d1.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 9, 0));
983
+ expect(d1.format).toBe("mm dd hh:mm a");
984
+
985
+ const d2 = parseDateTime("1 03 6 PM")!;
986
+ expect(d2.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 18, 0, 0));
987
+ expect(d2.format).toBe("mm dd hh:mm a");
988
+ });
989
+
990
+ test("can detect and parse 'mm dd hh:mm:ss a' datetime", () => {
991
+ const d = parseDateTime("1 03 12:08:06 AM")!;
992
+ expect(d.jsDate).toEqual(new Date(CURRENT_YEAR, 0, 3, 0, 8, 6));
993
+ expect(d.format).toBe("mm dd hh:mm:ss a");
994
+ });
995
+ });
996
+
997
+ describe("formatDateTime", () => {
998
+ test("increment time test on datetime format with 'm/d/yyyy'", () => {
999
+ expect(formatDateTime(parseDateTime("12/5/2020 11:69")!)).toBe("12/5/2020 12:09");
1000
+ expect(formatDateTime(parseDateTime("12/5/2020 23:69")!)).toBe("12/6/2020 00:09:00");
1001
+ expect(formatDateTime(parseDateTime("12/5/2020 25 AM")!)).toBe("12/5/2020 01:00 PM");
1002
+ expect(formatDateTime(parseDateTime("12/5/2020 25:70 PM")!)).toBe("12/6/2020 02:10:00");
1003
+ });
1004
+
1005
+ test("increment time test on datetime format with 'yyyy/m/d'", () => {
1006
+ expect(formatDateTime(parseDateTime("2020/12/5 11:69")!)).toBe("2020/12/5 12:09");
1007
+ expect(formatDateTime(parseDateTime("2020/12/5 23:69")!)).toBe("2020/12/6 00:09:00");
1008
+ expect(formatDateTime(parseDateTime("2020/12/5 25 AM")!)).toBe("2020/12/5 01:00 PM");
1009
+ expect(formatDateTime(parseDateTime("2020/12/5 25:70 PM")!)).toBe("2020/12/6 02:10:00");
1010
+ });
1011
+
1012
+ test("increment time test on datetime format with 'mm/dd/yyyy'", () => {
1013
+ expect(formatDateTime(parseDateTime("12/05/2020 11:69")!)).toBe("12/05/2020 12:09");
1014
+ expect(formatDateTime(parseDateTime("12/05/2020 23:69")!)).toBe("12/06/2020 00:09:00");
1015
+ expect(formatDateTime(parseDateTime("12/05/2020 25 AM")!)).toBe("12/05/2020 01:00 PM");
1016
+ expect(formatDateTime(parseDateTime("12/05/2020 25:70 PM")!)).toBe("12/06/2020 02:10:00");
1017
+ });
1018
+
1019
+ test("increment time test on datetime format with 'yyyy/mm/dd'", () => {
1020
+ expect(formatDateTime(parseDateTime("2020/12/05 11:69")!)).toBe("2020/12/05 12:09");
1021
+ expect(formatDateTime(parseDateTime("2020/12/05 23:69")!)).toBe("2020/12/06 00:09:00");
1022
+ expect(formatDateTime(parseDateTime("2020/12/05 25 AM")!)).toBe("2020/12/05 01:00 PM");
1023
+ expect(formatDateTime(parseDateTime("2020/12/05 25:70 PM")!)).toBe("2020/12/06 02:10:00");
1024
+ });
1025
+
1026
+ test("increment time test on datetime format with 'm/d'", () => {
1027
+ expect(formatDateTime(parseDateTime("12/5 11:69")!)).toBe("12/5 12:09");
1028
+ expect(formatDateTime(parseDateTime("12/5 23:69")!)).toBe("12/6 00:09:00");
1029
+ expect(formatDateTime(parseDateTime("12/5 25 AM")!)).toBe("12/5 01:00 PM");
1030
+ expect(formatDateTime(parseDateTime("12/5 25:70 PM")!)).toBe("12/6 02:10:00");
1031
+ });
1032
+
1033
+ test("increment time test on datetime format with 'mm/dd'", () => {
1034
+ expect(formatDateTime(parseDateTime("12/05 11:69")!)).toBe("12/05 12:09");
1035
+ expect(formatDateTime(parseDateTime("12/05 23:69")!)).toBe("12/06 00:09:00");
1036
+ expect(formatDateTime(parseDateTime("12/05 25 AM")!)).toBe("12/05 01:00 PM");
1037
+ expect(formatDateTime(parseDateTime("12/05 25:70 PM")!)).toBe("12/06 02:10:00");
1038
+ });
1039
+
1040
+ test("increment time test on datetime format with 'm d yyyy'", () => {
1041
+ expect(formatDateTime(parseDateTime("12 5 2020 11:69")!)).toBe("12 5 2020 12:09");
1042
+ expect(formatDateTime(parseDateTime("12 5 2020 23:69")!)).toBe("12 6 2020 00:09:00");
1043
+ expect(formatDateTime(parseDateTime("12 5 2020 25 AM")!)).toBe("12 5 2020 01:00 PM");
1044
+ expect(formatDateTime(parseDateTime("12 5 2020 25:70 PM")!)).toBe("12 6 2020 02:10:00");
1045
+ });
1046
+
1047
+ test("increment time test on datetime format with 'yyyy m d'", () => {
1048
+ expect(formatDateTime(parseDateTime("2020 12 5 11:69")!)).toBe("2020 12 5 12:09");
1049
+ expect(formatDateTime(parseDateTime("2020 12 5 23:69")!)).toBe("2020 12 6 00:09:00");
1050
+ expect(formatDateTime(parseDateTime("2020 12 5 25 AM")!)).toBe("2020 12 5 01:00 PM");
1051
+ expect(formatDateTime(parseDateTime("2020 12 5 25:70 PM")!)).toBe("2020 12 6 02:10:00");
1052
+ });
1053
+
1054
+ test("increment time test on datetime format with 'mm dd yyyy'", () => {
1055
+ expect(formatDateTime(parseDateTime("12 05 2020 11:69")!)).toBe("12 05 2020 12:09");
1056
+ expect(formatDateTime(parseDateTime("12 05 2020 23:69")!)).toBe("12 06 2020 00:09:00");
1057
+ expect(formatDateTime(parseDateTime("12 05 2020 25 AM")!)).toBe("12 05 2020 01:00 PM");
1058
+ expect(formatDateTime(parseDateTime("12 05 2020 25:70 PM")!)).toBe("12 06 2020 02:10:00");
1059
+ });
1060
+
1061
+ test("increment time test on datetime format with 'yyyy mm dd'", () => {
1062
+ expect(formatDateTime(parseDateTime("2020 12 05 11:69")!)).toBe("2020 12 05 12:09");
1063
+ expect(formatDateTime(parseDateTime("2020 12 05 23:69")!)).toBe("2020 12 06 00:09:00");
1064
+ expect(formatDateTime(parseDateTime("2020 12 05 25 AM")!)).toBe("2020 12 05 01:00 PM");
1065
+ expect(formatDateTime(parseDateTime("2020 12 05 25:70 PM")!)).toBe("2020 12 06 02:10:00");
1066
+ });
1067
+
1068
+ test("increment time test on datetime format with 'm d'", () => {
1069
+ expect(formatDateTime(parseDateTime("12 5 11:69")!)).toBe("12 5 12:09");
1070
+ expect(formatDateTime(parseDateTime("12 5 23:69")!)).toBe("12 6 00:09:00");
1071
+ expect(formatDateTime(parseDateTime("12 5 25 AM")!)).toBe("12 5 01:00 PM");
1072
+ expect(formatDateTime(parseDateTime("12 5 25:70 PM")!)).toBe("12 6 02:10:00");
1073
+ });
1074
+
1075
+ test("increment time test on datetime format with 'mm dd'", () => {
1076
+ expect(formatDateTime(parseDateTime("12 05 11:69")!)).toBe("12 05 12:09");
1077
+ expect(formatDateTime(parseDateTime("12 05 23:69")!)).toBe("12 06 00:09:00");
1078
+ expect(formatDateTime(parseDateTime("12 05 25 AM")!)).toBe("12 05 01:00 PM");
1079
+ expect(formatDateTime(parseDateTime("12 05 25:70 PM")!)).toBe("12 06 02:10:00");
1080
+ });
1081
+ });