You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
refactor: add jiff and optimize date type sizes
This commit is contained in:
Generated
+60
@@ -166,6 +166,30 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
@@ -214,6 +238,7 @@ name = "parse_datetime"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"jiff",
|
||||
"num-traits",
|
||||
"regex",
|
||||
"rstest",
|
||||
@@ -232,6 +257,21 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.3.0"
|
||||
@@ -344,6 +384,26 @@ version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
||||
@@ -13,6 +13,7 @@ regex = "1.10.4"
|
||||
chrono = { version="0.4.38", default-features=false, features=["std", "alloc", "clock"] }
|
||||
winnow = "0.7.10"
|
||||
num-traits = "0.2.19"
|
||||
jiff = { version = "0.2.15", default-features = false, features = ["std"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = "0.26"
|
||||
|
||||
@@ -196,8 +196,8 @@ impl DateTimeBuilder {
|
||||
if let Some(date::Date { year, month, day }) = self.date {
|
||||
dt = new_date(
|
||||
year.map(|x| x as i32).unwrap_or(dt.year()),
|
||||
month,
|
||||
day,
|
||||
month as u32,
|
||||
day as u32,
|
||||
dt.hour(),
|
||||
dt.minute(),
|
||||
dt.second(),
|
||||
|
||||
+40
-29
@@ -41,13 +41,13 @@ use super::{
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Default)]
|
||||
pub struct Date {
|
||||
pub day: u32,
|
||||
pub month: u32,
|
||||
pub year: Option<u32>,
|
||||
pub(crate) struct Date {
|
||||
pub(crate) day: u8,
|
||||
pub(crate) month: u8,
|
||||
pub(crate) year: Option<u16>,
|
||||
}
|
||||
|
||||
impl TryFrom<(&str, u32, u32)> for Date {
|
||||
impl TryFrom<(&str, u8, u8)> for Date {
|
||||
type Error = &'static str;
|
||||
|
||||
/// Create a `Date` from a tuple of `(year, month, day)`.
|
||||
@@ -55,7 +55,7 @@ impl TryFrom<(&str, u32, u32)> for Date {
|
||||
/// Note: The `year` is represented as a `&str` to handle a specific GNU
|
||||
/// compatibility quirk. See the comment in [`year`](super::year) for more
|
||||
/// details.
|
||||
fn try_from(value: (&str, u32, u32)) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: (&str, u8, u8)) -> Result<Self, Self::Error> {
|
||||
let (year_str, month, day) = value;
|
||||
let year = year_from_str(year_str)?;
|
||||
|
||||
@@ -80,11 +80,11 @@ impl TryFrom<(&str, u32, u32)> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(u32, u32)> for Date {
|
||||
impl TryFrom<(u8, u8)> for Date {
|
||||
type Error = &'static str;
|
||||
|
||||
/// Create a `Date` from a tuple of `(month, day)`.
|
||||
fn try_from((month, day): (u32, u32)) -> Result<Self, Self::Error> {
|
||||
fn try_from((month, day): (u8, u8)) -> Result<Self, Self::Error> {
|
||||
if !(1..=12).contains(&month) {
|
||||
return Err("month must be between 1 and 12");
|
||||
}
|
||||
@@ -104,14 +104,27 @@ impl TryFrom<(u32, u32)> for Date {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &mut &str) -> ModalResult<Date> {
|
||||
impl TryFrom<Date> for jiff::civil::Date {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(date: Date) -> Result<Self, Self::Error> {
|
||||
jiff::civil::Date::new(
|
||||
date.year.unwrap_or(0) as i16,
|
||||
date.month as i8,
|
||||
date.day as i8,
|
||||
)
|
||||
.map_err(|_| "date is not valid")
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse(input: &mut &str) -> ModalResult<Date> {
|
||||
alt((iso1, iso2, us, literal1, literal2)).parse_next(input)
|
||||
}
|
||||
|
||||
/// Parse `[year]-[month]-[day]`
|
||||
///
|
||||
/// This is also used by [`combined`](super::combined).
|
||||
pub fn iso1(input: &mut &str) -> ModalResult<Date> {
|
||||
pub(super) fn iso1(input: &mut &str) -> ModalResult<Date> {
|
||||
let (year, _, month, _, day) =
|
||||
(year_str, s('-'), s(dec_uint), s('-'), s(dec_uint)).parse_next(input)?;
|
||||
|
||||
@@ -123,19 +136,13 @@ pub fn iso1(input: &mut &str) -> ModalResult<Date> {
|
||||
/// Parse `[year][month][day]`
|
||||
///
|
||||
/// This is also used by [`combined`](super::combined).
|
||||
pub fn iso2(input: &mut &str) -> ModalResult<Date> {
|
||||
pub(super) fn iso2(input: &mut &str) -> ModalResult<Date> {
|
||||
let date_str = take_while(5.., AsChar::is_dec_digit).parse_next(input)?;
|
||||
let len = date_str.len();
|
||||
|
||||
let year = &date_str[..len - 4];
|
||||
|
||||
let month = date_str[len - 4..len - 2]
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("month must be a valid number")))?;
|
||||
|
||||
let day = date_str[len - 2..]
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("day must be a valid number")))?;
|
||||
let month = month_from_str(&date_str[len - 4..len - 2])?;
|
||||
let day = day_from_str(&date_str[len - 2..])?;
|
||||
|
||||
(year, month, day)
|
||||
.try_into()
|
||||
@@ -158,27 +165,21 @@ fn us(input: &mut &str) -> ModalResult<Date> {
|
||||
//
|
||||
// GNU quirk: interpret as [year]/[month]/[day] if the first part is at
|
||||
// least 4 characters long.
|
||||
let day = s2
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("day must be a valid number")))?;
|
||||
let day = day_from_str(s2)?;
|
||||
(s1, n, day)
|
||||
.try_into()
|
||||
.map_err(|e| ErrMode::Cut(ctx_err(e)))
|
||||
}
|
||||
Some(s2) => {
|
||||
// [month]/[day]/[year]
|
||||
let month = s1
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("month must be a valid number")))?;
|
||||
let month = month_from_str(s1)?;
|
||||
(s2, month, n)
|
||||
.try_into()
|
||||
.map_err(|e| ErrMode::Cut(ctx_err(e)))
|
||||
}
|
||||
None => {
|
||||
// [month]/[day]
|
||||
let month = s1
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("month must be a valid number")))?;
|
||||
let month = month_from_str(s1)?;
|
||||
(month, n).try_into().map_err(|e| ErrMode::Cut(ctx_err(e)))
|
||||
}
|
||||
}
|
||||
@@ -239,7 +240,7 @@ fn literal2(input: &mut &str) -> ModalResult<Date> {
|
||||
}
|
||||
|
||||
/// Parse the name of a month (case-insensitive)
|
||||
fn literal_month(input: &mut &str) -> ModalResult<u32> {
|
||||
fn literal_month(input: &mut &str) -> ModalResult<u8> {
|
||||
s(alpha1)
|
||||
.verify_map(|s: &str| {
|
||||
Some(match s {
|
||||
@@ -261,6 +262,16 @@ fn literal_month(input: &mut &str) -> ModalResult<u32> {
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn month_from_str(s: &str) -> ModalResult<u8> {
|
||||
s.parse::<u8>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("month must be a valid u8 number")))
|
||||
}
|
||||
|
||||
fn day_from_str(s: &str) -> ModalResult<u8> {
|
||||
s.parse::<u8>()
|
||||
.map_err(|_| ErrMode::Cut(ctx_err("day must be a valid u8 number")))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse, Date};
|
||||
|
||||
+11
-11
@@ -15,10 +15,10 @@ use winnow::{stream::AsChar, token::take_while, ModalResult, Parser};
|
||||
use super::primitive::s;
|
||||
|
||||
// TODO: Leverage `TryFrom` trait.
|
||||
pub(super) fn year_from_str(year_str: &str) -> Result<u32, &'static str> {
|
||||
pub(super) fn year_from_str(year_str: &str) -> Result<u16, &'static str> {
|
||||
let mut year = year_str
|
||||
.parse::<u32>()
|
||||
.map_err(|_| "year must be a valid number")?;
|
||||
.parse::<u16>()
|
||||
.map_err(|_| "year must be a valid u16 number")?;
|
||||
|
||||
// If year is 68 or smaller, then 2000 is added to it; otherwise, if year
|
||||
// is less than 100, then 1900 is added to it.
|
||||
@@ -57,16 +57,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_year() {
|
||||
// 2-characters are converted to 19XX/20XX
|
||||
assert_eq!(year_from_str("10").unwrap(), 2010u32);
|
||||
assert_eq!(year_from_str("68").unwrap(), 2068u32);
|
||||
assert_eq!(year_from_str("69").unwrap(), 1969u32);
|
||||
assert_eq!(year_from_str("99").unwrap(), 1999u32);
|
||||
assert_eq!(year_from_str("10").unwrap(), 2010u16);
|
||||
assert_eq!(year_from_str("68").unwrap(), 2068u16);
|
||||
assert_eq!(year_from_str("69").unwrap(), 1969u16);
|
||||
assert_eq!(year_from_str("99").unwrap(), 1999u16);
|
||||
|
||||
// 3,4-characters are converted verbatim
|
||||
assert_eq!(year_from_str("468").unwrap(), 468u32);
|
||||
assert_eq!(year_from_str("469").unwrap(), 469u32);
|
||||
assert_eq!(year_from_str("1568").unwrap(), 1568u32);
|
||||
assert_eq!(year_from_str("1569").unwrap(), 1569u32);
|
||||
assert_eq!(year_from_str("468").unwrap(), 468u16);
|
||||
assert_eq!(year_from_str("469").unwrap(), 469u16);
|
||||
assert_eq!(year_from_str("1568").unwrap(), 1568u16);
|
||||
assert_eq!(year_from_str("1569").unwrap(), 1569u16);
|
||||
|
||||
// years greater than 9999 are not accepted
|
||||
assert!(year_from_str("10000").is_err());
|
||||
|
||||
Reference in New Issue
Block a user