use crate::TimeZoneError;
use core::str::FromStr;
use tinystr::{tinystr, TinyAsciiStr};
use zerovec::ule::{AsULE, ULE};
use zerovec::{ZeroSlice, ZeroVec};
#[derive(Copy, Clone, Debug)]
pub struct GmtOffset(i32);
impl Default for GmtOffset {
fn default() -> Self {
Self::utc()
}
}
fn try_get_time_component([tens, ones]: [u8; 2]) -> Option<i32> {
Some(((tens as char).to_digit(10)? * 10 + (ones as char).to_digit(10)?) as i32)
}
impl GmtOffset {
pub fn try_from_offset_seconds(seconds: i32) -> Result<Self, TimeZoneError> {
if seconds.unsigned_abs() > 18 * 60 * 60 {
Err(TimeZoneError::OffsetOutOfBounds)
} else {
Ok(Self(seconds))
}
}
pub const fn utc() -> Self {
Self(0)
}
pub fn try_from_bytes(mut chars: &[u8]) -> Result<Self, TimeZoneError> {
let offset_sign = match chars {
[b'+', rest @ ..] => {
chars = rest;
1
}
[b'-', rest @ ..] => {
chars = rest;
-1
}
[226, 136, 146, rest @ ..] => {
chars = rest;
-1
}
[b'Z'] => return Ok(Self(0)),
_ => return Err(TimeZoneError::InvalidOffset),
};
let hours = match chars {
&[h1, h2, ..] => try_get_time_component([h1, h2]),
_ => None,
}
.ok_or(TimeZoneError::InvalidOffset)?;
let minutes = match chars {
&[_, _] => Some(0),
&[_, _, m1, m2] | &[_, _, b':', m1, m2] => {
try_get_time_component([m1, m2]).filter(|&m| m < 60)
}
_ => None,
}
.ok_or(TimeZoneError::InvalidOffset)?;
Self::try_from_offset_seconds(offset_sign * (hours * 60 + minutes) * 60)
}
#[inline]
pub unsafe fn from_offset_seconds_unchecked(seconds: i32) -> Self {
Self(seconds)
}
pub fn offset_seconds(self) -> i32 {
self.0
}
pub fn is_positive(self) -> bool {
self.0 >= 0
}
pub fn is_zero(self) -> bool {
self.0 == 0
}
pub fn has_minutes(self) -> bool {
self.0 % 3600 / 60 > 0
}
pub fn has_seconds(self) -> bool {
self.0 % 3600 % 60 > 0
}
}
impl FromStr for GmtOffset {
type Err = TimeZoneError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
GmtOffset::try_from_bytes(input.as_bytes())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, ULE)]
#[repr(transparent)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake), databake(path = icu_timezone))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[allow(clippy::exhaustive_structs)] pub struct ZoneVariant(pub TinyAsciiStr<2>);
impl FromStr for ZoneVariant {
type Err = <TinyAsciiStr<2> as FromStr>::Err;
fn from_str(input: &str) -> Result<Self, Self::Err> {
input.parse().map(ZoneVariant)
}
}
impl ZoneVariant {
pub const fn standard() -> Self {
Self(tinystr!(2, "st"))
}
pub const fn daylight() -> Self {
Self(tinystr!(2, "dt"))
}
}
impl From<TinyAsciiStr<2>> for ZoneVariant {
fn from(other: TinyAsciiStr<2>) -> Self {
Self(other)
}
}
impl From<ZoneVariant> for TinyAsciiStr<2> {
fn from(other: ZoneVariant) -> Self {
other.0
}
}
impl AsULE for ZoneVariant {
type ULE = Self;
#[inline]
fn to_unaligned(self) -> Self::ULE {
self
}
#[inline]
fn from_unaligned(unaligned: Self::ULE) -> Self {
unaligned
}
}
impl<'a> zerovec::maps::ZeroMapKV<'a> for ZoneVariant {
type Container = ZeroVec<'a, ZoneVariant>;
type Slice = ZeroSlice<ZoneVariant>;
type GetType = ZoneVariant;
type OwnedType = ZoneVariant;
}