Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add JSON serialization for zoned timestamp values

When a ZONED_TIMESTAMP feature is returned by the feature server, this new oneof makes Value.WhichOneof("val") return zoned_timestamp_val, but feature_server_utils._value_to_native() and the proto_json patch only special-case lists/maps/scalars and otherwise return the raw protobuf message. feature_server.py then passes that dict to JSONResponse, which cannot serialize a ZonedTimestamp message, so any online response containing this new type fails instead of returning a JSON value.

Useful? React with 👍 / 👎.

}
}

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