@qrvey/formula-lang 3.2.0-rc.1078 → 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.
Files changed (96) hide show
  1. package/QRVEY-DATE-PRESETS.md +321 -278
  2. package/dist/cjs/constants/interfaces.d.ts +10 -4
  3. package/dist/cjs/date-presets/date-preset-tokens.js.map +1 -0
  4. package/dist/{module → cjs/date-presets}/date-presets.d.ts +1 -1
  5. package/dist/cjs/{date-presets.js → date-presets/date-presets.js} +89 -12
  6. package/dist/cjs/date-presets/date-presets.js.map +1 -0
  7. package/dist/cjs/date-presets/index.d.ts +2 -0
  8. package/dist/cjs/date-presets/index.js +11 -0
  9. package/dist/cjs/date-presets/index.js.map +1 -0
  10. package/dist/cjs/functions/calendarPeriod.js +1 -1
  11. package/dist/cjs/functions/calendarPeriod.js.map +1 -1
  12. package/dist/cjs/functions/date.js +1 -1
  13. package/dist/cjs/functions/date.js.map +1 -1
  14. package/dist/cjs/functions/dateRange.js +1 -1
  15. package/dist/cjs/functions/dateRange.js.map +1 -1
  16. package/dist/cjs/functions/endOf.js +1 -1
  17. package/dist/cjs/functions/endOf.js.map +1 -1
  18. package/dist/cjs/functions/part.js +1 -1
  19. package/dist/cjs/functions/part.js.map +1 -1
  20. package/dist/cjs/functions/partialDate.js +1 -1
  21. package/dist/cjs/functions/partialDate.js.map +1 -1
  22. package/dist/cjs/functions/periodAt.js +1 -1
  23. package/dist/cjs/functions/periodAt.js.map +1 -1
  24. package/dist/cjs/functions/relativePeriod.js +1 -1
  25. package/dist/cjs/functions/relativePeriod.js.map +1 -1
  26. package/dist/cjs/functions/startOf.js +1 -1
  27. package/dist/cjs/functions/startOf.js.map +1 -1
  28. package/dist/cjs/functions/today.js +1 -1
  29. package/dist/cjs/functions/today.js.map +1 -1
  30. package/dist/cjs/index.d.ts +2 -3
  31. package/dist/cjs/index.js +2 -3
  32. package/dist/cjs/index.js.map +1 -1
  33. package/dist/cjs/transpiler/columnTranspilation.js +5 -2
  34. package/dist/cjs/transpiler/columnTranspilation.js.map +1 -1
  35. package/dist/cjs/{functions → utils}/datePresetUtils.js +36 -15
  36. package/dist/cjs/utils/datePresetUtils.js.map +1 -0
  37. package/dist/cjs/utils/index.d.ts +1 -0
  38. package/dist/cjs/utils/index.js +1 -0
  39. package/dist/cjs/utils/index.js.map +1 -1
  40. package/dist/cjs/utils/timezone.d.ts +12 -0
  41. package/dist/cjs/utils/timezone.js +58 -0
  42. package/dist/cjs/utils/timezone.js.map +1 -0
  43. package/dist/module/constants/interfaces.d.ts +10 -4
  44. package/dist/module/date-presets/date-preset-tokens.js.map +1 -0
  45. package/dist/{cjs → module/date-presets}/date-presets.d.ts +1 -1
  46. package/dist/module/{date-presets.js → date-presets/date-presets.js} +89 -12
  47. package/dist/module/date-presets/date-presets.js.map +1 -0
  48. package/dist/module/date-presets/index.d.ts +2 -0
  49. package/dist/module/date-presets/index.js +3 -0
  50. package/dist/module/date-presets/index.js.map +1 -0
  51. package/dist/module/functions/calendarPeriod.js +1 -1
  52. package/dist/module/functions/calendarPeriod.js.map +1 -1
  53. package/dist/module/functions/date.js +1 -1
  54. package/dist/module/functions/date.js.map +1 -1
  55. package/dist/module/functions/dateRange.js +1 -1
  56. package/dist/module/functions/dateRange.js.map +1 -1
  57. package/dist/module/functions/endOf.js +1 -1
  58. package/dist/module/functions/endOf.js.map +1 -1
  59. package/dist/module/functions/part.js +1 -1
  60. package/dist/module/functions/part.js.map +1 -1
  61. package/dist/module/functions/partialDate.js +1 -1
  62. package/dist/module/functions/partialDate.js.map +1 -1
  63. package/dist/module/functions/periodAt.js +1 -1
  64. package/dist/module/functions/periodAt.js.map +1 -1
  65. package/dist/module/functions/relativePeriod.js +1 -1
  66. package/dist/module/functions/relativePeriod.js.map +1 -1
  67. package/dist/module/functions/startOf.js +1 -1
  68. package/dist/module/functions/startOf.js.map +1 -1
  69. package/dist/module/functions/today.js +1 -1
  70. package/dist/module/functions/today.js.map +1 -1
  71. package/dist/module/index.d.ts +2 -3
  72. package/dist/module/index.js +1 -2
  73. package/dist/module/index.js.map +1 -1
  74. package/dist/module/transpiler/columnTranspilation.js +5 -2
  75. package/dist/module/transpiler/columnTranspilation.js.map +1 -1
  76. package/dist/module/{functions → utils}/datePresetUtils.js +36 -15
  77. package/dist/module/utils/datePresetUtils.js.map +1 -0
  78. package/dist/module/utils/index.d.ts +1 -0
  79. package/dist/module/utils/index.js +1 -0
  80. package/dist/module/utils/index.js.map +1 -1
  81. package/dist/module/utils/timezone.d.ts +12 -0
  82. package/dist/module/utils/timezone.js +53 -0
  83. package/dist/module/utils/timezone.js.map +1 -0
  84. package/package.json +1 -1
  85. package/dist/cjs/date-preset-tokens.js.map +0 -1
  86. package/dist/cjs/date-presets.js.map +0 -1
  87. package/dist/cjs/functions/datePresetUtils.js.map +0 -1
  88. package/dist/module/date-preset-tokens.js.map +0 -1
  89. package/dist/module/date-presets.js.map +0 -1
  90. package/dist/module/functions/datePresetUtils.js.map +0 -1
  91. /package/dist/cjs/{date-preset-tokens.d.ts → date-presets/date-preset-tokens.d.ts} +0 -0
  92. /package/dist/cjs/{date-preset-tokens.js → date-presets/date-preset-tokens.js} +0 -0
  93. /package/dist/cjs/{functions → utils}/datePresetUtils.d.ts +0 -0
  94. /package/dist/module/{date-preset-tokens.d.ts → date-presets/date-preset-tokens.d.ts} +0 -0
  95. /package/dist/module/{date-preset-tokens.js → date-presets/date-preset-tokens.js} +0 -0
  96. /package/dist/module/{functions → utils}/datePresetUtils.d.ts +0 -0
