@qrvey/formula-lang 3.2.0-rc.1085 → 3.2.0-rc.1101

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.
@@ -48,8 +48,8 @@ type DatePresetValue = string | DateRangeValue | PartialDateValue;
48
48
 
49
49
  ```json
50
50
  {
51
- "start": "2026-07-01T00:00:00.000Z",
52
- "end": "2026-07-31T23:59:59.999Z"
51
+ "start": "2026-07-01T00:00:00.000Z",
52
+ "end": "2026-07-31T23:59:59.999Z"
53
53
  }
54
54
  ```
55
55
 
@@ -57,31 +57,31 @@ type DatePresetValue = string | DateRangeValue | PartialDateValue;
57
57
 
58
58
  ```json
59
59
  {
60
- "month": 4,
61
- "day": 15
60
+ "month": 4,
61
+ "day": 15
62
62
  }
63
63
  ```
64
64
 
65
65
  Supported fields for `partialDate`:
66
66
 
67
- | Field | Meaning | Range |
68
- | --- | --- | --- |
69
- | `year` | Specific year | Integer |
70
- | `month` | Month | 1-12 |
71
- | `quarter` | Quarter | 1-4 |
72
- | `week` | Week | 1-54 |
73
- | `day` | Day of month | 1-31 |
67
+ | Field | Meaning | Range |
68
+ | --------- | ------------- | ------- |
69
+ | `year` | Specific year | Integer |
70
+ | `month` | Month | 1-12 |
71
+ | `quarter` | Quarter | 1-4 |
72
+ | `week` | Week | 1-54 |
73
+ | `day` | Day of month | 1-31 |
74
74
 
75
75
  ## Value Types
76
76
 
77
77
  `TranspileDatePreset` returns `valueType` to describe the nature of the value.
78
78
 
79
- | Value type | Meaning | Examples |
80
- | --- | --- | --- |
81
- | `fixed` | Fixed value or materialized range | `DATE("2026-07-20")`, `DATE_RANGE(...)` |
82
- | `recurring` | Partial pattern that repeats | `PARTIAL_DATE("ANY", 4)`, `PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)` |
83
- | `relative` | Period relative to the calendar or positioned inside a period | `CALENDAR_PERIOD("MONTH", 0)`, `PERIOD_AT("MONTH", 4, 2026)` |
84
- | `rolling` | Moving window from an anchor | `RELATIVE_PERIOD(-29, "DAY")` |
79
+ | Value type | Meaning | Examples |
80
+ | ----------- | ------------------------------------------------------------- | -------------------------------------------------------------------- |
81
+ | `fixed` | Fixed value or materialized range | `DATE("2026-07-20")`, `DATE_RANGE(...)` |
82
+ | `recurring` | Partial pattern that repeats | `PARTIAL_DATE("ANY", 4)`, `PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)` |
83
+ | `relative` | Period relative to the calendar or positioned inside a period | `CALENDAR_PERIOD("MONTH", 0)`, `PERIOD_AT("MONTH", 4, 2026)` |
84
+ | `rolling` | Moving window from an anchor | `RELATIVE_PERIOD(-29, "DAY")` |
85
85
 
86
86
  Current note: `PERIOD_AT("MONTH", 4, 2026)` produces a concrete range, but it is classified as `relative` because the root expression is `PERIOD_AT`. If the business goal requires distinguishing `April 2026` as `fixed`, that would be a classification improvement, not a resolution change.
87
87
 
@@ -91,39 +91,37 @@ QDP functions can receive `FormulaContext` with date preset configuration:
91
91
 
92
92
  ```ts
93
93
  {
94
- timezone: {
95
- timeZone: 'America/Bogota'
96
- },
97
- datePreset: {
98
- calendar: 'gregorian',
99
- locale: 'en-US',
100
- fiscalYearStartMonth: 1,
101
- fiscalYearStartDay: 1,
102
- weekStartsOn: 0
103
- }
94
+ timezone: { timeZone: "America/Bogota" },
95
+ datePreset: {
96
+ calendar: "gregorian",
97
+ locale: "en-US",
98
+ fiscalYearStartMonth: 1,
99
+ fiscalYearStartDay: 1,
100
+ weekStartsOn: 0,
101
+ },
104
102
  }
105
103
  ```
106
104
 
107
105
  Current defaults:
108
106
 
109
- | Option | Default | Notes |
110
- | --- | --- | --- |
111
- | `timezone.offset` | `+00:00` | Supports `default`, `browser`, or custom offsets such as `UTC-5`, `-05:00`, `+5:30` |
112
- | `timezone.timeZone` | none | Optional IANA time zone identifier such as `America/Bogota`; when present it is used for calendar resolution |
113
- | `calendar` | `gregorian` | Also supports `corporate-fiscal`, `retail-4-4-5`, `retail-4-5-4` |
114
- | `locale` | `en-US` | Used by formatting options |
115
- | `fiscalYearStartMonth` | `1` | Clamped to 1-12 |
116
- | `fiscalYearStartDay` | `1` | Clamped to 1-31 and adjusted to the last valid day of the month |
117
- | `weekStartsOn` | `0` | Sunday. Accepts 0-6 or English weekday names |
107
+ | Option | Default | Notes |
108
+ | ---------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ |
109
+ | `timezone.offset` | `+00:00` | Supports `default`, `browser`, or custom offsets such as `UTC-5`, `-05:00`, `+5:30` |
110
+ | `timezone.timeZone` | none | Optional IANA time zone identifier such as `America/Bogota`; when present it is used for calendar resolution |
111
+ | `calendar` | `gregorian` | Also supports `corporate-fiscal`, `retail-4-4-5`, `retail-4-5-4` |
112
+ | `locale` | `en-US` | Used by formatting options |
113
+ | `fiscalYearStartMonth` | `1` | Clamped to 1-12 |
114
+ | `fiscalYearStartDay` | `1` | Clamped to 1-31 and adjusted to the last valid day of the month |
115
+ | `weekStartsOn` | `0` | Sunday. Accepts 0-6 or English weekday names |
118
116
 
119
- Use `timezone.timeZone` for IANA identifiers. Use `timezone.offset` for admin/default/browser/custom offset values such as `{ offset: 'default' }`, `{ offset: 'browser' }`, or `{ offset: '-05:00' }`.
117
+ Use `timezone.timeZone` for IANA identifiers. Use `timezone.offset` for admin/default/browser/custom offset values such as `{ offset: "default" }`, `{ offset: "browser" }`, or `{ offset: "-05:00" }`.
120
118
 
121
119
  Current week convention:
122
120
 
