From 3a9e2674a67cbd065123b2d3d3fec34793b9c33c Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Sat, 25 Mar 2023 02:12:57 +0000 Subject: [PATCH] #40: Nanosecond precision --- .travis.yml | 40 ---------------------------------------- Cargo.toml | 12 ++++++------ README.md | 1 - bors.toml | 4 ---- ci/install.sh | 47 ----------------------------------------------- ci/script.sh | 40 ---------------------------------------- src/lib.rs | 24 ++++++++++++------------ src/tests/mod.rs | 11 +++++++++++ 8 files changed, 29 insertions(+), 150 deletions(-) delete mode 100644 .travis.yml delete mode 100644 bors.toml delete mode 100755 ci/install.sh delete mode 100644 ci/script.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4a92d65..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -language: rust - -jobs: - include: - - rust: stable - os: linux - - rust: 1.28.0 - os: linux - env: DISABLE_TOOLS=true - - rust: stable - os: osx - - rust: stable-msvc - os: windows - - rust: stable - os: windows - -cache: - - cargo - -before_script: - - rustup show - # CMake doesn't like the `sh.exe` provided by Git being in PATH - - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then rm "C:/Program Files/Git/usr/bin/sh.exe"; fi - - if [[ "$DISABLE_TOOLS" == "" ]]; then rustup component add clippy; rustup component add rustfmt; fi - -script: - - if [[ "$DISABLE_TOOLS" == "" ]]; then cargo clippy --all && cargo fmt --all -- --check; fi - - # For default build, split up compilation and tests so we can track build times - - cargo test --no-run - - cargo test - - cargo test --release --no-run - - cargo test --release - -branches: - only: - - master - - staging - - trying - diff --git a/Cargo.toml b/Cargo.toml index 3b9e00d..9b603a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dtparse" -version = "1.3.0" +version = "1.4.0" authors = ["Bradlee Speice "] description = "A dateutil-compatible timestamp parser for Rust" repository = "https://github.com/bspeice/dtparse.git" @@ -16,10 +16,10 @@ maintenance = { status = "passively-maintained" } name = "dtparse" [dependencies] -chrono = "0.4" -lazy_static = "1.1" -num-traits = "0.2" -rust_decimal = { version = "1.17.0", default-features = false } +chrono = "0.4.24" +lazy_static = "1.4.0" +num-traits = "0.2.15" +rust_decimal = { version = "1.29.1", default-features = false } [dev-dependencies] -base64 = "0.13" +base64 = "0.21.0" diff --git a/README.md b/README.md index 8dbe81a..822f9ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # dtparse -[![travisci](https://travis-ci.org/bspeice/dtparse.svg?branch=master)](https://travis-ci.org/bspeice/dtparse) [![crates.io](https://img.shields.io/crates/v/dtparse.svg)](https://crates.io/crates/dtparse) [![docs.rs](https://docs.rs/dtparse/badge.svg)](https://docs.rs/dtparse/) diff --git a/bors.toml b/bors.toml deleted file mode 100644 index c1605d2..0000000 --- a/bors.toml +++ /dev/null @@ -1,4 +0,0 @@ -status = [ - "continuous-integration/travis-ci/push", -] -delete_merged_branches = true \ No newline at end of file diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100755 index 80e18e4..0000000 --- a/ci/install.sh +++ /dev/null @@ -1,47 +0,0 @@ -set -ex - -main() { - local target= - if [ $TRAVIS_OS_NAME = linux ]; then - target=x86_64-unknown-linux-musl - sort=sort - else - target=x86_64-apple-darwin - sort=gsort # for `sort --sort-version`, from brew's coreutils. - fi - - # Builds for iOS are done on OSX, but require the specific target to be - # installed. - case $TARGET in - aarch64-apple-ios) - rustup target install aarch64-apple-ios - ;; - armv7-apple-ios) - rustup target install armv7-apple-ios - ;; - armv7s-apple-ios) - rustup target install armv7s-apple-ios - ;; - i386-apple-ios) - rustup target install i386-apple-ios - ;; - x86_64-apple-ios) - rustup target install x86_64-apple-ios - ;; - esac - - # This fetches latest stable release - local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ - | cut -d/ -f3 \ - | grep -E '^v[0.1.0-9.]+$' \ - | $sort --version-sort \ - | tail -n1) - curl -LSfs https://japaric.github.io/trust/install.sh | \ - sh -s -- \ - --force \ - --git japaric/cross \ - --tag $tag \ - --target $target -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index 4c501e0..0000000 --- a/ci/script.sh +++ /dev/null @@ -1,40 +0,0 @@ -# This script takes care of testing your crate - -set -ex - -main() { - cross build --target $TARGET - cross build --target $TARGET --release - - if [ ! -z $DISABLE_TESTS ]; then - return - fi - - cross test --target $TARGET - cross test --target $TARGET --release -} - -main_web() { - CARGO_WEB_RELEASE="$(curl -L -s -H 'Accept: application/json' https://github.com/koute/cargo-web/releases/latest)" - CARGO_WEB_VERSION="$(echo $CARGO_WEB_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/')" - CARGO_WEB_URL="https://github.com/koute/cargo-web/releases/download/$CARGO_WEB_VERSION/cargo-web-x86_64-unknown-linux-gnu.gz" - - echo "Downloading cargo-web from: $CARGO_WEB_URL" - curl -L "$CARGO_WEB_URL" | gzip -d > cargo-web - chmod +x cargo-web - - mkdir -p ~/.cargo/bin - mv cargo-web ~/.cargo/bin - - cargo web build --target $TARGET - cargo web test --target $TARGET --release -} - -# we don't run the "test phase" when doing deploys -if [ -z $TRAVIS_TAG ]; then - if [ -z "$USE_CARGO_WEB" ]; then - main - else - main_web - fi -fi diff --git a/src/lib.rs b/src/lib.rs index 9f4dd7b..2cf1dba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -617,7 +617,7 @@ struct ParsingResult { hour: Option, minute: Option, second: Option, - microsecond: Option, + nanosecond: Option, tzname: Option, tzoffset: Option, ampm: Option, @@ -644,7 +644,7 @@ impl ParsingResult { + option_len!(self.hour) + option_len!(self.minute) + option_len!(self.second) - + option_len!(self.microsecond) + + option_len!(self.nanosecond) + option_len!(self.tzname) + option_len!(self.ampm) } @@ -976,18 +976,18 @@ impl Parser { let hour = res.hour.unwrap_or(default.hour() as i32) as u32; let minute = res.minute.unwrap_or(default.minute() as i32) as u32; let second = res.second.unwrap_or(default.second() as i32) as u32; - let microsecond = res - .microsecond - .unwrap_or(default.timestamp_subsec_micros() as i32) as u32; + let nanosecond = res + .nanosecond + .unwrap_or(default.timestamp_subsec_nanos() as i64) as u32; let t = - NaiveTime::from_hms_micro_opt(hour, minute, second, microsecond).ok_or_else(|| { + NaiveTime::from_hms_nano_opt(hour, minute, second, nanosecond).ok_or_else(|| { if hour >= 24 { ParseError::ImpossibleTimestamp("Invalid hour") } else if minute >= 60 { ParseError::ImpossibleTimestamp("Invalid minute") } else if second >= 60 { ParseError::ImpossibleTimestamp("Invalid second") - } else if microsecond >= 2_000_000 { + } else if nanosecond >= 2_000_000_000 { ParseError::ImpossibleTimestamp("Invalid microsecond") } else { unreachable!(); @@ -1071,7 +1071,7 @@ impl Parser { let t = self.parsems(&s[4..])?; res.second = Some(t.0); - res.microsecond = Some(t.1); + res.nanosecond = Some(t.1); } } else if vec![8, 12, 14].contains(&len_li) { // YYMMDD @@ -1109,7 +1109,7 @@ impl Parser { // TODO: (x, y) = (a, b) syntax? let ms = self.parsems(&tokens[idx + 4]).unwrap(); res.second = Some(ms.0); - res.microsecond = Some(ms.1); + res.nanosecond = Some(ms.1); idx += 2; } @@ -1183,13 +1183,13 @@ impl Parser { } } - fn parsems(&self, seconds_str: &str) -> ParseResult<(i32, i32)> { + fn parsems(&self, seconds_str: &str) -> ParseResult<(i32, i64)> { if seconds_str.contains('.') { let split: Vec<&str> = seconds_str.split('.').collect(); let (i, f): (&str, &str) = (split[0], split[1]); let i_parse = i.parse::()?; - let f_parse = ljust(f, 6, '0').parse::()?; + let f_parse = ljust(f, 9, '0').parse::()?; Ok((i_parse, f_parse)) } else { Ok((seconds_str.parse::()?, 0)) @@ -1281,7 +1281,7 @@ impl Parser { } else if hms == 2 { let (sec, micro) = self.parsems(value_repr).unwrap(); res.second = Some(sec); - res.microsecond = Some(micro); + res.nanosecond = Some(micro); } Ok(()) diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 1776124..6dc10bb 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,14 @@ mod fuzzing; mod pycompat_parser; mod pycompat_tokenizer; + +use chrono::NaiveDate; +use crate::parse; + +#[test] +fn nanosecond_precision() { + assert_eq!( + parse("2008.12.29T08:09:10.123456789").unwrap(), + (NaiveDate::from_ymd_opt(2008, 12, 29).unwrap().and_hms_nano_opt(8, 9, 10, 123_456_789).unwrap(), None) + ) +} \ No newline at end of file