use core::fmt;
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
use rkyv::{Archive, Deserialize, Serialize};
use crate::OutOfRange;
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
#[cfg_attr(
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
derive(Archive, Deserialize, Serialize),
archive(compare(PartialEq, PartialOrd)),
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
)]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
pub enum Month {
January = 0,
February = 1,
March = 2,
April = 3,
May = 4,
June = 5,
July = 6,
August = 7,
September = 8,
October = 9,
November = 10,
December = 11,
}
impl Month {
#[inline]
#[must_use]
pub const fn succ(&self) -> Month {
match *self {
Month::January => Month::February,
Month::February => Month::March,
Month::March => Month::April,
Month::April => Month::May,
Month::May => Month::June,
Month::June => Month::July,
Month::July => Month::August,
Month::August => Month::September,
Month::September => Month::October,
Month::October => Month::November,
Month::November => Month::December,
Month::December => Month::January,
}
}
#[inline]
#[must_use]
pub const fn pred(&self) -> Month {
match *self {
Month::January => Month::December,
Month::February => Month::January,
Month::March => Month::February,
Month::April => Month::March,
Month::May => Month::April,
Month::June => Month::May,
Month::July => Month::June,
Month::August => Month::July,
Month::September => Month::August,
Month::October => Month::September,
Month::November => Month::October,
Month::December => Month::November,
}
}
#[inline]
#[must_use]
pub const fn number_from_month(&self) -> u32 {
match *self {
Month::January => 1,
Month::February => 2,
Month::March => 3,
Month::April => 4,
Month::May => 5,
Month::June => 6,
Month::July => 7,
Month::August => 8,
Month::September => 9,
Month::October => 10,
Month::November => 11,
Month::December => 12,
}
}
#[must_use]
pub const fn name(&self) -> &'static str {
match *self {
Month::January => "January",
Month::February => "February",
Month::March => "March",
Month::April => "April",
Month::May => "May",
Month::June => "June",
Month::July => "July",
Month::August => "August",
Month::September => "September",
Month::October => "October",
Month::November => "November",
Month::December => "December",
}
}
}
impl TryFrom<u8> for Month {
type Error = OutOfRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Month::January),
2 => Ok(Month::February),
3 => Ok(Month::March),
4 => Ok(Month::April),
5 => Ok(Month::May),
6 => Ok(Month::June),
7 => Ok(Month::July),
8 => Ok(Month::August),
9 => Ok(Month::September),
10 => Ok(Month::October),
11 => Ok(Month::November),
12 => Ok(Month::December),
_ => Err(OutOfRange::new()),
}
}
}
impl num_traits::FromPrimitive for Month {
#[inline]
fn from_u64(n: u64) -> Option<Month> {
Self::from_u32(n as u32)
}
#[inline]
fn from_i64(n: i64) -> Option<Month> {
Self::from_u32(n as u32)
}
#[inline]
fn from_u32(n: u32) -> Option<Month> {
match n {
1 => Some(Month::January),
2 => Some(Month::February),
3 => Some(Month::March),
4 => Some(Month::April),
5 => Some(Month::May),
6 => Some(Month::June),
7 => Some(Month::July),
8 => Some(Month::August),
9 => Some(Month::September),
10 => Some(Month::October),
11 => Some(Month::November),
12 => Some(Month::December),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
pub struct Months(pub(crate) u32);
impl Months {
pub const fn new(num: u32) -> Self {
Self(num)
}
#[inline]
pub const fn as_u32(&self) -> u32 {
self.0
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ParseMonthError {
pub(crate) _dummy: (),
}
#[cfg(feature = "std")]
impl std::error::Error for ParseMonthError {}
impl fmt::Display for ParseMonthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ParseMonthError {{ .. }}")
}
}
impl fmt::Debug for ParseMonthError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ParseMonthError {{ .. }}")
}
}
#[cfg(feature = "serde")]
mod month_serde {
use super::Month;
use serde::{de, ser};
use core::fmt;
impl ser::Serialize for Month {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.collect_str(self.name())
}
}
struct MonthVisitor;
impl<'de> de::Visitor<'de> for MonthVisitor {
type Value = Month;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Month")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected"))
}
}
impl<'de> de::Deserialize<'de> for Month {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_str(MonthVisitor)
}
}
}
#[cfg(test)]
mod tests {
use super::Month;
use crate::{Datelike, Months, OutOfRange, TimeZone, Utc};
#[test]
fn test_month_enum_try_from() {
assert_eq!(Month::try_from(1), Ok(Month::January));
assert_eq!(Month::try_from(2), Ok(Month::February));
assert_eq!(Month::try_from(12), Ok(Month::December));
assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
let month = Month::January;
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
}
#[test]
fn test_month_enum_primitive_parse() {
use num_traits::FromPrimitive;
let jan_opt = Month::from_u32(1);
let feb_opt = Month::from_u64(2);
let dec_opt = Month::from_i64(12);
let no_month = Month::from_u32(13);
assert_eq!(jan_opt, Some(Month::January));
assert_eq!(feb_opt, Some(Month::February));
assert_eq!(dec_opt, Some(Month::December));
assert_eq!(no_month, None);
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
assert_eq!(Month::from_u32(date.month()), Some(Month::October));
let month = Month::January;
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
}
#[test]
fn test_month_enum_succ_pred() {
assert_eq!(Month::January.succ(), Month::February);
assert_eq!(Month::December.succ(), Month::January);
assert_eq!(Month::January.pred(), Month::December);
assert_eq!(Month::February.pred(), Month::January);
}
#[test]
fn test_month_partial_ord() {
assert!(Month::January <= Month::January);
assert!(Month::January < Month::February);
assert!(Month::January < Month::December);
assert!(Month::July >= Month::May);
assert!(Month::September > Month::March);
}
#[test]
fn test_months_as_u32() {
assert_eq!(Months::new(0).as_u32(), 0);
assert_eq!(Months::new(1).as_u32(), 1);
assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialize() {
use serde_json::to_string;
use Month::*;
let cases: Vec<(Month, &str)> = vec![
(January, "\"January\""),
(February, "\"February\""),
(March, "\"March\""),
(April, "\"April\""),
(May, "\"May\""),
(June, "\"June\""),
(July, "\"July\""),
(August, "\"August\""),
(September, "\"September\""),
(October, "\"October\""),
(November, "\"November\""),
(December, "\"December\""),
];
for (month, expected_str) in cases {
let string = to_string(&month).unwrap();
assert_eq!(string, expected_str);
}
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_deserialize() {
use serde_json::from_str;
use Month::*;
let cases: Vec<(&str, Month)> = vec![
("\"january\"", January),
("\"jan\"", January),
("\"FeB\"", February),
("\"MAR\"", March),
("\"mar\"", March),
("\"april\"", April),
("\"may\"", May),
("\"june\"", June),
("\"JULY\"", July),
("\"august\"", August),
("\"september\"", September),
("\"October\"", October),
("\"November\"", November),
("\"DECEmbEr\"", December),
];
for (string, expected_month) in cases {
let month = from_str::<Month>(string).unwrap();
assert_eq!(month, expected_month);
}
let errors: Vec<&str> =
vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""];
for string in errors {
from_str::<Month>(string).unwrap_err();
}
}
#[test]
#[cfg(feature = "rkyv-validation")]
fn test_rkyv_validation() {
let month = Month::January;
let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap();
assert_eq!(rkyv::from_bytes::<Month>(&bytes).unwrap(), month);
}
}