123
- - The default is Sunday (`weekStartsOn: 0`).
124
- - Week 1 of a year starts on the Sunday on or before Jan 1.
125
- - Some years can have W54 under this convention.
126
- - `PERIOD_AT("WEEK", "LAST", year)` is preferred for representing the last week of the year because it avoids hardcoding 52, 53, or 54.
121
+ - The default is Sunday (`weekStartsOn: 0`).
122
+ - Week 1 of a year starts on the Sunday on or before Jan 1.
123
+ - Some years can have W54 under this convention.
124
+ - `PERIOD_AT("WEEK", "LAST", year)` is preferred for representing the last week of the year because it avoids hardcoding 52, 53, or 54.
127
125
 
128
126
  ## QDP Date Preset Functions
129
127
 
@@ -132,7 +130,7 @@ Current week convention:
132
130
  Returns the current timestamp.
133
131
 
134
132
  ```ts
135
- NOW()
133
+ NOW();
136
134
  // 2026-07-22T23:42:50.413Z
137
135
  ```
138
136
 
@@ -145,7 +143,7 @@ Value type when used as the final result: `fixed`.
145
143
  Returns the start of the current day in the context timezone.
146
144
 
147
145
  ```ts
148
- TODAY()
146
+ TODAY();
149
147
  // 2026-07-22T00:00:00.000Z
150
148
  ```
151
149
 
@@ -158,30 +156,30 @@ Value type when used as the final result: `fixed`.
158
156
  Normalizes an ISO date or ISO date-time string to UTC ISO.
159
157
 
160
158
  ```ts
161
- DATE("2026-07-20")
159
+ DATE("2026-07-20");
162
160
  // 2026-07-20T00:00:00.000Z
163
161
  ```
164
162
 
165
163
  Supported input formats:
166
164
 
167
- | Input | Example | Resolved value |
168
- | --- | --- | --- |
169
- | ISO date | `DATE("2026-07-08")` | `2026-07-08T00:00:00.000Z` |
170
- | ISO date-time without timezone | `DATE("2026-07-08T15:30")` | `2026-07-08T15:30:00.000Z` |
171
- | ISO date-time with UTC timezone | `DATE("2026-07-08T15:30:45Z")` | `2026-07-08T15:30:45.000Z` |
165
+ | Input | Example | Resolved value |
166
+ | ---------------------------------- | ----------------------------------- | -------------------------- |
167
+ | ISO date | `DATE("2026-07-08")` | `2026-07-08T00:00:00.000Z` |
168
+ | ISO date-time without timezone | `DATE("2026-07-08T15:30")` | `2026-07-08T15:30:00.000Z` |
169
+ | ISO date-time with UTC timezone | `DATE("2026-07-08T15:30:45Z")` | `2026-07-08T15:30:45.000Z` |
172
170
  | ISO date-time with offset timezone | `DATE("2026-07-08T15:30:45-05:00")` | `2026-07-08T20:30:45.000Z` |
173
171
 
174
172
  Parameters:
175
173
 
176
- | Parameter | Type | Required | Validation |
177
- | --- | --- | --- | --- |
178
- | `VALUE` | `string` | Yes | ISO date or ISO date-time |
174
+ | Parameter | Type | Required | Validation |
175
+ | --------- | -------- | -------- | ------------------------- |
176
+ | `VALUE` | `string` | Yes | ISO date or ISO date-time |
179
177
 
180
178
  Rules:
181
179
 
182
- - `DATE("2026-07-08")` is valid and resolves to `2026-07-08T00:00:00.000Z`.
183
- - Calendrically invalid dates, such as `DATE("2026-02-31T00:00")`, are rejected.
184
- - If the string does not include a timezone, UTC is assumed.
180
+ - `DATE("2026-07-08")` is valid and resolves to `2026-07-08T00:00:00.000Z`.
181
+ - Calendrically invalid dates, such as `DATE("2026-02-31T00:00")`, are rejected.
182
+ - If the string does not include a timezone, UTC is assumed.
185
183
 
186
184
  Output: `date`.
187
185
 
@@ -192,24 +190,21 @@ Value type: `fixed`.
192
190
  Builds a range between two dates.
193
191
 
194
192
  ```ts
195
- DATE_RANGE(
196
- DATE("2026-07-20"),
197
- END_OF(DATE("2026-07-25"))
198
- )
193
+ DATE_RANGE(DATE("2026-07-20"), END_OF(DATE("2026-07-25")));
199
194
  // { start: "2026-07-20T00:00:00.000Z", end: "2026-07-25T23:59:59.999Z" }
200
195
  ```
201
196
 
202
197
  Parameters:
203
198
 
204
- | Parameter | Type | Required |
205
- | --- | --- | --- |
206
- | `START` | `date` | Yes |
207
- | `END` | `date` | Yes |
199
+ | Parameter | Type | Required |
200
+ | --------- | ------ | -------- |
201
+ | `START` | `date` | Yes |
202
+ | `END` | `date` | Yes |
208
203
 
209
204
  Rules:
210
205
 
211
- - `END` must be greater than or equal to `START`.
212
- - If `END < START`, the function returns `INVALID_DATE_RANGE`.
206
+ - `END` must be greater than or equal to `START`.
207
+ - If `END < START`, the function returns `INVALID_DATE_RANGE`.
213
208
 
214
209
  Output: `dateRange`.
215
210
 
@@ -220,28 +215,28 @@ Value type: `fixed`.
220
215
  Creates a moving window relative to an anchor. If no anchor is passed, it uses `TODAY()`.
221
216
 
222
217
  ```ts
223
- RELATIVE_PERIOD(-29, "DAY")
218
+ RELATIVE_PERIOD(-29, "DAY");
224
219
  // { start: "2026-06-23T00:00:00.000Z", end: "2026-07-22T23:59:59.999Z" }
225
220
  ```
226
221
 
227
222
  Parameters:
228
223
 
229
- | Parameter | Type | Required | Values |
230
- | --- | --- | --- | --- |
231
- | `OFFSET` | Integer `number` | Yes | Negative, zero, or positive |
232
- | `UNIT` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR` |
233
- | `ANCHOR` | `date` | No | Base date |
224
+ | Parameter | Type | Required | Values |
225
+ | --------- | ---------------- | -------- | ----------------------------------------- |
226
+ | `OFFSET` | Integer `number` | Yes | Negative, zero, or positive |
227
+ | `UNIT` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR` |
228
+ | `ANCHOR` | `date` | No | Base date |
234
229
 
235
230
  Semantics:
236
231
 
237
- - Negative offset: from `anchor + offset` to the end of the anchor day.
238
- - Positive offset: from the start of the anchor day to `anchor + offset`.
239
- - It is rolling and not necessarily aligned to calendar boundaries.
232
+ - Negative offset: from `anchor + offset` to the end of the anchor day.
233
+ - Positive offset: from the start of the anchor day to `anchor + offset`.
234
+ - It is rolling and not necessarily aligned to calendar boundaries.
240
235
 
241
236
  Examples:
242
237
 
