use crate::{Offset, TimeZone, Tz};
use std::fmt::{Display, Formatter};
use thiserror::Error;
use time::{OffsetDateTime, UtcOffset};
mod r#abstract;
mod intermediate;
mod parser;
#[derive(Debug)]
pub enum RangeError {
Time,
Date,
}
impl Display for RangeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RangeError::Time => f.write_str("time field out of range"),
RangeError::Date => f.write_str("date field out of range"),
}
}
}
#[derive(Debug, Error)]
pub enum ParseError<'a> {
#[error("nom error: {:?}", .0)]
Nom(nom::error::ErrorKind),
#[error("unknown short timezone name `{0}`")]
UnknownName(&'a str),
#[error("range error: {0}")]
Range(RangeError),
#[error("time component range error: {0}")]
ComponentRange(time::error::ComponentRange),
}
#[derive(Debug, Error)]
pub enum Error {
#[error("time component range error: {0}")]
ComponentRange(time::error::ComponentRange),
#[error("value of Date too large")]
DateTooLarge,
}
pub struct PosixTzOffset<'a> {
inner: r#abstract::TzOrExpandedOffset<'a>,
}
impl<'a> Offset for PosixTzOffset<'a> {
fn to_utc(&self) -> UtcOffset {
match &self.inner {
r#abstract::TzOrExpandedOffset::Expanded(v) => v.to_utc(),
r#abstract::TzOrExpandedOffset::Tz(v) => v.to_utc(),
}
}
fn name(&self) -> &str {
match &self.inner {
r#abstract::TzOrExpandedOffset::Expanded(v) => v.name(),
r#abstract::TzOrExpandedOffset::Tz(v) => v.name(),
}
}
fn is_dst(&self) -> bool {
match &self.inner {
r#abstract::TzOrExpandedOffset::Expanded(v) => v.is_dst(),
r#abstract::TzOrExpandedOffset::Tz(v) => v.is_dst(),
}
}
}
pub struct PosixTz<'a> {
inner: r#abstract::TzOrExpanded<'a>,
}
impl<'a> PosixTz<'a> {
pub fn parse(input: &'a str) -> Result<PosixTz<'a>, ParseError> {
let intermediate = intermediate::parse_intermediate(input)?;
let inner = r#abstract::parse_abstract(intermediate)?;
Ok(PosixTz { inner })
}
pub fn convert(&self, date_time: &OffsetDateTime) -> Result<OffsetDateTime, Error> {
let offset = self.get_offset(date_time)?;
Ok(date_time.to_offset(offset.to_utc()))
}
pub fn get_offset(&self, date_time: &OffsetDateTime) -> Result<PosixTzOffset<'a>, Error> {
Ok(match &self.inner {
r#abstract::TzOrExpanded::Tz(v) => PosixTzOffset {
inner: r#abstract::TzOrExpandedOffset::Tz(v.get_offset_utc(date_time)),
},
r#abstract::TzOrExpanded::Expanded(v) => PosixTzOffset {
inner: r#abstract::TzOrExpandedOffset::Expanded(v.get_offset_utc(date_time)?),
},
})
}
pub fn now(&self) -> Result<OffsetDateTime, Error> {
self.convert(&OffsetDateTime::now_utc())
}
pub fn as_iana(&self) -> Option<&'static Tz> {
match &self.inner {
r#abstract::TzOrExpanded::Tz(v) => Some(*v),
r#abstract::TzOrExpanded::Expanded(_) => None,
}
}
}