Skip to content

calternal_plugin::zone

The user’s time zone and local-day arithmetic (#141).

A day key (YYYY-MM-DD: a Daily note, a Calendar day, date:today in search) is a date in the user’s IANA zone, never in UTC. In IST the local date is one day ahead of UTC from 00:00 to 05:30, and in America/Los_Angeles it is one day behind from 17:00 (16:00 in summer) to midnight. A server that takes “today” from Utc::now() puts entries on the wrong Daily note in that window.

Resolution order, the same for every route:

  1. The zone the request carries: the X-Calternal-Timezone header, which every web request sends (@calternal/api-client), or a route’s own explicit tz parameter or body field. An invalid value is an error, never a silent fallback, so a client bug cannot move data to another day.
  2. The user setting timezone in .calternal/settings.json, for requests that carry no zone (CalDAV clients, the CLI, background work).
  3. UTC, only when neither exists.

Source: crates/calternal-plugin/src/zone.rs

  • pub use chrono_tz::Tz
pub struct InvalidZone;

A zone name that is not a known IANA zone.

Implements: Clone, Copy, Debug, PartialEq, Eq, std::fmt::Display, std::error::Error

Source: crates/calternal-plugin/src/zone.rs:34

pub fn day_bounds(day: NaiveDate, zone: Tz) -> (DateTime<Utc>, DateTime<Utc>)

The half-open instant range [start, end) of the local day day.

Source: crates/calternal-plugin/src/zone.rs:132

pub fn day_start(day: NaiveDate, zone: Tz) -> DateTime<Utc>

The first instant of the local day day in zone.

Midnight does not exist on some DST change days (America/Santiago, Asia/Beirut and others move 00:00 to 01:00), so the first valid local time of the day is used. In a repeated hour the earlier instant wins. A day that a zone skipped entirely (Pacific/Apia on 2011-12-30) starts where the next day starts, so the range of that day is empty.

Source: crates/calternal-plugin/src/zone.rs:114

pub fn header_zone(headers: &HeaderMap) -> Result<Option<Tz>, InvalidZone>

The zone in the X-Calternal-Timezone header, if the request has one.

Source: crates/calternal-plugin/src/zone.rs:54

pub fn local_day(instant: DateTime<Utc>, zone: Tz) -> NaiveDate

The local date of an instant in zone.

Source: crates/calternal-plugin/src/zone.rs:98

pub fn parse_zone(name: &str) -> Result<Tz, InvalidZone>

Parse an IANA zone name. Rejects empty and oversized values before the table lookup.

Source: crates/calternal-plugin/src/zone.rs:46

pub fn request_zone(headers: &HeaderMap, root: &Root, user: &str) -> Result<Tz, InvalidZone>

resolve_zone for a request that carries its zone in the header.

Source: crates/calternal-plugin/src/zone.rs:90

pub fn resolve_zone(requested: Option<&str>, root: &Root, user: &str) -> Result<Tz, InvalidZone>

The user’s zone for one request: requested (a route’s own tz value or the header), else the user setting, else UTC. See the module docs.

Source: crates/calternal-plugin/src/zone.rs:82

pub fn settings_document_zone(settings: &[u8]) -> Option<Tz>

The timezone field of a settings document. A missing, malformed or unknown value is None: the setting is optional.

Source: crates/calternal-plugin/src/zone.rs:63

pub fn settings_zone(root: &Root, user: &str) -> Option<Tz>

The user’s timezone setting from .calternal/settings.json.

Source: crates/calternal-plugin/src/zone.rs:72

pub fn today(zone: Tz) -> NaiveDate

Today in zone.

Source: crates/calternal-plugin/src/zone.rs:103

pub const TIMEZONE_HEADER: &str

The request header that carries the client’s IANA zone.

Source: crates/calternal-plugin/src/zone.rs:27