243
238
  ```ts
244
- RELATIVE_PERIOD(-2, "WEEK", DATE("2026-07-08T15:30"))
239
+ RELATIVE_PERIOD(-2, "WEEK", DATE("2026-07-08T15:30"));
245
240
  // 2026-06-24T00:00:00.000Z -> 2026-07-08T23:59:59.999Z
246
241
  ```
247
242
 
@@ -254,31 +249,31 @@ Value type: `rolling`.
254
249
  Returns a complete calendar period containing the current day, shifted by `offset`.
255
250
 
256
251
  ```ts
257
- CALENDAR_PERIOD("MONTH", 0)
252
+ CALENDAR_PERIOD("MONTH", 0);
258
253
  // 2026-07-01T00:00:00.000Z -> 2026-07-31T23:59:59.999Z
259
254
  ```
260
255
 
261
256
  Parameters:
262
257
 
263
- | Parameter | Type | Required | Values |
264
- | --- | --- | --- | --- |
265
- | `PERIOD` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR` |
266
- | `OFFSET` | Integer `number` | No | Default `0` |
258
+ | Parameter | Type | Required | Values |
259
+ | --------- | ---------------- | -------- | ----------------------------------------- |
260
+ | `PERIOD` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR` |
261
+ | `OFFSET` | Integer `number` | No | Default `0` |
267
262
 
268
263
  Semantics:
269
264
 
270
- - `0` means the current period.
271
- - `-1` means the previous period.
272
- - `1` means the next period.
273
- - Respects calendar boundaries, timezone, fiscal calendar, and week start from the context.
265
+ - `0` means the current period.
266
+ - `-1` means the previous period.
267
+ - `1` means the next period.
268
+ - Respects calendar boundaries, timezone, fiscal calendar, and week start from the context.
274
269
 
275
270
  Examples:
276
271
 
277
272
  ```ts
278
- CALENDAR_PERIOD("WEEK", 0)
273
+ CALENDAR_PERIOD("WEEK", 0);
279
274
  // with weekStartsOn Sunday: 2026-07-19T00:00:00.000Z -> 2026-07-25T23:59:59.999Z
280
275
 
281
- CALENDAR_PERIOD("QUARTER", -1)
276
+ CALENDAR_PERIOD("QUARTER", -1);
282
277
  // 2026-04-01T00:00:00.000Z -> 2026-06-30T23:59:59.999Z
283
278
  ```
284
279
 
@@ -291,37 +286,37 @@ Value type: `relative`.
291
286
  Returns the period located at a position inside a year. If `year` is not passed, it uses the current year from the context.
292
287
 
293
288
  ```ts
294
- PERIOD_AT("MONTH", 4, 2026)
289
+ PERIOD_AT("MONTH", 4, 2026);
295
290
  // 2026-04-01T00:00:00.000Z -> 2026-04-30T23:59:59.999Z
296
291
  ```
297
292
 
298
293
  Parameters:
299
294
 
300
- | Parameter | Type | Required | Values |
301
- | --- | --- | --- | --- |
302
- | `PERIOD` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER` |
303
- | `POSITION` | Integer `number` or `string` | Yes | Number, `FIRST`, `LAST` |
304
- | `YEAR` | Integer `number` | No | Default: current year |
295
+ | Parameter | Type | Required | Values |
296
+ | ---------- | ---------------------------- | -------- | --------------------------------- |
297
+ | `PERIOD` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER` |
298
+ | `POSITION` | Integer `number` or `string` | Yes | Number, `FIRST`, `LAST` |
299
+ | `YEAR` | Integer `number` | No | Default: current year |
305
300
 
306
301
  Examples:
307
302
 
308
303
  ```ts
309
- PERIOD_AT("QUARTER", 2, 2026)
304
+ PERIOD_AT("QUARTER", 2, 2026);
310
305
  // 2026-04-01T00:00:00.000Z -> 2026-06-30T23:59:59.999Z
311
306
 
312
- PERIOD_AT("MONTH", "LAST", 2025)
307
+ PERIOD_AT("MONTH", "LAST", 2025);
313
308
  // 2025-12-01T00:00:00.000Z -> 2025-12-31T23:59:59.999Z
314
309
 
315
- PERIOD_AT("WEEK", "LAST", 2028)
310
+ PERIOD_AT("WEEK", "LAST", 2028);
316
311
  // 2028-12-31T00:00:00.000Z -> 2029-01-06T23:59:59.999Z
317
312
  ```
318
313
 
319
314
  Week rules:
320
315
 
321
- - For `WEEK`, the start of the year is anchored to the start of the week that falls on or before the start of the year.
322
- - The last week can cross into the next year.
323
- - Numeric `POSITION` does not force an error if it points to a computable range outside the nominal year. For example, W54 can resolve by shifting even when the year does not nominally have W54.
324
- - `"MIDDLE"` and other strings different from `FIRST` or `LAST` are invalid.
316
+ - For `WEEK`, the start of the year is anchored to the start of the week that falls on or before the start of the year.
317
+ - The last week can cross into the next year.
318
+ - Numeric `POSITION` does not force an error if it points to a computable range outside the nominal year. For example, W54 can resolve by shifting even when the year does not nominally have W54.
319
+ - `"MIDDLE"` and other strings different from `FIRST` or `LAST` are invalid.
325
320
 
326
321
  Output: `dateRange`.
327
322
 
@@ -332,71 +327,71 @@ Current value type: `relative`.
332
327
  Represents a partial or recurring date. Each field can be a number or `"ANY"`. Omitted fields are treated as `ANY`.
333
328
 
334
329
  ```ts
335
- PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)
330
+ PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15);
336
331
  // { month: 4, day: 15 }
337
332
  ```
338
333
 
339
334
  Parameters:
340
335
 
341
- | Position | Field | Range | Example |
342
- | --- | --- | --- | --- |
343
- | 1 | `YEAR` | Integer or `ANY` | `2026` |
344
- | 2 | `MONTH` | 1-12 or `ANY` | `4` |
345
- | 3 | `QUARTER` | 1-4 or `ANY` | `2` |
346
- | 4 | `WEEK` | 1-54 or `ANY` | `40` |
347
- | 5 | `DAY` | 1-31 or `ANY` | `15` |
336
+ | Position | Field | Range | Example |
337
+ | -------- | --------- | ---------------- | ------- |
338
+ | 1 | `YEAR` | Integer or `ANY` | `2026` |
339
+ | 2 | `MONTH` | 1-12 or `ANY` | `4` |
340
+ | 3 | `QUARTER` | 1-4 or `ANY` | `2` |
341
+ | 4 | `WEEK` | 1-54 or `ANY` | `40` |
342
+ | 5 | `DAY` | 1-31 or `ANY` | `15` |
348
343
 
349
344
  Date picker examples:
350
345
 
