GitoxideLabs/gitoxide · error

Could not convert date to a point in time

Error message

Could not convert date to a point in time

What it means

The final step of `Fields::normalize()` converts the normalized civil datetime plus time into a `Zoned` point in time using the configured timezone; if jiff cannot perform that conversion (e.g. ambiguity/timezone resolution failure), the code raises Error 'Could not convert date to a point in time'. This indicates the calendar date is valid but cannot be anchored to a concrete instant.

Solutions

  1. Ensure a valid timezone is configured (valid TZ environment/database entry) or pass an explicit known-good `TimeZone`
  2. Catch the Error and retry with UTC as a fallback timezone
  3. Verify the gix-date/jiff timezone database feature is enabled in your build

Example fix

// before
parse_relative("2 weeks ago", None) // relies on ambient TZ
// after
parse_relative("2 weeks ago", Some(TimeZone::UTC))
Defensive patterns

Strategy: fallback

Validate before calling

let tz = gix_date::time::TimeZone::from_env().unwrap_or(TimeZone::UTC);

Try / catch

let zoned = fields.normalize().or_else(|_| normalize_in_utc());

Prevention

When it happens

Trigger: Relative-date parsing where `to_zoned(timezone)` fails for the computed date/time in the given timezone — e.g. a timezone whose database lookup fails or a local time that cannot be resolved at that date.

Common situations: Systems with missing/invalid TZ settings, or relative dates near timezone-boundary edge cases when gix-date is used to compute '3 weeks ago'-style timestamps.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/5b04812c6f1dc563. Report an issue: GitHub.

Appendix: source

Thrown at gix-date/src/parse/relative.rs:177

                time: zdt.time(),
                timezone: zdt.time_zone().clone(),
            }
        }
    }

    impl Fields {
        /// Turn the fields back into a point in time: a day beyond the end of the month rolls over into the
        /// following month. One month before May 31st is thus May 1st, a day after April 30th.
        fn normalize(&self) -> Result<Zoned, Exn<Error>> {
            let first_of_month = civil::Date::new(self.year, self.month, 1)
                .or_raise(|| Error::new(format!("Date lies out of range: {}-{:02}", self.year, self.month)))?;
            let days_beyond_first = SignedDuration::from_secs((i64::from(self.day) - 1) * 24 * 60 * 60);
            first_of_month
                .checked_add(days_beyond_first)
                .or_raise(|| Error::new(format!("Day {} lies out of range", self.day)))?
                .to_datetime(self.time)
                .to_zoned(self.timezone.clone())
                .or_raise(|| Error::new("Could not convert date to a point in time"))
        }
    }

    let now = now.ok_or(ValidationError::new("Missing current time"))?;
    let mut fields = Fields::from(now);
    for Pair { period, count, unit } in pairs {
        let err = || Error::new(format!("Couldn't parse span from '{period} {count}'"));
        match unit {
            Unit::Seconds(factor) => {
                let seconds = count
                    .checked_mul(*factor)
                    .map(SignedDuration::from_secs)
                    .ok_or_else(err)?;
                let ts = fields.normalize()?.timestamp().checked_sub(seconds).or_raise(err)?;
                fields = ts.to_zoned(fields.timezone.clone()).into();
            }
            Unit::Months(factor) => {
                let months = count.checked_mul(*factor).ok_or_else(err)?;

View on GitHub (pinned to e73179060b)