pola-rs/polars · error

Invalid Offset format (must be [-]00:00) or timezones featur

Error message

Invalid Offset format (must be [-]00:00) or timezones feature not active

What it means

Without the timezones cargo feature, polars-json can only serialize tz-aware timestamps if the zone parses as a fixed offset via parse_offset ("[-]HH:MM"); every other tz string on a Timestamp column reaches the cfg(not(feature = "timezones")) arm and panics with this combined message. It means: tz-aware column + a zone that is not a bare offset + a build without chrono-tz support.

Source

Thrown at crates/polars-json/src/json/write/serialize.rs:481

            Ok(parsed_tz) => {
                let f = move |x: Option<&i64>, buf: &mut Vec<u8>| {
                    if let Some(x) = x {
                        let dt_str = timestamp_to_datetime(*x, time_unit, &parsed_tz).to_rfc3339();
                        write!(buf, "\"{dt_str}\"").unwrap();
                    } else {
                        buf.extend_from_slice(b"null")
                    }
                };

                materialize_serializer(f, array.iter(), offset, take)
            },
            _ => {
                panic!("Timezone {tz} is invalid or not supported");
            },
        },
        #[cfg(not(feature = "timezones"))]
        _ => {
            panic!("Invalid Offset format (must be [-]00:00) or timezones feature not active");
        },
    }
}

pub fn new_serializer<'a>(
    array: &'a dyn Array,
    offset: usize,
    take: usize,
) -> Box<dyn JsonSerializer<Item = [u8]> + 'a + Send + Sync> {
    match array.dtype().to_storage() {
        ArrowDataType::Boolean => {
            boolean_serializer(array.as_any().downcast_ref().unwrap(), offset, take)
        },
        ArrowDataType::Int8 => {
            primitive_serializer::<i8>(array.as_any().downcast_ref().unwrap(), offset, take)
        },
        ArrowDataType::Int16 => {
            primitive_serializer::<i16>(array.as_any().downcast_ref().unwrap(), offset, take)

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Enable the timezones feature: polars = { version = "...", features = ["timezones"] }
  2. Or drop the zone before writing: col("ts").dt().replace_time_zone(None)
  3. Or write Parquet instead, which serializes tz without going through this path

Example fix

# before
polars = { version = "0.4", default-features = false }
# write_json on Datetime(_, Some("Europe/Amsterdam")) -> panic

# after
polars = { version = "0.4", default-features = false, features = ["timezones"] }
# or in code: col("ts").dt().replace_time_zone(None)
Defensive patterns

Strategy: fallback

Validate before calling

fn has_tz_aware(df: &DataFrame) -> bool {
    df.schema().iter_values().any(|dt| matches!(dt, DataType::Datetime(_, Some(_))))
}
// without the `timezones` feature only fixed offsets (+HH:MM) serialize;
// fallback: strip zones before write_json
// df.apply("ts", |s| s.datetime().map(|ca| ca.replace_time_zone(None).into_series()))?;

Try / catch

catch_unwind around write_json merely re-labels the panic; the schema pre-check plus replace_time_zone(None) fallback keeps the export working without the feature.

Prevention

When it happens

Trigger: write_json (JsonWriter/JsonLinesWriter) over Datetime(_, Some("Europe/Amsterdam")) in a build where the timezones feature is disabled. Fixed offsets like "+02:00" still work; IANA names do not.

Common situations: Minimal-feature Rust services exporting JSON; data ported from python-polars (where the feature is always present) into a trimmed Rust deployment.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/3a3af21d21a2c166. Report an issue: GitHub.