351
346
  ```ts
352
- PARTIAL_DATE()
347
+ PARTIAL_DATE();
353
348
  // {}
354
349
  // Short format: Any date
355
350
 
356
- PARTIAL_DATE("ANY", 4)
351
+ PARTIAL_DATE("ANY", 4);
357
352
  // { month: 4 }
358
353
  // Short format: Apr
359
354
 
360
- PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)
355
+ PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15);
361
356
  // { month: 4, day: 15 }
362
357
  // Short format: Apr 15
363
358
 
364
- PARTIAL_DATE("ANY", "ANY", 2)
359
+ PARTIAL_DATE("ANY", "ANY", 2);
365
360
  // { quarter: 2 }
366
361
  // Short format: Q2
367
362
 
368
- PARTIAL_DATE("ANY", "ANY", "ANY", 40)
363
+ PARTIAL_DATE("ANY", "ANY", "ANY", 40);
369
364
  // { week: 40 }
370
365
  // Short format: W40
371
366
 
372
- PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15)
367
+ PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15);
373
368
  // { day: 15 }
374
369
  // Short format: Day 15
375
370
  ```
376
371
 
377
372
  Value type:
378
373
 
379
- - `fixed` if it includes `year`, `month`, and `day`, without `week`.
380
- - `relative` if it includes `week`.
381
- - `recurring` for all other partial dates.
374
+ - `fixed` if it includes `year`, `month`, and `day`, without `week`.
375
+ - `relative` if it includes `week`.
376
+ - `recurring` for all other partial dates.
382
377
 
383
378
  ### `START_OF(value)`
384
379
 
385
380
  Returns the start of a `date` or the `start` of a `dateRange`.
386
381
 
387
382
  ```ts
388
- START_OF(DATE("2026-07-08T15:30"))
383
+ START_OF(DATE("2026-07-08T15:30"));
389
384
  // 2026-07-08T00:00:00.000Z
390
385
 
391
- START_OF(CALENDAR_PERIOD("MONTH", -1))
386
+ START_OF(CALENDAR_PERIOD("MONTH", -1));
392
387
  // 2026-06-01T00:00:00.000Z
393
388
  ```
394
389
 
395
390
  Parameter:
396
391
 
397
- | Parameter | Type |
398
- | --- | --- |
399
- | `VALUE` | `date` or `dateRange` |
392
+ | Parameter | Type |
393
+ | --------- | --------------------- |
394
+ | `VALUE` | `date` or `dateRange` |
400
395
 
401
396
  Output: `date`.
402
397
 
@@ -407,18 +402,18 @@ Value type when used as root: `relative`.
407
402
  Returns the end of a `date` or the `end` of a `dateRange`.
408
403
 
409
404
  ```ts
410
- END_OF(DATE("2026-07-08T15:30"))
405
+ END_OF(DATE("2026-07-08T15:30"));
411
406
  // 2026-07-08T23:59:59.999Z
412
407
 
413
- END_OF(CALENDAR_PERIOD("YEAR", -1))
408
+ END_OF(CALENDAR_PERIOD("YEAR", -1));
414
409
  // 2025-12-31T23:59:59.999Z
415
410
  ```
416
411
 
417
412
  Parameter:
418
413
 
419
- | Parameter | Type |
420
- | --- | --- |
421
- | `VALUE` | `date` or `dateRange` |
414
+ | Parameter | Type |
415
+ | --------- | --------------------- |
416
+ | `VALUE` | `date` or `dateRange` |
422
417
 
423
418
  Output: `date`.
424
419
 
@@ -429,55 +424,55 @@ Value type when used as root: `relative`.
429
424
  Extracts one part of a date.
430
425
 
431
426
  ```ts
432
- PART(DATE("2026-07-08T15:30"), "DAY_OF_WEEK_NAME")
427
+ PART(DATE("2026-07-08T15:30"), "DAY_OF_WEEK_NAME");
433
428
  // Wednesday
434
429
  ```
435
430
 
436
431
  Parameters:
437
432
 
438
- | Parameter | Type | Values |
439
- | --- | --- | --- |
440
- | `DATE` | `date` | Resolved ISO date |
441
- | `PART` | `string` | See the table below |
433
+ | Parameter | Type | Values |
434
+ | --------- | -------- | ------------------- |
435
+ | `DATE` | `date` | Resolved ISO date |
436
+ | `PART` | `string` | See the table below |
442
437
 
443
438
  Supported parts:
444
439
 
445
- | Value | Output |
446
- | --- | --- |
447
- | `YEAR` | UTC year |
448
- | `QUARTER` | Quarter 1-4 |
449
- | `MONTH` | Month 1-12 |
450
- | `MONTH_NAME` | English month name |
451
- | `DAY` | Day of month |
452
- | `DAY_OF_MONTH` | Day of month |
453
- | `DAY_OF_WEEK` | UTC weekday, Sunday = 0 |
454
- | `DAY_OF_WEEK_NAME` | English weekday name |
455
- | `DAY_OF_YEAR` | Day of year |
456
- | `WEEK_OF_YEAR` | Week according to the current convention |
457
- | `HOUR` | UTC hour |
458
- | `MINUTE` | UTC minute |
459
- | `SECOND` | UTC second |
440
+ | Value | Output |
441
+ | ------------------ | ---------------------------------------- |
442
+ | `YEAR` | UTC year |
443
+ | `QUARTER` | Quarter 1-4 |
444
+ | `MONTH` | Month 1-12 |
445
+ | `MONTH_NAME` | English month name |
446
+ | `DAY` | Day of month |
447
+ | `DAY_OF_MONTH` | Day of month |
448
+ | `DAY_OF_WEEK` | UTC weekday, Sunday = 0 |
449
+ | `DAY_OF_WEEK_NAME` | English weekday name |
450
+ | `DAY_OF_YEAR` | Day of year |
451
+ | `WEEK_OF_YEAR` | Week according to the current convention |
452
+ | `HOUR` | UTC hour |
453
+ | `MINUTE` | UTC minute |
454
+ | `SECOND` | UTC second |
460
455
 
461
456
  Important: `PART` is available as a QDP function, but it is not a valid final result for `TranspileDatePreset` because it returns `string`. It can be used as a helper function when accessing the QDP transpiler directly, but a final date preset expression must return `date`, `dateRange`, or `partialDate`.
462
457
 
463
458
  ## Differences Between Period Functions
464
459
 
465
- | Function | Question it answers | Example | Type |
466
- | --- | --- | --- | --- |
467
- | `CALENDAR_PERIOD` | What is this/previous/next calendar period? | This month, last week | `relative` |
468
- | `PERIOD_AT` | What is period N inside a year? | April 2026, Q2 2026, W40 2026 | Current `relative` |
469
- | `RELATIVE_PERIOD` | What is the moving window from today/anchor? | Last 30 days | `rolling` |
460
+ | Function | Question it answers | Example | Type |
461
+ | ----------------- | -------------------------------------------- | ----------------------------- | ------------------ |
462
+ | `CALENDAR_PERIOD` | What is this/previous/next calendar period? | This month, last week | `relative` |
463
+ | `PERIOD_AT` | What is period N inside a year? | April 2026, Q2 2026, W40 2026 | Current `relative` |
464
+ | `RELATIVE_PERIOD` | What is the moving window from today/anchor? | Last 30 days | `rolling` |
470
465
 
