@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.
- package/QRVEY-DATE-PRESETS.md +321 -278
- package/dist/cjs/constants/interfaces.d.ts +10 -4
- package/dist/cjs/date-presets/date-preset-tokens.js.map +1 -0
- package/dist/{module → cjs/date-presets}/date-presets.d.ts +1 -1
- package/dist/cjs/{date-presets.js → date-presets/date-presets.js} +89 -12
- package/dist/cjs/date-presets/date-presets.js.map +1 -0
- package/dist/cjs/date-presets/index.d.ts +2 -0
- package/dist/cjs/date-presets/index.js +11 -0
- package/dist/cjs/date-presets/index.js.map +1 -0
- package/dist/cjs/functions/calendarPeriod.js +1 -1
- package/dist/cjs/functions/calendarPeriod.js.map +1 -1
- package/dist/cjs/functions/date.js +1 -1
- package/dist/cjs/functions/date.js.map +1 -1
- package/dist/cjs/functions/dateRange.js +1 -1
- package/dist/cjs/functions/dateRange.js.map +1 -1
- package/dist/cjs/functions/endOf.js +1 -1
- package/dist/cjs/functions/endOf.js.map +1 -1
- package/dist/cjs/functions/part.js +1 -1
- package/dist/cjs/functions/part.js.map +1 -1
- package/dist/cjs/functions/partialDate.js +1 -1
- package/dist/cjs/functions/partialDate.js.map +1 -1
- package/dist/cjs/functions/periodAt.js +1 -1
- package/dist/cjs/functions/periodAt.js.map +1 -1
- package/dist/cjs/functions/relativePeriod.js +1 -1
- package/dist/cjs/functions/relativePeriod.js.map +1 -1
- package/dist/cjs/functions/startOf.js +1 -1
- package/dist/cjs/functions/startOf.js.map +1 -1
- package/dist/cjs/functions/today.js +1 -1
- package/dist/cjs/functions/today.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -3
- package/dist/cjs/index.js +2 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/transpiler/columnTranspilation.js +5 -2
- package/dist/cjs/transpiler/columnTranspilation.js.map +1 -1
- package/dist/cjs/{functions → utils}/datePresetUtils.js +36 -15
- package/dist/cjs/utils/datePresetUtils.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/timezone.d.ts +12 -0
- package/dist/cjs/utils/timezone.js +58 -0
- package/dist/cjs/utils/timezone.js.map +1 -0
- package/dist/module/constants/interfaces.d.ts +10 -4
- package/dist/module/date-presets/date-preset-tokens.js.map +1 -0
- package/dist/{cjs → module/date-presets}/date-presets.d.ts +1 -1
- package/dist/module/{date-presets.js → date-presets/date-presets.js} +89 -12
- package/dist/module/date-presets/date-presets.js.map +1 -0
- package/dist/module/date-presets/index.d.ts +2 -0
- package/dist/module/date-presets/index.js +3 -0
- package/dist/module/date-presets/index.js.map +1 -0
- package/dist/module/functions/calendarPeriod.js +1 -1
- package/dist/module/functions/calendarPeriod.js.map +1 -1
- package/dist/module/functions/date.js +1 -1
- package/dist/module/functions/date.js.map +1 -1
- package/dist/module/functions/dateRange.js +1 -1
- package/dist/module/functions/dateRange.js.map +1 -1
- package/dist/module/functions/endOf.js +1 -1
- package/dist/module/functions/endOf.js.map +1 -1
- package/dist/module/functions/part.js +1 -1
- package/dist/module/functions/part.js.map +1 -1
- package/dist/module/functions/partialDate.js +1 -1
- package/dist/module/functions/partialDate.js.map +1 -1
- package/dist/module/functions/periodAt.js +1 -1
- package/dist/module/functions/periodAt.js.map +1 -1
- package/dist/module/functions/relativePeriod.js +1 -1
- package/dist/module/functions/relativePeriod.js.map +1 -1
- package/dist/module/functions/startOf.js +1 -1
- package/dist/module/functions/startOf.js.map +1 -1
- package/dist/module/functions/today.js +1 -1
- package/dist/module/functions/today.js.map +1 -1
- package/dist/module/index.d.ts +2 -3
- package/dist/module/index.js +1 -2
- package/dist/module/index.js.map +1 -1
- package/dist/module/transpiler/columnTranspilation.js +5 -2
- package/dist/module/transpiler/columnTranspilation.js.map +1 -1
- package/dist/module/{functions → utils}/datePresetUtils.js +36 -15
- package/dist/module/utils/datePresetUtils.js.map +1 -0
- package/dist/module/utils/index.d.ts +1 -0
- package/dist/module/utils/index.js +1 -0
- package/dist/module/utils/index.js.map +1 -1
- package/dist/module/utils/timezone.d.ts +12 -0
- package/dist/module/utils/timezone.js +53 -0
- package/dist/module/utils/timezone.js.map +1 -0
- package/package.json +1 -1
- package/dist/cjs/date-preset-tokens.js.map +0 -1
- package/dist/cjs/date-presets.js.map +0 -1
- package/dist/cjs/functions/datePresetUtils.js.map +0 -1
- package/dist/module/date-preset-tokens.js.map +0 -1
- package/dist/module/date-presets.js.map +0 -1
- package/dist/module/functions/datePresetUtils.js.map +0 -1
- /package/dist/cjs/{date-preset-tokens.d.ts → date-presets/date-preset-tokens.d.ts} +0 -0
- /package/dist/cjs/{date-preset-tokens.js → date-presets/date-preset-tokens.js} +0 -0
- /package/dist/cjs/{functions → utils}/datePresetUtils.d.ts +0 -0
- /package/dist/module/{date-preset-tokens.d.ts → date-presets/date-preset-tokens.d.ts} +0 -0
- /package/dist/module/{date-preset-tokens.js → date-presets/date-preset-tokens.js} +0 -0
- /package/dist/module/{functions → utils}/datePresetUtils.d.ts +0 -0
package/QRVEY-DATE-PRESETS.md
CHANGED
|
@@ -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
|
-
|
|
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,34 +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
|
-
|
|
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
|
|
108
|
-
|
|
|
109
|
-
| `
|
|
110
|
-
| `timezone` |
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
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
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
-
|
|
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
|
|
163
|
-
|
|
|
164
|
-
| ISO date
|
|
165
|
-
| ISO date-time without timezone
|
|
166
|
-
| 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` |
|
|
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
|
|
172
|
-
|
|
|
173
|
-
| `VALUE`
|
|
174
|
+
| Parameter | Type | Required | Validation |
|
|
175
|
+
| --------- | -------- | -------- | ------------------------- |
|
|
176
|
+
| `VALUE` | `string` | Yes | ISO date or ISO date-time |
|
|
174
177
|
|
|
175
178
|
Rules:
|
|
176
179
|
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
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
|
|
200
|
-
|
|
|
201
|
-
| `START`
|
|
202
|
-
| `END`
|
|
199
|
+
| Parameter | Type | Required |
|
|
200
|
+
| --------- | ------ | -------- |
|
|
201
|
+
| `START` | `date` | Yes |
|
|
202
|
+
| `END` | `date` | Yes |
|
|
203
203
|
|
|
204
204
|
Rules:
|
|
205
205
|
|
|
206
|
-
-
|
|
207
|
-
-
|
|
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
|
|
225
|
-
|
|
|
226
|
-
| `OFFSET`
|
|
227
|
-
| `UNIT`
|
|
228
|
-
| `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 |
|
|
229
229
|
|
|
230
230
|
Semantics:
|
|
231
231
|
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
-
|
|
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
|
|
259
|
-
|
|
|
260
|
-
| `PERIOD`
|
|
261
|
-
| `OFFSET`
|
|
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
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
-
|
|
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
|
|
296
|
-
|
|
|
297
|
-
| `PERIOD`
|
|
298
|
-
| `POSITION` | Integer `number` or `string` | Yes
|
|
299
|
-
| `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
|
-
-
|
|
317
|
-
-
|
|
318
|
-
-
|
|
319
|
-
-
|
|
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
|
|
337
|
-
|
|
|
338
|
-
| 1
|
|
339
|
-
| 2
|
|
340
|
-
| 3
|
|
341
|
-
| 4
|
|
342
|
-
| 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` |
|
|
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
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
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`
|
|
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`
|
|
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
|
|
434
|
-
|
|
|
435
|
-
| `DATE`
|
|
436
|
-
| `PART`
|
|
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
|
|
441
|
-
|
|
|
442
|
-
| `YEAR`
|
|
443
|
-
| `QUARTER`
|
|
444
|
-
| `MONTH`
|
|
445
|
-
| `MONTH_NAME`
|
|
446
|
-
| `DAY`
|
|
447
|
-
| `DAY_OF_MONTH`
|
|
448
|
-
| `DAY_OF_WEEK`
|
|
449
|
-
| `DAY_OF_WEEK_NAME` | English weekday name
|
|
450
|
-
| `DAY_OF_YEAR`
|
|
451
|
-
| `WEEK_OF_YEAR`
|
|
452
|
-
| `HOUR`
|
|
453
|
-
| `MINUTE`
|
|
454
|
-
| `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
|
|
461
|
-
|
|
|
462
|
-
| `CALENDAR_PERIOD` | What is this/previous/next calendar period?
|
|
463
|
-
| `PERIOD_AT`
|
|
464
|
-
| `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` |
|
|
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:
|
|
489
|
-
{ type:
|
|
490
|
-
{ type:
|
|
491
|
-
{ type:
|
|
492
|
-
{ type:
|
|
493
|
-
{ type:
|
|
494
|
-
{ type:
|
|
495
|
-
{ type:
|
|
496
|
-
{ type:
|
|
497
|
-
{ 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 }
|
|
498
498
|
```
|
|
499
499
|
|
|
500
500
|
Example:
|
|
501
501
|
|
|
502
502
|
```ts
|
|
503
503
|
TranspileJSONToDatePreset({
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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:
|
|
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:
|
|
520
|
+
{ type: "SINGLE_PERIOD", period: "month", offset: -1 }
|
|
521
521
|
// CALENDAR_PERIOD("MONTH", -1)
|
|
522
522
|
|
|
523
|
-
{ type:
|
|
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:
|
|
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
|
-
-
|
|
533
|
-
-
|
|
534
|
-
-
|
|
535
|
-
-
|
|
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:
|
|
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
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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(
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
)
|
|
573
|
-
// { 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" }
|
|
574
599
|
|
|
575
|
-
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 });
|
|
576
612
|
// April 15
|
|
577
613
|
|
|
578
|
-
FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle:
|
|
614
|
+
FormatDatePreset({ month: 4, day: 15 }, { partialDateStyle: "short" });
|
|
579
615
|
// Apr 15
|
|
580
616
|
|
|
581
|
-
FormatDatePreset({ week: 40 }, { partialDateStyle:
|
|
617
|
+
FormatDatePreset({ week: 40 }, { partialDateStyle: "short" });
|
|
582
618
|
// W40
|
|
583
619
|
|
|
584
|
-
FormatDatePreset({ day: 15 }, { partialDateStyle:
|
|
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
|
|
593
|
-
|
|
|
594
|
-
| `NOW`
|
|
595
|
-
| `TODAY`
|
|
596
|
-
| `CURRENT_DATE`
|
|
597
|
-
| `TODAY-7`
|
|
598
|
-
| `TODAY-30`
|
|
599
|
-
| `TODAY-60`
|
|
600
|
-
| `TODAY-90`
|
|
601
|
-
| `TODAY-120`
|
|
602
|
-
| `TODAY-365`
|
|
603
|
-
| `YESTERDAY`
|
|
604
|
-
| `TOMORROW`
|
|
605
|
-
| `CURRENT_MONTH`
|
|
606
|
-
| `CURRENT_MONTH_START`
|
|
607
|
-
| `CURRENT_MONTH_END`
|
|
608
|
-
| `LAST_MONTH`
|
|
609
|
-
| `LAST_MONTH_START`
|
|
610
|
-
| `LAST_MONTH_END`
|
|
611
|
-
| `CURRENT_WEEK`
|
|
612
|
-
| `CURRENT_WEEK_START`
|
|
613
|
-
| `CURRENT_WEEK_END`
|
|
614
|
-
| `LAST_WEEK`
|
|
615
|
-
| `LAST_WEEK_START`
|
|
616
|
-
| `LAST_WEEK_END`
|
|
617
|
-
| `CURRENT_QUARTER`
|
|
618
|
-
| `CURRENT_QUARTER_START`
|
|
619
|
-
| `CURRENT_QUARTER_END`
|
|
620
|
-
| `LAST_QUARTER`
|
|
621
|
-
| `LAST_QUARTER_START`
|
|
622
|
-
| `LAST_QUARTER_END`
|
|
623
|
-
| `CURRENT_YEAR`
|
|
624
|
-
| `CURRENT_YEAR_START`
|
|
625
|
-
| `LAST_YEAR`
|
|
626
|
-
| `LAST_YEAR_START`
|
|
627
|
-
| `LAST_YEAR_END`
|
|
628
|
-
| `YEAR_BEFORE_LAST_YEAR_START` | `START_OF(CALENDAR_PERIOD("YEAR", -2))`
|
|
629
|
-
| `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))` |
|
|
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
|
|
638
|
-
|
|
|
639
|
-
| Today
|
|
640
|
-
| Yesterday
|
|
641
|
-
| Tomorrow
|
|
642
|
-
| This month
|
|
643
|
-
| Previous month
|
|
644
|
-
| Last 30 days inclusive
|
|
645
|
-
| April
|
|
646
|
-
| April 2026
|
|
647
|
-
| Apr 15
|
|
648
|
-
| Day 15
|
|
649
|
-
| Q2
|
|
650
|
-
| Q2 2026
|
|
651
|
-
| W40
|
|
652
|
-
| W40 2026
|
|
653
|
-
| Last week of 2028
|
|
654
|
-
| Last month of the past year | `PERIOD_AT("MONTH", "LAST", 2025)`
|
|
655
|
-
| Between inclusive
|
|
656
|
-
| 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 |
|
|
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
|
|
690
|
-
|
|
|
691
|
-
| `src/date-presets.ts`
|
|
692
|
-
| `src/date-preset-tokens.ts`
|
|
693
|
-
| `src/functions/index.ts`
|
|
694
|
-
| `src/
|
|
695
|
-
| `src/
|
|
696
|
-
| `
|
|
697
|
-
| `__tests__/unit/
|
|
698
|
-
| `__tests__/unit/
|
|
699
|
-
| `__tests__/unit/
|
|
700
|
-
| `
|
|
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
|
-
-
|
|
705
|
-
-
|
|
706
|
-
-
|
|
707
|
-
-
|
|
708
|
-
-
|
|
709
|
-
-
|
|
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.
|