# Time types

> Create and use timestamps, time spans, time ranges, and rates with nanosecond precision in the Python and TypeScript clients.

Synnax stores each timestamp as a 64-bit integer: the number of nanoseconds since the Unix epoch, 1970-01-01 00:00:00 UTC. The clients give you four types that keep this precision:

| Type        | Holds                       | Python                     | TypeScript                  |
| ----------- | --------------------------- | -------------------------- | --------------------------- |
| `TimeStamp` | A point in time.            | `sy.TimeStamp.now()`       | `TimeStamp.now()`           |
| `TimeSpan`  | A duration.                 | `sy.TimeSpan.SECOND * 5`   | `TimeSpan.seconds(5)`       |
| `TimeRange` | The time from start to end. | `sy.TimeRange(start, end)` | `new TimeRange(start, end)` |
| `Rate`      | A frequency, in hertz.      | `sy.Rate.HZ * 100`         | `Rate.hz(100)`              |

Methods such as `read` and `write` take a time type, or any value that its constructor takes, such as a `datetime` in Python or a `Date` in TypeScript.

## TimeStamp

### Create a timestamp

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
from datetime import datetime, timezone

import pandas as pd
import synnax as sy

now = sy.TimeStamp.now()

# From a datetime. A datetime with no time zone is in local time.
start = sy.TimeStamp(datetime(2026, 10, 3, 12, 0, tzinfo=timezone.utc))

# From a pandas Timestamp.
start = sy.TimeStamp(pd.Timestamp("2026-10-03 12:00:00", tz="UTC"))

# From nanoseconds since the Unix epoch.
start = sy.TimeStamp(1_791_028_800_000_000_000)
```

The Python client does not parse strings: `sy.TimeStamp("2026-10-03")` raises a `ValueError`. Parse the string with `datetime.fromisoformat` or pandas first.

**TypeScript**

```typescript
import { TimeSpan, TimeStamp } from "@synnaxlabs/client";

const now = TimeStamp.now();

// From an ISO 8601 string. A string with no time zone is in UTC.
const start = new TimeStamp("2026-10-03T12:00:00Z");

// From a Date.
const fromDate = new TimeStamp(new Date("2026-10-03T12:00:00Z"));

// From nanoseconds since the Unix epoch.
const fromNanoseconds = new TimeStamp(1_791_028_800_000_000_000n);
```

A number is also nanoseconds: `new TimeStamp(1_000_000_000)` is one second after the epoch. To read a string in local time, pass `"local"` as the second argument.

### Add, subtract, and compare

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
later = start + sy.TimeSpan.MINUTE
print(later - start)  # 1m
print(later > start)  # True
print(later.after_eq(start))  # True
```

Subtracting two timestamps returns the time between them as a `TimeSpan`. Add or subtract a `TimeSpan`, not a bare number: `start + 5` raises a `TypeError`.

**TypeScript**

```typescript
const later = start.add(TimeSpan.MINUTE);
console.log(later.span(start).toString()); // 1m
// 2026-10-03T12:00:30.000Z
console.log(later.sub(TimeSpan.seconds(30)).toString());
console.log(later.after(start)); // true
console.log(later.afterEq(start)); // true
```

`before` and `beforeEq` compare in the other direction.

### Convert a timestamp

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
print(start.datetime(tz=timezone.utc))  # 2026-10-03 12:00:00+00:00
print(start.datetime())  # The same time, in the local time zone.
print(int(start))  # 1791028800000000000
```

**TypeScript**

```typescript
console.log(start.date()); // 2026-10-03T12:00:00.000Z
console.log(start.toString()); // 2026-10-03T12:00:00.000Z
console.log(start.valueOf()); // 1791028800000000000n
```

## TimeSpan

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
from datetime import timedelta

timeout = sy.TimeSpan.MILLISECOND * 250
print(timeout)  # 250ms
print(timeout.seconds)  # 0.25
print(sy.TimeSpan.from_seconds(1.5))  # 1s 500ms
print(sy.TimeSpan(timedelta(minutes=2)))  # 2m
print(timeout / 2)  # 125ms
print(sy.TimeSpan.MINUTE / sy.TimeSpan.SECOND)  # 60.0
```