471
466
  Examples with current date `2026-07-22`:
472
467
 
473
468
  ```ts
474
- CALENDAR_PERIOD("MONTH", -1)
469
+ CALENDAR_PERIOD("MONTH", -1);
475
470
  // previous calendar month: Jun 1 -> Jun 30
476
471
 
477
- PERIOD_AT("MONTH", 4, 2026)
472
+ PERIOD_AT("MONTH", 4, 2026);
478
473
  // fourth month of 2026: Apr 1 -> Apr 30
479
474
 
480
- RELATIVE_PERIOD(-29, "DAY")
475
+ RELATIVE_PERIOD(-29, "DAY");
481
476
  // last 30 days inclusive: Jun 23 -> Jul 22
482
477
  ```
483
478
 
@@ -490,27 +485,27 @@ It is not recommended to merge them into a single public function because they e
490
485
  ### Expression Nodes
491
486
 
492
487
  ```ts
493
- { type: 'NOW' }
494
- { type: 'TODAY' }
495
- { type: 'DATE', value: '2026-07-20' }
496
- { type: 'DATE_RANGE', start: DatePresetJSON, end: DatePresetJSON }
497
- { type: 'PARTIAL_DATE', value: { year?, quarter?, month?, week?, day? } }
498
- { type: 'RELATIVE_PERIOD', offset: -30, unit: 'DAY', anchor?: DatePresetJSON }
499
- { type: 'CALENDAR_PERIOD', period: 'MONTH', offset?: 0 }
500
- { type: 'PERIOD_AT', period: 'MONTH', position: 4 | 'FIRST' | 'LAST', year?: 2026 }
501
- { type: 'START_OF', input: DatePresetJSON }
502
- { type: 'END_OF', input: DatePresetJSON }
488
+ { type: "NOW" }
489
+ { type: "TODAY" }
490
+ { type: "DATE", value: "2026-07-20" }
491
+ { type: "DATE_RANGE", start: DatePresetJSON, end: DatePresetJSON }
492
+ { type: "PARTIAL_DATE", value: { year?, quarter?, month?, week?, day? } }
493
+ { type: "RELATIVE_PERIOD", offset: -30, unit: "DAY", anchor?: DatePresetJSON }
494
+ { type: "CALENDAR_PERIOD", period: "MONTH", offset?: 0 }
495
+ { type: "PERIOD_AT", period: "MONTH", position: 4 | "FIRST" | "LAST", year?: 2026 }
496
+ { type: "START_OF", input: DatePresetJSON }
497
+ { type: "END_OF", input: DatePresetJSON }
503
498
  ```
504
499
 
505
500
  Example:
506
501
 
507
502
  ```ts
508
503
  TranspileJSONToDatePreset({
509
- type: 'PERIOD_AT',
510
- period: 'WEEK',
511
- position: 'LAST',
512
- year: 2028,
513
- })
504
+ type: "PERIOD_AT",
505
+ period: "WEEK",
506
+ position: "LAST",
507
+ year: 2028,
508
+ });
514
509
  // PERIOD_AT("WEEK", "LAST", 2028)
515
510
  ```
516
511
 