@@ -12,7 +12,7 @@ The API does not change the main grammar. It uses regular Formula Lang functions
12
12
 
13
13
  ## Public API
14
14
 
15
- The main APIs are in `src/date-presets.ts` and are exported from `src/index.ts`.
15
+ The main APIs are in `src/date-presets/date-presets.ts` and are exported from `src/index.ts`.
16
16
 
17
17
  ```ts
18
18
  TranspileDatePreset(program: string, context?: FormulaContext): DatePresetTranspilationResponse | undefined
@@ -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,34 +91,37 @@ QDP functions can receive `FormulaContext` with date preset configuration:
91
91
 
92
92
  ```ts
93
93
  {
94
- datePreset: {
95
- calendar: 'gregorian',
96
- timezone: 'UTC',
97
- locale: 'en-US',
98
- fiscalYearStartMonth: 1,
99
- fiscalYearStartDay: 1,
100
- weekStartsOn: 0
101
- }
94
+ timezone: { timeZone: "America/Bogota" },
95
+ datePreset: {
96
+ calendar: "gregorian",
97
+ locale: "en-US",
98
+ fiscalYearStartMonth: 1,
99
+ fiscalYearStartDay: 1,
100
+ weekStartsOn: 0,
101
+ },
102
102
  }
103
103
  ```
104
104
 
105
105
  Current defaults:
106
106
 
107
- | Option | Default | Notes |
108
- | --- | --- | --- |
109
- | `calendar` | `gregorian` | Also supports `corporate-fiscal`, `retail-4-4-5`, `retail-4-5-4` |
110
- | `timezone` | `UTC` | Affects day starts/ends and periods |
111
- | `locale` | `en-US` | Used by formatting options |
112
- | `fiscalYearStartMonth` | `1` | Clamped to 1-12 |
113
- | `fiscalYearStartDay` | `1` | Clamped to 1-31 and adjusted to the last valid day of the month |
114
- | `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 |
116
+
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" }`.
115
118
 
