From 0c7c7356c7e11002f02d2f3ee348419db55ac238 Mon Sep 17 00:00:00 2001 From: Marko Topolnik Date: Tue, 3 Feb 2026 13:51:52 +0100 Subject: [PATCH 1/5] Markdown lint --- documentation/query/operators/tick.md | 61 ++++++++++++++++----------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/documentation/query/operators/tick.md b/documentation/query/operators/tick.md index bb60a74da..bd81d9231 100644 --- a/documentation/query/operators/tick.md +++ b/documentation/query/operators/tick.md @@ -20,16 +20,18 @@ This single expression generates interval scans for every weekday in January, each starting at 9:30 AM New York time and lasting 6 hours 30 minutes. :::tip Key Points + - TICK = declarative syntax for complex time intervals in `WHERE ts IN '...'` - **Syntax order:** `date [T time] @timezone #dayFilter ;duration` -- Each generated interval uses optimized [interval scan](/docs/concepts/deep-dive/interval-scan/) (binary search) +- Each generated interval uses optimized + [interval scan](/docs/concepts/deep-dive/interval-scan/) (binary search) - Use `[a,b,c]` for values, `[a..b]` for ranges, `#workday` for day filters - Overlapping intervals are automatically merged ::: ## Grammar summary -``` +```text TICK_EXPR = DATE_PART [TIME] [TIMEZONE] [FILTER] [DURATION] DATE_PART = literal_date -- '2024-01-15' @@ -74,6 +76,7 @@ unit = 'y' | 'M' | 'w' | 'd' | 'bd' | 'h' | 'm' | 's' | 'T' | 'u' | 'n' ## Why TICK Traditional approaches to complex time queries require: + - Multiple `UNION ALL` statements - Application-side date generation - Complex `BETWEEN` logic with many `OR` clauses @@ -82,6 +85,7 @@ TICK replaces all of these with a declarative syntax that generates multiple optimized interval scans from a single expression. **Use TICK when:** + - Querying relative time windows (`$now - 1h..$now`, `$today`) - Building rolling windows with business day calculations - Working with schedules (workdays, weekends, specific days) @@ -89,6 +93,7 @@ optimized interval scans from a single expression. - Querying multiple non-contiguous dates or time windows **Use simple `IN` or `BETWEEN` when:** + - Single continuous time range with absolute dates (`WHERE ts IN '2024-01-15'`) - Simple date/time literals without patterns or variables @@ -123,7 +128,7 @@ WHERE ts IN '2024-01-15T09:30@America/New_York;6h30m' Components must appear in this order: -``` +```text date [T time] @ timezone # dayFilter ; duration │ │ │ │ │ │ │ │ │ └─ interval length (e.g., ;6h30m) @@ -136,7 +141,7 @@ date [T time] @ timezone # dayFilter ; duration **Examples showing the order:** | Expression | Components used | -|------------|-----------------| +| ---------- | --------------- | | `'2024-01-15'` | date only | | `'2024-01-15T09:30'` | date + time | | `'2024-01-15T09:30@UTC'` | date + time + timezone | @@ -147,7 +152,7 @@ date [T time] @ timezone # dayFilter ; duration ## Quick reference | Feature | Syntax | Example | -|---------|--------|---------| +| ------- | ------ | ------- | | Bracket expansion | `[a,b,c]` | `'2024-01-[10,15,20]'` | | Range expansion | `[a..b]` | `'2024-01-[10..15]'` | | Date list | `[date1,date2]` | `'[2024-01-15,2024-03-20]'` | @@ -224,7 +229,7 @@ WHERE ts IN '[$today, $yesterday, 2024-01-15]' Use dynamic date references that resolve at query time: | Variable | Description | Interval type | Example value (Jan 22, 2026 at 14:35:22) | -|----------|-------------|---------------|------------------------------------------| +| -------- | ----------- | ------------- | ---------------------------------------- | | `$today` | Current day | Full day | `2026-01-22T00:00:00` to `2026-01-22T23:59:59.999999` | | `$yesterday` | Previous day | Full day | `2026-01-21T00:00:00` to `2026-01-21T23:59:59.999999` | | `$tomorrow` | Next day | Full day | `2026-01-23T00:00:00` to `2026-01-23T23:59:59.999999` | @@ -232,10 +237,13 @@ Use dynamic date references that resolve at query time: :::info Interval vs point-in-time -- **`$today`**, **`$yesterday`**, **`$tomorrow`** produce **full day intervals** (midnight to midnight) -- **`$now`** produces a **point-in-time** (exact moment with microsecond precision) +- **`$today`**, **`$yesterday`**, **`$tomorrow`** produce **full day intervals** + (midnight to midnight) +- **`$now`** produces a **point-in-time** (exact moment with microsecond + precision) -Without a duration suffix, `$now` matches only the exact microsecond. Add a duration or use a range to create a useful window: +Without a duration suffix, `$now` matches only the exact microsecond. Add a +duration or use a range to create a useful window: ```questdb-sql -- Point-in-time: matches only the exact microsecond (rarely useful alone) @@ -254,8 +262,8 @@ Variables are case-insensitive: `$TODAY`, `$Today`, and `$today` are equivalent. ### Date arithmetic -Add or subtract time from date variables using any [time unit](#time-units). -All units except `bd` (business days) work in both duration and arithmetic contexts. +Add or subtract time from date variables using any [time unit](#time-units). All +units except `bd` (business days) work in both duration and arithmetic contexts. ```questdb-sql -- Calendar day arithmetic @@ -307,7 +315,8 @@ Generate multiple intervals from start to end: :::note Ranges vs durations -**Ranges** (`$start..$end`) create a single continuous interval from start to end: +**Ranges** (`$start..$end`) create a single continuous interval from start to +end: ```questdb-sql -- Single interval: from 2 hours ago until now @@ -330,6 +339,7 @@ For multiple discrete intervals, use a list with duration: -- Three separate 1-hour intervals '[$now - 3h, $now - 2h, $now - 1h];1h' ``` + ::: ### Mixed date lists @@ -377,7 +387,7 @@ SELECT * FROM trades WHERE ts IN '2024-[01,06]-[10,15]'; Brackets work in any numeric field: | Field | Example | Result | -|-------|---------|--------| +| ----- | ------- | ------ | | Month | `'2024-[01,06]-15'` | Jan 15, Jun 15 | | Day | `'2024-01-[10,15]'` | 10th, 15th | | Hour | `'2024-01-10T[09,14]:30'` | 09:30, 14:30 | @@ -423,7 +433,7 @@ SELECT * FROM metrics WHERE ts IN '2024-01-15T[08:00,12:00,18:00];30m'; The presence of `:` inside the bracket determines the mode: | Syntax | Mode | Expands to | -|--------|------|------------| +| ------ | ---- | ---------- | | `T[09,14]:30` | Numeric expansion (hour field) | 09:30 and 14:30 | | `T[09:00,14:30]` | Time list (complete times) | 09:00 and 14:30 | @@ -450,7 +460,7 @@ SELECT * FROM trades WHERE ts IN '2024-01-15T09:30@UTC'; ### Supported timezone formats | Format | Example | -|--------|---------| +| ------ | ------- | | IANA name | `@America/New_York`, `@Europe/London` | | Offset | `@+03:00`, `@-05:00` | | Compact offset | `@+0300`, `@-0500` | @@ -489,7 +499,7 @@ SELECT * FROM attendance WHERE ts IN '2024-01-[01..31]#Mon,Wed,Fri'; ### Available filters | Filter | Days included | -|--------|---------------| +| ------ | ------------- | | `#workday` or `#wd` | Monday - Friday | | `#weekend` | Saturday, Sunday | | `#Mon`, `#Tue`, etc. | Specific day | @@ -526,7 +536,7 @@ SELECT * FROM hft_data WHERE ts IN '2024-01-15T09:30:00;1s500T'; ### Time units | Unit | Name | Description | Duration | Arithmetic | -|------|------|-------------|:--------:|:----------:| +| ---- | ---- | ----------- | :------: | :--------: | | `y` | Years | Calendar years (handles leap years) | Yes | Yes | | `M` | Months | Calendar months (handles varying lengths) | Yes | Yes | | `w` | Weeks | 7 days | Yes | Yes | @@ -539,8 +549,8 @@ SELECT * FROM hft_data WHERE ts IN '2024-01-15T09:30:00;1s500T'; | `u` | Microseconds | 1,000 nanoseconds | Yes | Yes | | `n` | Nanoseconds | Base unit | Yes | Yes | -Units are case-sensitive: `M` = months, `m` = minutes, `T` = milliseconds. -The `d` unit also accepts uppercase `D` for backward compatibility. +Units are case-sensitive: `M` = months, `m` = minutes, `T` = milliseconds. The +`d` unit also accepts uppercase `D` for backward compatibility. ### Multi-unit durations @@ -588,7 +598,7 @@ SELECT * FROM trades WHERE ts IN '2024-W[01..04]-[1,5]'; ### Day-of-week values | Value | Day | -|-------|-----| +| ----- | --- | | 1 | Monday | | 2 | Tuesday | | 3 | Wednesday | @@ -694,7 +704,7 @@ WHERE ts IN '2024-01-[15,16,17]T09:00;1h'; ## Error messages | Error | Cause | -|-------|-------| +| ----- | ----- | | `Unclosed '[' in interval` | Missing closing bracket | | `Empty bracket expansion` | Nothing inside brackets | | `Range must be ascending: 15..10` | End before start in range | @@ -704,7 +714,10 @@ WHERE ts IN '2024-01-[15,16,17]T09:00;1h'; ## See also -- [Designated timestamp](/docs/concepts/designated-timestamp/) — Required for interval scan optimization -- [Interval scan](/docs/concepts/deep-dive/interval-scan/) — How QuestDB optimizes time queries +- [Designated timestamp](/docs/concepts/designated-timestamp/) — Required for + interval scan optimization +- [Interval scan](/docs/concepts/deep-dive/interval-scan/) — How QuestDB + optimizes time queries - [WHERE clause](/docs/query/sql/where/) — Full WHERE syntax reference -- [Date/time operators](/docs/query/operators/date-time/) — Additional timestamp operators +- [Date/time operators](/docs/query/operators/date-time/) — Additional timestamp + operators From 5263999c64da9943dec3e68b1f7101a87f9d0df0 Mon Sep 17 00:00:00 2001 From: Marko Topolnik Date: Tue, 3 Feb 2026 16:08:50 +0100 Subject: [PATCH 2/5] Document TICK Exchange Calendars --- .../query/operators/exchange-calendars.md | 390 ++++++++++++++++++ documentation/query/operators/tick.md | 28 +- documentation/sidebars.js | 5 + 3 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 documentation/query/operators/exchange-calendars.md diff --git a/documentation/query/operators/exchange-calendars.md b/documentation/query/operators/exchange-calendars.md new file mode 100644 index 000000000..3987b6c61 --- /dev/null +++ b/documentation/query/operators/exchange-calendars.md @@ -0,0 +1,390 @@ +--- +title: Exchange calendars +sidebar_label: Exchange calendars +description: + Exchange calendars filter TICK intervals to real exchange trading sessions, + handling holidays, early closes, lunch breaks, and DST automatically. + QuestDB Enterprise feature. +--- + +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + Exchange calendars provide real exchange trading schedules for TICK interval + filtering. + + +Exchange calendars extend [TICK interval syntax](/docs/query/operators/tick/) +with real exchange trading schedules. Instead of manually specifying trading +hours, day filters, and holidays, reference an exchange by its +[ISO 10383 MIC code](https://www.iso20022.org/market-identifier-codes): + +```questdb-sql +-- NYSE regular trading hours for January, holidays excluded automatically +SELECT * FROM trades +WHERE ts IN '2025-01-[01..31]#XNYS'; +``` + +This single expression generates interval scans for every trading session in +January, automatically handling weekends, holidays (New Year's Day, MLK Day), +and the exact trading hours including DST transitions. + +## Syntax + +Exchange calendars use the `#` filter position in TICK expressions: + +```text +date [T time] [@timezone] [#EXCHANGE] [;duration] +``` + +The exchange code replaces the day-of-week filter (`#workday`, `#Mon,Wed,Fri`). +You cannot combine an exchange calendar with a day-of-week filter in the same +position. + +```questdb-sql +-- Single day +WHERE ts IN '2025-01-24#XNYS' + +-- Date range +WHERE ts IN '2025-01-[06..10]#XNYS' + +-- Full month +WHERE ts IN '2025-03#XNYS' + +-- Full year +WHERE ts IN '2025#XNYS' +``` + +Exchange codes are **case-insensitive**: `#XNYS`, `#xnys`, and `#Xnys` are all +equivalent. + +## What the calendar provides + +An exchange calendar defines, for each trading day of the year: + +- **Session open and close times** in UTC +- **Holiday closures** (the day is skipped entirely) +- **Early closes** (shortened trading hours) +- **Multiple sessions per day** (e.g., morning and afternoon with a lunch break) +- **DST transitions** (UTC times shift when the exchange's local timezone + changes clocks) + +### Single-session exchanges + +Most exchanges have one continuous trading session per day. For example, NYSE +(XNYS) trades from 9:30 AM to 4:00 PM Eastern Time: + +```questdb-sql +WHERE ts IN '2025-01-24#XNYS' +-- Winter (EST, UTC-5): 14:30 - 21:00 UTC +-- Summer (EDT, UTC-4): 13:30 - 20:00 UTC +``` + +### Multi-session exchanges + +Some exchanges have a lunch break, producing two intervals per trading day. Hong +Kong (XHKG) has morning and afternoon sessions: + +```questdb-sql +WHERE ts IN '2025-02-03#XHKG' +-- Morning: 01:30 - 04:00 UTC (09:30 - 12:00 HKT) +-- Afternoon: 05:00 - 08:00 UTC (13:00 - 16:00 HKT) +``` + +## Holidays and early closes + +Exchange calendars automatically exclude holidays and apply early closes. + +### Holiday closure + +The day is completely removed from results: + +```questdb-sql +-- April 14-18, 2025: Good Friday (Apr 18) is a NYSE holiday +WHERE ts IN '2025-04-[14..18]#XNYS' +-- Returns intervals for Mon-Thu only; Friday is skipped +``` + +### Early close + +The session ends earlier than usual: + +```questdb-sql +-- July 2-7, 2025: July 3 is an early close (1:00 PM ET), July 4 is closed +WHERE ts IN '2025-07-[02..07]#XNYS' +-- Jul 2 (Wed): 13:30 - 20:00 UTC (normal) +-- Jul 3 (Thu): 13:30 - 17:00 UTC (early close, 3h shorter) +-- Jul 4 (Fri): closed +-- Jul 7 (Mon): 13:30 - 20:00 UTC (normal) +``` + +### Multi-session early close + +For exchanges with multiple sessions, an early close may mean only the morning +session runs: + +```questdb-sql +-- XHKG around Lunar New Year 2025 +WHERE ts IN '2025-01-[27..31]#XHKG' +-- Jan 27 (Mon): morning 01:30-04:00 + afternoon 05:00-08:00 (normal) +-- Jan 28 (Tue): morning 01:30-04:00 only (LNY Eve, no afternoon session) +-- Jan 29-31: closed (Lunar New Year holidays) +``` + +## Interaction with TICK features + +Exchange calendars combine with all standard +[TICK features](/docs/query/operators/tick/). The behaviors specific to exchange +calendars are described below. + +### Duration + +The `;duration` suffix extends the close of each trading session: + +```questdb-sql +WHERE ts IN '2025-01-24#XNYS;1h' +-- Without duration: 14:30 - 21:00 UTC +-- With ;1h: 14:30 - 22:00 UTC +``` + +For multi-session exchanges, each session is extended independently. If extended +sessions overlap, they merge into a single continuous interval: + +```questdb-sql +WHERE ts IN '2025-01-24#XHKG;1h' +-- Morning: 01:30 - 05:00 UTC (extended from 04:00) +-- Afternoon: 05:00 - 09:00 UTC (extended from 08:00) +``` + +Non-trading days remain excluded even with a duration. + +### Timezone + +The `@timezone` resolves the date to a UTC range first, then the exchange +schedule intersects that range: + +```questdb-sql +WHERE ts IN '2025-01-24@-05:00#XNYS' +-- Jan 24 in EST = 05:00Z Jan 24 to 05:00Z Jan 25 +-- Intersected with NYSE hours: 14:30 - 21:00 UTC on Jan 24 +``` + +This matters when timezone offsets shift a date across midnight UTC. A large +positive offset like `@+14:00` causes the UTC range to span two calendar days, +so trading sessions from both days may appear. + +### Time suffix + +A `T time` suffix is intersected with trading sessions. Times outside trading +hours produce an empty result: + +```questdb-sql +-- 15:00 UTC is within NYSE hours +WHERE ts IN '2025-01-24T15:00#XNYS' +-- Result: 15:00 - 15:59:59.999999 UTC + +-- 04:30 UTC falls in the XHKG lunch break +WHERE ts IN '2025-02-03T04:30#XHKG' +-- Result: [] (empty) +``` + +### Per-element filters + +Each element in a date list can specify its own exchange. A per-element filter +takes precedence over a global filter for that element: + +```questdb-sql +-- Different exchanges per date +WHERE ts IN '[2025-01-24#XNYS, 2025-02-03#XHKG]' + +-- Per-element overrides global +WHERE ts IN '[2025-01-24#XNYS, 2025-02-03]#XHKG' +-- Jan 24 uses XNYS; Feb 3 uses XHKG +``` + +## Custom calendars + +QuestDB Enterprise ships with built-in schedules for major exchanges. You can +customize these schedules — or define entirely new ones — through the +`_tick_calendars_custom` system table. + +### Setup + +Call `reload_tick_calendars()` to create the table where you'll put your custom +calendar data: + +```questdb-sql +SELECT reload_tick_calendars(); +``` + +This function creates the `_tick_calendars_custom` table if it does not exist. +It requires system admin privileges. You'll use the same function to reload the +calendars after you make changes to this table. + +### Custom table schema + +The `_tick_calendars_custom` table has the following columns: + +| Column | Type | Description | +| ------ | ---- | ----------- | +| `exchange` | `SYMBOL` | Exchange MIC code (e.g., `XNYS`) | +| `session` | `VARCHAR` | Session key, typically a date string (e.g., `2025-01-24`) | +| `open` | `TIMESTAMP` | Session open time (UTC) | +| `break_start` | `TIMESTAMP` | Lunch break start (UTC), or `NULL` if no break | +| `break_end` | `TIMESTAMP` | Lunch break end (UTC), or `NULL` if no break | +| `close` | `TIMESTAMP` | Session close time (UTC) | +| `deleted` | `BOOLEAN` | Set to `true` to soft-delete this custom row | + +The `session` column is the merge key. When a custom row has the same `exchange` +and `session` as a built-in entry, the custom row takes precedence. + +### Add a session + +Insert a row with the exchange, session date, and UTC timestamps: + +```questdb-sql +-- Add a Saturday trading session to NYSE +INSERT INTO _tick_calendars_custom + (exchange, session, open, close) +VALUES + ('XNYS', '2025-01-25', + '2025-01-25T10:00:00.000000Z', '2025-01-25T14:00:00.000000Z'); + +SELECT reload_tick_calendars(); +``` + +After reloading, `2025-01-25#XNYS` returns a 10:00-14:00 UTC session instead of +being empty. + +### Override a built-in session + +Insert a custom row with the same session key to replace it: + +```questdb-sql +-- Override NYSE Jan 27: late open at 16:00 instead of 14:30 +INSERT INTO _tick_calendars_custom + (exchange, session, open, close) +VALUES + ('XNYS', '2025-01-27', + '2025-01-27T16:00:00.000000Z', '2025-01-27T21:00:00.000000Z'); + +SELECT reload_tick_calendars(); +``` + +### Remove a built-in session + +Insert a row with all four timestamp columns left as `NULL`: + +```questdb-sql +-- Close NYSE on Jan 27 (remove the built-in session entirely) +INSERT INTO _tick_calendars_custom + (exchange, session) +VALUES + ('XNYS', '2025-01-27'); + +SELECT reload_tick_calendars(); +``` + +### Define a custom exchange + +You can define entirely new exchange codes not present in the built-in data: + +```questdb-sql +-- Define a custom exchange with a lunch break +INSERT INTO _tick_calendars_custom + (exchange, session, open, break_start, break_end, close) +VALUES + ('MINE', '2025-03-03', + '2025-03-03T09:00:00.000000Z', '2025-03-03T12:00:00.000000Z', + '2025-03-03T13:00:00.000000Z', '2025-03-03T17:00:00.000000Z'), + ('MINE', '2025-03-04', + '2025-03-04T09:00:00.000000Z', '2025-03-04T12:00:00.000000Z', + '2025-03-04T13:00:00.000000Z', '2025-03-04T17:00:00.000000Z'); + +SELECT reload_tick_calendars(); +``` + +Then use it like any other exchange: + +```questdb-sql +SELECT * FROM trades WHERE ts IN '2025-03-[03..04]#MINE'; +``` + +### Undo a custom override + +QuestDB does not support `DELETE`. Instead, the `deleted` column provides +soft-delete semantics. To restore a built-in session after overriding it, mark +the custom row as deleted: + +```questdb-sql +UPDATE _tick_calendars_custom +SET deleted = true +WHERE exchange = 'XNYS' AND session = '2025-01-27'; + +SELECT reload_tick_calendars(); +``` + +The built-in session is restored because deleted rows are excluded from the +merge. + +### Inspect effective schedules + +Use `tick_calendars()` to view the merged result of built-in and custom data: + +```questdb-sql +-- All effective sessions for NYSE +SELECT * FROM tick_calendars() WHERE exchange = 'XNYS'; + +-- Check a specific session +SELECT * FROM tick_calendars() +WHERE exchange = 'XNYS' AND session = '2025-01-27'; +``` + +The function returns one row per session with columns: `exchange`, `session`, +`open`, `break_start`, `break_end`, `close`. + +### Validation rules + +Custom rows are validated on load. Invalid rows are skipped with a log warning: + +- `open` and `close` must both be `NULL` (removal) or both non-`NULL` +- `break_start` and `break_end` must both be `NULL` or both non-`NULL` +- When non-`NULL`: `open < break_start < break_end < close` (or `open < close` + without a break) +- Session duration must be less than 24 hours +- If multiple non-deleted rows share the same `exchange` and `session`, the last + row wins + +:::warning Changes require reload + +Custom calendar changes are **not** applied automatically. You must call +`reload_tick_calendars()` after modifying the `_tick_calendars_custom` table. +Until then, queries continue using the cached schedules. + +::: + +## Processing order + +Within a TICK expression, components are applied in this order: + +1. **Date and time** are parsed as local time (or UTC if no timezone is given) +2. **Timezone** (`@`) converts local time intervals to UTC +3. **Exchange calendar** (`#EXCHANGE`) intersects with the UTC trading sessions +4. **Duration** (`;`) extends the close of each resulting interval +5. **Interval merging** combines any overlapping intervals + +:::note Exchange calendars vs day-of-week filters + +[Day-of-week filters](/docs/query/operators/tick/#day-of-week-filter) like +`#workday` apply to the **local date** (before timezone conversion), so "Monday" +means Monday in the specified timezone. Exchange calendars apply **after** +conversion to UTC, because exchange schedules are defined in UTC. + +::: + +## See also + +- [TICK interval syntax](/docs/query/operators/tick/) — Full TICK syntax + reference +- [Interval scan](/docs/concepts/deep-dive/interval-scan/) — How QuestDB + optimizes time queries diff --git a/documentation/query/operators/tick.md b/documentation/query/operators/tick.md index bd81d9231..4b9f9028b 100644 --- a/documentation/query/operators/tick.md +++ b/documentation/query/operators/tick.md @@ -6,6 +6,8 @@ description: complex temporal intervals in QuestDB queries. --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + TICK (Temporal Interval Calendar Kit) is a syntax for expressing complex temporal intervals in a single string. Use it with the `IN` operator to query multiple time ranges, schedules, and patterns efficiently. @@ -13,11 +15,26 @@ multiple time ranges, schedules, and patterns efficiently. ```questdb-sql -- NYSE trading hours on workdays for January SELECT * FROM trades -WHERE ts IN '2024-01-[01..31]T09:30@America/New_York#workday;6h30m'; +WHERE ts IN '2025-01-[02..8,10..19,21..31]T09:30@America/New_York#workday;6h30m'; ``` -This single expression generates interval scans for every weekday in January, -each starting at 9:30 AM New York time and lasting 6 hours 30 minutes. +This single expression generates interval scans for every weekday except +holidays in January, each starting at 9:30 AM New York time and lasting 6 hours +30 minutes. + + + With [exchange calendars](/docs/query/operators/exchange-calendars/), TICK + directly understands exchange schedules including holidays, early closes, and + lunch breaks. Here's an expression equivalent to the one above (XNYS is the + ISO 10383 MIC code of NYSE): + +```questdb-sql +-- NYSE trading hours for January, holidays excluded automatically +SELECT * FROM trades +WHERE ts IN '2025-01-[01..31]#XNYS'; +``` + + :::tip Key Points @@ -48,6 +65,7 @@ TIMEZONE = '@' iana_name -- '@America/New_York' FILTER = '#workday' | '#weekend' -- business day filters | '#' day_list -- '#Mon,Wed,Fri' + | '#' exchange_code -- '#XNYS' (exchange calendar, Enterprise) DURATION = ';' duration_value -- ';6h30m' @@ -73,6 +91,10 @@ unit = 'y' | 'M' | 'w' | 'd' | 'bd' | 'h' | 'm' | 's' | 'T' | 'u' | 'n' -- 'bd' (business days) valid only in date arithmetic, not duration ``` +The `exchange_code` filter uses an ISO 10383 MIC code (e.g., `#XNYS`) to apply +real exchange trading schedules. See +[exchange calendars](/docs/query/operators/exchange-calendars/) for details. + ## Why TICK Traditional approaches to complex time queries require: diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 020a9c6f2..29bff47ef 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -486,6 +486,11 @@ module.exports = { "query/operators/comparison", "query/operators/date-time", "query/operators/tick", + { + id: "query/operators/exchange-calendars", + type: "doc", + customProps: { tag: "Enterprise" }, + }, "query/operators/ipv4", "query/operators/logical", "query/operators/misc", From f72dd6c95c564cf81b30342aab96455adf82f8cb Mon Sep 17 00:00:00 2001 From: Marko Topolnik Date: Tue, 3 Feb 2026 16:23:59 +0100 Subject: [PATCH 3/5] Document Parquet file behavior --- .../query/operators/exchange-calendars.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/documentation/query/operators/exchange-calendars.md b/documentation/query/operators/exchange-calendars.md index 3987b6c61..29c3b333e 100644 --- a/documentation/query/operators/exchange-calendars.md +++ b/documentation/query/operators/exchange-calendars.md @@ -204,9 +204,20 @@ WHERE ts IN '[2025-01-24#XNYS, 2025-02-03]#XHKG' ## Custom calendars -QuestDB Enterprise ships with built-in schedules for major exchanges. You can -customize these schedules — or define entirely new ones — through the -`_tick_calendars_custom` system table. +### Built-in schedules + +QuestDB Enterprise ships with a Parquet file inside the JAR containing +pre-configured schedules for major exchanges. On every startup, this file is +extracted to: + +```text +/import/.questdb-internal/tick_calendars.parquet +``` + +The file is **overwritten on each restart**, so any manual edits to it are lost. +To customize schedules, use the `_tick_calendars_custom` table described below. +Custom entries are merged with the built-in data at query time, and take +precedence when both define the same session. ### Setup From 97243e37e5d47d660975fbca0c6317503c4aa037 Mon Sep 17 00:00:00 2001 From: Marko Topolnik Date: Tue, 3 Feb 2026 16:30:12 +0100 Subject: [PATCH 4/5] Remove Enterprise badge from sidebar --- documentation/sidebars.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 29bff47ef..1be70053a 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -486,11 +486,7 @@ module.exports = { "query/operators/comparison", "query/operators/date-time", "query/operators/tick", - { - id: "query/operators/exchange-calendars", - type: "doc", - customProps: { tag: "Enterprise" }, - }, + "query/operators/exchange-calendars", "query/operators/ipv4", "query/operators/logical", "query/operators/misc", From cba3dc8bed7710982769cf9f632f50d3ad8d526c Mon Sep 17 00:00:00 2001 From: Vlad Ilyushchenko Date: Tue, 3 Feb 2026 19:30:12 +0000 Subject: [PATCH 5/5] Improve TICK discoverability and standardize EnterpriseNote usage - Add TICK callout to WHERE page for better discoverability - Add search keywords to tick.md and exchange-calendars.md - Rename sidebar label to "Time Intervals (TICK)" - Replace :::note with EnterpriseNote component in ACL and backup pages - Use descriptive text in EnterpriseNote (not "X is Enterprise feature") Co-Authored-By: Claude Opus 4.5 --- .../query/operators/exchange-calendars.md | 12 +++++++++++ documentation/query/operators/tick.md | 11 ++++++++++ documentation/query/sql/acl/add-user.md | 13 ++++++------ .../query/sql/acl/alter-service-account.md | 13 ++++++------ documentation/query/sql/acl/alter-user.md | 17 +++++++-------- .../query/sql/acl/assume-service-account.md | 13 ++++++------ documentation/query/sql/acl/create-group.md | 13 ++++++------ .../query/sql/acl/create-service-account.md | 13 ++++++------ documentation/query/sql/acl/create-user.md | 13 ++++++------ documentation/query/sql/acl/drop-group.md | 13 ++++++------ .../query/sql/acl/drop-service-account.md | 13 ++++++------ documentation/query/sql/acl/drop-user.md | 13 ++++++------ .../query/sql/acl/exit-service-account.md | 13 ++++++------ .../sql/acl/grant-assume-service-account.md | 13 ++++++------ documentation/query/sql/acl/grant.md | 13 ++++++------ documentation/query/sql/acl/remove-user.md | 13 ++++++------ .../sql/acl/revoke-assume-service-account.md | 13 ++++++------ documentation/query/sql/acl/revoke.md | 13 ++++++------ documentation/query/sql/backup.md | 10 ++++----- documentation/query/sql/where.md | 21 +++++++++++++++++++ documentation/sidebars.js | 12 +++++------ 21 files changed, 152 insertions(+), 126 deletions(-) diff --git a/documentation/query/operators/exchange-calendars.md b/documentation/query/operators/exchange-calendars.md index 29c3b333e..5672cf4a5 100644 --- a/documentation/query/operators/exchange-calendars.md +++ b/documentation/query/operators/exchange-calendars.md @@ -5,6 +5,18 @@ description: Exchange calendars filter TICK intervals to real exchange trading sessions, handling holidays, early closes, lunch breaks, and DST automatically. QuestDB Enterprise feature. +keywords: + - NYSE + - NASDAQ + - trading hours + - market hours + - exchange schedule + - XNYS + - XNAS + - MIC code + - stock exchange + - trading session + - market holiday --- import { EnterpriseNote } from "@site/src/components/EnterpriseNote" diff --git a/documentation/query/operators/tick.md b/documentation/query/operators/tick.md index 4b9f9028b..6431293b5 100644 --- a/documentation/query/operators/tick.md +++ b/documentation/query/operators/tick.md @@ -4,6 +4,17 @@ sidebar_label: TICK intervals description: TICK (Temporal Interval Calendar Kit) - a powerful syntax for expressing complex temporal intervals in QuestDB queries. +keywords: + - time filter + - date range + - time range + - business days + - trading hours + - workday + - $today + - $now + - timestamp interval + - WHERE IN --- import { EnterpriseNote } from "@site/src/components/EnterpriseNote" diff --git a/documentation/query/sql/acl/add-user.md b/documentation/query/sql/acl/add-user.md index 0bc1487f7..a46736a6c 100644 --- a/documentation/query/sql/acl/add-user.md +++ b/documentation/query/sql/acl/add-user.md @@ -6,19 +6,18 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + To add user to one or more groups in the database, the `ADD USER` keywords are used. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/alter-service-account.md b/documentation/query/sql/acl/alter-service-account.md index eb81a3bf2..f7f556f3f 100644 --- a/documentation/query/sql/acl/alter-service-account.md +++ b/documentation/query/sql/acl/alter-service-account.md @@ -6,18 +6,17 @@ description: in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `ALTER SERVICE ACCOUNT` modifies service account settings. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/alter-user.md b/documentation/query/sql/acl/alter-user.md index f495be6a9..eb4404625 100644 --- a/documentation/query/sql/acl/alter-user.md +++ b/documentation/query/sql/acl/alter-user.md @@ -6,20 +6,17 @@ description: Enterprise." --- -For full documentation of the Access Control List and Role-based Access Control, -see the [RBAC operations](/docs/security/rbac) page. - -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" -::: - ---- + + RBAC provides fine-grained database permissions management. + `ALTER USER` modifies user settings. +For full documentation of the Access Control List and Role-based Access Control, +see the [RBAC operations](/docs/security/rbac) page. + ## Syntax ![Flow chart showing the syntax of the ALTER USER keyword](/images/docs/diagrams/alterUser.svg) diff --git a/documentation/query/sql/acl/assume-service-account.md b/documentation/query/sql/acl/assume-service-account.md index 6cf037371..5c85e4493 100644 --- a/documentation/query/sql/acl/assume-service-account.md +++ b/documentation/query/sql/acl/assume-service-account.md @@ -6,19 +6,18 @@ description: in QuestDB Enterprise" --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `ASSUME SERVICE ACCOUNT` switches current user to a service account, basically replacing its current access list with the service account's access list. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/create-group.md b/documentation/query/sql/acl/create-group.md index 8d264f308..d876d6af1 100644 --- a/documentation/query/sql/acl/create-group.md +++ b/documentation/query/sql/acl/create-group.md @@ -6,18 +6,17 @@ description: QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `CREATE GROUP` - create a new group For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/create-service-account.md b/documentation/query/sql/acl/create-service-account.md index eab46ffc9..3c18ecaa8 100644 --- a/documentation/query/sql/acl/create-service-account.md +++ b/documentation/query/sql/acl/create-service-account.md @@ -6,19 +6,18 @@ description: in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + To create a new service account in the database, the `CREATE SERVICE ACCOUNT` keywords are used. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/create-user.md b/documentation/query/sql/acl/create-user.md index 0ce542178..cbb417781 100644 --- a/documentation/query/sql/acl/create-user.md +++ b/documentation/query/sql/acl/create-user.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `CREATE USER` - create a new user in the database. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/drop-group.md b/documentation/query/sql/acl/drop-group.md index 9dd435c26..faa2206b6 100644 --- a/documentation/query/sql/acl/drop-group.md +++ b/documentation/query/sql/acl/drop-group.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `DROP GROUP` - remove an existing group. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/drop-service-account.md b/documentation/query/sql/acl/drop-service-account.md index c6860219c..9c07c677b 100644 --- a/documentation/query/sql/acl/drop-service-account.md +++ b/documentation/query/sql/acl/drop-service-account.md @@ -6,18 +6,17 @@ description: in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `DROP SERVICE ACCOUNT` - drop an existing service account For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/drop-user.md b/documentation/query/sql/acl/drop-user.md index f22929399..92962e229 100644 --- a/documentation/query/sql/acl/drop-user.md +++ b/documentation/query/sql/acl/drop-user.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `DROP USER` - drop an existing user For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/exit-service-account.md b/documentation/query/sql/acl/exit-service-account.md index 0a0febcab..cdaf16e03 100644 --- a/documentation/query/sql/acl/exit-service-account.md +++ b/documentation/query/sql/acl/exit-service-account.md @@ -6,6 +6,12 @@ description: in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `EXIT SERVICE ACCOUNT` - switches current user back from service account, basically replacing its current access list (belonging to a user account) with the user's access list. @@ -13,13 +19,6 @@ the user's access list. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/grant-assume-service-account.md b/documentation/query/sql/acl/grant-assume-service-account.md index 8efa6bc6b..2cd6385a6 100644 --- a/documentation/query/sql/acl/grant-assume-service-account.md +++ b/documentation/query/sql/acl/grant-assume-service-account.md @@ -6,18 +6,17 @@ description: to RBAC in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `GRANT ASSUME SERVICE ACCOUNT` - assigns a service account to a user or a group. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/grant.md b/documentation/query/sql/acl/grant.md index f15f00d6c..692a2a538 100644 --- a/documentation/query/sql/acl/grant.md +++ b/documentation/query/sql/acl/grant.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `GRANT` - grants permissions to a user, group or service account. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/remove-user.md b/documentation/query/sql/acl/remove-user.md index ab79b1d04..b14380f10 100644 --- a/documentation/query/sql/acl/remove-user.md +++ b/documentation/query/sql/acl/remove-user.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `REMOVE USER` - removes user from one or more groups. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/revoke-assume-service-account.md b/documentation/query/sql/acl/revoke-assume-service-account.md index da840b25a..a93ef3612 100644 --- a/documentation/query/sql/acl/revoke-assume-service-account.md +++ b/documentation/query/sql/acl/revoke-assume-service-account.md @@ -6,19 +6,18 @@ description: to RBAC in QuestDB Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `REVOKE ASSUME SERVICE ACCOUNT` - revokes a service account from a user or a group. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/acl/revoke.md b/documentation/query/sql/acl/revoke.md index a9bf05bf6..c811bff5f 100644 --- a/documentation/query/sql/acl/revoke.md +++ b/documentation/query/sql/acl/revoke.md @@ -6,18 +6,17 @@ description: Enterprise." --- +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" + + + RBAC provides fine-grained database permissions management. + + `REVOKE` - revoke permission from user, group or service account. For full documentation of the Access Control List and Role-based Access Control, see the [RBAC operations](/docs/security/rbac) page. -:::note - -Role-based Access Control (RBAC) operations are only available in QuestDB -Enterprise. - -::: - --- ## Syntax diff --git a/documentation/query/sql/backup.md b/documentation/query/sql/backup.md index 42cb9bbc5..597ca8892 100644 --- a/documentation/query/sql/backup.md +++ b/documentation/query/sql/backup.md @@ -4,13 +4,13 @@ sidebar_label: BACKUP description: "BACKUP SQL keyword reference documentation. Applies to QuestDB Enterprise." --- -`BACKUP` - start and abort incremental backups to object storage. - -:::note +import { EnterpriseNote } from "@site/src/components/EnterpriseNote" -Backup operations are only available in QuestDB Enterprise. + + Object storage backups with incremental and point-in-time recovery support. + -::: +`BACKUP` - start and abort incremental backups to object storage. _Looking for a detailed guide on backup creation and restoration? Check out our [Backup and Restore](/docs/operations/backup/) guide!_ diff --git a/documentation/query/sql/where.md b/documentation/query/sql/where.md index a992c0846..6b8ed6a46 100644 --- a/documentation/query/sql/where.md +++ b/documentation/query/sql/where.md @@ -258,6 +258,27 @@ SELECT scores WHERE ts = '2010-01-12T00:02:26.000000Z'; Returns results within a defined range. +:::tip Recommended: TICK syntax + +For complex timestamp filtering, use [TICK interval syntax](/docs/query/operators/tick/). +TICK handles date ranges, business days, timezones, and schedules in a single +expression: + +```questdb-sql +-- Last 5 business days, 9:30 AM New York time, 6.5 hour windows +WHERE ts IN '[$today-5bd..$today-1bd]T09:30@America/New_York#workday;6h30m' +``` + +With [exchange calendars](/docs/query/operators/exchange-calendars/) (Enterprise), +you can filter by real exchange schedules including holidays and early closes: + +```questdb-sql +-- NYSE trading hours for January, holidays excluded automatically +WHERE ts IN '2025-01-[01..31]#XNYS' +``` + +::: + #### Syntax ![Flow chart showing the syntax of the WHERE clause with a partial timestamp comparison](/images/docs/diagrams/whereTimestampPartial.svg) diff --git a/documentation/sidebars.js b/documentation/sidebars.js index 1be70053a..b39f153f3 100644 --- a/documentation/sidebars.js +++ b/documentation/sidebars.js @@ -317,11 +317,7 @@ module.exports = { "query/sql/alter-view", ], }, - { - id: "query/sql/acl/assume-service-account", - type: "doc", - customProps: { tag: "Enterprise" }, - }, + "query/sql/acl/assume-service-account", "query/sql/backup", "query/sql/cancel-query", "query/sql/checkpoint", @@ -485,7 +481,11 @@ module.exports = { "query/operators/bitwise", "query/operators/comparison", "query/operators/date-time", - "query/operators/tick", + { + id: "query/operators/tick", + type: "doc", + label: "Time Intervals (TICK)", + }, "query/operators/exchange-calendars", "query/operators/ipv4", "query/operators/logical",