The constants are `NANOSECOND`, `MICROSECOND`, `MILLISECOND`, `SECOND`, `MINUTE`, `HOUR`, and `DAY`. The properties `seconds`, `milliseconds`, and the other units return floats, and `timedelta` returns a Python `timedelta`.

**TypeScript**

```typescript
const timeout = TimeSpan.milliseconds(250);
console.log(timeout.toString()); // 250ms
console.log(timeout.seconds); // 0.25
console.log(TimeSpan.SECOND.mult(5).toString()); // 5s
console.log(TimeSpan.parse("2h 30m").toString()); // 2h 30m
```

The static methods are `nanoseconds`, `microseconds`, `milliseconds`, `seconds`, `minutes`, `hours`, and `days`, and each has a constant such as `TimeSpan.SECOND`.

## TimeRange

A time range includes its start and excludes its end. A sample at the end of a time range belongs to the next time range, so time ranges that touch never hold the same sample:

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
end = start + sy.TimeSpan.HOUR
tr = sy.TimeRange(start, end)
print(tr.span)  # 1h
print(tr.contains(start))  # True
print(tr.contains(end))  # False

# Other ways to make the same range.
tr = start.range(end)
tr = start.span_range(sy.TimeSpan.HOUR)
```

`contains` also takes a `TimeRange`, and returns true when the whole time range is inside, ends included.

**TypeScript**

```typescript
import { TimeRange } from "@synnaxlabs/client";

const end = start.add(TimeSpan.HOUR);
const tr = new TimeRange(start, end);
console.log(tr.span.toString()); // 1h
console.log(tr.contains(start)); // true
console.log(tr.contains(end)); // false

// Ranges overlap when they share any time.
const next = new TimeRange(end, end.add(TimeSpan.HOUR));
console.log(tr.overlapsWith(next)); // false

// Other ways to make the same range.
const fromObject = new TimeRange({ start, end });
const fromSpan = start.spanRange(TimeSpan.HOUR);
```

`contains` also takes a `TimeRange`. It returns true only when the other time range starts at or after this time range and ends before this time range ends, so `tr.contains(tr)` is false.

## Rate

A rate is a frequency in hertz.

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

```python
rate = sy.Rate.HZ * 100
print(rate)  # 100.0 Hz
print(rate.period)  # 10ms
```

`sy.Rate(100)` is the same rate. The constants are `HZ`, `KHZ`, and `MHZ`. Hardware task configurations take a `Rate` or a number of hertz for `sample_rate` and `stream_rate`.

**TypeScript**

```typescript
import { Rate } from "@synnaxlabs/client";

const rate = Rate.hz(100);
console.log(rate.toString()); // 100 Hz
console.log(rate.period.toString()); // 10ms
```

`Rate.khz(1)` is 1000 Hz. Hardware task configurations take a number of hertz for `sampleRate` and `streamRate`, not a `Rate`.

## Precision

The time types keep every nanosecond. The native types of each language keep less, so a conversion can lose precision:

[Enterprise](https://docs.synnaxlabs.com/reference/get-started/editions "Needs Synnax Enterprise")

**Python**

A `datetime` keeps microseconds. `TimeStamp.datetime` rounds to the nearest microsecond:

```python
ts = sy.TimeStamp(1_609_459_200_123_456_789)
print(ts.datetime(tz=timezone.utc))  # 2021-01-01 00:00:00.123457+00:00
```

**TypeScript**

A `Date` keeps milliseconds, and `TimeStamp.date` drops the rest. A `number` holds integers exactly only up to 2^53, so keep nanoseconds as the `bigint` that `valueOf` returns:

```typescript
const ts = new TimeStamp(1_609_459_200_123_456_789n);
console.log(ts.date()); // 2021-01-01T00:00:00.123Z
console.log(ts.valueOf()); // 1609459200123456789n
```
