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