116
119
  Current week convention:
117
120
 
118
- - The default is Sunday (`weekStartsOn: 0`).
119
- - Week 1 of a year starts on the Sunday on or before Jan 1.
120
- - Some years can have W54 under this convention.
121
- - `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.
122
125
 
123
126
  ## QDP Date Preset Functions
124
127
 
@@ -127,7 +130,7 @@ Current week convention:
127
130
  Returns the current timestamp.
128
131
 
129
132
  ```ts
130
- NOW()
133
+ NOW();
131
134
  // 2026-07-22T23:42:50.413Z
132
135
  ```
133
136
 
@@ -140,7 +143,7 @@ Value type when used as the final result: `fixed`.
140
143
  Returns the start of the current day in the context timezone.
141
144
 
142
145
  ```ts
143
- TODAY()
146
+ TODAY();
144
147
  // 2026-07-22T00:00:00.000Z
145
148
  ```
146
149
 
@@ -153,30 +156,30 @@ Value type when used as the final result: `fixed`.
153
156
  Normalizes an ISO date or ISO date-time string to UTC ISO.
154
157
 
155
158
  ```ts
156
- DATE("2026-07-20")
159
+ DATE("2026-07-20");
157
160
  // 2026-07-20T00:00:00.000Z
158
161
  ```
159
162
 
160
163
  Supported input formats:
161
164
 
162
- | Input | Example | Resolved value |
163
- | --- | --- | --- |
164
- | ISO date | `DATE("2026-07-08")` | `2026-07-08T00:00:00.000Z` |
165
- | ISO date-time without timezone | `DATE("2026-07-08T15:30")` | `2026-07-08T15:30:00.000Z` |
166
- | 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` |
167
170
  | ISO date-time with offset timezone | `DATE("2026-07-08T15:30:45-05:00")` | `2026-07-08T20:30:45.000Z` |
168
171
 
169
172
  Parameters:
170
173
 
171
- | Parameter | Type | Required | Validation |
172
- | --- | --- | --- | --- |
173
- | `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 |
174
177
 
175
178
  Rules:
176
179
 
177
- - `DATE("2026-07-08")` is valid and resolves to `2026-07-08T00:00:00.000Z`.
178
- - Calendrically invalid dates, such as `DATE("2026-02-31T00:00")`, are rejected.
179
- - 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.
180
183
 
181
184
  Output: `date`.
182
185
 
@@ -187,24 +190,21 @@ Value type: `fixed`.
187
190
  Builds a range between two dates.
188
191
 
189
192
  ```ts
190
- DATE_RANGE(
191
- DATE("2026-07-20"),
192
- END_OF(DATE("2026-07-25"))
193
- )
193
+ DATE_RANGE(DATE("2026-07-20"), END_OF(DATE("2026-07-25")));
194
194
  // { start: "2026-07-20T00:00:00.000Z", end: "2026-07-25T23:59:59.999Z" }
195
195
  ```
196
196
 
197
197
  Parameters:
198
198
 
199
- | Parameter | Type | Required |
200
- | --- | --- | --- |
201
- | `START` | `date` | Yes |
202
- | `END` | `date` | Yes |
199
+ | Parameter | Type | Required |
200
+ | --------- | ------ | -------- |
201
+ | `START` | `date` | Yes |
202
+ | `END` | `date` | Yes |
203
203
 
204
204
  Rules:
205
205
 
206
- - `END` must be greater than or equal to `START`.
207
- - 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`.
208
208
 
209
209
  Output: `dateRange`.
210
210
 
@@ -215,28 +215,28 @@ Value type: `fixed`.
215
215
  Creates a moving window relative to an anchor. If no anchor is passed, it uses `TODAY()`.
216
216
 
217
217
  ```ts
218
- RELATIVE_PERIOD(-29, "DAY")
218
+ RELATIVE_PERIOD(-29, "DAY");
219
219
  // { start: "2026-06-23T00:00:00.000Z", end: "2026-07-22T23:59:59.999Z" }
220
220
  ```
221
221
 
222
222
  Parameters:
223
223
 
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 |
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 |
229
229
 
230
230
  Semantics:
231
231
 
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.
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.
235
235
 
236
236
  Examples:
237
237
 
238
238
  ```ts
239
- RELATIVE_PERIOD(-2, "WEEK", DATE("2026-07-08T15:30"))
239
+ RELATIVE_PERIOD(-2, "WEEK", DATE("2026-07-08T15:30"));
240
240
  // 2026-06-24T00:00:00.000Z -> 2026-07-08T23:59:59.999Z
241
241
  ```
