use crate::model::{OutOfRangeError, ParseDurationError};
use std::convert::{TryFrom, TryInto};
use std::time::{SystemTime, UNIX_EPOCH};
use std::fmt::{self, Debug, Display};
use std::str::FromStr;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Duration {
    pub(crate) micros: i64,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LocalDatetime {
    pub(crate) micros: i64,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LocalDate {
    pub(crate) days: i32,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LocalTime {
    pub(crate) micros: u64,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Datetime {
    pub(crate) micros: i64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RelativeDuration {
    pub(crate) micros: i64,
    pub(crate) days: i32,
    pub(crate) months: i32,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DateDuration {
    pub(crate) days: i32,
    pub(crate) months: i32,
}
const SECS_PER_DAY : u64 = 86_400;
const MICROS_PER_DAY : u64 = SECS_PER_DAY * 1_000_000;
const DAYS_IN_400_YEARS : u32 = 400 * 365 + 97;
const MIN_YEAR : i32 = 1;
const MAX_YEAR : i32 = 9999;
const BASE_YEAR : i32 = -4800;
#[allow(dead_code)] const DAYS_IN_2000_YEARS : i32 = 5 * DAYS_IN_400_YEARS as i32;
const DAY_TO_MONTH_365 : [u32; 13] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
const DAY_TO_MONTH_366 : [u32; 13] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
const MICROS_PER_MS : i64 = 1_000;
const MICROS_PER_SECOND : i64 = MICROS_PER_MS * 1_000;
const MICROS_PER_MINUTE : i64 = MICROS_PER_SECOND * 60;
const MICROS_PER_HOUR : i64 = MICROS_PER_MINUTE * 60;
impl Duration {
    pub const MIN : Duration = Duration { micros: i64::MIN };
    pub const MAX : Duration = Duration { micros: i64::MAX };
    pub fn from_micros(micros: i64) -> Duration {
        Duration { micros }
    }
    pub fn to_micros(self) -> i64 {
        self.micros
    }
    pub fn is_positive(&self) -> bool {
        self.micros.is_positive()
    }
    pub fn is_negative(&self) -> bool {
        self.micros.is_negative()
    }
    pub fn abs_duration(&self) -> std::time::Duration {
        if self.micros.is_negative() {
            return std::time::Duration::from_micros(
                u64::MAX - self.micros as u64 + 1);
        } else {
            return std::time::Duration::from_micros(self.micros as u64);
        }
    }
    fn try_from_pg_simple_format(input: &str) -> Result<Self, ParseDurationError> {
        let mut split = input.trim_end().splitn(3, ":");
        let mut value: i64 = 0;
        let negative;
        let mut pos: usize = 0;
        {
            let hour_str = split
                .next()
                .filter(|s| s.len() > 0)
                .ok_or_else(|| ParseDurationError::new(
                    "EOF met, expecting `+`, `-` or int")
                    .not_final()
                    .pos(input.len())
                )?;
            pos += hour_str.len() - 1;
            let hour_str = hour_str.trim_start();
            let hour = hour_str
                .strip_prefix("-")
                .unwrap_or(hour_str)
                .parse::<i32>()
                .map_err(|e|
                    ParseDurationError::from(e).not_final().pos(pos)
                )?;
            negative = hour_str.starts_with("-");
            value += (hour.abs() as i64) * MICROS_PER_HOUR;
        }
        {
            pos += 1;
            let minute_str = split
                .next()
                .ok_or_else(||
                    ParseDurationError::new("EOF met, expecting `:`")
                        .not_final()
                        .pos(pos)
                )?;
            if minute_str.len() > 0 {
                pos += minute_str.len();
                let minute = minute_str
                    .parse::<u8>()
                    .map_err(|e| ParseDurationError::from(e).pos(pos))
                    .and_then(|m| {
                        if m <= 59 {
                            Ok(m)
                        } else {
                            Err(ParseDurationError::new(
                                "minutes value out of range")
                                .pos(pos)
                            )
                        }
                    })?;
                value += (minute as i64) * MICROS_PER_MINUTE;
            }
        }
        if let Some(remaining) = split.last() {
            pos += 1;
            let mut sec_split = remaining.splitn(2, ".");
            {
                let second_str = sec_split.next().unwrap();
                pos += second_str.len();
                let second = second_str
                    .parse::<u8>()
                    .map_err(|e| ParseDurationError::from(e).pos(pos))
                    .and_then(|s| {
                        if s <= 59 {
                            Ok(s)
                        } else {
                            Err(ParseDurationError::new(
                                "seconds value out of range")
                                .pos(pos)
                            )
                        }
                    })?;
                value += (second as i64) * MICROS_PER_SECOND;
            }
            if let Some(sub_sec_str) = sec_split.last() {
                pos += 1;
                for (i, c) in sub_sec_str.char_indices() {
                    let d = c
                        .to_digit(10)
                        .ok_or_else(||
                            ParseDurationError::new("not a digit")
                                .pos(pos + i + 1)
                        )?;
                    if i < 6 {
                        value += (d * 10_u32.pow((5 - i) as u32)) as i64;
                    } else {
                        if d >= 5 {
                            value += 1;
                        }
                        break;
                    }
                }
            }
        }
        if negative {
            value = -value;
        }
        Ok(Self { micros: value })
    }
    fn try_from_iso_format(input: &str) -> Result<Self, ParseDurationError> {
        if let Some(input) = input.strip_prefix("PT") {
            let mut pos = 2;
            let mut result = 0;
            let mut parts = input.split_inclusive(|c: char| c.is_alphabetic());
            let mut current = parts.next();
            if let Some(part) = current {
                if let Some(hour_str) = part.strip_suffix("H") {
                    let hour = hour_str
                        .parse::<i32>()
                        .map_err(|e| ParseDurationError::from(e)
                            .pos(pos)
                        )?;
                    result += (hour as i64) * MICROS_PER_HOUR;
                    pos += part.len();
                    current = parts.next();
                }
            }
            if let Some(part) = current {
                if let Some(minute_str) = part.strip_suffix("M") {
                    let minute = minute_str
                        .parse::<i32>()
                        .map_err(|e| ParseDurationError::from(e)
                            .pos(pos)
                        )?;
                    result += (minute as i64) * MICROS_PER_MINUTE;
                    pos += part.len();
                    current = parts.next();
                }
            }
            if let Some(part) = current {
                if let Some(second_str) = part.strip_suffix("S") {
                    let (second_str, subsec_str) = second_str
                        .split_once('.')
                        .map(|(sec, sub)|
                            (sec, sub.get(..6).or_else(||Some(sub))))
                        .unwrap_or_else(|| (second_str, None));
                    let second = second_str
                        .parse::<i32>()
                        .map_err(|e| ParseDurationError::from(e)
                            .pos(pos)
                        )?;
                    result += (second as i64) * MICROS_PER_SECOND;
                    pos += second_str.len() + 1;
                    if let Some(subsec_str) = subsec_str {
                        let subsec = subsec_str
                            .parse::<i32>()
                            .map_err(|e| ParseDurationError::from(e)
                                .pos(pos)
                            )?;
                        result += (subsec as i64) * 10_i64.pow(
                            (6 - subsec_str.len()) as u32
                        ) * if second < 0 { -1 } else { 1 };
                        pos += subsec_str.len()
                    }
                    current = parts.next();
                }
            }
            if current.is_some() {
                Err(ParseDurationError::new("expecting EOF").pos(pos))
            } else {
                Ok(Self { micros: result })
            }
        } else {
            Err(ParseDurationError::new("not ISO format").not_final())
        }
    }
    fn get_pg_format_value(
        input: &str, start: usize, end: usize
    ) -> Result<i64, ParseDurationError> {
        if let Some(val) = input.get(start..end) {
            match val.parse::<i32>() {
                Ok(v) => Ok(v as i64),
                Err(e) => Err(
                    ParseDurationError::from(e).pos(end.saturating_sub(1))
                ),
            }
        } else {
            Err(ParseDurationError::new("expecting value").pos(end))
        }
    }
    fn try_from_pg_format(input: &str) -> Result<Self, ParseDurationError> {
        enum Expect {
            Numeric { begin: usize },
            Alphabetic { begin: usize, numeric: i64 },
            Whitespace { numeric: Option<i64> }
        }
        let mut seen = Vec::new();
        let mut get_unit = |start: usize, end: usize, default: Option<&str>| {
            input
                .get(start..end)
                .or(default)
                .and_then(|u| match u.to_lowercase().as_str() {
                    "h"|"hr"|"hrs"|"hour"|"hours" => Some(MICROS_PER_HOUR),
                    "m"|"min"|"mins"
                        |"minute"|"minutes" => Some(MICROS_PER_MINUTE),
                    "ms"|"millisecon"|"millisecons"
                        |"millisecond"|"milliseconds" => Some(MICROS_PER_MS),
                    "us"|"microsecond"|"microseconds" => Some(1),
                    "s"|"sec"|"secs"|"second"
                        |"seconds" => Some(MICROS_PER_SECOND),
                    _ => None,
                })
                .ok_or_else(||
                    ParseDurationError::new("unknown unit").pos(start)
                )
                .and_then(|u| {
                    if seen.contains(&u) {
                        Err(ParseDurationError::new("specified more than once")
                            .pos(start))
                    } else {
                        seen.push(u.clone());
                        Ok(u)
                    }
                })
        };
        let mut state = Expect::Whitespace { numeric: None };
        let mut result = 0;
        for (pos, c) in input.char_indices() {
            let is_whitespace = c.is_whitespace();
            let is_numeric = c.is_numeric() || c == '+' || c == '-';
            let is_alphabetic = c.is_alphabetic();
            if !(is_whitespace || is_numeric || is_alphabetic) {
                return Err(
                    ParseDurationError::new("unexpected character").pos(pos)
                )
            }
            match state {
                Expect::Numeric { begin } if !is_numeric => {
                    let numeric = Self::get_pg_format_value(
                        input, begin, pos
                    )?;
                    if is_alphabetic {
                        state = Expect::Alphabetic {
                            begin: pos,
                            numeric,
                        };
                    } else {
                        state = Expect::Whitespace {
                            numeric: Some(numeric),
                        };
                    }
                }
                Expect::Alphabetic { begin, numeric } if !is_alphabetic => {
                    result += numeric * get_unit(begin, pos, None)?;
                    if is_numeric {
                        state = Expect::Numeric { begin: pos };
                    } else {
                        state = Expect::Whitespace { numeric: None };
                    }
                }
                Expect::Whitespace { numeric: None } if !is_whitespace => {
                    if is_numeric {
                        state = Expect::Numeric { begin: pos };
                    } else {
                        return Err(
                            ParseDurationError::new(
                                "expecting whitespace or numeric")
                                .pos(pos)
                        )
                    }
                }
                Expect::Whitespace {
                    numeric: Some(numeric)
                } if !is_whitespace => {
                    if is_alphabetic {
                        state = Expect::Alphabetic { begin: pos, numeric };
                    } else {
                        return Err(ParseDurationError::new(
                            "expecting whitespace or alphabetic").pos(pos))
                    }
                }
                _ => {}
            }
        }
        match state {
            Expect::Numeric { begin } => {
                result += Self::get_pg_format_value(
                    input, begin, input.len()
                )? * MICROS_PER_SECOND;
            }
            Expect::Alphabetic { begin, numeric } => {
                result += numeric * get_unit(begin, input.len(), Some("s"))?;
            }
            Expect::Whitespace { numeric: Some(numeric) } => {
                result += numeric * MICROS_PER_SECOND;
            }
            _ => {}
        }
        Ok(Self { micros: result })
    }
}
impl FromStr for Duration {
    type Err = ParseDurationError;
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        if let Ok(seconds) = input.trim().parse::<i64>() {
            seconds
                .checked_mul(MICROS_PER_SECOND)
                .map(|micros| Self::from_micros(micros))
                .ok_or_else(|| Self::Err::new("seconds value out of range")
                    .pos(input.len() - 1))
        } else {
            Self::try_from_pg_simple_format(input)
                .or_else(|e|
                    if e.is_final {
                        Err(e)
                    } else {
                        Self::try_from_iso_format(input)
                    }
                )
                .or_else(|e|
                    if e.is_final {
                        Err(e)
                    } else {
                        Self::try_from_pg_format(input)
                    }
                )
        }
    }
}
impl LocalDatetime {
    pub const MIN: LocalDatetime = LocalDatetime { micros: -63082281600000000 };
    pub const MAX: LocalDatetime = LocalDatetime { micros: 252455615999999999 };
    pub(crate) fn from_postgres_micros(micros: i64)
        -> Result<LocalDatetime, OutOfRangeError>
    {
        if micros < Self::MIN.micros || micros > Self::MAX.micros {
            return Err(OutOfRangeError);
        }
        Ok(LocalDatetime { micros })
    }
    #[deprecated(
        since="0.5.0",
        note="use Datetime::try_from_unix_micros(v).into() instead",
    )]
    pub fn from_micros(micros: i64) -> LocalDatetime {
        Self::from_postgres_micros(micros).expect(&format!(
            "LocalDatetime::from_micros({}) is outside the valid datetime range",
             micros))
    }
    #[deprecated(
        since="0.5.0",
        note="use .to_utc().to_unix_micros() instead",
    )]
    pub fn to_micros(self) -> i64 {
        self.micros
    }
    pub fn new(date: LocalDate, time: LocalTime) -> LocalDatetime {
        let micros = date.to_days() as i64 * MICROS_PER_DAY as i64 +
            time.to_micros() as i64;
        LocalDatetime { micros }
    }
    pub fn date(self) -> LocalDate {
        LocalDate::from_days(self.micros.wrapping_div_euclid(MICROS_PER_DAY as i64) as i32)
    }
    pub fn time(self) -> LocalTime {
        LocalTime::from_micros(self.micros.wrapping_rem_euclid(MICROS_PER_DAY as i64) as u64)
    }
    pub fn to_utc(self) -> Datetime {
        Datetime { micros: self.micros }
    }
}
impl From<Datetime> for LocalDatetime {
    fn from(d: Datetime) -> LocalDatetime {
        return LocalDatetime { micros: d.micros }
    }
}
impl Display for LocalDatetime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} {}", self.date(), self.time())
    }
}
impl Debug for LocalDatetime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}T{}", self.date(), self.time())
    }
}
impl LocalTime {
    pub const MIN : LocalTime = LocalTime { micros: 0 };
    pub const MIDNIGHT : LocalTime = LocalTime { micros: 0 };
    pub const MAX : LocalTime = LocalTime { micros: MICROS_PER_DAY - 1 };
    pub(crate) fn try_from_micros(micros: u64) -> Result<LocalTime, OutOfRangeError> {
        if micros < MICROS_PER_DAY {
            Ok(LocalTime { micros: micros })
        } else {
             Err(OutOfRangeError)
        }
    }
    pub fn from_micros(micros: u64) -> LocalTime {
        Self::try_from_micros(micros).ok().expect("LocalTime is out of range")
    }
    pub fn to_micros(self) -> u64 {
        self.micros
    }
    fn to_hmsu(self) -> (u8, u8, u8, u32) {
        let micros = self.micros;
        let microsecond = (micros % 1_000_000) as u32;
        let micros = micros / 1_000_000;
        let second = (micros % 60) as u8;
        let micros = micros / 60;
        let minute = (micros % 60) as u8;
        let micros = micros / 60;
        let hour = (micros % 24) as u8;
        let micros = micros / 24;
        debug_assert_eq!(0, micros);
        (hour, minute, second, microsecond)
    }
    #[cfg(test)] fn from_hmsu(hour: u8, minute: u8, second:u8, microsecond: u32) -> LocalTime {
        assert!(microsecond < 1000_000);
        assert!(second < 60);
        assert!(minute < 60);
        assert!(hour < 24);
        let micros =
        microsecond as u64
        + 1000_000 * (second as u64
             + 60 * (minute as u64
                + 60 * (hour as u64)));
        LocalTime::from_micros(micros)
    }
}
impl Display for LocalTime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}
impl Debug for LocalTime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (hour, minute, second, microsecond) = self.to_hmsu();
        write!(f, "{:02}:{:02}:{:02}", hour, minute, second)?;
        if microsecond != 0 {
            if microsecond % 1000 == 0 {
                write!(f, ".{:03}", microsecond / 1000)?;
            } else {
                write!(f, ".{:06}", microsecond)?;
            }
        };
        Ok(())
    }
}
impl LocalDate {
    pub const MIN : LocalDate = LocalDate { days: -730119 }; pub const MAX : LocalDate = LocalDate { days: 2921939 }; pub const UNIX_EPOCH : LocalDate = LocalDate { days: -(30 * 365 + 7) }; fn try_from_days(days: i32) -> Result<LocalDate, OutOfRangeError> {
        if days < Self::MIN.days || days > Self::MAX.days {
            return Err(OutOfRangeError);
        }
        Ok(LocalDate { days })
    }
    pub fn from_days(days: i32) -> LocalDate {
        Self::try_from_days(days)
            .expect(&format!("LocalDate::from_days({}) is outside the valid date range", days))
    }
    pub fn to_days(self) -> i32 {
        self.days
    }
    pub fn from_ymd(year:i32, month: u8, day:u8) -> LocalDate {
        Self::try_from_ymd(year, month, day).expect(&format!(
            "invalid date {:04}-{:02}-{:02}",
            year, month, day))
    }
    fn try_from_ymd(year:i32, month: u8, day:u8) -> Result<LocalDate, OutOfRangeError> {
        if day < 1 || day > 31 {
            return Err(OutOfRangeError);
        }
        if month < 1 || month > 12 {
            return Err(OutOfRangeError);
        }
        if year < MIN_YEAR || year > MAX_YEAR {
           return Err(OutOfRangeError);
        }
        let passed_years = (year - BASE_YEAR - 1) as u32;
        let days_from_year =
            365 * passed_years
            + passed_years / 4
            - passed_years / 100
            + passed_years / 400
            + 366;
        let is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
        let day_to_month =
            if is_leap_year { DAY_TO_MONTH_366 } else { DAY_TO_MONTH_365 };
        let day_in_year = (day - 1) as u32 + day_to_month[month as usize - 1];
        if day_in_year >= day_to_month[month as usize] {
            return Err(OutOfRangeError);
        }
        LocalDate::try_from_days((days_from_year + day_in_year) as i32
         - DAYS_IN_400_YEARS as i32 * ((2000 - BASE_YEAR) / 400))
    }
    fn to_ymd(self) -> (i32, u8, u8) {
        const DAYS_IN_100_YEARS : u32 = 100 * 365 + 24;
        const DAYS_IN_4_YEARS :u32 = 4 * 365 + 1;
        const DAYS_IN_1_YEAR : u32 = 365;
        const DAY_TO_MONTH_MARCH : [u32; 12] = [0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337];
        const MARCH_1 : u32 = 31 + 29;
        const MARCH_1_MINUS_BASE_YEAR_TO_POSTGRES_EPOCH : u32
            = (2000 - BASE_YEAR) as u32 / 400 * DAYS_IN_400_YEARS - MARCH_1;
        let days = (self.days as u32).wrapping_add(MARCH_1_MINUS_BASE_YEAR_TO_POSTGRES_EPOCH);
        let years400 = days / DAYS_IN_400_YEARS;
        let days = days % DAYS_IN_400_YEARS;
        let mut years100 = days / DAYS_IN_100_YEARS;
        if years100 == 4 { years100 = 3 }; let days = days - DAYS_IN_100_YEARS * years100;
        let years4 = days / DAYS_IN_4_YEARS;
        let days = days % DAYS_IN_4_YEARS;
        let mut years1 = days / DAYS_IN_1_YEAR;
        if years1 == 4 { years1 = 3 }; let days = days - DAYS_IN_1_YEAR * years1;
        let years = years1 + years4 * 4 + years100 * 100 + years400 * 400;
        let month_entry = DAY_TO_MONTH_MARCH
            .iter()
            .filter(|d| days >= **d)
            .enumerate()
            .last()
            .unwrap();
        let months = years * 12 + 2 + month_entry.0 as u32;
        let year = (months / 12) as i32 + BASE_YEAR;
        let month = (months % 12 + 1) as u8;
        let day = (days - month_entry.1 + 1) as u8;
        (year, month, day)
    }
}
impl Display for LocalDate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}
impl Debug for LocalDate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (year, month, day) = self.to_ymd();
        if year >= 10_000 { write!(f, "+")?;
        }
        if year >= 0 {
            write!(f, "{:04}-{:02}-{:02}", year, month, day)
        } else {
            write!(f, "{:05}-{:02}-{:02}", year, month, day)
        }
    }
}
impl Datetime {
    pub const MIN : Datetime = Datetime { micros: LocalDatetime::MIN.micros };
    pub const MAX : Datetime = Datetime { micros: LocalDatetime::MAX.micros };
    pub const UNIX_EPOCH : Datetime = Datetime {
        micros: LocalDate::UNIX_EPOCH.days as i64 * MICROS_PER_DAY as i64
   };
    pub fn try_from_unix_micros(micros: i64)
        -> Result<Datetime, OutOfRangeError>
    {
        Self::_from_micros(micros).ok_or(OutOfRangeError)
    }
    #[deprecated(
        since="0.5.0",
        note="use try_from_unix_micros instead",
    )]
    pub fn try_from_micros(micros: i64) -> Result<Datetime, OutOfRangeError> {
        Self::from_postgres_micros(micros)
    }
    pub(crate) fn from_postgres_micros(micros: i64)
        -> Result<Datetime, OutOfRangeError>
    {
        if micros < Self::MIN.micros || micros > Self::MAX.micros {
            return Err(OutOfRangeError);
        }
        Ok(Datetime { micros })
    }
    fn _from_micros(micros: i64) -> Option<Datetime> {
        let micros = micros.checked_add(Self::UNIX_EPOCH.micros)?;
        if micros < Self::MIN.micros || micros > Self::MAX.micros {
            return None;
        }
        Some(Datetime { micros })
    }
    #[deprecated(
        since="0.5.0",
        note="use from_unix_micros instead",
    )]
    pub fn from_micros(micros: i64) -> Datetime {
        Self::from_postgres_micros(micros).expect(&format!(
            "Datetime::from_micros({}) is outside the valid datetime range",
             micros))
    }
    pub fn from_unix_micros(micros: i64) -> Datetime {
        if let Some(result) = Self::_from_micros(micros) {
            return result
        }
        panic!("Datetime::from_micros({}) is outside the valid datetime range",
             micros);
    }
    #[deprecated(
        since="0.5.0",
        note="use to_unix_micros instead",
    )]
    pub fn to_micros(self) -> i64 {
        self.micros
    }
    pub fn to_unix_micros(self) -> i64 {
        self.micros + Datetime::UNIX_EPOCH.micros
    }
    fn postgres_epoch_unix() -> SystemTime {
        use std::time::Duration;
        UNIX_EPOCH + Duration::from_micros((-Datetime::UNIX_EPOCH.micros) as u64)
    }
}
impl Display for Datetime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} UTC", LocalDatetime { micros: self.micros })
    }
}
impl Debug for Datetime {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}Z", LocalDatetime { micros: self.micros })
    }
}
impl TryFrom<Datetime> for SystemTime {
    type Error = OutOfRangeError;
    fn try_from(value: Datetime) -> Result<Self, Self::Error> {
        use std::time::Duration;
        if value.micros > 0 {
            Datetime::postgres_epoch_unix()
                .checked_add(Duration::from_micros(value.micros as u64))
        } else {
            Datetime::postgres_epoch_unix()
                .checked_sub(Duration::from_micros((-value.micros) as u64))
        }.ok_or(OutOfRangeError)
    }
}
impl TryFrom<std::time::Duration> for Duration {
    type Error = OutOfRangeError;
    fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> {
        TryFrom::try_from(&value)
    }
}
impl TryFrom<&std::time::Duration> for Duration {
    type Error = OutOfRangeError;
    fn try_from(value: &std::time::Duration) -> Result<Self, Self::Error> {
        let secs = value.as_secs();
        let subsec_nanos = value.subsec_nanos();
        let subsec_micros = nanos_to_micros(subsec_nanos.into());
        let micros = i64::try_from(secs).ok()
            .and_then(|x| x.checked_mul(1_000_000))
            .and_then(|x| x.checked_add(subsec_micros))
            .ok_or(OutOfRangeError)?;
        Ok(Duration { micros })
    }
}
impl TryFrom<&Duration> for std::time::Duration {
    type Error = OutOfRangeError;
    fn try_from(value: &Duration) -> Result<std::time::Duration, Self::Error> {
        let micros = value.micros.try_into()
            .map_err(|_| OutOfRangeError)?;
        Ok(std::time::Duration::from_micros(micros))
    }
}
impl TryFrom<Duration> for std::time::Duration {
    type Error = OutOfRangeError;
    fn try_from(value: Duration) -> Result<std::time::Duration, Self::Error> {
        (&value).try_into()
    }
}
impl TryFrom<SystemTime> for Datetime {
    type Error = OutOfRangeError;
    fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
        match value.duration_since(UNIX_EPOCH) {
            Ok(duration) => {
                let secs = duration.as_secs();
                let subsec_nanos = duration.subsec_nanos();
                let subsec_micros = nanos_to_micros(subsec_nanos.into());
                let micros = i64::try_from(secs).ok()
                    .and_then(|x| x.checked_mul(1_000_000))
                    .and_then(|x| x.checked_add(subsec_micros))
                    .and_then(|x| x.checked_add(Datetime::UNIX_EPOCH.micros))
                    .ok_or(OutOfRangeError)?;
                if micros > Datetime::MAX.micros {
                    return Err(OutOfRangeError);
                }
                Ok(Datetime { micros })
            }
            Err(e) => {
                let mut secs = e.duration().as_secs();
                let mut subsec_nanos = e.duration().subsec_nanos();
                if subsec_nanos > 0 {
                    secs = secs.checked_add(1).ok_or(OutOfRangeError)?;
                    subsec_nanos = 1_000_000_000 - subsec_nanos;
                }
                let subsec_micros = nanos_to_micros(subsec_nanos.into());
                let micros = i64::try_from(secs).ok()
                    .and_then(|x| x.checked_mul(1_000_000))
                    .and_then(|x| Datetime::UNIX_EPOCH.micros.checked_sub(x))
                    .and_then(|x| x.checked_add(subsec_micros))
                    .ok_or(OutOfRangeError)?;
                if micros < Datetime::MIN.micros {
                    return Err(OutOfRangeError);
                }
                Ok(Datetime { micros })
            }
        }
    }
}
impl std::ops::Add<&'_ std::time::Duration> for Datetime {
    type Output = Datetime;
    fn add(self, other: &std::time::Duration) -> Datetime {
        let Ok(duration) = Duration::try_from(other) else {
            debug_assert!(false,
                "duration is out of range");
            return Datetime::MAX;
        };
        if let Some(micros) = self.micros.checked_add(duration.micros) {
            return Datetime { micros };
        } else {
            debug_assert!(false,
                "duration is out of range");
            return Datetime::MAX;
        }
    }
}
impl std::ops::Add<std::time::Duration> for Datetime {
    type Output = Datetime;
    fn add(self, other: std::time::Duration) -> Datetime {
        self + &other
    }
}
impl Display for Duration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let abs = if self.micros < 0 {
            write!(f, "-")?;
            - self.micros
        } else {
            self.micros
        };
        let (sec, micros) = (abs / 1_000_000, abs % 1_000_000);
        if micros != 0 {
            let mut fract = micros;
            let mut zeros = 0;
            while fract % 10 == 0 {
                zeros += 1;
                fract /= 10;
            }
            write!(f, "{hours}:{minutes:02}:{seconds:02}.{fract:0>fsize$}",
                hours=sec / 3600,
                minutes=sec / 60 % 60,
                seconds=sec % 60,
                fract=fract,
                fsize=6 - zeros,
            )
        } else {
            write!(f, "{}:{:02}:{:02}",
                sec / 3600, sec / 60 % 60, sec % 60)
        }
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn micros_conv() {
        let datetime = Datetime::from_unix_micros(1645681383000002);
        assert_eq!(datetime.micros, 698996583000002);
        assert_eq!(to_debug(datetime), "2022-02-24T05:43:03.000002Z");
    }
    #[test]
    fn big_duration_abs() {
        use super::Duration as Src;
        use std::time::Duration as Trg;
        assert_eq!(Src { micros: -1 }.abs_duration(), Trg::new(0, 1000));
        assert_eq!(Src { micros: -1000 }.abs_duration(), Trg::new(0, 1000000));
        assert_eq!(Src { micros: -1000000 }.abs_duration(), Trg::new(1, 0));
        assert_eq!(
            Src {
                micros: i64::min_value()
            }
            .abs_duration(),
            Trg::new(9223372036854, 775808000)
        );
    }
    #[test]
    fn local_date_from_ymd() {
        assert_eq!(0, LocalDate::from_ymd(2000, 1, 1).to_days());
        assert_eq!(-365, LocalDate::from_ymd(1999, 1, 1).to_days());
        assert_eq!(366, LocalDate::from_ymd(2001, 1, 1).to_days());
        assert_eq!(-730119, LocalDate::from_ymd(0001, 1, 1).to_days());
        assert_eq!(2921575, LocalDate::from_ymd(9999, 1, 1).to_days());
        assert_eq!(Err(OutOfRangeError), LocalDate::try_from_ymd(2001, 1, 32));
        assert_eq!(Err(OutOfRangeError), LocalDate::try_from_ymd(2001, 2, 29));
    }
    #[test]
    fn local_date_from_ymd_leap_year() {
        let days_in_month_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        let mut total_days = 0;
        let start_of_year = 365 * 4 + 1;
        for month in 1..=12 {
            let start_of_month = LocalDate::from_ymd(2004, month as u8, 1).to_days();
            assert_eq!(total_days, start_of_month - start_of_year);
            let days_in_current_month = days_in_month_leap[month - 1];
            total_days += days_in_current_month;
            let end_of_month = LocalDate::from_ymd(2004, month as u8, days_in_current_month as u8).to_days();
            assert_eq!(total_days - 1, end_of_month - start_of_year);
        }
        assert_eq!(366, total_days);
    }
    const DAYS_IN_MONTH_LEAP :[u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    #[test]
    fn local_date_from_ymd_normal_year() {
        let mut total_days = 0;
        let start_of_year = 365 + 1;
        for month in 1..=12 {
            let start_of_month = LocalDate::from_ymd(2001, month as u8, 1).to_days();
            assert_eq!(total_days, start_of_month - start_of_year);
            let days_in_current_month = DAYS_IN_MONTH_LEAP[month - 1];
            total_days += days_in_current_month as i32;
            let end_of_month = LocalDate::from_ymd(2001, month as u8, days_in_current_month as u8).to_days();
            assert_eq!(total_days - 1, end_of_month - start_of_year);
        }
        assert_eq!(365, total_days);
    }
    pub const CHRONO_MAX_YEAR : i32 = 262_143;
    fn extended_test_dates() -> impl Iterator<Item=(i32, u8, u8)> {
        const YEARS :[i32; 36]= [
            1,
            2,
            1000,
            1969,
            1970, 1971,
            1999,
            2000, 2001,
            2002,
            2003,
            2004,
            2008,
            2009,
            2010,
            2100,
            2200,
            2300,
            2400,
            9000,
            9999,
            10_000,
            10_001,
            11_000,
            20_000,
            100_000,
            200_000,
            CHRONO_MAX_YEAR - 1,
            CHRONO_MAX_YEAR,
            CHRONO_MAX_YEAR + 1,
            MAX_YEAR - 1000,
            MAX_YEAR - 31,
            MAX_YEAR - 30, MAX_YEAR - 29, MAX_YEAR - 1,
            MAX_YEAR,
        ];
        const MONTHS : std::ops::RangeInclusive<u8>= 1u8..=12;
        const DAYS :[u8; 6] = [1u8, 13, 28, 29, 30, 31];
        let dates = MONTHS
            .flat_map(|month| DAYS.iter().map(move |day| (month, *day)));
        YEARS
            .iter()
            .flat_map(move|year| dates.clone().map(move |date| (*year, date.0, date.1)))
    }
    pub fn valid_test_dates() -> impl Iterator<Item=(i32, u8, u8)> {
        extended_test_dates().filter(|date|
                LocalDate::try_from_ymd(date.0, date.1, date.2).is_ok())
    }
    pub fn test_times() -> impl Iterator<Item=u64> {
        const TIMES: [u64; 7] = [
            0,
            10,
            10_020,
            12345 * 1000_000,
            12345 * 1001_000,
            12345 * 1001_001,
            MICROS_PER_DAY - 1,
        ];
        TIMES.iter().copied()
    }
    #[test]
    fn check_test_dates() {
        assert!(valid_test_dates().count() > 1000);
    }
    #[test]
    fn local_date_ymd_roundtrip() {
        for (year, month, day) in valid_test_dates() {
            let date = LocalDate::from_ymd(year, month, day);
            assert_eq!((year, month, day), date.to_ymd());
        }
    }
    #[test]
    fn local_time_parts_roundtrip() {
        for time in test_times() {
            let expected_time = LocalTime::from_micros(time);
            let (hour, minute, second, microsecond) = expected_time.to_hmsu();
            let actual_time = LocalTime::from_hmsu(hour, minute, second, microsecond);
            assert_eq!(expected_time, actual_time);
        }
    }
    #[test]
    fn format_local_date() {
        assert_eq!("2000-01-01", LocalDate::from_days(0).to_string());
        assert_eq!("0001-01-01", LocalDate::MIN.to_string());
        assert_eq!("9999-12-31", LocalDate::MAX.to_string());
    }
    #[test]
    fn format_local_time() {
        assert_eq!("00:00:00", LocalTime::MIDNIGHT.to_string());
        assert_eq!("00:00:00.010", LocalTime::from_micros(10_000).to_string());
        assert_eq!("00:00:00.010020", LocalTime::from_micros(10_020).to_string());
        assert_eq!("23:59:59.999999", LocalTime::MAX.to_string());
    }
    pub fn to_debug<T:Debug>(x:T) -> String {
        format!("{:?}", x)
    }
    #[test]
    #[allow(deprecated)]
    fn format_local_datetime() {
        assert_eq!("2039-02-13 23:31:30.123456",
                   LocalDatetime::from_micros(1_234_567_890_123_456).to_string());
        assert_eq!("2039-02-13T23:31:30.123456",
                   to_debug(LocalDatetime::from_micros(1_234_567_890_123_456)));
        assert_eq!("0001-01-01 00:00:00",
                   LocalDatetime::MIN.to_string());
        assert_eq!("0001-01-01T00:00:00",
                   to_debug(LocalDatetime::MIN));
        assert_eq!("9999-12-31 23:59:59.999999",
                   LocalDatetime::MAX.to_string());
        assert_eq!("9999-12-31T23:59:59.999999",
                   to_debug(LocalDatetime::MAX));
    }
    #[test]
    #[allow(deprecated)]
    fn format_datetime() {
        assert_eq!("2039-02-13 23:31:30.123456 UTC",
                   Datetime::from_micros(1_234_567_890_123_456).to_string());
        assert_eq!("2039-02-13T23:31:30.123456Z",
                   to_debug(Datetime::from_micros(1_234_567_890_123_456)));
        assert_eq!("0001-01-01 00:00:00 UTC", Datetime::MIN.to_string());
        assert_eq!("0001-01-01T00:00:00Z", to_debug(Datetime::MIN));
        assert_eq!("9999-12-31 23:59:59.999999 UTC", Datetime::MAX.to_string());
        assert_eq!("9999-12-31T23:59:59.999999Z", to_debug(Datetime::MAX));
    }
    #[test]
    fn format_duration() {
        fn dur_str(msec: i64) -> String {
            Duration::from_micros(msec).to_string()
        }
        assert_eq!(dur_str(1_000_000), "0:00:01");
        assert_eq!(dur_str(1), "0:00:00.000001");
        assert_eq!(dur_str(7_015_000), "0:00:07.015");
        assert_eq!(dur_str(10_000_000__015_000), "2777:46:40.015");
        assert_eq!(dur_str(12_345_678__000_000), "3429:21:18");
    }
    #[test]
    fn parse_duration_str() {
        fn micros(input: &str) -> i64 {
            Duration::from_str(input).unwrap().micros
        }
        assert_eq!(micros(" 100   "), 100_000_000);
        assert_eq!(micros("123"), 123_000_000);
        assert_eq!(micros("-123"), -123_000_000);
        assert_eq!(micros("  20 mins 1hr "), 4800_000_000);
        assert_eq!(micros("  20 mins -1hr "), -2400_000_000);
        assert_eq!(micros("  20us  1h    20   "), 3620_000_020);
        assert_eq!(micros("  -20us  1h    20   "), 3619_999_980);
        assert_eq!(micros("  -20US  1H    20   "), 3619_999_980);
        assert_eq!(micros("1 hour 20 minutes 30 seconds 40 milliseconds 50 microseconds"), 4830_040_050);
        assert_eq!(micros("1 hour 20 minutes +30seconds 40 milliseconds -50microseconds"), 4830_039_950);
        assert_eq!(micros("1 houR  20 minutes 30SECOND 40 milliseconds 50 us"), 4830_040_050);
        assert_eq!(micros("  20 us 1H 20 minutes "), 4800_000_020);
        assert_eq!(micros("-1h"), -3600_000_000);
        assert_eq!(micros("100h"), 3600_000_000_00);
        let h12 = 12 * 3600_000_000 as i64;
        let m12 = 12 * 60_000_000 as i64;
        assert_eq!(micros("   12:12:12.2131   "), h12 + m12 + 12_213_100);
        assert_eq!(micros("-12:12:12.21313"), -(h12 + m12 + 12_213_130));
        assert_eq!(micros("-12:12:12.213134"), -(h12 + m12 + 12_213_134));
        assert_eq!(micros("-12:12:12.2131341"), -(h12 + m12 + 12_213_134));
        assert_eq!(micros("-12:12:12.2131341111111"), -(h12 + m12 + 12_213_134));
        assert_eq!(micros("-12:12:12.2131315111111"), -(h12 + m12 + 12_213_132));
        assert_eq!(micros("-12:12:12.2131316111111"), -(h12 + m12 + 12_213_132));
        assert_eq!(micros("-12:12:12.2131314511111"), -(h12 + m12 + 12_213_131));
        assert_eq!(micros("-0:12:12.2131"), -(m12 + 12_213_100));
        assert_eq!(micros("12:12"), h12 + m12);
        assert_eq!(micros("-12:12"), -(h12 + m12));
        assert_eq!(micros("-12:1:1"), -(h12 + 61_000_000));
        assert_eq!(micros("+12:1:1"), h12 + 61_000_000);
        assert_eq!(micros("-12:1:1.1234"), -(h12 + 61_123_400));
        assert_eq!(micros("1211:59:59.9999"), h12 * 100 + h12 - 100);
        assert_eq!(micros("-12:"), -h12);
        assert_eq!(micros("0"), 0);
        assert_eq!(micros("00:00:00"), 0);
        assert_eq!(micros("00:00:10.9"), 10_900_000);
        assert_eq!(micros("00:00:10.09"), 10_090_000);
        assert_eq!(micros("00:00:10.009"), 10_009_000);
        assert_eq!(micros("00:00:10.0009"), 10_000_900);
        assert_eq!(micros("00:00:00.5"), 500_000);
        assert_eq!(micros("  +00005"), 5_000_000);
        assert_eq!(micros("  -00005"), -5_000_000);
        assert_eq!(micros("PT"), 0);
        assert_eq!(micros("PT1H1M1S"), 3661_000_000);
        assert_eq!(micros("PT1M1S"), 61_000_000);
        assert_eq!(micros("PT1S"), 1_000_000);
        assert_eq!(micros("PT1H1S"), 3601_000_000);
        assert_eq!(micros("PT1H1M1.1S"), 3661_100_000);
        assert_eq!(micros("PT1H1M1.01S"), 3661_010_000);
        assert_eq!(micros("PT1H1M1.10S"), 3661_100_000);
        assert_eq!(micros("PT1H1M1.1234567S"), 3661_123_456);
        assert_eq!(micros("PT1H1M1.1234564S"), 3661_123_456);
        assert_eq!(micros("PT-1H1M1.1S"), -3538_900_000);
        assert_eq!(micros("PT+1H-1M1.1S"), 3541_100_000);
        assert_eq!(micros("PT1H+1M-1.1S"), 3658_900_000);
        fn assert_error(input: &str, expected_pos: usize, pat: &str) {
            let ParseDurationError {
                pos,
                message,
                is_final: _,
            } = Duration::from_str(input).unwrap_err();
            assert_eq!(pos, expected_pos);
            assert!(
                message.contains(pat),
                "`{}` not found in `{}`",
                pat,
                message,
            );
        }
        assert_error("blah", 0, "numeric");
        assert_error("!", 0, "unexpected");
        assert_error("-", 0, "invalid digit");
        assert_error("+", 0, "invalid digit");
        assert_error("  20 us 1H 20 30 minutes ", 14, "alphabetic");
        assert_error("   12:12:121.2131   ", 11, "seconds");
        assert_error("   12:60:21.2131   ", 7, "minutes");
        assert_error("  20us 20   1h       ", 12, "alphabetic");
        assert_error("  20us $ 20   1h       ", 7, "unexpected");
        assert_error(
            "1 houR  20 minutes 30SECOND 40 milliseconds 50 uss", 47, "unit"
        );
        assert_error("PT1M1H", 4, "EOF");
        assert_error("PT1S1M", 4, "EOF");
    }
    #[test]
    fn add_duration_rounding() {
        assert_eq!(
            Datetime::UNIX_EPOCH + std::time::Duration::new(17, 500),
            Datetime::UNIX_EPOCH + std::time::Duration::new(17, 0),
        );
        assert_eq!(
            Datetime::UNIX_EPOCH + std::time::Duration::new(12345, 1500),
            Datetime::UNIX_EPOCH + std::time::Duration::new(12345, 2000),
        );
    }
}
impl RelativeDuration {
    pub fn try_from_years(years: i32)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: years.checked_mul(12).ok_or(OutOfRangeError)?,
            days: 0,
            micros: 0,
        })
    }
    pub fn from_years(years: i32) -> RelativeDuration {
        RelativeDuration::try_from_years(years).unwrap()
    }
    pub fn try_from_months(months: i32)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: months,
            days: 0,
            micros: 0,
        })
    }
    pub fn from_months(months: i32) -> RelativeDuration {
        RelativeDuration::try_from_months(months).unwrap()
    }
    pub fn try_from_days(days: i32)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days,
            micros: 0,
        })
    }
    pub fn from_days(days: i32) -> RelativeDuration {
        RelativeDuration::try_from_days(days).unwrap()
    }
    pub fn try_from_hours(hours: i64)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days: 0,
            micros: hours.checked_mul(3_600_000_000).ok_or(OutOfRangeError)?,
        })
    }
    pub fn from_hours(hours: i64) -> RelativeDuration {
        RelativeDuration::try_from_hours(hours).unwrap()
    }
    pub fn try_from_minutes(minutes: i64)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days: 0,
            micros: minutes.checked_mul(60_000_000).ok_or(OutOfRangeError)?,
        })
    }
    pub fn from_minutes(minutes: i64) -> RelativeDuration {
        RelativeDuration::try_from_minutes(minutes).unwrap()
    }
    pub fn try_from_secs(secs: i64)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days: 0,
            micros: secs.checked_mul(1_000_000).ok_or(OutOfRangeError)?,
        })
    }
    pub fn from_secs(secs: i64) -> RelativeDuration {
        RelativeDuration::try_from_secs(secs).unwrap()
    }
    pub fn try_from_millis(millis: i64)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days: 0,
            micros: millis.checked_mul(1_000).ok_or(OutOfRangeError)?,
        })
    }
    pub fn from_millis(millis: i64) -> RelativeDuration {
        RelativeDuration::try_from_millis(millis).unwrap()
    }
    pub fn try_from_micros(micros: i64)
        -> Result<RelativeDuration, OutOfRangeError>
    {
        Ok(RelativeDuration {
            months: 0,
            days: 0,
            micros,
        })
    }
    pub fn from_micros(micros: i64) -> RelativeDuration {
        RelativeDuration::try_from_micros(micros).unwrap()
    }
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(RelativeDuration {
            months: self.months.checked_add(other.months)?,
            days: self.days.checked_add(other.days)?,
            micros: self.micros.checked_add(other.micros)?,
        })
    }
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(RelativeDuration {
            months: self.months.checked_sub(other.months)?,
            days: self.days.checked_sub(other.days)?,
            micros: self.micros.checked_sub(other.micros)?,
        })
    }
}
impl std::ops::Add for RelativeDuration {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        RelativeDuration {
            months: self.months + other.months,
            days: self.days + other.days,
            micros: self.micros + other.micros,
        }
    }
}
impl std::ops::Sub for RelativeDuration {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        RelativeDuration {
            months: self.months - other.months,
            days: self.days - other.days,
            micros: self.micros - other.micros,
        }
    }
}
impl Display for RelativeDuration {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.months == 0 && self.days == 0 && self.micros == 0 {
            return write!(f, "PT0S");
        }
        write!(f, "P")?;
        if self.months.abs() >= 12 {
            write!(f, "{}Y", self.months / 12)?;
        }
        if (self.months % 12).abs() > 0 {
            write!(f, "{}M", self.months % 12)?;
        }
        if self.days.abs() > 0 {
            write!(f, "{}D", self.days)?;
        }
        if self.micros.abs() > 0 {
            write!(f, "T")?;
            if self.micros.abs() >= 3_600_000_000 {
                write!(f, "{}H", self.micros / 3_600_000_000)?;
            }
            let minutes = self.micros % 3_600_000_000;
            if minutes.abs() >= 60_000_000 {
                write!(f, "{}M", minutes / 60_000_000)?;
            }
            let seconds = minutes % 60_000_000;
            if seconds.abs() >= 1_000_000 {
                write!(f, "{}", seconds / 1_000_000)?;
            }
            let micros = seconds % 1_000_000;
            if micros.abs() > 0 {
                let mut buf = [0u8; 6];
                let text = {
                    use std::io::{Write, Cursor};
                    let mut cur = Cursor::new(&mut buf[..]);
                    write!(cur, "{:06}", micros.abs()).unwrap();
                    let mut len = buf.len();
                    while buf[len-1] == b'0' {
                        len -= 1;
                    }
                    std::str::from_utf8(&buf[..len]).unwrap()
                };
                write!(f, ".{}", text)?;
            }
            if seconds.abs() > 0 {
                write!(f, "S")?;
            }
        }
        Ok(())
    }
}
#[test]
fn relative_duration_display() {
    let dur = RelativeDuration::from_years(2) +
            RelativeDuration::from_months(56) +
            RelativeDuration::from_days(-16) +
            RelativeDuration::from_hours(48) +
            RelativeDuration::from_minutes(245) +
            RelativeDuration::from_secs(7) +
            RelativeDuration::from_millis(600);
    assert_eq!(dur.to_string(), "P6Y8M-16DT52H5M7.6S");
    let dur = RelativeDuration::from_years(2) +
            RelativeDuration::from_months(-56) +
            RelativeDuration::from_days(-16) +
            RelativeDuration::from_minutes(-245) +
            RelativeDuration::from_secs(7) +
            RelativeDuration::from_millis(600);
    assert_eq!(dur.to_string(), "P-2Y-8M-16DT-4H-4M-52.4S");
    let dur = RelativeDuration::from_years(1);
    assert_eq!(dur.to_string(), "P1Y");
    let dur = RelativeDuration::from_months(1);
    assert_eq!(dur.to_string(), "P1M");
    let dur = RelativeDuration::from_hours(1);
    assert_eq!(dur.to_string(), "PT1H");
    let dur = RelativeDuration::from_minutes(1);
    assert_eq!(dur.to_string(), "PT1M");
    let dur = RelativeDuration::from_secs(1);
    assert_eq!(dur.to_string(), "PT1S");
}
impl DateDuration {
    pub fn try_from_years(years: i32)
        -> Result<DateDuration, OutOfRangeError>
    {
        Ok(DateDuration {
            months: years.checked_mul(12).ok_or(OutOfRangeError)?,
            days: 0,
        })
    }
    pub fn from_years(years: i32) -> DateDuration {
        DateDuration::try_from_years(years).unwrap()
    }
    pub fn try_from_months(months: i32)
        -> Result<DateDuration, OutOfRangeError>
    {
        Ok(DateDuration {
            months: months,
            days: 0,
        })
    }
    pub fn from_months(months: i32) -> DateDuration {
        DateDuration::try_from_months(months).unwrap()
    }
    pub fn try_from_days(days: i32)
        -> Result<DateDuration, OutOfRangeError>
    {
        Ok(DateDuration {
            months: 0,
            days,
        })
    }
    pub fn from_days(days: i32) -> DateDuration {
        DateDuration::try_from_days(days).unwrap()
    }
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(DateDuration {
            months: self.months.checked_add(other.months)?,
            days: self.days.checked_add(other.days)?,
        })
    }
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(DateDuration {
            months: self.months.checked_sub(other.months)?,
            days: self.days.checked_sub(other.days)?,
        })
    }
}
impl std::ops::Add for DateDuration {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        DateDuration {
            months: self.months + other.months,
            days: self.days + other.days,
        }
    }
}
impl std::ops::Sub for DateDuration {
    type Output = Self;
    fn sub(self, other: Self) -> Self {
        DateDuration {
            months: self.months - other.months,
            days: self.days - other.days,
        }
    }
}
impl Display for DateDuration {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.months == 0 && self.days == 0 {
            return write!(f, "PT0D"); }
        write!(f, "P")?;
        if self.months.abs() >= 12 {
            write!(f, "{}Y", self.months / 12)?;
        }
        if (self.months % 12).abs() > 0 {
            write!(f, "{}M", self.months % 12)?;
        }
        if self.days.abs() > 0 {
            write!(f, "{}D", self.days)?;
        }
        Ok(())
    }
}
fn nanos_to_micros(nanos: i64) -> i64 {
   let mut micros = nanos / 1000;
   let remainder = nanos % 1000;
   if remainder == 500 && micros % 2 == 1 || remainder > 500 {
       micros += 1;
   }
   return micros;
}
#[cfg(feature = "chrono")]
mod chrono_interop {
    use super::*;
    use chrono::naive::{NaiveDate, NaiveDateTime, NaiveTime };
    use std::convert::{From, Into, TryFrom};
    type ChronoDatetime = chrono::DateTime<chrono::Utc>;
    impl From<&LocalDatetime> for NaiveDateTime {
        fn from(value: &LocalDatetime) -> NaiveDateTime {
            let timestamp_seconds = value.micros.wrapping_div_euclid(1000_000) - (Datetime::UNIX_EPOCH.micros / 1000_000);
            let timestamp_nanos = (value.micros.wrapping_rem_euclid(1000_000) * 1000) as u32;
            NaiveDateTime::from_timestamp_opt(timestamp_seconds, timestamp_nanos)
                .expect("NaiveDateTime range is bigger than LocalDatetime")
        }
    }
    impl TryFrom<&NaiveDateTime> for LocalDatetime {
        type Error = OutOfRangeError;
        fn try_from(d: &NaiveDateTime)
            -> Result<LocalDatetime, Self::Error>
        {
            let secs = d.timestamp();
            let subsec_nanos = d.timestamp_subsec_nanos();
            let subsec_micros = nanos_to_micros(subsec_nanos.into());
            let micros = secs.checked_mul(1_000_000)
                .and_then(|x| x.checked_add(subsec_micros))
                .and_then(|x| x.checked_add(Datetime::UNIX_EPOCH.micros))
                .ok_or(OutOfRangeError)?;
            if micros < LocalDatetime::MIN.micros ||
                micros > LocalDatetime::MAX.micros
            {
                return Err(OutOfRangeError)
            }
            Ok(LocalDatetime { micros })
        }
    }
    impl From<&Datetime> for ChronoDatetime {
        fn from(value: &Datetime) -> ChronoDatetime {
            use chrono::TimeZone;
            let pg_epoch = chrono::Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
                .unwrap();
            let duration = chrono::Duration::microseconds(value.micros);
            pg_epoch.checked_add_signed(duration)
                .expect("EdgeDB datetime range is smaller than Chrono's")
        }
    }
    impl From<Datetime> for ChronoDatetime {
        fn from(value: Datetime) -> ChronoDatetime {
            (&value).into()
        }
    }
    impl TryFrom<&ChronoDatetime> for Datetime {
        type Error = OutOfRangeError;
        fn try_from(value:&ChronoDatetime) -> Result<Datetime, Self::Error> {
            let min = ChronoDatetime::try_from(Datetime::MIN).unwrap();
            let duration = value.signed_duration_since(min).to_std()
                .map_err(|_| OutOfRangeError)?;
            let secs = duration.as_secs();
            let subsec_micros = nanos_to_micros(duration.subsec_nanos().into());
            let micros = i64::try_from(secs).ok()
                .and_then(|x| x.checked_mul(1_000_000))
                .and_then(|x| x.checked_add(subsec_micros))
                .and_then(|x| x.checked_add(Datetime::MIN.micros))
                .ok_or(OutOfRangeError)?;
            if micros > Datetime::MAX.micros {
                return Err(OutOfRangeError);
            }
            Ok(Datetime { micros })
        }
    }
    impl TryFrom<&NaiveDate> for LocalDate {
        type Error = OutOfRangeError;
        fn try_from(d: &NaiveDate) -> Result<LocalDate, Self::Error>
        {
            let days = chrono::Datelike::num_days_from_ce(d);
            Ok(LocalDate {
                days: days.checked_sub(DAYS_IN_2000_YEARS - 365)
                    .ok_or(OutOfRangeError)?,
            })
        }
    }
    impl From<&LocalDate> for NaiveDate {
        fn from(value: &LocalDate) -> NaiveDate {
            value.days.checked_add(DAYS_IN_2000_YEARS - 365)
            .and_then(NaiveDate::from_num_days_from_ce_opt)
            .expect("NaiveDate range is bigger than LocalDate")
        }
    }
    impl From<&LocalTime> for NaiveTime {
        fn from(value: &LocalTime) -> NaiveTime {
            NaiveTime::from_num_seconds_from_midnight_opt(
                (value.micros / 1000_000) as u32,
                ((value.micros % 1000_000) * 1000) as u32
            ).expect("localtime and native time have equal range")
        }
    }
    impl From<&NaiveTime> for LocalTime {
        fn from(time: &NaiveTime) -> LocalTime {
            let sec = chrono::Timelike::num_seconds_from_midnight(time);
            let nanos = nanos_to_micros(
                chrono::Timelike::nanosecond(time) as i64
            ) as u64;
            let mut micros = sec as u64 * 1000_000 + nanos;
            if micros >= 86400_000_000 {
                micros -= 86400_000_000;
            }
            LocalTime { micros }
        }
    }
    impl From<LocalDatetime> for NaiveDateTime {
        fn from(value: LocalDatetime) -> NaiveDateTime {
            (&value).into()
        }
    }
    impl From<LocalDate> for NaiveDate {
        fn from(value: LocalDate) -> NaiveDate {
            (&value).into()
        }
    }
    impl TryFrom<NaiveDate> for LocalDate {
        type Error = OutOfRangeError;
        fn try_from(d: NaiveDate) -> Result<LocalDate, Self::Error>
        {
            std::convert::TryFrom::try_from(&d)
        }
    }
    impl From<LocalTime> for NaiveTime {
        fn from(value: LocalTime) -> NaiveTime {
            (&value).into()
        }
    }
    impl TryFrom<NaiveDateTime> for LocalDatetime {
        type Error = OutOfRangeError;
        fn try_from(d: NaiveDateTime)
            -> Result<LocalDatetime, Self::Error>
        {
            std::convert::TryFrom::try_from(&d)
        }
    }
    impl TryFrom<ChronoDatetime> for Datetime {
        type Error = OutOfRangeError;
        fn try_from(d: ChronoDatetime)
            -> Result<Datetime, Self::Error>
        {
            std::convert::TryFrom::try_from(&d)
        }
    }
    impl From<NaiveTime> for LocalTime {
        fn from(time: NaiveTime) -> LocalTime {
            From::from(&time)
        }
    }
    #[cfg(test)]
    mod test {
        use super::*;
        use crate::model::time::test::{ test_times, valid_test_dates, to_debug, CHRONO_MAX_YEAR};
        use std::convert::{TryFrom, TryInto};
        use std::str::FromStr;
        use std::fmt::{ Display, Debug };
        #[test]
        fn chrono_roundtrips() -> Result<(), Box<dyn std::error::Error>> {
            let naive = NaiveDateTime::from_str("2019-12-27T01:02:03.123456")?;
            assert_eq!(naive,
                TryInto::<NaiveDateTime>::try_into(
                    LocalDatetime::try_from(naive)?)?);
            let naive = NaiveDate::from_str("2019-12-27")?;
            assert_eq!(naive,
                TryInto::<NaiveDate>::try_into(LocalDate::try_from(naive)?)?);
            let naive = NaiveTime::from_str("01:02:03.123456")?;
            assert_eq!(naive,
                TryInto::<NaiveTime>::try_into(LocalTime::try_from(naive)?)?);
            Ok(())
        }
        fn check_display<E:Display, A:Display>(expected_value:E, actual_value:A) {
            let expected_display = expected_value.to_string();
            let actual_display = actual_value.to_string();
            assert_eq!(expected_display, actual_display);
        }
        fn check_debug<E:Debug, A:Debug>(expected_value:E, actual_value:A) {
            let expected_debug = to_debug(expected_value);
            let actual_debug = to_debug(actual_value);
            assert_eq!(expected_debug, actual_debug);
        }
        #[test]
        fn format_local_time() {
            for time in test_times() {
                let actual_value = LocalTime::from_micros(time);
                let expected_value = NaiveTime::try_from(actual_value).unwrap();
                check_display(expected_value, actual_value);
                check_debug(expected_value, actual_value);
            }
        }
        #[test]
        fn format_local_date() {
            let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
            for (y, m, d) in dates {
                let actual_value = LocalDate::from_ymd(y, m, d);
                let expected = NaiveDate::from_ymd_opt(y, m as u32, d as u32)
                    .unwrap();
                check_display(expected, actual_value);
                check_debug(expected, actual_value);
            }
        }
        #[test]
        fn format_local_datetime() {
            let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
            for date in dates {
                for time in test_times() {
                    let actual_date = LocalDate::from_ymd(date.0, date.1, date.2);
                    let actual_time = LocalTime::from_micros(time);
                    let actual_value = LocalDatetime::new(actual_date, actual_time);
                    let expected_value = NaiveDateTime::try_from(actual_value)
                        .expect(&format!("Could not convert LocalDatetime '{}'", actual_value));
                    check_display(expected_value, actual_value);
                    check_debug(expected_value, actual_value);
                }
            }
        }
        #[test]
        fn format_datetime() {
            let dates = valid_test_dates().filter(|d| d.0 <= CHRONO_MAX_YEAR);
            for date in dates {
                for time in test_times() {
                    let actual_date = LocalDate::from_ymd(date.0, date.1, date.2);
                    let actual_time = LocalTime::from_micros(time);
                    let local_datetime = LocalDatetime::new(actual_date, actual_time);
                    let actual_value = local_datetime.to_utc();
                    let expected_value = ChronoDatetime::try_from(actual_value)
                        .expect(&format!("Could not convert Datetime '{}'", actual_value));
                    check_display(expected_value, actual_value);
                    check_debug(expected_value, actual_value);
                }
            }
        }
        #[test]
        fn date_duration() -> Result<(), Box<dyn std::error::Error>> {
            assert_eq!(DateDuration::from_years(1).to_string(), "P1Y");
            assert_eq!(DateDuration::from_months(1).to_string(), "P1M");
            assert_eq!(DateDuration::from_days(1).to_string(), "P1D");
            assert_eq!(DateDuration::from_months(10).to_string(), "P10M");
            assert_eq!(DateDuration::from_months(20).to_string(), "P1Y8M");
            assert_eq!(DateDuration::from_days(131).to_string(), "P131D");
            assert_eq!((DateDuration::from_months(7) +
                        DateDuration::from_days(131)).to_string(), "P7M131D");
            Ok(())
        }
    }
}