# Conversion functions > Change a value from one type to another with CAST, ::, to_timestamp and to_date. - Category: Functions — conversion - Keywords: convert, conversion, type conversion, change type, cast, ::, to number, to text, parse number, parse date, epoch, unix time, milliseconds, cannot convert, INTEGER, BIGINT, BOOLEAN, VARCHAR, TEXT, JSONB, BYTEA, BLOB, FLOAT, SHORT, BYTE - See also: [Column types](column-types.md), [Date and time functions (to_char)](functions-date-time.md#to_char), [Conditional and NULL functions (coalesce)](functions-conditional.md#coalesce), [Math functions (round)](functions-math.md#round), [JSON functions](functions-json.md), [Operators](operators.md) A value has a type: a number, a text, a time and so on. Sometimes a value has the wrong type for what you want to do. A price read from a text file is the text `'512.50'`, not a number. `CAST` changes the type. - `CAST(x AS type)` and `x::type` do the same thing. - `to_timestamp(x)` turns text or seconds into a time; `to_date(x)` turns text or milliseconds into a date. - KDB never guesses. When a value does not fit the new type, you get an error like `cannot convert '5.5' to INT`. It does not round or cut the value for you. - `NULL` stays `NULL` in every conversion. The rules are the same as when a value goes into a table column of that type (see [column types](column-types.md)). The entries "Into ..." below list them for each type. **Example: Prices, quantities and times that arrived as text** Setup: ```sql CREATE TABLE price_import (line INT PRIMARY KEY, symbol STRING, price STRING, qty STRING, traded STRING, is_buy STRING); INSERT INTO price_import VALUES (1, 'CITY', '512.50', '100', '2026-09-10T11:05:00+05:45', 'true'), (2, 'COAST', '398', '20', '2026-09-10T13:00:00+05:45', 'false'), (3, 'VOLT', '184.0', '50', '2026-09-10T14:59:00+05:45', '1'); ``` ```sql SELECT symbol, CAST(price AS DOUBLE) * CAST(qty AS LONG) AS amount, CAST(traded AS TIMESTAMP) AS traded_utc, is_buy::BOOL AS is_buy FROM price_import ORDER BY line; ``` Result: | symbol (STRING) | amount (DOUBLE) | traded_utc (TIMESTAMP) | is_buy (BOOL) | |---|---|---|---| | CITY | 51250 | 2026-09-10T05:20:00.000Z | true | | COAST | 7960 | 2026-09-10T07:15:00.000Z | false | | VOLT | 9200 | 2026-09-10T09:14:00.000Z | true | (3 rows) The examples below also use this small accounting table. Money is kept in `DECIMAL(2)`: an exact number with 2 places after the point. Setup used by the examples below: ```sql CREATE TABLE ledger ( id INT PRIMARY KEY, day DATE, account STRING, amount DECIMAL(2) ); INSERT INTO ledger VALUES (1, '2026-09-01', 'Sales', 1250.00), (2, '2026-09-02', 'Sales', 899.99), (3, '2026-09-02', 'Rent', 1200.50); ``` ## CAST Syntax: ```sql CAST(value AS type) ``` Changes `value` into `type`. | Argument | Type | Meaning | |---|---|---| | `value` | any | the value to change | | `type` | a type name | `BOOL` (`BOOLEAN`), `BYTE`, `SHORT`, `INT` (`INTEGER`), `LONG` (`BIGINT`), `FLOAT`, `DOUBLE`, `DECIMAL(s)`, `DATE`, `TIMESTAMP`, `SYMBOL`, `STRING` (`VARCHAR`, `TEXT`), `JSON` (`JSONB`), `BYTES` (`BYTEA`, `BLOB`) | - **Returns:** a value of `type`. - **NULL:** `CAST(NULL AS type)` is `NULL`. - **Notes:** - When the value does not fit, the error is `cannot convert '...' to TYPE`. For JSON and BYTES the message also says what is wrong. - `DECIMAL` without a scale means `DOUBLE`. Write `CAST(x AS DECIMAL(2))` when you want exact money. A length like `VARCHAR(10)` or `CHAR(3)` is read and **ignored**: nothing is cut. - Many things need no `CAST`. Text compared with a time is read as a time (`time >= '2026-09-01'`). `||` joins numbers as text (`'CITY' || 1`). - To turn a time into formatted text, use [to_char](functions-date-time.md#to_char), not `CAST`. **Example** ```sql SELECT CAST('512.5' AS DOUBLE) + 1 AS a, CAST(42 AS STRING) AS b, CAST('2026-09-10T11:00:00+05:45' AS TIMESTAMP) AS c, CAST('12.5' AS DECIMAL(2)) AS d, CAST(NULL AS INT) AS e; ``` Result: | a (DOUBLE) | b (STRING) | c (TIMESTAMP) | d (DECIMAL(2)) | e (STRING) | |---|---|---|---|---| | 513.5 | 42 | 2026-09-10T05:15:00.000Z | 12.50 | NULL | (1 row) **Example: 13% VAT on each ledger line, as exact money** ```sql SELECT id, account, amount, CAST(round(amount * 0.13, 2) AS DECIMAL(2)) AS vat, amount * 0.13 AS vat_as_double FROM ledger ORDER BY id; ``` Result: | id (INT) | account (STRING) | amount (DECIMAL(2)) | vat (DECIMAL(2)) | vat_as_double (DECIMAL(4)) | |---|---|---|---|---| | 1 | Sales | 1250.00 | 162.50 | 162.5000 | | 2 | Sales | 899.99 | 117.00 | 116.9987 | | 3 | Rent | 1200.50 | 156.07 | 156.0650 | (3 rows) **Example: Into a whole number: the fraction is rounded** ```sql SELECT CAST(5.5 AS INT) AS half_up, CAST(5.4 AS INT) AS down, CAST(-5.5 AS INT) AS negative; ``` Result: | half_up (LONG) | down (LONG) | negative (LONG) | |---|---|---| | 6 | 5 | -6 | (1 row) **Example: DECIMAL without a scale is DOUBLE** ```sql SELECT CAST('12.50' AS DECIMAL) AS as_double, CAST('12.50' AS DECIMAL(2)) AS exact_money; ``` Result: | as_double (DOUBLE) | exact_money (DECIMAL(2)) | |---|---| | 12.5 | 12.50 | (1 row) ## :: (short cast) Syntax: ```sql value::type ``` The short way to write `CAST(value AS type)`. `'42'::INT` is `CAST('42' AS INT)`. It comes from PostgreSQL. - **Returns:** a value of `type`, as with [CAST](functions-conversion.md#cast). - **NULL:** `NULL::INT` is `NULL`. - **Notes:** `::` holds on to the value right before it, more tightly than any operator. `2 + 3::DOUBLE` changes only `3`. Put `( )` around a longer expression: `(2 + 3)::STRING`. **Example** ```sql SELECT '42'::INT + 1 AS a, 2 + 3::DOUBLE AS b, (2 + 3)::STRING AS c, '12.5'::DECIMAL(2) AS d, NULL::BOOL AS e; ``` Result: | a (LONG) | b (DOUBLE) | c (STRING) | d (DECIMAL(2)) | e (STRING) | |---|---|---|---|---| | 43 | 5 | 5 | 12.50 | NULL | (1 row) **Example: Filter candles with a Nepal time written as text** (sample data: sample) ```sql SELECT time, symbol, close FROM ochl_minutes WHERE symbol = 'COAST' AND time >= '2026-09-10T11:00:00+05:45'::TIMESTAMP AND time < '2026-09-10T11:03:00+05:45'::TIMESTAMP; ``` Result: | time (TIMESTAMP) | symbol (SYMBOL) | close (DOUBLE) | |---|---|---| | 2026-09-10T05:15:00.000Z | COAST | 380 | | 2026-09-10T05:16:00.000Z | COAST | 379.6 | | 2026-09-10T05:17:00.000Z | COAST | 380.1 | (3 rows) **Example: A length after VARCHAR is read and ignored** ```sql SELECT CAST('CITY' AS VARCHAR(10)) AS a, CAST('CITY' AS VARCHAR(3)) AS nothing_is_cut; ``` Result: | a (STRING) | nothing_is_cut (STRING) | |---|---| | CITY | CITY | (1 row) ## to_timestamp Syntax: ```sql to_timestamp(value) ``` Makes a `TIMESTAMP` (a moment in time, stored in UTC) from time text, a `DATE` or a number of seconds. | Argument | Type | Meaning | |---|---|---| | `value` | text, `DATE`, `TIMESTAMP` or number | time text like `'2026-09-10T11:00:00+05:45'`, or seconds since 1970-01-01 UTC (a fraction is allowed) | - **Returns:** `TIMESTAMP`. - **NULL:** `NULL` in, `NULL` out. - **Notes:** - A number means **seconds**, as in PostgreSQL and Unix time: `to_timestamp(1789020900)`. A fraction keeps the milliseconds: `to_timestamp(1789020900.25)`. Before 2026-09-25 a number meant milliseconds. - Milliseconds, like JavaScript's `Date.now()`: divide by 1000.0 first, or cast: `CAST(ms AS TIMESTAMP)`. - There is no second "format" argument. The text must look like `YYYY-MM-DD`, then optional `T` or space and `HH:MM:SS.fff`, then optional `Z` or `+05:45`. Text without a zone is UTC. - A `DATE` becomes midnight UTC of that day. **Example** ```sql SELECT to_timestamp('2026-09-10T11:00:00+05:45') AS nepal_text, to_timestamp('2026-09-10') AS date_text, to_timestamp(1789020900) AS from_seconds, to_timestamp(1789020900.25) AS with_fraction, to_timestamp(1789020900000 / 1000.0) AS from_ms, to_timestamp(NULL) AS nothing; ``` Result: | nepal_text (TIMESTAMP) | date_text (TIMESTAMP) | from_seconds (TIMESTAMP) | with_fraction (TIMESTAMP) | from_ms (TIMESTAMP) | nothing (STRING) | |---|---|---|---|---|---| | 2026-09-10T05:15:00.000Z | 2026-09-10T00:00:00.000Z | 2026-09-10T06:15:00.000Z | 2026-09-10T06:15:00.250Z | 2026-09-10T06:15:00.000Z | NULL | (1 row) **Example: App sign-up times sent as milliseconds** Setup: ```sql CREATE TABLE app_signups (user_id INT PRIMARY KEY, name STRING, created_ms LONG); INSERT INTO app_signups VALUES (1, 'Sita', 1788900300000), (2, 'Ram', 1788928200000), (3, 'Hari', 1789020900000); ``` ```sql SELECT user_id, name, created_ms, to_timestamp(created_ms / 1000.0) AS created, CAST(created_ms AS TIMESTAMP) AS same FROM app_signups ORDER BY user_id; ``` Result: | user_id (INT) | name (STRING) | created_ms (LONG) | created (TIMESTAMP) | same (TIMESTAMP) | |---|---|---|---|---| | 1 | Sita | 1788900300000 | 2026-09-08T20:45:00.000Z | 2026-09-08T20:45:00.000Z | | 2 | Ram | 1788928200000 | 2026-09-09T04:30:00.000Z | 2026-09-09T04:30:00.000Z | | 3 | Hari | 1789020900000 | 2026-09-10T06:15:00.000Z | 2026-09-10T06:15:00.000Z | (3 rows) **Example: Text that is not a time** (shows an error) ```sql SELECT to_timestamp('10/09/2026'); ``` Result: Error: `line 1, column 8: to_timestamp: argument 1 must be a time, got '10/09/2026'` ## to_date Syntax: ```sql to_date(value) ``` Makes a `DATE` (a day without a clock time) from time text, a `TIMESTAMP` or a number of milliseconds. It keeps the **UTC** date. | Argument | Type | Meaning | |---|---|---| | `value` | text, `TIMESTAMP`, `DATE` or whole number | a time, or milliseconds since 1970-01-01 UTC | - **Returns:** `DATE`. - **NULL:** `NULL` in, `NULL` out. - **Notes:** - The date is the UTC date. Nepal is 5 hours 45 minutes ahead of UTC, so between 00:00 and 05:45 Nepal time the UTC date is the day before. For the Nepal date write `to_date(time + 20700000)` (20700000 ms = 5 h 45 min). - `to_date(86400000)` works (a number of milliseconds), but `CAST(86400000 AS DATE)` is an error. `CAST` into `DATE` does not take numbers. **Example** ```sql SELECT to_date('2026-09-10T20:00:00Z') AS a, to_date('2026-09-10T03:00:00+05:45') AS nepal_3am_is_utc_day_before, to_date(86400000) AS b, to_date(NULL) AS c; ``` Result: | a (DATE) | nepal_3am_is_utc_day_before (DATE) | b (DATE) | c (STRING) | |---|---|---|---| | 2026-09-10 | 2026-09-09 | 1970-01-02 | NULL | (1 row) **Example: Sign-ups per day: UTC date and Nepal date** Setup: ```sql CREATE TABLE app_signups (user_id INT PRIMARY KEY, name STRING, created TIMESTAMP); INSERT INTO app_signups VALUES (1, 'Sita', '2026-09-09T02:30:00+05:45'), (2, 'Ram', '2026-09-09T10:15:00+05:45'), (3, 'Hari', '2026-09-09T15:45:00+05:45'), (4, 'Gita', '2026-09-10T12:00:00+05:45'); ``` ```sql SELECT to_date(created) AS utc_day, count(*) AS signups FROM app_signups GROUP BY to_date(created) ORDER BY utc_day; SELECT to_date(created + 20700000) AS nepal_day, count(*) AS signups FROM app_signups GROUP BY to_date(created + 20700000) ORDER BY nepal_day; ``` Result: `SELECT to_date(created) AS utc_day, count(*) AS signups FROM app_signups GROUP BY to_date(created) O` | utc_day (DATE) | signups (LONG) | |---|---| | 2026-09-08 | 1 | | 2026-09-09 | 2 | | 2026-09-10 | 1 | (3 rows) `SELECT to_date(created + 20700000) AS nepal_day, count(*) AS signups FROM app_signups GROUP BY to_da` | nepal_day (DATE) | signups (LONG) | |---|---| | 2026-09-09 | 3 | | 2026-09-10 | 1 | (2 rows) **Example: true is not a time** (shows an error) ```sql SELECT to_date(TRUE); ``` Result: Error: `line 1, column 8: to_date: argument 1 must be a time, got 'true'` ## Into integers Syntax: ```sql CAST(value AS BYTE | SHORT | INT | INTEGER | LONG | BIGINT) ``` Whole numbers. The types differ only in size: | Type | Smallest | Biggest | |---|---|---| | `BYTE` | -127 | 127 | | `SHORT` | -32767 | 32767 | | `INT`, `INTEGER` | -2147483647 | 2147483647 | | `LONG`, `BIGINT` | -9223372036854775807 | 9223372036854775807 | | From | Result | |---|---| | a whole number | the same number, if it is in range | | `DOUBLE` | rounded to the nearest whole number: `5.4` gives `5`, `5.5` gives `6`, `-5.5` gives `-6` | | `DECIMAL(s)` | rounded the same way: `1.50` gives `2` | | text | a number like `'42'`, `' -7'` or `'5.5'` (spaces before are allowed, spaces after are not); a fraction is rounded | | `TRUE` / `FALSE` | `1` / `0` | | `TIMESTAMP` | the milliseconds since 1970-01-01, as PostgreSQL apps expect | | `DATE`, `JSON`, `BYTES` | error | - **Returns:** the chosen integer type. - **NULL:** stays `NULL`. - **Notes:** - A fraction is **rounded to the nearest** whole number, half away from zero, as PostgreSQL does. To cut instead, use [floor](functions-math.md#floor), [ceil](functions-math.md#ceil) or [trunc](functions-math.md#trunc) first. - A number too big for the type is still an error: `CAST(300 AS BYTE)`. - For the milliseconds of a time, `CAST(t AS LONG)` works; `time - 0::TIMESTAMP` does the same. **Example** ```sql SELECT CAST('42' AS INT) AS a, CAST(5.0 AS INT) AS b, CAST(TRUE AS INT) AS c, CAST(CAST('2.00' AS DECIMAL(2)) AS LONG) AS d, CAST(round(512.5) AS INT) AS e, CAST(-127 AS BYTE) AS f; ``` Result: | a (LONG) | b (LONG) | c (LONG) | d (LONG) | e (LONG) | f (LONG) | |---|---|---|---|---|---| | 42 | 5 | 1 | 2 | 513 | -127 | (1 row) **Example: Shares per trade from text, and a time as milliseconds** (sample data: sample) ```sql SELECT transaction_id, symbol, CAST(quantity AS STRING) AS qty_text, CAST(CAST(quantity AS STRING) AS LONG) + 1 AS back_to_number, date - 0::TIMESTAMP AS date_ms FROM trade_log ORDER BY transaction_id LIMIT 3; ``` Result: | transaction_id (LONG) | symbol (SYMBOL) | qty_text (STRING) | back_to_number (LONG) | date_ms (LONG) | |---|---|---|---|---| | 100001 | TELCO | 1000 | 1001 | 1788912000000 | | 100002 | CITY | 10 | 11 | 1788912000000 | | 100003 | CITY | 500 | 501 | 1788912000000 | (3 rows) **Example: A fraction is rounded to the nearest whole number** ```sql SELECT CAST(512.5 AS INT) AS a, CAST(512.4 AS INT) AS b, CAST('5.5' AS INT) AS from_text; ``` Result: | a (LONG) | b (LONG) | from_text (LONG) | |---|---|---| | 513 | 512 | 6 | (1 row) **Example: Too big for BYTE** (shows an error) ```sql SELECT CAST(300 AS BYTE); ``` Result: Error: `line 1, column 8: cannot convert '300' to BYTE` **Example: A TIMESTAMP gives its milliseconds; a DATE does not** (shows an error) ```sql SELECT CAST(CAST('2026-09-10' AS TIMESTAMP) AS LONG) AS milliseconds; SELECT CAST(CAST('2026-09-10' AS DATE) AS LONG); ``` Result: | milliseconds (LONG) | |---| | 1788998400000 | (1 row) Error: `line 2, column 8: cannot convert '2026-09-10' to LONG` ## Into DOUBLE and FLOAT Syntax: ```sql CAST(value AS DOUBLE | FLOAT) ``` Numbers with a fraction. `DOUBLE` keeps about 15 digits. `FLOAT` is smaller and keeps about 7 digits. | From | Result | |---|---| | any number (`LONG`, `DOUBLE`, `DECIMAL`) | the number | | text | a number like `'512.5'`, `'-3'` or `'1e3'` | | `BOOL`, times, `JSON`, `BYTES` | error | - **Returns:** `DOUBLE` or `FLOAT`. - **NULL:** stays `NULL`. - **Notes:** - A `DOUBLE` cannot hold every decimal exactly. `0.1 + 0.2` is shown as `0.3`, but the real value is `0.30000000000000004` (see [Into STRING](functions-conversion.md#into-string)). For money use `DECIMAL(2)`. - `FLOAT` loses digits: `CAST(0.1 AS FLOAT)` shows `0.100000001490116`. A value above about 3.4e38 is an error. - `/` already gives a `DOUBLE` (`7 / 2` is `3.5`), so `CAST(7 AS DOUBLE) / 2` is not needed (it is after `SET int_division = integer`: [integer division](operators.md#integer-division)). **Example** ```sql SELECT CAST('512.5' AS DOUBLE) AS a, CAST('1e3' AS DOUBLE) AS b, CAST(CAST('1.25' AS DECIMAL(2)) AS DOUBLE) AS c, CAST(0.1 AS FLOAT) AS d, 0.1 + 0.2 AS e; ``` Result: | a (DOUBLE) | b (DOUBLE) | c (DOUBLE) | d (DOUBLE) | e (DOUBLE) | |---|---|---|---|---| | 512.5 | 1000 | 1.25 | 0.100000001490116 | 0.3 | (1 row) **Example: Ledger amounts as DOUBLE for a chart** ```sql SELECT day, account, amount, CAST(amount AS DOUBLE) AS amount_double FROM ledger ORDER BY id; ``` Result: | day (DATE) | account (STRING) | amount (DECIMAL(2)) | amount_double (DOUBLE) | |---|---|---|---| | 2026-09-01 | Sales | 1250.00 | 1250 | | 2026-09-02 | Sales | 899.99 | 899.99 | | 2026-09-02 | Rent | 1200.50 | 1200.5 | (3 rows) **Example: Text that is not a number** (shows an error) ```sql SELECT CAST('$512' AS DOUBLE); ``` Result: Error: `line 1, column 8: cannot convert '$512' to DOUBLE` ## Into DECIMAL(s) Syntax: ```sql CAST(value AS DECIMAL(scale)) ``` An exact number with `scale` places after the point. `DECIMAL(2)` is right for dollars and cents. | From | Result | |---|---| | a whole number | `7` becomes `7.00` | | `DECIMAL` | the same value with the new scale, if no digit is lost | | `DOUBLE` | the value rounded to `scale` places: `12.345` gives `12.35` for `DECIMAL(2)` | | text | a number like `'1250.75'`; more places than `scale` are rounded | | `BOOL`, times, `JSON`, `BYTES` | error | | Part | Meaning | |---|---| | `scale` | `DECIMAL(s)`: 0 to 18 (18 digits in all). `NUMERIC(p, s)`: 0 to 38, `p` up to 38 digits | - **Returns:** `DECIMAL(scale)`. - **NULL:** stays `NULL`. - **Notes:** - More places than `scale` are **rounded** to the nearest, half away from zero. `CAST(round(x, 2) AS DECIMAL(2))` says the same thing and is clearer to read. - `DECIMAL` **without** a scale is not exact money: it is the same as `DOUBLE`. - Arithmetic with `DECIMAL` values (`amount * 0.13`, `debit - credit`) is exact and gives a `DECIMAL`, as in PostgreSQL ([DECIMAL](column-types.md#decimal)). CAST it to `DECIMAL(2)` to show it with 2 places. `sum` and `avg` of `DECIMAL` values are `DECIMAL` too ([sum](functions-aggregate.md#sum)). - A `DOUBLE` like `0.1 + 0.2` is close enough to `0.30`, so `CAST(0.1 + 0.2 AS DECIMAL(2))` gives `0.30`. - `DECIMAL(s)` keeps 18 digits in 64 bits: with 2 places the largest value is `92233720368547758.07`. `NUMERIC(p, s)` keeps its `p` digits, up to 38: `CAST(x AS NUMERIC(24,2))` holds 22 digits before the point, and more is an error that names the type you wrote: `cannot convert '...' to NUMERIC(24,2): numeric field overflow`. **Example** ```sql SELECT CAST(7 AS DECIMAL(2)) AS a, CAST('1250.75' AS DECIMAL(2)) AS b, CAST(1500.256 AS DECIMAL(3)) AS c, CAST(0.1 + 0.2 AS DECIMAL(2)) AS d, CAST(CAST('1.5' AS DECIMAL(1)) AS DECIMAL(3)) AS e; ``` Result: | a (DECIMAL(2)) | b (DECIMAL(2)) | c (DECIMAL(3)) | d (DECIMAL(2)) | e (DECIMAL(3)) | |---|---|---|---|---| | 7.00 | 1250.75 | 1500.256 | 0.30 | 1.500 | (1 row) **Example: Month report: totals and average per account, exact to the cent** ```sql SELECT account, sum(amount) AS total, CAST(round(avg(amount), 2) AS DECIMAL(2)) AS average, count(*) AS lines FROM ledger WHERE day IN '2026-09' GROUP BY account ORDER BY account; ``` Result: | account (STRING) | total (DECIMAL(2)) | average (DECIMAL(2)) | lines (LONG) | |---|---|---|---| | Rent | 1200.50 | 1200.50 | 1 | | Sales | 2149.99 | 1075.00 | 2 | (2 rows) **Example: Too many places are rounded, not refused** ```sql SELECT CAST(12.345 AS DECIMAL(2)) AS rounded_up, CAST(12.344 AS DECIMAL(2)) AS rounded_down, CAST('1250.756' AS DECIMAL(2)) AS from_text; ``` Result: | rounded_up (DECIMAL(2)) | rounded_down (DECIMAL(2)) | from_text (DECIMAL(2)) | |---|---|---| | 12.35 | 12.34 | 1250.76 | (1 row) **Example: NUMERIC(24,2) holds 24 digits** ```sql SELECT CAST(123456789012345678.12 AS NUMERIC(24,2)) AS big, CAST(1 AS NUMERIC(20,2)) / 3 AS third; ``` Result: | big (DECIMAL(38,2)) | third (DECIMAL(38,20)) | |---|---| | 123456789012345678.12 | 0.33333333333333333333 | (1 row) ## Into TIMESTAMP Syntax: ```sql CAST(value AS TIMESTAMP) ``` A moment in time, stored as milliseconds since 1970-01-01 UTC. KDB always shows it in UTC, like `2026-09-10T05:15:00.000Z`. | From | Result | |---|---| | time text | `'2026-09-10'`, `'2026-09-10 11:00'`, `'2026-09-10T11:00:00.123'`, with an optional `Z`, `+05:45` or `+0545` at the end. No zone means UTC | | `DATE` | midnight UTC of that day | | a whole number | that many milliseconds after 1970-01-01 UTC | | `DOUBLE`, `BOOL`, `JSON`, `BYTES` | error | - **Returns:** `TIMESTAMP`. - **NULL:** stays `NULL`. - **Notes:** - A local time must be written with its zone offset, for example `+05:45` for Nepal. `'2026-09-10 11:00'` is 11:00 **UTC**, which is 16:45 in Nepal. - Zone names (`NPT`, `Asia/Kathmandu`) are not allowed inside the text. Only an offset like `+05:45`. - Other date orders (`'10/09/2026'`), one-digit months (`'2026-9-1'`) and `24:00` are errors. More than 3 digits after the second are cut to milliseconds. - The same as [to_timestamp](functions-conversion.md#to_timestamp). **Example** ```sql SELECT CAST('2026-09-10T11:00:00+05:45' AS TIMESTAMP) AS nepal_11am, CAST('2026-09-10 11:00' AS TIMESTAMP) AS utc_11am, CAST(CAST('2026-09-10' AS DATE) AS TIMESTAMP) AS from_date, CAST(0 AS TIMESTAMP) AS zero, CAST('2026-09-10T11:00:00.123456+0545' AS TIMESTAMP) AS micro_cut; ``` Result: | nepal_11am (TIMESTAMP) | utc_11am (TIMESTAMP) | from_date (TIMESTAMP) | zero (TIMESTAMP) | micro_cut (TIMESTAMP) | |---|---|---|---|---| | 2026-09-10T05:15:00.000Z | 2026-09-10T11:00:00.000Z | 2026-09-10T00:00:00.000Z | 1970-01-01T00:00:00.000Z | 2026-09-10T05:15:00.123Z | (1 row) **Example: Market open and close (Nepal time) as UTC times** (sample data: sample) ```sql SELECT symbol, count(*) AS minutes, min(time) AS first_minute, max(time) AS last_minute FROM ochl_minutes WHERE time >= CAST('2026-09-10T11:00:00+05:45' AS TIMESTAMP) AND time < CAST('2026-09-10T15:00:00+05:45' AS TIMESTAMP) GROUP BY symbol ORDER BY symbol; ``` Result: | symbol (SYMBOL) | minutes (LONG) | first_minute (TIMESTAMP) | last_minute (TIMESTAMP) | |---|---|---|---| | CITY | 240 | 2026-09-10T05:15:00.000Z | 2026-09-10T09:14:00.000Z | | COAST | 236 | 2026-09-10T05:15:00.000Z | 2026-09-10T09:14:00.000Z | (2 rows) **Example: Day/month/year order is not read** (shows an error) ```sql SELECT CAST('10/09/2026' AS TIMESTAMP); ``` Result: Error: `line 1, column 8: cannot convert '10/09/2026' to TIMESTAMP` ## Into DATE Syntax: ```sql CAST(value AS DATE) ``` A day without a clock time, shown like `2026-09-10`. | From | Result | |---|---| | date text | `'2026-09-10'` | | time text | the **UTC** date of that time | | `TIMESTAMP` | the UTC date | | numbers, `BOOL`, `JSON`, `BYTES` | error (use [to_date](functions-conversion.md#to_date) for milliseconds) | - **Returns:** `DATE`. - **NULL:** stays `NULL`. - **Notes:** - `'2026-09-10T03:00:00+05:45'` is 21:15 UTC on 9 September, so its `DATE` is `2026-09-09`. - A date that does not exist (`'2026-02-30'`) is an error. - `DATE + 1` is the next day. **Example** ```sql SELECT CAST('2026-09-10' AS DATE) AS a, CAST('2026-09-10T03:00:00+05:45' AS DATE) AS nepal_early_morning, CAST(CAST('2026-09-10T23:59:00Z' AS TIMESTAMP) AS DATE) AS b, CAST('2026-09-10' AS DATE) + 1 AS next_day; ``` Result: | a (DATE) | nepal_early_morning (DATE) | b (DATE) | next_day (DATE) | |---|---|---|---| | 2026-09-10 | 2026-09-09 | 2026-09-10 | 2026-09-11 | (1 row) **Example: Trades per day from the trade log** (sample data: sample) ```sql SELECT CAST(date AS DATE) AS trade_day, count(*) AS trades, sum(quantity) AS shares FROM trade_log GROUP BY CAST(date AS DATE) ORDER BY trade_day; ``` Result: | trade_day (DATE) | trades (LONG) | shares (LONG) | |---|---|---| | 2026-09-09 | 8 | 3730 | | 2026-09-10 | 24 | 4930 | (2 rows) **Example: There is no 30 February** (shows an error) ```sql SELECT CAST('2026-02-30' AS DATE); ``` Result: Error: `line 1, column 8: cannot convert '2026-02-30' to DATE` **Example: CAST into DATE does not take a number** (shows an error) ```sql SELECT CAST(20260910 AS DATE); ``` Result: Error: `line 1, column 8: cannot convert '20260910' to DATE` ## Into BOOL Syntax: ```sql CAST(value AS BOOL | BOOLEAN) ``` `true` or `false`. | From | Result | |---|---| | `TRUE` / `FALSE` | the same | | the whole numbers `1` / `0` | `true` / `false`; any other number is an error | | text | `'true'`, `'t'`, `'1'` are `true`; `'false'`, `'f'`, `'0'` are `false` (any letter case) | | `DOUBLE`, times, `JSON`, `BYTES` | error | - **Returns:** `BOOL`. - **NULL:** stays `NULL`. - **Notes:** `'yes'`, `'no'`, `2` and `1.0` are errors. Map them with [CASE](functions-conditional.md#case-simple) first. **Example** ```sql SELECT CAST(1 AS BOOL) AS a, CAST(0 AS BOOL) AS b, CAST('TRUE' AS BOOL) AS c, CAST('f' AS BOOL) AS d, '1'::BOOLEAN AS e; ``` Result: | a (BOOL) | b (BOOL) | c (BOOL) | d (BOOL) | e (BOOL) | |---|---|---|---|---| | true | false | true | false | true | (1 row) **Example: A yes/no column from an import** (shows an error) ```sql SELECT CAST('yes' AS BOOL); ``` Result: Error: `line 1, column 8: cannot convert 'yes' to BOOL` **Example: Map yes/no with CASE, then it works** ```sql SELECT answer, CASE lower(answer) WHEN 'yes' THEN TRUE WHEN 'no' THEN FALSE END AS agreed FROM (SELECT 'Yes' AS answer UNION ALL SELECT 'no') t; ``` Result: | answer (STRING) | agreed (BOOL) | |---|---| | Yes | true | | no | false | (2 rows) ## Into STRING and SYMBOL Syntax: ```sql CAST(value AS STRING | VARCHAR | TEXT | SYMBOL) ``` Text. Every value can become text. | From | Text | |---|---| | whole number | `42` | | `DOUBLE` | the shortest form that reads back the same: `512.5`, `0.30000000000000004` | | `DECIMAL(s)` | with all `s` places: `1.50` | | `BOOL` | `true` / `false` | | `TIMESTAMP` | UTC in ISO form: `2026-09-10T05:15:00.000Z` | | `DATE` | `2026-09-10` | | `JSON` | the JSON text; a JSON string keeps its quotes (`"text"`) | | `BYTES` | hex with `\x` in front: `\x4e41` | - **Returns:** `STRING` (or `SYMBOL`). - **NULL:** stays `NULL`. An empty text `''` is not `NULL`. - **Notes:** - For a time in another format or in Nepal time use [to_char](functions-date-time.md#to_char). - To get the text inside a JSON string without quotes, use `->>` or `json_value_text` ([JSON functions](functions-json.md)). - `||` already turns numbers into text: `'CITY-' || 1`. **Example** ```sql SELECT CAST(42 AS STRING) AS a, CAST(512.5 AS TEXT) AS b, CAST(CAST('1.50' AS DECIMAL(2)) AS VARCHAR) AS c, CAST(TRUE AS STRING) AS d, CAST(CAST('2026-09-10T11:00:00+05:45' AS TIMESTAMP) AS STRING) AS e, CAST(0.1 + 0.2 AS STRING) AS f; ``` Result: | a (STRING) | b (STRING) | c (STRING) | d (STRING) | e (STRING) | f (STRING) | |---|---|---|---|---|---| | 42 | 512.5 | 1.50 | true | 2026-09-10T05:15:00.000Z | 0.30000000000000004 | (1 row) **Example: A label for a chart: symbol, date and close** (sample data: sample) ```sql SELECT symbol || ' ' || CAST(CAST(time AS DATE) AS STRING) || ': ' || CAST(close AS STRING) AS label FROM ochl_day WHERE symbol = 'VOLT' ORDER BY time DESC LIMIT 3; ``` Result: | label (STRING) | |---| | VOLT 2026-09-10: 191.2 | | VOLT 2026-09-09: 187.3 | | VOLT 2026-09-08: 182.4 | (3 rows) **Example: JSON and BYTES as text** ```sql SELECT CAST(CAST('{"a": 1}' AS JSON) AS STRING) AS json_object, CAST(CAST('"text"' AS JSON) AS STRING) AS json_string, CAST(CAST('NA' AS BYTES) AS STRING) AS bytes; ``` Result: | json_object (STRING) | json_string (STRING) | bytes (STRING) | |---|---|---| | {"a":1} | "text" | \\x4e41 | (1 row) ## Into JSON Syntax: ```sql CAST(value AS JSON | JSONB) ``` A JSON document (a text format for data, like `{"name": "Sita", "tags": [1, 2]}`). KDB checks it and stores it without extra spaces. | From | Result | |---|---| | text | parsed as JSON; bad JSON is an error that says where the problem is | | numbers, `DECIMAL` | a JSON number: `5`, `1.50` | | `BOOL` | `true` / `false` | | `TIMESTAMP`, `DATE`, `BYTES` | a JSON string with their text: `"2026-09-10"` | - **Returns:** `JSON`. - **NULL:** SQL `NULL` stays `NULL`. The text `'null'` becomes the JSON value `null`, which is **not** SQL `NULL` (`IS NULL` is false). - **Notes:** - A text value must be valid JSON. The word `CITY` is not JSON; `'"CITY"'` (with double quotes inside) is. See [JSON functions](functions-json.md) for reading parts of a document. - The result tables on this page show a JSON string without its quotes, and a JSON `null` as `NULL`. **Example** ```sql SELECT CAST('{"symbol": "CITY", "levels": [500, 520]}' AS JSON) AS a, CAST(5 AS JSON) AS b, CAST(TRUE AS JSONB) AS c, CAST(CAST('2026-09-10' AS DATE) AS JSON) AS d, CAST('null' AS JSON) AS json_null, CAST('null' AS JSON) IS NULL AS is_sql_null; ``` Result: | a (JSON) | b (JSON) | c (JSON) | d (JSON) | json_null (JSON) | is_sql_null (BOOL) | |---|---|---|---|---|---| | {"symbol":"CITY","levels":[500,520]} | 5 | true | "2026-09-10" | NULL | false | (1 row) **Example: App settings saved as JSON text** Setup: ```sql CREATE TABLE user_settings (user_id INT PRIMARY KEY, settings STRING); INSERT INTO user_settings VALUES (1, '{ "theme": "dark", "alerts": ["CITY", "COAST"] }'), (2, '{"theme": "light", "alerts": []}'); ``` ```sql SELECT user_id, CAST(settings AS JSON) AS settings_json FROM user_settings ORDER BY user_id; ``` Result: | user_id (INT) | settings_json (JSON) | |---|---| | 1 | {"theme":"dark","alerts":["CITY","COAST"]} | | 2 | {"theme":"light","alerts":[]} | (2 rows) **Example: Keys need double quotes** (shows an error) ```sql SELECT CAST('{theme: "dark"}' AS JSON); ``` Result: Error: `line 1, column 8: cannot convert '{theme: "dark"}' to JSON: not valid JSON: expected a key in double quotes at character 2 (near 'theme: "dark...')` ## Into BYTES Syntax: ```sql CAST(value AS BYTES | BYTEA | BLOB) ``` Raw bytes, shown as hex with `\x` in front (`\x0a0b`). | From | Result | |---|---| | text that starts with `\x` | read as hex digits: `'\x4e41'` is the two bytes 4e and 41 | | other text | its UTF-8 bytes: `'NA'` is `\x4e41`; a Nepali letter is 3 bytes | | `X'0A0B'` | already bytes | | numbers, `BOOL`, times, `JSON` | error | - **Returns:** `BYTES`. - **NULL:** stays `NULL`. - **Notes:** `CAST(bytes AS STRING)` does not give the letters back; it gives the hex text. See [bytes functions](functions-bytes.md) for `hex`, `unhex` and base64. **Example** ```sql SELECT CAST('\x4e41' AS BYTES) AS from_hex, CAST('CITY' AS BYTEA) AS from_text, CAST(X'0A0B' AS BLOB) AS literal, CAST('नेपाल' AS BYTES) AS nepali, length(CAST('नेपाल' AS BYTES)) AS byte_count; ``` Result: | from_hex (BYTES) | from_text (BYTES) | literal (BYTES) | nepali (BYTES) | byte_count (LONG) | |---|---|---|---|---| | \\x4e41 | \\x43495459 | \\x0a0b | \\xe0a4a8e0a587e0a4aae0a4bee0a4b2 | 15 | (1 row) **Example: Bad hex digits** (shows an error) ```sql SELECT CAST('\xZZ' AS BYTES); ``` Result: Error: `line 1, column 8: cannot convert '\xZZ' to BYTES: '\xZZ' is not hex bytes; write them like '\x0a0b' or X'0A0B'` **Example: A number is not bytes** (shows an error) ```sql SELECT CAST(5 AS BYTES); ``` Result: Error: `line 1, column 8: cannot convert '5' to BYTES: expected bytes like X'0A0B' or '\x0a0b', got LONG 5`