242
242
 
@@ -249,31 +249,31 @@ Value type: `rolling`.
249
249
  Returns a complete calendar period containing the current day, shifted by `offset`.
250
250
 
251
251
  ```ts
252
- CALENDAR_PERIOD("MONTH", 0)
252
+ CALENDAR_PERIOD("MONTH", 0);
253
253
  // 2026-07-01T00:00:00.000Z -> 2026-07-31T23:59:59.999Z
254
254
  ```
255
255
 
256
256
  Parameters:
257
257
 
258
- | Parameter | Type | Required | Values |
259
- | --- | --- | --- | --- |
260
- | `PERIOD` | `string` | Yes | `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR` |
261
- | `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` |
262
262
 
263
263
  Semantics:
264
264
 
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.
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.
269
269
 
270
270
  Examples:
271
271
 
272
272
  ```ts
273
- CALENDAR_PERIOD("WEEK", 0)
273
+ CALENDAR_PERIOD("WEEK", 0);
274
274
  // with weekStartsOn Sunday: 2026-07-19T00:00:00.000Z -> 2026-07-25T23:59:59.999Z
275
275
 
276
- CALENDAR_PERIOD("QUARTER", -1)
276
+ CALENDAR_PERIOD("QUARTER", -1);
277
277
  // 2026-04-01T00:00:00.000Z -> 2026-06-30T23:59:59.999Z
278
278
  ```
279
279
 
@@ -286,37 +286,37 @@ Value type: `relative`.
286
286
  Returns the period located at a position inside a year. If `year` is not passed, it uses the current year from the context.
287
287
 
288
288
  ```ts
289
- PERIOD_AT("MONTH", 4, 2026)
289
+ PERIOD_AT("MONTH", 4, 2026);
290
290
  // 2026-04-01T00:00:00.000Z -> 2026-04-30T23:59:59.999Z
291
291
  ```
292
292
 
293
293
  Parameters:
294
294
 
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 |
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 |
300
300
 
301
301
  Examples:
302
302
 
303
303
  ```ts
304
- PERIOD_AT("QUARTER", 2, 2026)
304
+ PERIOD_AT("QUARTER", 2, 2026);
305
305
  // 2026-04-01T00:00:00.000Z -> 2026-06-30T23:59:59.999Z
306
306
 
307
- PERIOD_AT("MONTH", "LAST", 2025)
307
+ PERIOD_AT("MONTH", "LAST", 2025);
308
308
  // 2025-12-01T00:00:00.000Z -> 2025-12-31T23:59:59.999Z
309
309
 
310
- PERIOD_AT("WEEK", "LAST", 2028)
310
+ PERIOD_AT("WEEK", "LAST", 2028);
311
311
  // 2028-12-31T00:00:00.000Z -> 2029-01-06T23:59:59.999Z
312
312
  ```
313
313
 
314
314
  Week rules:
315
315
 
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.
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.
320
320
 
321
321
  Output: `dateRange`.
322
322
 
@@ -327,71 +327,71 @@ Current value type: `relative`.
327
327
  Represents a partial or recurring date. Each field can be a number or `"ANY"`. Omitted fields are treated as `ANY`.
328
328
 
329
329
  ```ts
330
- PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)
330
+ PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15);
331
331
  // { month: 4, day: 15 }
332
332
  ```
333
333
 
334
334
  Parameters:
335
335
 
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` |
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` |
343
343
 
344
344
  Date picker examples:
345
345
 
346
346
  ```ts
347
- PARTIAL_DATE()
347
+ PARTIAL_DATE();
348
348
  // {}
349
349
  // Short format: Any date
350
350
 
351
- PARTIAL_DATE("ANY", 4)
351
+ PARTIAL_DATE("ANY", 4);
352
352
  // { month: 4 }
353
353
  // Short format: Apr
354
354
 
355
- PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)
355
+ PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15);
356
356
  // { month: 4, day: 15 }
357
357
  // Short format: Apr 15
358
358
 
359
- PARTIAL_DATE("ANY", "ANY", 2)
359
+ PARTIAL_DATE("ANY", "ANY", 2);
360
360
  // { quarter: 2 }
361
361
  // Short format: Q2
362
362
 
363
- PARTIAL_DATE("ANY", "ANY", "ANY", 40)
363
+ PARTIAL_DATE("ANY", "ANY", "ANY", 40);
364
364
  // { week: 40 }
365
365
  // Short format: W40
366
366
 
367
- PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15)
367
+ PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15);
368
368
  // { day: 15 }
369
369
  // Short format: Day 15
370
370
  ```