@@ -519,126 +514,161 @@ TranspileJSONToDatePreset({
519
514
  Date picker oriented shapes are also supported:
520
515
 
521
516
  ```ts
522
- { type: 'SINGLE_PERIOD', date: { year: 2026, month: 7 } }
517
+ { type: "SINGLE_PERIOD", date: { year: 2026, month: 7 } }
523
518
  // DATE_RANGE(DATE("2026-07-01"), END_OF(DATE("2026-07-31")))
524
519
 
525
- { type: 'SINGLE_PERIOD', period: 'month', offset: -1 }
520
+ { type: "SINGLE_PERIOD", period: "month", offset: -1 }
526
521
  // CALENDAR_PERIOD("MONTH", -1)
527
522
 
528
- { type: 'CUSTOM_RANGE', from: { year: 2026, month: 5, day: 31 }, to: { year: 2026, month: 6, day: 30 } }
523
+ { type: "CUSTOM_RANGE", from: { year: 2026, month: 5, day: 31 }, to: { year: 2026, month: 6, day: 30 } }
529
524
  // DATE_RANGE(DATE("2026-05-31"), END_OF(DATE("2026-06-30")))
530
525
 
531
- { type: 'ROLLING_WINDOW', direction: 'last', amount: 30, unit: 'days' }
526
+ { type: "ROLLING_WINDOW", direction: "last", amount: 30, unit: "days" }
532
527
  // RELATIVE_PERIOD(-30, "DAY")
533
528
  ```
534
529
 
535
530
  Rules:
536
531
 
537
- - `SINGLE_PERIOD` with `date` requires `year`.
538
- - `CUSTOM_RANGE` materializes dates with start-of-day and end-of-day boundaries.
539
- - `ROLLING_WINDOW` normalizes plural units by removing the final `s`.
540
- - `direction: 'NEXT'` produces a positive offset; any other value behaves as `LAST`.
532
+ - `SINGLE_PERIOD` with `date` requires `year`.
533
+ - `CUSTOM_RANGE` materializes dates with start-of-day and end-of-day boundaries.
534
+ - `ROLLING_WINDOW` normalizes plural units by removing the final `s`.
535
+ - `direction: "NEXT"` produces a positive offset; any other value behaves as `LAST`.
541
536
 
542
537
  ## Formatting
543
538
 
544
539
  `FormatDatePreset` formats values using `Intl.DateTimeFormat`.
545
540
 
546
- By default, `date` and `dateRange` values include time using `timeStyle: 'short'`. When `partialDateStyle: 'short'` is used and no explicit `timeStyle` is provided, `date` and `dateRange` values omit time so they align with picker-style labels. Passing `timeStyle` explicitly keeps time in the output.
541
+ By default, `date` and `dateRange` values include time using `timeStyle: "short"`. When `partialDateStyle: "short"` is used and no explicit `timeStyle` is provided, `date` and `dateRange` values omit time so they align with picker-style labels. Passing `timeStyle` explicitly keeps time in the output.
542
+
543
+ `dateStyle: "short-padded"` is a Formula Lang formatting extension. It keeps the date order and separators from the selected locale, but renders year, month, and day with two digits. For example, `en-US` renders `07/30/26`, while `es-CO` renders `30/07/26`.
544
+
545
+ `customFormat` has precedence over `dateStyle`, `timeStyle`, and locale date ordering. It is intended for explicit UI contracts that require a fixed token pattern. Supported tokens are `yyyy`, `yy`, `MM`, `M`, `dd`, `d`, `HH`, `H`, `mm`, `m`, `ss`, and `s`.
547
546
 
548
547
  Main options:
549
548
 
550
549
  ```ts
551
550
  {
552
- locale?: 'en-US',
553
- timezone?: {
554
- offset?: string,
555
- timeZone?: string,
556
- name?: string,
557
- type?: string,
558
- timeZoneId?: string,
559
- numericFormat?: number
560
- },
561
- dateStyle?: 'full' | 'long' | 'medium' | 'short',
562
- timeStyle?: 'full' | 'long' | 'medium' | 'short',
563
- partialDateStyle?: 'long' | 'short'
551
+ locale?: "en-US",
552
+ timezone?: { offset?: string; timeZone?: string; name?: string; type?: string; timeZoneId?: string; numericFormat?: number },
553
+ customFormat?: string,
554
+ dateStyle?: "full" | "long" | "medium" | "short" | "short-padded",
555
+ timeStyle?: "full" | "long" | "medium" | "short",
556
+ partialDateStyle?: "long" | "short"
564
557
  }
565
558
  ```
566
559
 
567
560
  Examples:
568
561
 
569
562
  ```ts
570
- FormatDatePreset('2026-07-15T14:35:27.000Z', {
571
- locale: 'en-US',
572
- timezone: { timeZone: 'America/Chicago' },
573
- dateStyle: 'medium',
574
- timeStyle: 'short',
575
- })
563
+ FormatDatePreset("2026-07-15T14:35:27.000Z", {
564
+ locale: "en-US",
565
+ timezone: { timeZone: "America/Chicago" },
566
+ dateStyle: "medium",
567
+ timeStyle: "short",
568
+ });
576
569
  // Jul 15, 2026, 9:35 AM
577
570
 
578
- FormatDatePreset('2026-07-15T14:35:27.000Z', { partialDateStyle: 'short' })
571
+ FormatDatePreset("2026-07-15T14:35:27.000Z", { partialDateStyle: "short" });
579
572
  // Jul 15, 2026
580
573
 
574
+ FormatDatePreset("2026-07-30T14:35:27.000Z", {
575
+ locale: "en-US",
576
+ dateStyle: "short-padded",
577
+ partialDateStyle: "short",
578
+ });
579
+ // 07/30/26
580
+
581
+ FormatDatePreset("2026-07-30T14:35:27.000Z", {
582
+ locale: "es-CO",
583
+ dateStyle: "short-padded",
584
+ partialDateStyle: "short",
585
+ });
586
+ // 30/07/26
587
+
588
+ FormatDatePreset("2026-07-30T14:35:27.000Z", {
589
+ locale: "es-CO",
590
+ customFormat: "MM/dd/yy",
591
+ });
592
+ // 07/30/26
593
+
581
594
  FormatDatePreset(
582
- { start: '2026-07-01T00:00:00.000Z', end: '2026-07-31T23:59:59.999Z' },
583
- { partialDateStyle: 'short' },
584
- )
585
- // { start: 'Jul 1, 2026', end: 'Jul 31, 2026' }
595
+ { start: "2026-07-01T00:00:00.000Z", end: "2026-07-31T23:59:59.999Z" },
596
+ { partialDateStyle: "short" },
597
+ );
598
+ // { start: "Jul 1, 2026", end: "Jul 31, 2026" }
586
599
 
587
- FormatDatePreset({ month: 4, day: 15 })
600
+ FormatDatePreset(
601
+ { start: "2026-07-01T05:00:00.000Z", end: "2026-08-01T04:59:59.999Z" },
602
+ {
603
+ locale: "en-US",
604
+ timezone: { timeZone: "America/Chicago" },
605
+ dateStyle: "short-padded",
606
+ partialDateStyle: "short",
607
+ },
608
+ );
609
+ // { start: "07/01/26", end: "07/31/26" }
610
+
611
+ FormatDatePreset({ month: 4, day: 15 });
588
612
  // April 15
589
613
 
590
- FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: 'short' })
614
+ FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: "short" });
591
615
  // Apr 15
592
616
 
593
- FormatDatePreset({ week: 40 }, { partialDateStyle: 'short' })
617
+ FormatDatePreset({ week: 40 }, { partialDateStyle: "short" });
594
618
  // W40
595
619
 
596
- FormatDatePreset({ day: 15 }, { partialDateStyle: 'short' })
620
+ FormatDatePreset({ day: 15 }, { partialDateStyle: "short" });
597
621
  // Day 15
622
+
623
+ FormatDatePreset(
624
+ { year: 2026, month: 7, day: 3 },
625
+ { locale: "en-US", dateStyle: "short-padded" },
626
+ );
627
+ // 07/03/26
598
628
  ```
599
629
 
600
630
  ## Legacy Tokens
601
631
 
602
632
  Legacy tokens are resolved with `TranspileJSONToDatePreset(token)` using `DATE_PRESET_TOKEN_MAP`.
603
633
 
604
- | Token | QDP expression |
605
- | --- | --- |
606
- | `NOW` | `NOW()` |
607
- | `TODAY` | `TODAY()` |
608
- | `CURRENT_DATE` | `TODAY()` |
609
- | `TODAY-7` | `START_OF(RELATIVE_PERIOD(-7, "DAY"))` |
610
- | `TODAY-30` | `START_OF(RELATIVE_PERIOD(-30, "DAY"))` |
611
- | `TODAY-60` | `START_OF(RELATIVE_PERIOD(-60, "DAY"))` |
612
- | `TODAY-90` | `START_OF(RELATIVE_PERIOD(-90, "DAY"))` |
613
- | `TODAY-120` | `START_OF(RELATIVE_PERIOD(-120, "DAY"))` |
614
- | `TODAY-365` | `START_OF(RELATIVE_PERIOD(-365, "DAY"))` |
615
- | `YESTERDAY` | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` |
616
- | `TOMORROW` | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` |
617
- | `CURRENT_MONTH` | `CALENDAR_PERIOD("MONTH", 0)` |
618
- | `CURRENT_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", 0))` |
619
- | `CURRENT_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", 0))` |
620
- | `LAST_MONTH` | `CALENDAR_PERIOD("MONTH", -1)` |
621
- | `LAST_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", -1))` |
622
- | `LAST_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", -1))` |
623
- | `CURRENT_WEEK` | `CALENDAR_PERIOD("WEEK", 0)` |
624
- | `CURRENT_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", 0))` |
625
- | `CURRENT_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", 0))` |
626
- | `LAST_WEEK` | `CALENDAR_PERIOD("WEEK", -1)` |
627
- | `LAST_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", -1))` |
628
- | `LAST_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", -1))` |
629
- | `CURRENT_QUARTER` | `CALENDAR_PERIOD("QUARTER", 0)` |
630
- | `CURRENT_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", 0))` |
631
- | `CURRENT_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", 0))` |
632
- | `LAST_QUARTER` | `CALENDAR_PERIOD("QUARTER", -1)` |
633
- | `LAST_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", -1))` |
634
- | `LAST_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", -1))` |
635
- | `CURRENT_YEAR` | `CALENDAR_PERIOD("YEAR", 0)` |
636
- | `CURRENT_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", 0))` |
637
- | `LAST_YEAR` | `CALENDAR_PERIOD("YEAR", -1)` |
638
- | `LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -1))` |
639
- | `LAST_YEAR_END` | `END_OF(CALENDAR_PERIOD("YEAR", -1))` |
640
- | `YEAR_BEFORE_LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -2))` |
641
- | `YEAR_BEFORE_LAST_YEAR_END` | `END_OF(CALENDAR_PERIOD("YEAR", -2))` |
634
+ | Token | QDP expression |
635
+ | ----------------------------- | --------------------------------------------- |
636
+ | `NOW` | `NOW()` |
637
+ | `TODAY` | `TODAY()` |
638
+ | `CURRENT_DATE` | `TODAY()` |
639
+ | `TODAY-7` | `START_OF(RELATIVE_PERIOD(-7, "DAY"))` |
640
+ | `TODAY-30` | `START_OF(RELATIVE_PERIOD(-30, "DAY"))` |
641
+ | `TODAY-60` | `START_OF(RELATIVE_PERIOD(-60, "DAY"))` |
642
+ | `TODAY-90` | `START_OF(RELATIVE_PERIOD(-90, "DAY"))` |
643
+ | `TODAY-120` | `START_OF(RELATIVE_PERIOD(-120, "DAY"))` |
644
+ | `TODAY-365` | `START_OF(RELATIVE_PERIOD(-365, "DAY"))` |
645
+ | `YESTERDAY` | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` |
646
+ | `TOMORROW` | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` |
647
+ | `CURRENT_MONTH` | `CALENDAR_PERIOD("MONTH", 0)` |
648
+ | `CURRENT_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", 0))` |
649
+ | `CURRENT_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", 0))` |
650
+ | `LAST_MONTH` | `CALENDAR_PERIOD("MONTH", -1)` |
651
+ | `LAST_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", -1))` |
652
+ | `LAST_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", -1))` |
653
+ | `CURRENT_WEEK` | `CALENDAR_PERIOD("WEEK", 0)` |
654
+ | `CURRENT_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", 0))` |
655
+ | `CURRENT_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", 0))` |
656
+ | `LAST_WEEK` | `CALENDAR_PERIOD("WEEK", -1)` |
657
+ | `LAST_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", -1))` |
658
+ | `LAST_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", -1))` |
659
+ | `CURRENT_QUARTER` | `CALENDAR_PERIOD("QUARTER", 0)` |
660
+ | `CURRENT_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", 0))` |
661
+ | `CURRENT_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", 0))` |
662
+ | `LAST_QUARTER` | `CALENDAR_PERIOD("QUARTER", -1)` |
663
+ | `LAST_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", -1))` |
664
+ | `LAST_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", -1))` |
665
+ | `CURRENT_YEAR` | `CALENDAR_PERIOD("YEAR", 0)` |
666
+ | `CURRENT_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", 0))` |
667
+ | `LAST_YEAR` | `CALENDAR_PERIOD("YEAR", -1)` |
668
+ | `LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -1))` |
669
+ | `LAST_YEAR_END` | `END_OF(CALENDAR_PERIOD("YEAR", -1))` |
670
+ | `YEAR_BEFORE_LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -2))` |
671
+ | `YEAR_BEFORE_LAST_YEAR_END` | `END_OF(CALENDAR_PERIOD("YEAR", -2))` |
642
672
 
643
673
  Tokens can be wrapped like `{{TODAY-7}}`. The normalizer removes braces and outer spaces.
644
674
 
@@ -646,51 +676,51 @@ Scalar tokens such as `CURRENT_TIME`, `CURRENT_TIMEZONE`, and `CURRENT_DAY_OF_WE
646
676
 
647
677
  ## Main Equivalences With The Date Picker
648
678
 
649
- | Picker expression | QDP expression | Note |
650
- | --- | --- | --- |
651
- | Today | `TODAY()` | Fixed date at the start of the current day |
652
- | Yesterday | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` | Start of yesterday |
653
- | Tomorrow | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` | Start of tomorrow |
654
- | This month | `CALENDAR_PERIOD("MONTH", 0)` | Current calendar month |
655
- | Previous month | `CALENDAR_PERIOD("MONTH", -1)` | Previous calendar month |
656
- | Last 30 days inclusive | `RELATIVE_PERIOD(-29, "DAY")` | Inclusive 30-day rolling window counting today |
657
- | April | `PARTIAL_DATE("ANY", 4)` | Recurring month |
658
- | April 2026 | `PERIOD_AT("MONTH", 4, 2026)` | Positional month in a year |
659
- | Apr 15 | `PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)` | Recurring month/day |
660
- | Day 15 | `PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15)` | Recurring day of month |
661
- | Q2 | `PARTIAL_DATE("ANY", "ANY", 2)` | Recurring quarter |
662
- | Q2 2026 | `PERIOD_AT("QUARTER", 2, 2026)` | Positional quarter in a year |
663
- | W40 | `PARTIAL_DATE("ANY", "ANY", "ANY", 40)` | Recurring week |
664
- | W40 2026 | `PERIOD_AT("WEEK", 40, 2026)` | Positional week in a year |
665
- | Last week of 2028 | `PERIOD_AT("WEEK", "LAST", 2028)` | Avoids assuming W53/W54 |
666
- | Last month of the past year | `PERIOD_AT("MONTH", "LAST", 2025)` | Equivalent to December 2025 with current date 2026 |
667
- | Between inclusive | `DATE_RANGE(DATE(start), END_OF(DATE(end)))` | Materialize inclusive boundaries |
668
- | Between exclusive | `DATE_RANGE(DATE(adjustedStart), END_OF(DATE(adjustedEnd)))` | Materialize exclusion in the dates |
679
+ | Picker expression | QDP expression | Note |
680
+ | --------------------------- | ------------------------------------------------------------ | -------------------------------------------------- |
681
+ | Today | `TODAY()` | Fixed date at the start of the current day |
682
+ | Yesterday | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` | Start of yesterday |
683
+ | Tomorrow | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` | Start of tomorrow |
684
+ | This month | `CALENDAR_PERIOD("MONTH", 0)` | Current calendar month |
685
+ | Previous month | `CALENDAR_PERIOD("MONTH", -1)` | Previous calendar month |
686
+ | Last 30 days inclusive | `RELATIVE_PERIOD(-29, "DAY")` | Inclusive 30-day rolling window counting today |
687
+ | April | `PARTIAL_DATE("ANY", 4)` | Recurring month |
688
+ | April 2026 | `PERIOD_AT("MONTH", 4, 2026)` | Positional month in a year |
689
+ | Apr 15 | `PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)` | Recurring month/day |
690
+ | Day 15 | `PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15)` | Recurring day of month |
691
+ | Q2 | `PARTIAL_DATE("ANY", "ANY", 2)` | Recurring quarter |
692
+ | Q2 2026 | `PERIOD_AT("QUARTER", 2, 2026)` | Positional quarter in a year |
693
+ | W40 | `PARTIAL_DATE("ANY", "ANY", "ANY", 40)` | Recurring week |
694
+ | W40 2026 | `PERIOD_AT("WEEK", 40, 2026)` | Positional week in a year |
695
+ | Last week of 2028 | `PERIOD_AT("WEEK", "LAST", 2028)` | Avoids assuming W53/W54 |
696
+ | Last month of the past year | `PERIOD_AT("MONTH", "LAST", 2025)` | Equivalent to December 2025 with current date 2026 |
697
+ | Between inclusive | `DATE_RANGE(DATE(start), END_OF(DATE(end)))` | Materialize inclusive boundaries |
698
+ | Between exclusive | `DATE_RANGE(DATE(adjustedStart), END_OF(DATE(adjustedEnd)))` | Materialize exclusion in the dates |
669
699
 
670
700
  ## Validations And Expected Errors
671
701
 
672
702
  Important invalid cases:
673
703
 
674
704
  ```ts
