[go: up one dir, main page]

chrono-tz 0.1.1

TimeZone implementations for rust-chrono from the IANA database
Documentation
use chrono::{Offset, TimeZone, NaiveDate, NaiveDateTime, LocalResult, Duration};
use std::fmt::{Display, Formatter, Error};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct FixedTimespan {
    pub utc_offset: i64,
    pub dst_offset: i64,
    pub name: &'static str,
}

impl Offset for FixedTimespan {
    fn local_minus_utc(&self) -> Duration {
        Duration::seconds(self.utc_offset + self.dst_offset)
    }
}

impl Display for FixedTimespan {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "{}", self.name)
    }
}

#[derive(Copy, Clone)]
pub struct FixedTimespanSet {
    pub first: FixedTimespan,
    pub rest: &'static [(i64, FixedTimespan)],
}

pub trait Timespans {
    fn this() -> Self;
    fn timespans() -> FixedTimespanSet;
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Wrap<T>(pub T);

impl<T: Timespans + Clone> TimeZone for Wrap<T> {
    type Offset = FixedTimespan;

    fn from_offset(_: &Self::Offset) -> Self { Wrap(T::this()) }

    fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset> {
        self.offset_from_local_datetime(&local.and_hms(0, 0, 0))
    }

    fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset> {
        let timestamp = local.timestamp();
        let timespans = T::timespans();
        for index in 0..timespans.rest.len() {
            let (_, FixedTimespan { utc_offset: utc_offset1, dst_offset: dst_offset1, name: name1 })
                    = if index == 0 { (0, timespans.first) } else { timespans.rest[index - 1] };
            let (start2, FixedTimespan { utc_offset: utc_offset2, dst_offset: dst_offset2, name: name2 })
                                                    = timespans.rest[index];
            let localtime1 = start2 + utc_offset1 + dst_offset1;
            let localtime2 = start2 + utc_offset2 + dst_offset2;
            if localtime1 >= timestamp && localtime2 > timestamp {
                return LocalResult::Single(
                    FixedTimespan {
                        utc_offset: utc_offset1,
                        dst_offset: dst_offset1,
                        name: name1,
                    }
                );
            } else if localtime1 >= timestamp && localtime2 <= timestamp {
                return LocalResult::Ambiguous(
                    FixedTimespan {
                        utc_offset: utc_offset1,
                        dst_offset: dst_offset1,
                        name: name1,
                    },
                    FixedTimespan {
                        utc_offset: utc_offset2,
                        dst_offset: dst_offset2,
                        name: name2,
                    },
                );
            } else if localtime1 < timestamp && localtime2 > timestamp {
                return LocalResult::None
            }
        }
        if timespans.rest.len() > 0 {
            return LocalResult::Single(timespans.rest[timespans.rest.len() - 1].1);
        } else {
            return LocalResult::Single(timespans.first);
        }
    }

    fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset {
        self.offset_from_utc_datetime(&utc.and_hms(0, 0, 0))
    }

    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset {
        let timestamp = utc.timestamp();
        let timespans = T::timespans();
        let timespan = timespans.rest.iter().rev().find(|&&(start, _)| timestamp >= start);
        if let Some(&(_, timespan)) = timespan {
            timespan
        } else {
            timespans.first
        }
    }
}