371
371
 
372
372
  Value type:
373
373
 
374
- - `fixed` if it includes `year`, `month`, and `day`, without `week`.
375
- - `relative` if it includes `week`.
376
- - `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.
377
377
 
378
378
  ### `START_OF(value)`
379
379
 
380
380
  Returns the start of a `date` or the `start` of a `dateRange`.
381
381
 
382
382
  ```ts
383
- START_OF(DATE("2026-07-08T15:30"))
383
+ START_OF(DATE("2026-07-08T15:30"));
384
384
  // 2026-07-08T00:00:00.000Z
385
385
 
386
- START_OF(CALENDAR_PERIOD("MONTH", -1))
386
+ START_OF(CALENDAR_PERIOD("MONTH", -1));
387
387
  // 2026-06-01T00:00:00.000Z
388
388
  ```
389
389
 
390
390
  Parameter:
391
391
 
392
- | Parameter | Type |
393
- | --- | --- |
394
- | `VALUE` | `date` or `dateRange` |
392
+ | Parameter | Type |
393
+ | --------- | --------------------- |
394
+ | `VALUE` | `date` or `dateRange` |
395
395
 
396
396
  Output: `date`.
397
397
 
@@ -402,18 +402,18 @@ Value type when used as root: `relative`.
402
402
  Returns the end of a `date` or the `end` of a `dateRange`.
403
403
 
404
404
  ```ts
405
- END_OF(DATE("2026-07-08T15:30"))
405
+ END_OF(DATE("2026-07-08T15:30"));
406
406
  // 2026-07-08T23:59:59.999Z
407
407
 
408
- END_OF(CALENDAR_PERIOD("YEAR", -1))
408
+ END_OF(CALENDAR_PERIOD("YEAR", -1));
409
409
  // 2025-12-31T23:59:59.999Z
410
410
  ```
411
411
 
412
412
  Parameter:
413
413
 
414
- | Parameter | Type |
415
- | --- | --- |
416
- | `VALUE` | `date` or `dateRange` |
414
+ | Parameter | Type |
415
+ | --------- | --------------------- |
416
+ | `VALUE` | `date` or `dateRange` |
417
417
 
418
418
  Output: `date`.
419
419
 
@@ -424,55 +424,55 @@ Value type when used as root: `relative`.
424
424
  Extracts one part of a date.
425
425
 
426
426
  ```ts
427
- PART(DATE("2026-07-08T15:30"), "DAY_OF_WEEK_NAME")
427
+ PART(DATE("2026-07-08T15:30"), "DAY_OF_WEEK_NAME");
428
428
  // Wednesday
429
429
  ```
430
430
 
431
431
  Parameters:
432
432
 
433
- | Parameter | Type | Values |
434
- | --- | --- | --- |
435
- | `DATE` | `date` | Resolved ISO date |
436
- | `PART` | `string` | See the table below |
433
+ | Parameter | Type | Values |
434
+ | --------- | -------- | ------------------- |
435
+ | `DATE` | `date` | Resolved ISO date |
436
+ | `PART` | `string` | See the table below |
437
437
 
438
438
  Supported parts:
439
439
 
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 |
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 |
455
455
 
456
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`.
457
457
 
458
458
  ## Differences Between Period Functions
459
459
 
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` |
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` |
465
465
 
466
466
  Examples with current date `2026-07-22`:
467
467
 
468
468
  ```ts
469
- CALENDAR_PERIOD("MONTH", -1)
469
+ CALENDAR_PERIOD("MONTH", -1);
470
470
  // previous calendar month: Jun 1 -> Jun 30
471
471
 
472
- PERIOD_AT("MONTH", 4, 2026)
472
+ PERIOD_AT("MONTH", 4, 2026);
473
473
  // fourth month of 2026: Apr 1 -> Apr 30
474
474
 
475
- RELATIVE_PERIOD(-29, "DAY")
475
+ RELATIVE_PERIOD(-29, "DAY");
476
476
  // last 30 days inclusive: Jun 23 -> Jul 22
477
477
  ```
478
478
 
@@ -485,27 +485,27 @@ It is not recommended to merge them into a single public function because they e
485
485
  ### Expression Nodes
