///|
fn ParserFields::has_value(self : ParserFields) -> Bool {
self.year is Some(_) ||
self.month is Some(_) ||
self.day is Some(_) ||
self.weekday is Some(_) ||
self.hour is Some(_) ||
self.minute is Some(_) ||
self.second is Some(_) ||
self.microsecond is Some(_) ||
self.tzname is Some(_) ||
self.tzoffset is Some(_) ||
self.ampm is Some(_)
}
///|
// datetime.replace preserves a default's fold. Recover that distinction from
// its exact rule-zone instant, and select the later target candidate only
// when the default really represented the later side of a repeated wall time.
fn parser_replace_zone(
value : DateTime,
fallback : DateTime,
zone : DateTimeZone,
) -> DateTime raise DateError {
let earliest = zone.resolve_arithmetic_local(value)
if zone.resolve_arithmetic_local(fallback).offset_seconds ==
fallback.offset_seconds {
return earliest
}
let mut latest : Int64? = None
let mut selected = earliest
for offset in zone.offsets() {
let instant = locator_microseconds(value) -
Int64::from_int(offset) * 1_000_000L
let (active, name) = zone.offset_info_at_microseconds(instant) catch {
_ => continue
}
if active == offset && (latest is None || instant > latest.unwrap()) {
latest = Some(instant)
selected = arithmetic_zoned_date(value, zone, offset, name)
}
}
selected
}
///|
// Field replacement keeps omitted time components and the default's zone.
// Only an omitted day is clamped to month end; an explicit invalid day fails.
fn parser_build_naive(
fields : ParserFields,
fallback : DateTime,
) -> DateTime raise DateError {
if !fields.has_value() {
raise DateError::InvalidDate(message="string does not contain a date")
}
let year = fields.year.unwrap_or(fallback.year)
let month = fields.month.unwrap_or(fallback.month)
let day = fields.day.unwrap_or(
Int32::min(fallback.day, days_in_month(year, month)),
)
let value = datetime(
year,
month,
day,
hour=fields.hour.unwrap_or(fallback.hour),
minute=fields.minute.unwrap_or(fallback.minute),
second=fields.second.unwrap_or(fallback.second),
microsecond=fields.microsecond.unwrap_or(fallback.microsecond),
offset_seconds=fallback.offset_seconds,
)
let value = match fallback.timezone {
Some(zone) => parser_replace_zone(value, fallback, zone)
None => value
}
if fields.day is None && fields.weekday is Some(weekday) {
value.add_timedelta(timedelta(days=(weekday - value.weekday() + 7) % 7))
} else {
value
}
}