#![allow(non_snake_case)]
use crate::{
error,
format::{
parse::{
consume_padding, try_consume_digits, try_consume_exact_digits, try_consume_first_match,
},
Padding, ParseResult, ParsedItems,
},
Date, Weekday,
};
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use core::{
fmt::{self, Formatter},
num::{NonZeroU16, NonZeroU8},
};
#[allow(unused_imports)]
use standback::prelude::*;
const WEEKDAYS: [Weekday; 7] = [
Weekday::Monday,
Weekday::Tuesday,
Weekday::Wednesday,
Weekday::Thursday,
Weekday::Friday,
Weekday::Saturday,
Weekday::Sunday,
];
const WEEKDAYS_FULL: [&str; 7] = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
const WEEKDAYS_ABBR: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const MONTHS_FULL: [&str; 12] = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const MONTHS_ABBR: [&str; 12] = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
pub(crate) fn fmt_a(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
f.write_str(WEEKDAYS_ABBR[date.weekday().number_days_from_monday() as usize])
}
pub(crate) fn parse_a(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
items.weekday = Some(
try_consume_first_match(s, WEEKDAYS_ABBR.iter().zip(WEEKDAYS.iter().cloned()))
.ok_or(error::Parse::InvalidDayOfWeek)?,
);
Ok(())
}
pub(crate) fn fmt_A(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
f.write_str(WEEKDAYS_FULL[date.weekday().number_days_from_monday() as usize])
}
pub(crate) fn parse_A(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
items.weekday = Some(
try_consume_first_match(s, WEEKDAYS_FULL.iter().zip(WEEKDAYS.iter().cloned()))
.ok_or(error::Parse::InvalidDayOfWeek)?,
);
Ok(())
}
pub(crate) fn fmt_b(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
f.write_str(MONTHS_ABBR[date.month() as usize - 1])
}
pub(crate) fn parse_b(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
items.month = Some(
try_consume_first_match(s, MONTHS_ABBR.iter().cloned().zip(1..))
.and_then(NonZeroU8::new)
.ok_or(error::Parse::InvalidMonth)?,
);
Ok(())
}
pub(crate) fn fmt_B(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
f.write_str(MONTHS_FULL[date.month() as usize - 1])
}
pub(crate) fn parse_B(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
items.month = Some(
try_consume_first_match(s, MONTHS_FULL.iter().cloned().zip(1..))
.and_then(NonZeroU8::new)
.ok_or(error::Parse::InvalidMonth)?,
);
Ok(())
}
pub(crate) fn fmt_C(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.year() / 100)
}
pub(crate) fn parse_C(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
let padding_length = consume_padding(s, padding, 1);
items.year = Some(
try_consume_digits::<i32>(s, 2 - padding_length, 3 - padding_length)
.ok_or(error::Parse::InvalidYear)?
* 100
+ items.year.unwrap_or(0).rem_euclid(100),
);
Ok(())
}
pub(crate) fn fmt_d(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.day())
}
pub(crate) fn parse_d(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.day = Some(
try_consume_exact_digits(s, 2, padding)
.and_then(NonZeroU8::new)
.ok_or(error::Parse::InvalidDayOfMonth)?,
);
Ok(())
}
pub(crate) fn fmt_g(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.iso_year_week().0.rem_euclid(100))
}
pub(crate) fn parse_g(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.week_based_year = Some(
items.week_based_year.unwrap_or(0) / 100 * 100
+ try_consume_exact_digits::<i32>(s, 2, padding).ok_or(error::Parse::InvalidYear)?,
);
Ok(())
}
pub(crate) fn fmt_G(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
let year = date.iso_year_week().0;
if year >= 10_000 {
f.write_str("+")?;
}
pad!(f, padding, 4, year)
}
pub(crate) fn parse_G(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
let sign = try_consume_first_match(s, [("+", 1), ("-", -1)].iter().cloned()).unwrap_or(1);
consume_padding(s, padding, 4);
items.week_based_year = Some(
try_consume_digits(s, 1, 6)
.map(|v: i32| sign * v)
.ok_or(error::Parse::InvalidYear)?,
);
Ok(())
}
pub(crate) fn fmt_j(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 3, date.ordinal())
}
pub(crate) fn parse_j(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.ordinal_day = Some(
try_consume_exact_digits(s, 3, padding)
.and_then(NonZeroU16::new)
.ok_or(error::Parse::InvalidDayOfYear)?,
);
Ok(())
}
pub(crate) fn fmt_m(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.month())
}
pub(crate) fn parse_m(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.month = Some(
try_consume_exact_digits(s, 2, padding)
.and_then(NonZeroU8::new)
.ok_or(error::Parse::InvalidMonth)?,
);
Ok(())
}
pub(crate) fn fmt_u(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
write!(f, "{}", date.weekday().iso_weekday_number())
}
pub(crate) fn parse_u(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
items.weekday = Some(
try_consume_first_match(
s,
(1..).map(|d| d.to_string()).zip(WEEKDAYS.iter().cloned()),
)
.ok_or(error::Parse::InvalidDayOfWeek)?,
);
Ok(())
}
pub(crate) fn fmt_U(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.sunday_based_week())
}
pub(crate) fn parse_U(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.sunday_week =
Some(try_consume_exact_digits(s, 2, padding).ok_or(error::Parse::InvalidWeek)?);
Ok(())
}
pub(crate) fn fmt_V(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.week())
}
pub(crate) fn parse_V(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.iso_week = Some(
try_consume_exact_digits(s, 2, padding)
.and_then(NonZeroU8::new)
.ok_or(error::Parse::InvalidWeek)?,
);
Ok(())
}
pub(crate) fn fmt_w(f: &mut Formatter<'_>, date: Date) -> fmt::Result {
write!(f, "{}", date.weekday().number_days_from_sunday())
}
pub(crate) fn parse_w(items: &mut ParsedItems, s: &mut &str) -> ParseResult<()> {
let mut weekdays = WEEKDAYS;
weekdays.rotate_left(1);
items.weekday = Some(
try_consume_first_match(
s,
(0..)
.map(|d: u8| d.to_string())
.zip(weekdays.iter().cloned()),
)
.ok_or(error::Parse::InvalidDayOfWeek)?,
);
Ok(())
}
pub(crate) fn fmt_W(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.monday_based_week())
}
pub(crate) fn parse_W(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.monday_week =
Some(try_consume_exact_digits(s, 2, padding).ok_or(error::Parse::InvalidWeek)?);
Ok(())
}
pub(crate) fn fmt_y(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
pad!(f, padding, 2, date.year().rem_euclid(100))
}
pub(crate) fn parse_y(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
items.year = Some(
items.year.unwrap_or(0) / 100 * 100
+ try_consume_exact_digits::<i32>(s, 2, padding).ok_or(error::Parse::InvalidYear)?,
);
Ok(())
}
pub(crate) fn fmt_Y(f: &mut Formatter<'_>, date: Date, padding: Padding) -> fmt::Result {
let year = date.year();
if year >= 10_000 {
f.write_str("+")?;
}
pad!(f, padding, 4, year)
}
pub(crate) fn parse_Y(items: &mut ParsedItems, s: &mut &str, padding: Padding) -> ParseResult<()> {
let (sign, max_digits) =
try_consume_first_match(s, [("+", (1, 6)), ("-", (-1, 6))].iter().cloned())
.unwrap_or((1, 4));
consume_padding(s, padding, 3);
items.year = Some(
try_consume_digits(s, 1, max_digits)
.map(|v: i32| sign * v)
.ok_or(error::Parse::InvalidYear)?,
);
Ok(())
}