486
486
 
487
487
  ```ts
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 }
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 }
498
498
  ```
499
499
 
500
500
  Example:
501
501
 
502
502
  ```ts
503
503
  TranspileJSONToDatePreset({
504
- type: 'PERIOD_AT',
505
- period: 'WEEK',
506
- position: 'LAST',
507
- year: 2028,
508
- })
504
+ type: "PERIOD_AT",
505
+ period: "WEEK",
506
+ position: "LAST",
507
+ year: 2028,
508
+ });
509
509
  // PERIOD_AT("WEEK", "LAST", 2028)
510
510
  ```
511
511
 
@@ -514,119 +514,161 @@ TranspileJSONToDatePreset({
514
514
  Date picker oriented shapes are also supported:
515
515
 
516
516
  ```ts
517
- { type: 'SINGLE_PERIOD', date: { year: 2026, month: 7 } }
517
+ { type: "SINGLE_PERIOD", date: { year: 2026, month: 7 } }
518
518
  // DATE_RANGE(DATE("2026-07-01"), END_OF(DATE("2026-07-31")))
519
519
 
520
- { type: 'SINGLE_PERIOD', period: 'month', offset: -1 }
520
+ { type: "SINGLE_PERIOD", period: "month", offset: -1 }
521
521
  // CALENDAR_PERIOD("MONTH", -1)
522
522
 
523
- { 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 } }
524
524
  // DATE_RANGE(DATE("2026-05-31"), END_OF(DATE("2026-06-30")))
525
525
 
526
- { type: 'ROLLING_WINDOW', direction: 'last', amount: 30, unit: 'days' }
526
+ { type: "ROLLING_WINDOW", direction: "last", amount: 30, unit: "days" }
527
527
  // RELATIVE_PERIOD(-30, "DAY")
528
528
  ```
529
529
 
530
530
  Rules:
531
531
 
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`.
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`.
536
536
 
537
537
  ## Formatting
538
538
 
539
539
  `FormatDatePreset` formats values using `Intl.DateTimeFormat`.
540
540
 
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.
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`.
542
546
 
543
547
  Main options:
544
548
 
545
549
  ```ts
546
550
  {
547
- locale?: 'en-US',
548
- timezone?: 'UTC',
549
- dateStyle?: 'full' | 'long' | 'medium' | 'short',
550
- timeStyle?: 'full' | 'long' | 'medium' | 'short',
551
- 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"
552
557
  }
553
558
  ```
554
559
 
555
560
  Examples:
556
561
 
557
562
  ```ts
558
- FormatDatePreset('2026-07-15T14:35:27.000Z', {
559
- locale: 'en-US',
560
- timezone: 'America/Chicago',
561
- dateStyle: 'medium',
562
- timeStyle: 'short',
563
- })
563
+ FormatDatePreset("2026-07-15T14:35:27.000Z", {
564
+ locale: "en-US",
565
+ timezone: { timeZone: "America/Chicago" },
566
+ dateStyle: "medium",
567
+ timeStyle: "short",
568
+ });
564
569
  // Jul 15, 2026, 9:35 AM
565
570
 
566
- FormatDatePreset('2026-07-15T14:35:27.000Z', { partialDateStyle: 'short' })
571
+ FormatDatePreset("2026-07-15T14:35:27.000Z", { partialDateStyle: "short" });
567
572
  // Jul 15, 2026
568
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
+
569
594
  FormatDatePreset(
570
- { start: '2026-07-01T00:00:00.000Z', end: '2026-07-31T23:59:59.999Z' },
571
- { partialDateStyle: 'short' },
572
- )
573
- // { 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" }
574
599
 
575
- 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 });
576
612
  // April 15
577
613
 
578
- FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: 'short' })
614
+ FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: "short" });
579
615
  // Apr 15
580
616
 
581
- FormatDatePreset({ week: 40 }, { partialDateStyle: 'short' })
617
+ FormatDatePreset({ week: 40 }, { partialDateStyle: "short" });
582
618
  // W40
583
619
 
