Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/getting-started/concepts/feast-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Feast's type system is built on top of [protobuf](https://github.com/protocolbuf
Feast supports the following categories of data types:

- **Primitive types**: numerical values (`Int32`, `Int64`, `Float32`, `Float64`), `String`, `Bytes`, `Bool`, and `UnixTimestamp`.
- **Zoned timestamp type**: `ZonedTimestamp` stores a timezone-aware datetime as both the UTC instant and its originating zone, so the original wall-clock zone round-trips losslessly. This differs from `UnixTimestamp`, which is always decoded as UTC and discards the source zone. Use `ZonedTimestamp` when local time-of-day or the offset/zone itself is meaningful. It must be explicitly declared in schema (it is not inferred by any backend), and is not supported as an entity key.
- **Domain-specific primitives**: `PdfBytes` (PDF binary data for RAG/document pipelines) and `ImageBytes` (image binary data for multimodal pipelines). These are semantic aliases over `Bytes` and must be explicitly declared in schema — no backend infers them.
- **UUID types**: `Uuid` and `TimeUuid` for universally unique identifiers. Stored as strings at the proto level but deserialized to `uuid.UUID` objects in Python.
- **Array types**: ordered lists of any primitive type, e.g. `Array(Int64)`, `Array(String)`, `Array(Uuid)`.
Expand Down
42 changes: 41 additions & 1 deletion docs/reference/type-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Feast supports the following data types:
| `Bytes` | `bytes` | Binary data |
| `Bool` | `bool` | Boolean value |
| `UnixTimestamp` | `datetime` | Unix timestamp (nullable) |
| `ZonedTimestamp` | `datetime` | Timezone-aware datetime preserving its source zone (nullable) |
| `Uuid` | `uuid.UUID` | UUID (any version) |
| `TimeUuid` | `uuid.UUID` | Time-based UUID (version 1) |
| `Decimal` | `decimal.Decimal` | Arbitrary-precision decimal number |
Expand Down Expand Up @@ -202,7 +203,8 @@ from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource
from feast.types import (
Int32, Int64, Float32, Float64, String, Bytes, Bool, UnixTimestamp,
Uuid, TimeUuid, Decimal, Array, Set, Map, ScalarMap, Json, Struct
Uuid, TimeUuid, Decimal, Array, Set, Map, ScalarMap, Json, Struct,
ZonedTimestamp
)

# Define a data source
Expand Down Expand Up @@ -232,6 +234,7 @@ user_features = FeatureView(
Field(name="profile_picture", dtype=Bytes),
Field(name="is_active", dtype=Bool),
Field(name="last_login", dtype=UnixTimestamp),
Field(name="event_time", dtype=ZonedTimestamp),
Field(name="session_id", dtype=Uuid),
Field(name="event_id", dtype=TimeUuid),
Field(name="price", dtype=Decimal),
Expand Down Expand Up @@ -362,6 +365,43 @@ unique_prices = {decimal.Decimal("9.99"), decimal.Decimal("19.99"), decimal.Deci
`Decimal` is **not** inferred from any backend schema. You must declare it explicitly in your feature view schema. The pandas dtype for `Decimal` columns is `object` (holding `decimal.Decimal` instances), not a numeric dtype.
{% endhint %}

### ZonedTimestamp Type Usage Examples

The `ZonedTimestamp` type stores a timezone-aware `datetime` as both the UTC instant
and its originating zone, so the original wall-clock zone round-trips losslessly.
By contrast, `UnixTimestamp` always decodes to UTC and discards the source zone.

```python
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# A datetime in a specific zone — both the instant and "America/Los_Angeles" are kept
event_time = datetime(2026, 6, 17, 9, 0, 0, tzinfo=ZoneInfo("America/Los_Angeles"))

# ZonedTimestamp values are returned as tz-aware datetime objects, in their own zone
response = store.get_online_features(
features=["event_features:event_time"],
entity_rows=[{"user_id": 1001}],
)
result = response.to_dict()
# result["event_time"][0] == event_time (same instant AND same zone, e.g. 09:00-07:00)

# Two values at the same instant but different zones stay distinct
la = datetime(2026, 6, 17, 9, 0, 0, tzinfo=ZoneInfo("America/Los_Angeles"))
utc = datetime(2026, 6, 17, 16, 0, 0, tzinfo=timezone.utc) # same instant as `la`

# A naive (tz-less) datetime is interpreted as UTC
naive = datetime(2026, 6, 17, 12, 0, 0) # stored zone is empty, decoded as UTC
```

{% hint style="warning" %}
`ZonedTimestamp` is **not** inferred from any backend schema — you must declare it
explicitly in your feature view schema. It is not supported as an entity key. The
zone is stored as an IANA name (e.g. `America/Los_Angeles`) when available, falling
back to a fixed-offset string; offline stores that cannot natively carry a zone may
normalize to UTC on that backend.
{% endhint %}

### Nested Collection Type Usage Examples

```python
Expand Down
13 changes: 13 additions & 0 deletions protos/feast/types/Value.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ message ValueType {
DECIMAL_LIST = 45;
DECIMAL_SET = 46;
SCALAR_MAP = 47;
ZONED_TIMESTAMP = 48;
}
}

Expand Down Expand Up @@ -120,13 +121,25 @@ message Value {
StringList decimal_list_val = 45;
StringSet decimal_set_val = 46;
ScalarMap scalar_map_val = 47;
ZonedTimestamp zoned_timestamp_val = 48;
Comment thread
nquinn408 marked this conversation as resolved.
}
}

enum Null {
NULL = 0;
}

// A timezone-aware datetime: the UTC instant plus its originating zone, so a
// zoned datetime round-trips losslessly (unlike UNIX_TIMESTAMP, which is decoded
// as UTC and discards the original zone).
message ZonedTimestamp {
// Epoch seconds (UTC instant), same convention as unix_timestamp_val.
int64 unix_timestamp = 1;
// IANA zone name (e.g. "America/Los_Angeles") or a fixed-offset string
// (e.g. "-07:00", "UTC"). Empty string is treated as UTC on read.
string zone = 2;
}

message BytesList {
repeated bytes val = 1;
}
Expand Down
Loading
Loading