675
- DATE("2026-02-31")
705
+ DATE("2026-02-31");
676
706
  // INVALID_ALLOW_VALUE: calendrically invalid date
677
707
 
678
- DATE("2026-02-31T00:00")
708
+ DATE("2026-02-31T00:00");
679
709
  // INVALID_ALLOW_VALUE: calendrically invalid date
680
710
 
681
- RELATIVE_PERIOD(-1, "HOUR")
711
+ RELATIVE_PERIOD(-1, "HOUR");
682
712
  // INVALID_ALLOW_VALUE: unsupported unit
683
713
 
684
- CALENDAR_PERIOD("DECADE", 0)
714
+ CALENDAR_PERIOD("DECADE", 0);
685
715
  // INVALID_ALLOW_VALUE: unsupported period
686
716
 
687
- PERIOD_AT("MONTH", "MIDDLE", 2026)
717
+ PERIOD_AT("MONTH", "MIDDLE", 2026);
688
718
  // INVALID_ALLOW_VALUE: unsupported symbolic position
689
719
 
690
- PARTIAL_DATE("ANY", 13)
720
+ PARTIAL_DATE("ANY", 13);
691
721
  // INVALID_ALLOW_VALUE: month out of range
692
722
 
693
- DATE_RANGE(DATE("2026-07-31T00:00"), DATE("2026-07-01T00:00"))
723
+ DATE_RANGE(DATE("2026-07-31T00:00"), DATE("2026-07-01T00:00"));
694
724
  // INVALID_DATE_RANGE: end before start