584
- FormatDatePreset({ day: 15 }, { partialDateStyle: 'short' })
620
+ FormatDatePreset({ day: 15 }, { partialDateStyle: "short" });
585
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
586
628
  ```
587
629
 
588
630
  ## Legacy Tokens
589
631
 
590
632
  Legacy tokens are resolved with `TranspileJSONToDatePreset(token)` using `DATE_PRESET_TOKEN_MAP`.
591
633
 
592
- | Token | QDP expression |
593
- | --- | --- |
594
- | `NOW` | `NOW()` |
595
- | `TODAY` | `TODAY()` |
596
- | `CURRENT_DATE` | `TODAY()` |
597
- | `TODAY-7` | `START_OF(RELATIVE_PERIOD(-7, "DAY"))` |
598
- | `TODAY-30` | `START_OF(RELATIVE_PERIOD(-30, "DAY"))` |
599
- | `TODAY-60` | `START_OF(RELATIVE_PERIOD(-60, "DAY"))` |
600
- | `TODAY-90` | `START_OF(RELATIVE_PERIOD(-90, "DAY"))` |
601
- | `TODAY-120` | `START_OF(RELATIVE_PERIOD(-120, "DAY"))` |
602
- | `TODAY-365` | `START_OF(RELATIVE_PERIOD(-365, "DAY"))` |
603
- | `YESTERDAY` | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` |
604
- | `TOMORROW` | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` |
605
- | `CURRENT_MONTH` | `CALENDAR_PERIOD("MONTH", 0)` |
606
- | `CURRENT_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", 0))` |
607
- | `CURRENT_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", 0))` |
608
- | `LAST_MONTH` | `CALENDAR_PERIOD("MONTH", -1)` |
609
- | `LAST_MONTH_START` | `START_OF(CALENDAR_PERIOD("MONTH", -1))` |
610
- | `LAST_MONTH_END` | `END_OF(CALENDAR_PERIOD("MONTH", -1))` |
611
- | `CURRENT_WEEK` | `CALENDAR_PERIOD("WEEK", 0)` |
612
- | `CURRENT_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", 0))` |
613
- | `CURRENT_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", 0))` |
614
- | `LAST_WEEK` | `CALENDAR_PERIOD("WEEK", -1)` |
615
- | `LAST_WEEK_START` | `START_OF(CALENDAR_PERIOD("WEEK", -1))` |
616
- | `LAST_WEEK_END` | `END_OF(CALENDAR_PERIOD("WEEK", -1))` |
617
- | `CURRENT_QUARTER` | `CALENDAR_PERIOD("QUARTER", 0)` |
618
- | `CURRENT_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", 0))` |
619
- | `CURRENT_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", 0))` |
620
- | `LAST_QUARTER` | `CALENDAR_PERIOD("QUARTER", -1)` |
621
- | `LAST_QUARTER_START` | `START_OF(CALENDAR_PERIOD("QUARTER", -1))` |
622
- | `LAST_QUARTER_END` | `END_OF(CALENDAR_PERIOD("QUARTER", -1))` |
623
- | `CURRENT_YEAR` | `CALENDAR_PERIOD("YEAR", 0)` |
624
- | `CURRENT_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", 0))` |
625
- | `LAST_YEAR` | `CALENDAR_PERIOD("YEAR", -1)` |
626
- | `LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -1))` |
627
- | `LAST_YEAR_END` | `END_OF(CALENDAR_PERIOD("YEAR", -1))` |
628
- | `YEAR_BEFORE_LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -2))` |
629
- | `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))` |
630
672
 
631
673
  Tokens can be wrapped like `{{TODAY-7}}`. The normalizer removes braces and outer spaces.
632
674
 
