use regex::Regex;
use once_cell::sync::Lazy;
use crate::str::Str;
use crate::itoa;
use crate::macros::{
impl_traits,impl_common,
impl_const,
};
use crate::date::free::{
ok_year,ok_month,ok_day,ok,
};
pub(super) static NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\d{4}|[0-9][0-9][0-9][0-9]+)$").unwrap());
pub(super) static YEAR: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}.*$").unwrap());
pub(super) static YM_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}[1-9].*$").unwrap());
pub(super) static YMM_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}([0][1-9]|1[012]).*$").unwrap());
pub(super) static YMD_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}[1-9][1-9].*$").unwrap());
pub(super) static YMMD_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}(0[1-9]|1[012])[1-9].*$").unwrap());
pub(super) static YMDD_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}[1-9](0[1-9]|[12][0-9]|30|31).*$").unwrap());
pub(super) static YMMDD_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}(0[1-9]|1[012])(0[1-9]|[12][0-9]|30|31).*$").unwrap());
pub(super) static MY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9][1-9]\d{3}.*$").unwrap());
pub(super) static MDY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9][1-9][1-9]\d{3}.*$").unwrap());
pub(super) static MMDY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|1[012])[1-9][1-9]\d{3}.*$").unwrap());
pub(super) static MDDY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9](0[1-9]|[12][0-9]|30|31)[1-9]\d{3}.*$").unwrap());
pub(super) static MMDDY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|1[012])(0[1-9]|[12][0-9]|30|31)[1-9]\d{3}.*$").unwrap());
pub(super) static DMY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9][1-9][1-9]\d{3}.*$").unwrap());
pub(super) static DDMY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|[12][0-9]|3[01])[1-9][1-9]\d{3}.*$").unwrap());
pub(super) static DMMY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9](0[1-9]|1[012])[1-9]\d{3}.*$").unwrap());
pub(super) static DDMMY_NUM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|[12][0-9]|30|31)(0[1-9]|1[012])[1-9]\d{3}.*$").unwrap());
pub(super) static YM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D[1-9].*$").unwrap());
pub(super) static YMM: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D(0[1-9]|1[012]).*$").unwrap());
pub(super) static YMD: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D[1-9]\D[1-9].*$").unwrap());
pub(super) static YMMD: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D(0[1-9]|1[012])\D[1-9].*$").unwrap());
pub(super) static YMDD: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D[1-9]\D(0[1-9]|[12][0-9]|30|31).*$").unwrap());
pub(super) static YMMDD: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{3}\D(0[1-9]|1[012])\D(0[1-9]|[12][0-9]|30|31).*$").unwrap());
pub(super) static MY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\D[1-9]\d{3}.*$").unwrap());
pub(super) static MMY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^([0][1-9]|1[012])\D[1-9]\d{3}.*$").unwrap());
pub(super) static MDY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\D[1-9]\D[1-9]\d{3}.*$").unwrap());
pub(super) static MMDY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|1[012])\D[1-9]\D[1-9]\d{3}.*$").unwrap());
pub(super) static MDDY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\D(0[1-9]|[12][0-9]|30|31)\D[1-9]\d{3}.*$").unwrap());
pub(super) static MMDDY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|1[012])\D(0[1-9]|[12][0-9]|30|31)\D[1-9]\d{3}.*$").unwrap());
pub(super) static DMY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\D[1-9]\D[1-9]\d{3}.*$").unwrap());
pub(super) static DDMY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|[12][0-9]|3[01])\D[1-9]\D[1-9]\d{3}.*$").unwrap());
pub(super) static DMMY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\D(0[1-9]|1[012])\D[1-9]\d{3}.*$").unwrap());
pub(super) static DDMMY: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(0[1-9]|[12][0-9]|30|31)\D(0[1-9]|1[012])\D[1-9]\d{3}.*$").unwrap());
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Date((u16, u8, u8), Str<{ Date::MAX_LEN }>);
impl_traits!(Date, (u16, u8, u8));
impl Date {
pub const MAX_LEN: usize = 10;
pub const DASH: u8 = b'-';
pub const ZERO: Self = Self::UNKNOWN;
pub const UNKNOWN: Self = Self((0, 0, 0), Str::from_static_str("????-??-??"));
}
impl Date {
impl_common!((u16, u8, u8));
impl_const!();
#[inline]
#[must_use]
pub const fn year(&self) -> u16 {
self.0.0
}
#[inline]
#[must_use]
pub const fn month(&self) -> u8 {
self.0.1
}
#[inline]
#[must_use]
pub const fn day(&self) -> u8 {
self.0.2
}
#[inline]
#[must_use]
pub const fn ok_year(&self) -> bool {
ok_year(self.0.0)
}
#[inline]
#[must_use]
pub const fn ok_month(&self) -> bool {
ok_month(self.0.1)
}
#[inline]
#[must_use]
pub const fn ok_day(&self) -> bool {
ok_day(self.0.2)
}
#[inline]
#[must_use]
pub const fn ok(&self) -> bool {
ok(self.0.0, self.0.1, self.0.2)
}
#[inline]
pub fn from_y(year: u16) -> Result<Self, Self> {
if ok_year(year) {
Ok(Self::priv_y_num(year))
} else {
Err(Self::UNKNOWN)
}
}
#[inline]
pub fn from_ym(year: u16, month: u8) -> Result<Self, Self> {
if ok_year(year) && ok_month(month) {
Ok(Self::priv_ym_num(year, month))
} else {
Err(Self::UNKNOWN)
}
}
#[inline]
pub fn from_ymd(year: u16, month: u8, day: u8) -> Result<Self, Self> {
if ok(year, month, day) {
Ok(Self::priv_ymd_num(year, month, day))
} else {
Err(Self::UNKNOWN)
}
}
#[inline]
#[must_use]
pub fn from_y_silent(year: u16) -> Self {
if ok_year(year) {
Self::priv_y_num(year)
} else {
Self::UNKNOWN
}
}
#[inline]
#[must_use]
pub fn from_ym_silent(year: u16, month: u8) -> Self {
if ok_year(year) && ok_month(month) {
Self::priv_ym_num(year, month)
} else {
Self::UNKNOWN
}
}
#[inline]
#[must_use]
pub fn from_ymd_silent(year: u16, month: u8, day: u8) -> Self {
if ok(year, month, day) {
Self::priv_ymd_num(year, month, day)
} else {
Self::UNKNOWN
}
}
#[inline]
fn __serde(t: (u16, u8, u8)) -> Self {
let ok_year = ok_year(t.0);
let ok_month = ok_month(t.1);
let ok_day = ok_day(t.2);
if ok_year && ok_month && ok_day {
Self::priv_ymd_num(t.0, t.1, t.2)
} else if ok_year && ok_month {
Self::priv_ym_num(t.0, t.1)
} else if ok_year {
Self::priv_y_num(t.0)
} else {
Self::UNKNOWN
}
}
#[inline]
#[must_use]
pub const fn weekday(&self) -> Option<nichi::Weekday> {
#[allow(clippy::if_then_some_else_none)] if self.ok() {
#[allow(clippy::cast_possible_wrap)]
Some(nichi::Date::weekday_raw(self.year() as i16, self.month(), self.day()))
} else {
None
}
}
#[inline]
pub fn from_unix(unix_timestamp: u64) -> Result<Self, Self> {
let nichi = nichi::Date::from_unix(i128::from(unix_timestamp));
let year = nichi.year().inner() as u16;
if ok_year(year) {
Ok(Self::priv_ymd_num(
year,
nichi.month().inner(),
nichi.day().inner(),
))
} else {
Err(Self::UNKNOWN)
}
}
#[inline]
#[must_use]
pub fn from_unix_silent(unix_timestamp: u64) -> Self {
match Self::from_unix(unix_timestamp) {
Ok(s) | Err(s) => s,
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_wrap)]
pub const fn as_unix(&self) -> u64 {
let (y,m,d) = self.inner();
let m = if m == 0 { 1 } else { m };
let d = if d == 0 { 1 } else { d };
nichi::Date::new(y as i16, m, d).as_unix() as u64
}
#[inline]
#[must_use]
pub const fn as_str_year(&self) -> &str {
unsafe {
let slice = std::slice::from_raw_parts(
self.1.as_ptr(),
4,
);
std::str::from_utf8_unchecked(slice)
}
}
#[inline]
#[must_use]
pub const fn as_str_month(&self) -> &str {
if self.month() == 0 {
return "0"
}
unsafe {
let slice = std::slice::from_raw_parts(
self.1.as_ptr().offset(5),
2,
);
std::str::from_utf8_unchecked(slice)
}
}
#[inline]
#[must_use]
pub const fn as_str_day(&self) -> &str {
if self.day() == 0 {
return "0"
}
unsafe {
let slice = std::slice::from_raw_parts(
self.1.as_ptr().offset(8),
2,
);
std::str::from_utf8_unchecked(slice)
}
}
#[inline]
#[allow(clippy::should_implement_trait)] pub fn from_str(string: &str) -> Result<Self, Self> {
Self::priv_from_str(string)
}
#[inline]
#[must_use]
pub fn from_str_silent(string: &str) -> Self {
match Self::priv_from_str(string) {
Ok(s) | Err(s) => s,
}
}
#[inline]
#[allow(clippy::string_slice, clippy::else_if_without_else)]
fn priv_from_str(s: &str) -> Result<Self, Self> {
let len = s.len();
if len == 4 {
match s.parse::<u16>() {
Ok(y) if ok_year(y) => return Ok(Self::priv_y(s)),
_ => return Err(Self::UNKNOWN),
}
}
if len < 4 {
return Err(Self::UNKNOWN);
}
if NUM.is_match(s) {
match len {
5 => {
if YM_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..];
return Ok(Self::priv_ym(y, m));
} else if MY_NUM.is_match(s) {
let m = &s[..1];
let y = &s[1..];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
}
6 => {
if YMM_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..];
return Ok(Self::priv_ym(y, m));
} else if YMD_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..5];
let d = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if MDY_NUM.is_match(s) {
let m = &s[..1];
let d = &s[1..2];
let y = &s[2..];
return Ok(Self::priv_ymd(y, m, d));
} else if DMY_NUM.is_match(s) {
let d = &s[..1];
let m = &s[1..2];
let y = &s[2..];
return Ok(Self::priv_ymd(y, m, d));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
7 => {
if YMMD_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..6];
let d = &s[6..];
return Ok(Self::priv_ymd(y, m, d));
} else if YMDD_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..5];
let d = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if MMDY_NUM.is_match(s) {
let m = &s[..2];
let d = &s[2..3];
let y = &s[3..];
return Ok(Self::priv_ymd(y, m, d));
} else if MDDY_NUM.is_match(s) {
let m = &s[..1];
let d = &s[1..3];
let y = &s[3..];
return Ok(Self::priv_ymd(y, m, d));
} else if DMMY_NUM.is_match(s) {
let d = &s[..1];
let m = &s[1..3];
let y = &s[3..];
return Ok(Self::priv_ymd(y, m, d));
} else if DDMY_NUM.is_match(s) {
let d = &s[..2];
let m = &s[2..3];
let y = &s[3..];
return Ok(Self::priv_ymd(y, m, d));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
_ => {
if YMMDD_NUM.is_match(s) {
let y = &s[..4];
let m = &s[4..6];
let d = &s[6..8];
return Ok(Self::priv_ymd(y, m, d));
} else if MMDDY_NUM.is_match(s) {
let m = &s[..2];
let d = &s[2..4];
let y = &s[4..8];
return Ok(Self::priv_ymd(y, m, d));
} else if DDMMY_NUM.is_match(s) {
let d = &s[..2];
let m = &s[2..4];
let y = &s[4..8];
return Ok(Self::priv_ymd(y, m, d));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
}
}
match len {
6 => {
if YM.is_match(s) {
let y = &s[..4];
let m = &s[5..];
return Ok(Self::priv_ym(y, m));
} else if MY.is_match(s) {
let m = &s[..1];
let y = &s[2..];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
7 => {
if YMM.is_match(s) {
let y = &s[..4];
let m = &s[5..];
return Ok(Self::priv_ym(y, m));
} else if MMY.is_match(s) {
let m = &s[..2];
let y = &s[3..];
return Ok(Self::priv_ym(y, m));
} else if YM.is_match(s) {
let y = &s[..4];
let m = &s[5..6];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
8 => {
if YMD.is_match(s) {
let y = &s[..4];
let m = &s[5..6];
let d = &s[7..];
return Ok(Self::priv_ymd(y, m, d));
} else if MDY.is_match(s) {
let m = &s[..1];
let d = &s[2..3];
let y = &s[4..];
return Ok(Self::priv_ymd(y, m, d));
} else if DMY.is_match(s) {
let d = &s[..1];
let m = &s[2..3];
let y = &s[4..];
return Ok(Self::priv_ymd(y, m, d));
} else if YMM.is_match(s) {
let y = &s[..4];
let m = &s[5..7];
return Ok(Self::priv_ym(y, m));
} else if YM.is_match(s) {
let y = &s[..4];
let m = &s[5..6];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
9 => {
if YMMD.is_match(s) {
let y = &s[..4];
let m = &s[5..7];
return Ok(Self::priv_ym(y, m));
} else if YMDD.is_match(s) {
let y = &s[..4];
let m = &s[5..6];
let d = &s[7..];
return Ok(Self::priv_ymd(y, m, d));
} else if MMDY.is_match(s) {
let m = &s[..2];
let d = &s[3..4];
let y = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if MDDY.is_match(s) {
let m = &s[..1];
let d = &s[2..4];
let y = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if DMMY.is_match(s) {
let d = &s[..1];
let m = &s[2..4];
let y = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if DDMY.is_match(s) {
let d = &s[..2];
let m = &s[3..4];
let y = &s[5..];
return Ok(Self::priv_ymd(y, m, d));
} else if YMM.is_match(s) {
let y = &s[..4];
let m = &s[5..7];
return Ok(Self::priv_ym(y, m));
} else if YM.is_match(s) {
let y = &s[..4];
let m = &s[5..6];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
_ => {
if YMMDD.is_match(s) {
let y = &s[..4];
let m = &s[5..7];
let d = &s[8..10];
return Ok(Self::priv_ymd(y, m, d));
} else if MMDDY.is_match(s) {
let m = &s[..2];
let d = &s[3..5];
let y = &s[6..10];
return Ok(Self::priv_ymd(y, m, d));
} else if DDMMY.is_match(s) {
let d = &s[..2];
let m = &s[3..5];
let y = &s[6..10];
return Ok(Self::priv_ymd(y, m, d));
} else if YMM.is_match(s) {
let y = &s[..4];
let m = &s[5..7];
return Ok(Self::priv_ym(y, m));
} else if YM.is_match(s) { let y = &s[..4];
let m = &s[5..6];
return Ok(Self::priv_ym(y, m));
} else if YEAR.is_match(s) {
let y = &s[..4];
return Ok(Self::priv_y(y));
}
},
}
Err(Self::UNKNOWN)
}
#[inline]
#[must_use]
pub const fn is_unknown(&self) -> bool {
matches!(*self, Self::UNKNOWN)
}
}
impl Date {
#[inline]
fn priv_y(year: &str) -> Self {
debug_assert_eq!(year.len(), 4);
let y = year.parse::<u16>().unwrap();
Self::priv_y_num(y)
}
#[inline]
fn priv_ym(year: &str, month: &str) -> Self {
debug_assert_eq!(year.len(), 4);
debug_assert!(month.len() <= 2);
debug_assert!(month.len() >= 1);
let y = year.parse::<u16>().unwrap();
let m = month.parse::<u8>().unwrap();
Self::priv_ym_num(y, m)
}
#[inline]
fn priv_ymd(year: &str, month: &str, day: &str) -> Self {
debug_assert_eq!(year.len(), 4);
debug_assert!(month.len() <= 2);
debug_assert!(month.len() >= 1);
debug_assert!(day.len() <= 2);
debug_assert!(day.len() >= 1);
let y = year.parse::<u16>().unwrap();
let m = month.parse::<u8>().unwrap();
let d = day.parse::<u8>().unwrap();
Self::priv_ymd_num(y, m, d)
}
#[inline]
fn priv_y_num(y: u16) -> Self {
let mut buf = [0_u8; Self::MAX_LEN];
Self::format_year(&mut buf, itoa!(y));
let string = unsafe { Str::from_raw(buf, 4) };
Self((y, 0, 0), string)
}
#[inline]
fn priv_ym_num(y: u16, m: u8) -> Self {
let mut buf = [0_u8; Self::MAX_LEN];
let b = &mut buf;
Self::format_year(b, itoa!(y));
b[4] = Self::DASH;
Self::format_month(b, Self::match_month(m));
let string = unsafe { Str::from_raw(buf, 7) };
Self((y, m, 0), string)
}
#[inline]
pub(super) fn priv_ymd_num(y: u16, m: u8, d: u8) -> Self {
let mut buf = [0_u8; Self::MAX_LEN];
let b = &mut buf;
Self::format_year(b, itoa!(y));
b[4] = Self::DASH;
Self::format_month(b, Self::match_month(m));
b[7] = Self::DASH;
Self::format_day(b, Self::match_day(d));
let string = unsafe { Str::from_raw(buf, Self::MAX_LEN as u8) };
Self((y, m, d), string)
}
#[inline]
fn format_year(buf: &mut [u8; Self::MAX_LEN], year: &str) {
buf[..4].copy_from_slice(year.as_bytes());
}
#[inline]
fn format_month(buf: &mut [u8; Self::MAX_LEN], month: &str) {
let m = month.as_bytes();
debug_assert!(m.len() >= 1);
debug_assert!(m.len() <= 2);
if m.len() == 1 {
buf[5] = b'0';
buf[6] = m[0];
} else {
buf[5] = m[0];
buf[6] = m[1];
}
}
#[inline]
fn format_day(buf: &mut [u8; Self::MAX_LEN], day: &str) {
let d = day.as_bytes();
debug_assert!(d.len() >= 1);
debug_assert!(d.len() <= 2);
if d.len() == 1 {
buf[8] = b'0';
buf[9] = d[0];
} else {
buf[8] = d[0];
buf[9] = d[1];
}
}
#[inline]
const fn match_month(m: u8) -> &'static str {
debug_assert!(m >= 1);
debug_assert!(m <= 12);
match m {
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
10 => "10",
11 => "11",
12 => "12",
_ => unreachable!(),
}
}
#[inline]
const fn match_day(d: u8) -> &'static str {
debug_assert!(d >= 1);
debug_assert!(d <= 31);
match d {
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
10 => "10",
11 => "11",
12 => "12",
13 => "13",
14 => "14",
15 => "15",
16 => "16",
17 => "17",
18 => "18",
19 => "19",
20 => "20",
21 => "21",
22 => "22",
23 => "23",
24 => "24",
25 => "25",
26 => "26",
27 => "27",
28 => "28",
29 => "29",
30 => "30",
31 => "31",
_ => unreachable!(),
}
}
}
impl TryFrom<(u16, u8, u8)> for Date {
type Error = Self;
#[inline]
fn try_from(t: (u16, u8, u8)) -> Result<Self, Self> {
Self::from_ymd(t.0, t.1, t.2)
}
}
impl TryFrom<(u16, u8)> for Date {
type Error = Self;
#[inline]
fn try_from(t: (u16, u8)) -> Result<Self, Self> {
Self::from_ym(t.0, t.1)
}
}
impl TryFrom<u16> for Date {
type Error = Self;
#[inline]
fn try_from(t: u16) -> Result<Self, Self> {
Self::from_y(t)
}
}
impl From<nichi::Date> for Date {
fn from(value: nichi::Date) -> Self {
let (y,m,d) = value.inner();
Self::priv_ymd_num(y as u16,m,d)
}
}
impl From<crate::date::Nichi> for Date {
fn from(value: crate::date::Nichi) -> Self {
if value.is_unknown() {
Self::UNKNOWN
} else {
let (y,m,d) = value.inner();
Self::priv_ymd_num(y,m,d)
}
}
}
impl From<crate::date::NichiFull> for Date {
fn from(value: crate::date::NichiFull) -> Self {
if value.is_unknown() {
Self::UNKNOWN
} else {
let (y,m,d) = value.inner();
Self::priv_ymd_num(y,m,d)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cmp::Ordering;
use compact_str::format_compact;
const EXPECTED: (u16, u8, u8) = (2020, 12, 25);
const EXPECTED_STR: &str = "2020-12-25";
#[test]
fn cmp() {
let a = Date::from_str("2020-12-01").unwrap();
let b = Date::from_str("2020-12-01").unwrap();
let c = Date::from_str("2020-12").unwrap();
let d = Date::from_str("2020-01").unwrap();
assert_eq!(a.cmp(&b), Ordering::Equal);
assert_eq!(a.cmp(&c), Ordering::Greater);
assert_eq!(a.cmp(&d), Ordering::Greater);
for i in 1..12 {
let s = format_compact!("2020-{:0>2}-01",i);
let b = Date::from_str(&s).unwrap();
assert_eq!(a.cmp(&b), Ordering::Greater);
}
for i in 2..32 {
let s = format_compact!("2020-12-{:0>2}",i);
let b = Date::from_str(&s).unwrap();
assert_eq!(a.cmp(&b), Ordering::Less);
}
for i in 2021..9999 {
let s = format_compact!("{}-12-01",i);
let b = Date::from_str(&s).unwrap();
assert_eq!(a.cmp(&b), Ordering::Less);
}
}
fn variety(start: u16, end: u16) {
for y in start..end {
for m in 1..12 {
for d in 1..31 {
Date::from_str(&format_compact!("{y}{m}{d}")).unwrap();
Date::from_str(&format_compact!("{m}{d}{y}")).unwrap();
Date::from_str(&format_compact!("{d}{m}{y}")).unwrap();
Date::from_str(&format_compact!("{y}-{m}-{d}")).unwrap();
Date::from_str(&format_compact!("{m}-{d}-{y}")).unwrap();
Date::from_str(&format_compact!("{d}-{m}-{y}")).unwrap();
}
}
}
}
#[test]
fn variety_1() { variety(1000, 2000); }
#[test]
fn variety_2() { variety(2000, 3000); }
#[test]
fn variety_3() { variety(3000, 4000); }
#[test]
fn variety_4() { variety(4000, 5000); }
#[test]
fn variety_5() { variety(5000, 6000); }
#[test]
fn variety_6() { variety(6000, 7000); }
#[test]
fn variety_7() { variety(7000, 8000); }
#[test]
fn variety_8() { variety(8000, 9000); }
#[test]
fn variety_9() { variety(9000, 10_000); }
#[test]
fn year() {
for i in 1000..10_000 {
assert!(Date::from_str(&format_compact!("{i}")).unwrap() == (i, 0, 0));
}
}
#[test]
fn invalid_years() {
assert_eq!(Date::from_str_silent("0"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("100"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("010"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("0010"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("0100"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("999"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("0999"), Date::UNKNOWN);
}
#[test]
fn invalid_dates() {
assert_eq!(Date::from_str_silent("12-25-0100"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("01001225") , Date::UNKNOWN);
assert_eq!(Date::from_str_silent("25-12-0100"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("01000"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("010000"), Date::UNKNOWN);
assert_eq!(Date::from_str_silent("0100000"), Date::UNKNOWN);
}
#[test]
fn from_str_ymd() {
assert_eq!(Date::from_str("2020-12-25").unwrap(), EXPECTED);
assert_eq!(Date::from_str("2020-12-25").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("2020 12 25").unwrap(), EXPECTED);
assert_eq!(Date::from_str("2020 12 25").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("20201225").unwrap(), EXPECTED);
assert_eq!(Date::from_str("20201225").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("2020/12/25").unwrap(), EXPECTED);
assert_eq!(Date::from_str("2020/12/25").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("2020.12.25").unwrap(), EXPECTED);
assert_eq!(Date::from_str("2020.12.25").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("2020_12_25").unwrap(), EXPECTED);
assert_eq!(Date::from_str("2020_12_25").unwrap(), EXPECTED_STR);
}
#[test]
fn from_str_mdy() {
assert_eq!(Date::from_str("12-25-2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("12-25-2020").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("12 25 2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("12 25 2020").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("12252020").unwrap() , EXPECTED);
assert_eq!(Date::from_str("12252020").unwrap() , EXPECTED_STR);
assert_eq!(Date::from_str("12/25/2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("12/25/2020").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("12.25.2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("12.25.2020").unwrap(), EXPECTED_STR);
assert_eq!(Date::from_str("12_25_2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("12_25_2020").unwrap(), EXPECTED_STR);
}
#[test]
fn from_str_dmy() {
assert_eq!(Date::from_str("25-12-2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("25 12 2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("25122020").unwrap() , EXPECTED);
assert_eq!(Date::from_str("25/12/2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("25.12.2020").unwrap(), EXPECTED);
assert_eq!(Date::from_str("25_12_2020").unwrap(), EXPECTED);
}
#[test]
#[cfg(feature = "serde")]
fn serde() {
let this: Date = Date::try_from((2024, 1, 1)).unwrap();
let json = serde_json::to_string(&this).unwrap();
assert_eq!(json, r#"[[2024,1,1],"2024-01-01"]"#);
let this: Date = serde_json::from_str(&json).unwrap();
assert_eq!(this, (2024, 1, 1));
assert_eq!(this, "2024-01-01");
assert!(serde_json::from_str::<Date>(&"---").is_err());
let json = serde_json::to_string(&Date::UNKNOWN).unwrap();
assert_eq!(json, r#"[[0,0,0],"????-??-??"]"#);
assert!(serde_json::from_str::<Date>(&json).unwrap().is_unknown());
}
#[test]
#[cfg(feature = "bincode")]
fn bincode() {
let this: Date = Date::try_from((2024, 1, 1)).unwrap();
let config = bincode::config::standard();
let bytes = bincode::encode_to_vec(&this, config).unwrap();
let this: Date = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert_eq!(this, (2024, 1, 1));
assert_eq!(this, "2024-01-01");
let bytes = bincode::encode_to_vec(&Date::UNKNOWN, config).unwrap();
let this: Date = bincode::decode_from_slice(&bytes, config).unwrap().0;
assert!(this.is_unknown());
}
#[test]
#[cfg(feature = "bincode")]
fn borsh() {
let this: Date = Date::try_from((2024, 1, 1)).unwrap();
let bytes = borsh::to_vec(&this).unwrap();
let this: Date = borsh::from_slice(&bytes).unwrap();
assert_eq!(this, (2024, 1, 1));
assert_eq!(this, "2024-01-01");
assert!(borsh::from_slice::<Date>(b"bad .-;[]124/ bytes").is_err());
let bytes = borsh::to_vec(&Date::UNKNOWN).unwrap();
let this: Date = borsh::from_slice(&bytes).unwrap();
assert!(this.is_unknown());
}
}