use crate::error::InvalidOffsetError;
use core::str::FromStr;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct UtcOffset(i32);
impl UtcOffset {
pub fn try_from_seconds(seconds: i32) -> Result<Self, InvalidOffsetError> {
if seconds.unsigned_abs() > 18 * 60 * 60 {
Err(InvalidOffsetError)
} else {
Ok(Self(seconds))
}
}
pub const fn from_eighths_of_hour(eighths_of_hour: i8) -> Self {
Self(eighths_of_hour as i32 * 450)
}
pub const fn zero() -> Self {
Self(0)
}
#[inline]
pub fn try_from_str(s: &str) -> Result<Self, InvalidOffsetError> {
Self::try_from_utf8(s.as_bytes())
}
pub fn try_from_utf8(mut code_units: &[u8]) -> Result<Self, InvalidOffsetError> {
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)
}
let offset_sign = match code_units {
[b'+', rest @ ..] => {
code_units = rest;
1
}
[b'-', rest @ ..] => {
code_units = rest;
-1
}
[226, 136, 146, rest @ ..] => {
code_units = rest;
-1
}
[b'Z'] => return Ok(Self(0)),
_ => return Err(InvalidOffsetError),
};
let hours = match code_units {
&[h1, h2, ..] => try_get_time_component([h1, h2]),
_ => None,
}
.ok_or(InvalidOffsetError)?;
let minutes = match code_units {
&[_, _] => Some(0),
&[_, _, m1, m2] | &[_, _, b':', m1, m2] => {
try_get_time_component([m1, m2]).filter(|&m| m < 60)
}
_ => None,
}
.ok_or(InvalidOffsetError)?;
Self::try_from_seconds(offset_sign * (hours * 60 + minutes) * 60)
}
#[inline]
pub fn from_seconds_unchecked(seconds: i32) -> Self {
Self(seconds)
}
pub fn to_seconds(self) -> i32 {
self.0
}
pub fn to_eighths_of_hour(self) -> i8 {
(self.0 / 450) as i8
}
pub fn is_non_negative(self) -> bool {
self.0 >= 0
}
pub fn is_zero(self) -> bool {
self.0 == 0
}
pub fn hours_part(self) -> i32 {
self.0 / 3600
}
pub fn minutes_part(self) -> u32 {
(self.0 % 3600 / 60).unsigned_abs()
}
pub fn seconds_part(self) -> u32 {
(self.0 % 60).unsigned_abs()
}
}
impl FromStr for UtcOffset {
type Err = InvalidOffsetError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[zerovec::make_ule(ZoneVariantULE)]
#[repr(u8)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_timezone))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[non_exhaustive]
pub enum ZoneVariant {
Standard = 0,
Daylight = 1,
}