@@ -634,51 +676,51 @@ Scalar tokens such as `CURRENT_TIME`, `CURRENT_TIMEZONE`, and `CURRENT_DAY_OF_WE
634
676
 
635
677
  ## Main Equivalences With The Date Picker
636
678
 
637
- | Picker expression | QDP expression | Note |
638
- | --- | --- | --- |
639
- | Today | `TODAY()` | Fixed date at the start of the current day |
640
- | Yesterday | `START_OF(RELATIVE_PERIOD(-1, "DAY"))` | Start of yesterday |
641
- | Tomorrow | `START_OF(END_OF(RELATIVE_PERIOD(1, "DAY")))` | Start of tomorrow |
642
- | This month | `CALENDAR_PERIOD("MONTH", 0)` | Current calendar month |
643
- | Previous month | `CALENDAR_PERIOD("MONTH", -1)` | Previous calendar month |
644
- | Last 30 days inclusive | `RELATIVE_PERIOD(-29, "DAY")` | Inclusive 30-day rolling window counting today |
645
- | April | `PARTIAL_DATE("ANY", 4)` | Recurring month |
646
- | April 2026 | `PERIOD_AT("MONTH", 4, 2026)` | Positional month in a year |
647
- | Apr 15 | `PARTIAL_DATE("ANY", 4, "ANY", "ANY", 15)` | Recurring month/day |
648
- | Day 15 | `PARTIAL_DATE("ANY", "ANY", "ANY", "ANY", 15)` | Recurring day of month |
649
- | Q2 | `PARTIAL_DATE("ANY", "ANY", 2)` | Recurring quarter |
650
- | Q2 2026 | `PERIOD_AT("QUARTER", 2, 2026)` | Positional quarter in a year |
651
- | W40 | `PARTIAL_DATE("ANY", "ANY", "ANY", 40)` | Recurring week |
652
- | W40 2026 | `PERIOD_AT("WEEK", 40, 2026)` | Positional week in a year |
653
- | Last week of 2028 | `PERIOD_AT("WEEK", "LAST", 2028)` | Avoids assuming W53/W54 |
654
- | Last month of the past year | `PERIOD_AT("MONTH", "LAST", 2025)` | Equivalent to December 2025 with current date 2026 |
655
- | Between inclusive | `DATE_RANGE(DATE(start), END_OF(DATE(end)))` | Materialize inclusive boundaries |
656
- | 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 |
657
699
 
658
700
  ## Validations And Expected Errors
659
701
 
660
702
  Important invalid cases:
661
703
 
662
704
  ```ts
663
- DATE("2026-02-31")
705
+ DATE("2026-02-31");
664
706
  // INVALID_ALLOW_VALUE: calendrically invalid date
665
707
 
666
- DATE("2026-02-31T00:00")
708
+ DATE("2026-02-31T00:00");
667
709
  // INVALID_ALLOW_VALUE: calendrically invalid date
668
710
 
669
- RELATIVE_PERIOD(-1, "HOUR")
711
+ RELATIVE_PERIOD(-1, "HOUR");
670
712
  // INVALID_ALLOW_VALUE: unsupported unit
671
713
 
672
- CALENDAR_PERIOD("DECADE", 0)
714
+ CALENDAR_PERIOD("DECADE", 0);
673
715
  // INVALID_ALLOW_VALUE: unsupported period
674
716
 
675
- PERIOD_AT("MONTH", "MIDDLE", 2026)
717
+ PERIOD_AT("MONTH", "MIDDLE", 2026);
676
718
  // INVALID_ALLOW_VALUE: unsupported symbolic position
677
719
 
678
- PARTIAL_DATE("ANY", 13)
720
+ PARTIAL_DATE("ANY", 13);
679
721
  // INVALID_ALLOW_VALUE: month out of range
680
722
 
681
- 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"));
682
724
  // INVALID_DATE_RANGE: end before start
683
725
  ```
684
726
 
@@ -686,24 +728,25 @@ Additionally, any QDP expression whose final result is not `date`, `dateRange`,
686
728
 
687
729
  ## Relevant Files
688
730
 
689
- | File | Responsibility |
690
- | --- | --- |
691
- | `src/date-presets.ts` | Public API, JSON conversion, formatting, and `valueType` classification |
692
- | `src/date-preset-tokens.ts` | Legacy token map to QDP expressions |
693
- | `src/functions/index.ts` | Function registration for `ENGINES.QDP` |
694
- | `src/functions/datePresetUtils.ts` | Date, range, calendar, week, timezone, and partial date resolution |
695
- | `src/functions/*.ts` | Individual QDP function definitions |
696
- | `__tests__/unit/datePresetTranspiler.test.ts` | Main transpilation and picker compatibility cases |
697
- | `__tests__/unit/datePresetJson.test.ts` | JSON to QDP conversion |
698
- | `__tests__/unit/datePresetFormat.test.ts` | Date and partial date formatting |
699
- | `__tests__/unit/datePresetTokens.test.ts` | Legacy tokens |
700
- | `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 |
701
744
 
702
745
  ## Current State And Design Notes
703
746
 
704
- - QDP is limited to date preset functions in `ENGINE_FN_MAP[ENGINES.QDP]`.
705
- - Date preset expressions can be nested as long as the final result is `date`, `dateRange`, or `partialDate`.
706
- - `partialDate` was added as a primitive to represent incomplete date picker selections.
707
- - Week compatibility with the date picker uses Sunday as the default start day and supports W54.
708
- - `PERIOD_AT` supports symbolic positions `FIRST` and `LAST` to reduce ambiguity in variable-length periods.
709
- - 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.