695
725
  ```
696
726
 
@@ -698,25 +728,25 @@ Additionally, any QDP expression whose final result is not `date`, `dateRange`,
698
728
 
699
729
  ## Relevant Files
700
730
 
701
- | File | Responsibility |
702
- | --- | --- |
703
- | `src/date-presets/date-presets.ts` | Public API, JSON conversion, formatting, and `valueType` classification |
704
- | `src/date-presets/date-preset-tokens.ts` | Legacy token map to QDP expressions |
705
- | `src/functions/index.ts` | Function registration for `ENGINES.QDP` |
706
- | `src/utils/datePresetUtils.ts` | Date, range, calendar, week, and partial date resolution |
707
- | `src/utils/timezone.ts` | Timezone definition normalization and offset helpers |
708
- | `src/functions/*.ts` | Individual QDP function definitions |
709
- | `__tests__/unit/datePresetTranspiler.test.ts` | Main transpilation and picker compatibility cases |
710
- | `__tests__/unit/datePresetJson.test.ts` | JSON to QDP conversion |
711
- | `__tests__/unit/datePresetFormat.test.ts` | Date and partial date formatting |
712
- | `__tests__/unit/datePresetTokens.test.ts` | Legacy tokens |
713
- | `date-preset-qdp-examples.csv` | Generated examples with tokens and picker expressions |
731
+ | File | Responsibility |
732
+ | --------------------------------------------- | ----------------------------------------------------------------------- |
733
+ | `src/date-presets/date-presets.ts` | Public API, JSON conversion, formatting, and `valueType` classification |
734
+ | `src/date-presets/date-preset-tokens.ts` | Legacy token map to QDP expressions |
735
+ | `src/functions/index.ts` | Function registration for `ENGINES.QDP` |
736
+ | `src/utils/datePresetUtils.ts` | Date, range, calendar, week, and partial date resolution |
737
+ | `src/utils/timezone.ts` | Timezone definition normalization and offset helpers |
738
+ | `src/functions/*.ts` | Individual QDP function definitions |
739
+ | `__tests__/unit/datePresetTranspiler.test.ts` | Main transpilation and picker compatibility cases |
740
+ | `__tests__/unit/datePresetJson.test.ts` | JSON to QDP conversion |
741
+ | `__tests__/unit/datePresetFormat.test.ts` | Date and partial date formatting |
742
+ | `__tests__/unit/datePresetTokens.test.ts` | Legacy tokens |
743
+ | `date-preset-qdp-examples.csv` | Generated examples with tokens and picker expressions |
714
744
 
715
745
  ## Current State And Design Notes
716
746
 
717
- - QDP is limited to date preset functions in `ENGINE_FN_MAP[ENGINES.QDP]`.
718
- - Date preset expressions can be nested as long as the final result is `date`, `dateRange`, or `partialDate`.
719
- - `partialDate` was added as a primitive to represent incomplete date picker selections.
720
- - Week compatibility with the date picker uses Sunday as the default start day and supports W54.
721
- - `PERIOD_AT` supports symbolic positions `FIRST` and `LAST` to reduce ambiguity in variable-length periods.
722
- - For documentation and example auditing, the CSV `date-preset-qdp-examples.csv` contains resolved, formatted, and classified values.
747
+ - QDP is limited to date preset functions in `ENGINE_FN_MAP[ENGINES.QDP]`.
748
+ - Date preset expressions can be nested as long as the final result is `date`, `dateRange`, or `partialDate`.
749
+ - `partialDate` was added as a primitive to represent incomplete date picker selections.
750
+ - Week compatibility with the date picker uses Sunday as the default start day and supports W54.
751
+ - `PERIOD_AT` supports symbolic positions `FIRST` and `LAST` to reduce ambiguity in variable-length periods.
752
+ - For documentation and example auditing, the CSV `date-preset-qdp-examples.csv` contains resolved, formatted, and classified values.