diff --git a/documentation/query/operators/exchange-calendars.md b/documentation/query/operators/exchange-calendars.md
new file mode 100644
index 000000000..5672cf4a5
--- /dev/null
+++ b/documentation/query/operators/exchange-calendars.md
@@ -0,0 +1,413 @@
+---
+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.
+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"
+
+
+ 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
+
+### 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
+
+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 bb60a74da..6431293b5 100644
--- a/documentation/query/operators/tick.md
+++ b/documentation/query/operators/tick.md
@@ -4,8 +4,21 @@ 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"
+
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,23 +26,40 @@ 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 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';
```
-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'
@@ -46,6 +76,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'
@@ -71,9 +102,14 @@ 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:
+
- Multiple `UNION ALL` statements
- Application-side date generation
- Complex `BETWEEN` logic with many `OR` clauses
@@ -82,6 +118,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 +126,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 +161,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 +174,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 +185,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 +262,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 +270,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 +295,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 +348,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 +372,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 +420,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 +466,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 +493,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 +532,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 +569,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 +582,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 +631,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 +737,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 +747,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
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

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

diff --git a/documentation/sidebars.js b/documentation/sidebars.js
index 020a9c6f2..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,12 @@ 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",
"query/operators/misc",