@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.
- package/QRVEY-DATE-PRESETS.md +319 -289
- package/dist/cjs/constants/interfaces.d.ts +2 -1
- package/dist/cjs/date-presets/date-presets.js +73 -2
- package/dist/cjs/date-presets/date-presets.js.map +1 -1
- package/dist/module/constants/interfaces.d.ts +2 -1
- package/dist/module/date-presets/date-presets.js +73 -2
- package/dist/module/date-presets/date-presets.js.map +1 -1
- package/package.json +1 -1
package/QRVEY-DATE-PRESETS.md
CHANGED
|
@@ -48,8 +48,8 @@ type DatePresetValue = string | DateRangeValue | PartialDateValue;
|
|
|
48
48
|
|
|
49
49
|
```json
|
|
50
50
|
{
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
61
|
-
|
|
60
|
+
"month": 4,
|
|
61
|
+
"day": 15
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
Supported fields for `partialDate`:
|
|
66
66
|
|
|
67
|
-
| Field
|
|
68
|
-
|
|
|
69
|
-
| `year`
|
|
70
|
-
| `month`
|
|
71
|
-
| `quarter` | Quarter
|
|
72
|
-
| `week`
|
|
73
|
-
| `day`
|
|
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
|
|
80
|
-
|
|
|
81
|
-
| `fixed`
|
|
82
|
-
| `recurring` | Partial pattern that repeats
|
|
83
|
-
| `relative`
|
|
84
|
-
| `rolling`
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
110
|
-
|
|
|
111
|
-
| `timezone.offset`
|
|
112
|
-
| `timezone.timeZone`
|
|
113
|
-
| `calendar`
|
|
114
|
-
| `locale`
|
|
115
|
-
| `fiscalYearStartMonth` | `1`
|
|
116
|
-
| `fiscalYearStartDay`
|
|
117
|
-
| `weekStartsOn`
|
|
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:
|
|
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
|
-
-
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
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
|
|
168
|
-
|
|
|
169
|
-
| ISO date
|
|
170
|
-
| ISO date-time without timezone
|
|
171
|
-
| ISO date-time with UTC timezone
|
|
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
|
|
177
|
-
|
|
|
178
|
-
| `VALUE`
|
|
174
|
+
| Parameter | Type | Required | Validation |
|
|
175
|
+
| --------- | -------- | -------- | ------------------------- |
|
|
176
|
+
| `VALUE` | `string` | Yes | ISO date or ISO date-time |
|
|
179
177
|
|
|
180
178
|
Rules:
|
|
181
179
|
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
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
|
|
205
|
-
|
|
|
206
|
-
| `START`
|
|
207
|
-
| `END`
|
|
199
|
+
| Parameter | Type | Required |
|
|
200
|
+
| --------- | ------ | -------- |
|
|
201
|
+
| `START` | `date` | Yes |
|
|
202
|
+
| `END` | `date` | Yes |
|
|
208
203
|
|
|
209
204
|
Rules:
|
|
210
205
|
|
|
211
|
-
-
|
|
212
|
-
-
|
|
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
|
|
230
|
-
|
|
|
231
|
-
| `OFFSET`
|
|
232
|
-
| `UNIT`
|
|
233
|
-
| `ANCHOR`
|
|
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
|
-
-
|
|
238
|
-
-
|
|
239
|
-
-
|
|
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
|
|
264
|
-
|
|
|
265
|
-
| `PERIOD`
|
|
266
|
-
| `OFFSET`
|
|
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
|
-
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
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
|
|
301
|
-
|
|
|
302
|
-
| `PERIOD`
|
|
303
|
-
| `POSITION` | Integer `number` or `string` | Yes
|
|
304
|
-
| `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
|
-
-
|
|
322
|
-
-
|
|
323
|
-
-
|
|
324
|
-
-
|
|
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
|
|
342
|
-
|
|
|
343
|
-
| 1
|
|
344
|
-
| 2
|
|
345
|
-
| 3
|
|
346
|
-
| 4
|
|
347
|
-
| 5
|
|
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
|
-
-
|
|
380
|
-
-
|
|
381
|
-
-
|
|
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`
|
|
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`
|
|
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
|
|
439
|
-
|
|
|
440
|
-
| `DATE`
|
|
441
|
-
| `PART`
|
|
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
|
|
446
|
-
|
|
|
447
|
-
| `YEAR`
|
|
448
|
-
| `QUARTER`
|
|
449
|
-
| `MONTH`
|
|
450
|
-
| `MONTH_NAME`
|
|
451
|
-
| `DAY`
|
|
452
|
-
| `DAY_OF_MONTH`
|
|
453
|
-
| `DAY_OF_WEEK`
|
|
454
|
-
| `DAY_OF_WEEK_NAME` | English weekday name
|
|
455
|
-
| `DAY_OF_YEAR`
|
|
456
|
-
| `WEEK_OF_YEAR`
|
|
457
|
-
| `HOUR`
|
|
458
|
-
| `MINUTE`
|
|
459
|
-
| `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
|
|
466
|
-
|
|
|
467
|
-
| `CALENDAR_PERIOD` | What is this/previous/next calendar period?
|
|
468
|
-
| `PERIOD_AT`
|
|
469
|
-
| `RELATIVE_PERIOD` | What is the moving window from today/anchor? | Last 30 days
|
|
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:
|
|
494
|
-
{ type:
|
|
495
|
-
{ type:
|
|
496
|
-
{ type:
|
|
497
|
-
{ type:
|
|
498
|
-
{ type:
|
|
499
|
-
{ type:
|
|
500
|
-
{ type:
|
|
501
|
-
{ type:
|
|
502
|
-
{ type:
|
|
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
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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:
|
|
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:
|
|
520
|
+
{ type: "SINGLE_PERIOD", period: "month", offset: -1 }
|
|
526
521
|
// CALENDAR_PERIOD("MONTH", -1)
|
|
527
522
|
|
|
528
|
-
{ type:
|
|
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:
|
|
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
|
-
-
|
|
538
|
-
-
|
|
539
|
-
-
|
|
540
|
-
-
|
|
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:
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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(
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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(
|
|
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
|
-
|
|
583
|
-
|
|
584
|
-
)
|
|
585
|
-
// { start:
|
|
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(
|
|
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:
|
|
614
|
+
FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: "short" });
|
|
591
615
|
// Apr 15
|
|
592
616
|
|
|
593
|
-
FormatDatePreset({ week: 40 }, { partialDateStyle:
|
|
617
|
+
FormatDatePreset({ week: 40 }, { partialDateStyle: "short" });
|
|
594
618
|
// W40
|
|
595
619
|
|
|
596
|
-
FormatDatePreset({ day: 15 }, { partialDateStyle:
|
|
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
|
|
605
|
-
|
|
|
606
|
-
| `NOW`
|
|
607
|
-
| `TODAY`
|
|
608
|
-
| `CURRENT_DATE`
|
|
609
|
-
| `TODAY-7`
|
|
610
|
-
| `TODAY-30`
|
|
611
|
-
| `TODAY-60`
|
|
612
|
-
| `TODAY-90`
|
|
613
|
-
| `TODAY-120`
|
|
614
|
-
| `TODAY-365`
|
|
615
|
-
| `YESTERDAY`
|
|
616
|
-
| `TOMORROW`
|
|
617
|
-
| `CURRENT_MONTH`
|
|
618
|
-
| `CURRENT_MONTH_START`
|
|
619
|
-
| `CURRENT_MONTH_END`
|
|
620
|
-
| `LAST_MONTH`
|
|
621
|
-
| `LAST_MONTH_START`
|
|
622
|
-
| `LAST_MONTH_END`
|
|
623
|
-
| `CURRENT_WEEK`
|
|
624
|
-
| `CURRENT_WEEK_START`
|
|
625
|
-
| `CURRENT_WEEK_END`
|
|
626
|
-
| `LAST_WEEK`
|
|
627
|
-
| `LAST_WEEK_START`
|
|
628
|
-
| `LAST_WEEK_END`
|
|
629
|
-
| `CURRENT_QUARTER`
|
|
630
|
-
| `CURRENT_QUARTER_START`
|
|
631
|
-
| `CURRENT_QUARTER_END`
|
|
632
|
-
| `LAST_QUARTER`
|
|
633
|
-
| `LAST_QUARTER_START`
|
|
634
|
-
| `LAST_QUARTER_END`
|
|
635
|
-
| `CURRENT_YEAR`
|
|
636
|
-
| `CURRENT_YEAR_START`
|
|
637
|
-
| `LAST_YEAR`
|
|
638
|
-
| `LAST_YEAR_START`
|
|
639
|
-
| `LAST_YEAR_END`
|
|
640
|
-
| `YEAR_BEFORE_LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -2))`
|
|
641
|
-
| `YEAR_BEFORE_LAST_YEAR_END`
|
|
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
|
|
650
|
-
|
|
|
651
|
-
| Today
|
|
652
|
-
| Yesterday
|
|
653
|
-
| Tomorrow
|
|
654
|
-
| This month
|
|
655
|
-
| Previous month
|
|
656
|
-
| Last 30 days inclusive
|
|
657
|
-
| April
|
|
658
|
-
| April 2026
|
|
659
|
-
| Apr 15
|
|
660
|
-
| Day 15
|
|
661
|
-
| Q2
|
|
662
|
-
| Q2 2026
|
|
663
|
-
| W40
|
|
664
|
-
| W40 2026
|
|
665
|
-
| Last week of 2028
|
|
666
|
-
| Last month of the past year | `PERIOD_AT("MONTH", "LAST", 2025)`
|
|
667
|
-
| Between inclusive
|
|
668
|
-
| Between exclusive
|
|
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
|
|
702
|
-
|
|
|
703
|
-
| `src/date-presets/date-presets.ts`
|
|
704
|
-
| `src/date-presets/date-preset-tokens.ts`
|
|
705
|
-
| `src/functions/index.ts`
|
|
706
|
-
| `src/utils/datePresetUtils.ts`
|
|
707
|
-
| `src/utils/timezone.ts`
|
|
708
|
-
| `src/functions/*.ts`
|
|
709
|
-
| `__tests__/unit/datePresetTranspiler.test.ts` | Main transpilation and picker compatibility cases
|
|
710
|
-
| `__tests__/unit/datePresetJson.test.ts`
|
|
711
|
-
| `__tests__/unit/datePresetFormat.test.ts`
|
|
712
|
-
| `__tests__/unit/datePresetTokens.test.ts`
|
|
713
|
-
| `date-preset-qdp-examples.csv`
|
|
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
|
-
-
|
|
718
|
-
-
|
|
719
|
-
-
|
|
720
|
-
-
|
|
721
|
-
-
|
|
722
|
-
-
|
|
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.
|