///|
// tzstr builds relativedelta: apply calendar fields, then relative seconds,
// then weekday. End seconds are first converted to standard time. This order
// differs from a POSIX footer for large DST shifts and clocks crossing midnight.
fn dateutil_transition_point(
point : @tztypes.PosixTransitionRule,
year : Int32,
standard : Int32,
daylight : Int32,
is_end : Bool,
) -> Int64 {
let seconds = point.time_of_day -
(if is_end { daylight - standard } else { 0 })
let mut day = 0
match point.date_rule {
MonthWeekDay(month, week, weekday) => {
let anchor = if week == 5 { days_in_month(year, month) } else { 1 }
let base = Int64::from_int(days_from_civil(year, month, anchor)) +
locator_floor_div64(Int64::from_int(seconds), 86400L)
let current = ((base + 4L) % 7L + 7L) % 7L
let distance = if week == 5 {
-((current.to_int() - weekday + 7) % 7)
} else {
(weekday - current.to_int() + 7) % 7 + 7 * (week - 1)
}
let remainder = Int64::from_int(seconds) -
locator_floor_div64(Int64::from_int(seconds), 86400L) * 86400L
return (base + Int64::from_int(distance)) * 86400L +
remainder -
Int64::from_int(standard)
}
JulianNoLeap(value) | DayOfYear(value) => {
let yday = match point.date_rule {
DayOfYear(_) => value + 1
_ => value
}
let limits = [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366]
let mut month = 1
while month < 12 && yday > limits[month - 1] {
month += 1
}
let previous = if month == 1 { 0 } else { limits[month - 2] }
day = days_from_civil(
year,
month,
Int32::min(yday - previous, days_in_month(year, month)),
)
if point.date_rule is DayOfYear(_) &&
yday > 59 &&
month > 2 &&
is_leap_year(year) {
day -= 1
}
}
}
Int64::from_int(day) * 86400L + Int64::from_int(seconds - standard)
}
///|
fn dateutil_posix_info(
rule : @tztypes.PosixTzRule,
seconds : Int64,
) -> (Int32, String?) {
match (rule.start_rule, rule.end_rule, rule.dst_offset, rule.dst_abbr) {
(Some(start), Some(end), Some(dst), Some(name)) => {
let (year, _, _) = civil_from_days(
locator_floor_div64(seconds, 86400L).to_int(),
)
let first = dateutil_transition_point(
start,
year,
rule.std_offset,
dst,
false,
)
let last = dateutil_transition_point(
end,
year,
rule.std_offset,
dst,
true,
)
let daylight = if first < last {
seconds >= first && seconds < last
} else {
!(seconds >= last && seconds < first)
}
if daylight {
(dst, Some(name))
} else {
(rule.std_offset, Some(rule.std_abbr))
}
}
_ => (rule.std_offset, Some(rule.std_abbr))
}
}
///|
// tzrangebase.utcoffset evaluates its standard-wall transition labels directly.
// In negative DST this differs from solving for a valid UTC instant: its gap
// keeps the old DST offset and its reverse transition has no fold override.
fn dateutil_posix_local_info(
rule : @tztypes.PosixTzRule,
value : DateTime,
fold : Bool,
) -> (Int32, String?) {
match (rule.start_rule, rule.end_rule, rule.dst_offset, rule.dst_abbr) {
(Some(start), Some(end), Some(dst), Some(name)) => {
let first = dateutil_transition_point(
start,
value.year,
rule.std_offset,
dst,
false,
) +
Int64::from_int(rule.std_offset)
let last = dateutil_transition_point(
end,
value.year,
rule.std_offset,
dst,
true,
) +
Int64::from_int(rule.std_offset)
let wall = locator_microseconds(value)
let first = first * 1000000L
let last = last * 1000000L
let mut daylight = if first < last {
wall >= first && wall < last
} else {
!(wall >= last && wall < first)
}
if !daylight &&
wall >= last &&
wall < last + Int64::from_int(dst - rule.std_offset) * 1000000L {
daylight = !fold
}
if daylight {
(dst, Some(name))
} else {
(rule.std_offset, Some(rule.std_abbr))
}
}
_ => (rule.std_offset, Some(rule.std_abbr))
}
}