Compare commits
17 Commits
e9c994a755
...
6a5ec31d8e
Author | SHA1 | Date |
---|---|---|
Bradlee Speice | 6a5ec31d8e | |
bors[bot] | 23f50fb62b | |
Bradlee Speice | f1ca602e9f | |
bors[bot] | 899cd88280 | |
Bradlee Speice | a08bb2d9d7 | |
bors[bot] | af6c3238c4 | |
Bradlee Speice | b098f54f8b | |
Bradlee Speice | 61022c323e | |
Bradlee Speice | 4079b3ce2f | |
Bradlee Speice | 3e03b188b4 | |
bspeice | 7147677926 | |
Bradlee Speice | 22b6a321e6 | |
Bradlee Speice | 9edc2a3102 | |
bspeice | 245f746c8c | |
bspeice | 5782a573bc | |
Matthieu Guilbert | e895fbd9f3 | |
Bradlee Speice | 2a2f1e7fbd |
125
.travis.yml
125
.travis.yml
|
@ -1,109 +1,40 @@
|
||||||
# Based on the "trust" template v0.1.2
|
|
||||||
# https://github.com/japaric/trust/tree/v0.1.2
|
|
||||||
|
|
||||||
dist: trusty
|
|
||||||
language: rust
|
language: rust
|
||||||
services: docker
|
|
||||||
sudo: required
|
|
||||||
addons:
|
|
||||||
chrome: stable
|
|
||||||
|
|
||||||
env:
|
jobs:
|
||||||
global:
|
|
||||||
- CRATE_NAME=dtparse
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
include:
|
||||||
# Android
|
- rust: stable
|
||||||
- env: TARGET=aarch64-linux-android DISABLE_TESTS=1
|
os: linux
|
||||||
- env: TARGET=arm-linux-androideabi DISABLE_TESTS=1
|
- rust: 1.28.0
|
||||||
- env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1
|
os: linux
|
||||||
- env: TARGET=i686-linux-android DISABLE_TESTS=1
|
env: DISABLE_TOOLS=true
|
||||||
- env: TARGET=x86_64-linux-android DISABLE_TESTS=1
|
- rust: stable
|
||||||
|
os: osx
|
||||||
|
- rust: stable-msvc
|
||||||
|
os: windows
|
||||||
|
- rust: stable
|
||||||
|
os: windows
|
||||||
|
|
||||||
# iOS
|
cache:
|
||||||
- env: TARGET=aarch64-apple-ios DISABLE_TESTS=1
|
- cargo
|
||||||
os: osx
|
|
||||||
- env: TARGET=armv7-apple-ios DISABLE_TESTS=1
|
|
||||||
os: osx
|
|
||||||
- env: TARGET=armv7s-apple-ios DISABLE_TESTS=1
|
|
||||||
os: osx
|
|
||||||
- env: TARGET=i386-apple-ios DISABLE_TESTS=1
|
|
||||||
os: osx
|
|
||||||
- env: TARGET=x86_64-apple-ios DISABLE_TESTS=1
|
|
||||||
os: osx
|
|
||||||
|
|
||||||
# Linux
|
before_script:
|
||||||
- env: TARGET=aarch64-unknown-linux-gnu
|
- rustup show
|
||||||
- env: TARGET=arm-unknown-linux-gnueabi
|
# CMake doesn't like the `sh.exe` provided by Git being in PATH
|
||||||
- env: TARGET=armv7-unknown-linux-gnueabihf
|
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then rm "C:/Program Files/Git/usr/bin/sh.exe"; fi
|
||||||
- env: TARGET=i686-unknown-linux-gnu
|
- if [[ "$DISABLE_TOOLS" == "" ]]; then rustup component add clippy; rustup component add rustfmt; fi
|
||||||
- env: TARGET=i686-unknown-linux-musl
|
|
||||||
- env: TARGET=mips-unknown-linux-gnu
|
|
||||||
- env: TARGET=mips64-unknown-linux-gnuabi64
|
|
||||||
- env: TARGET=mips64el-unknown-linux-gnuabi64
|
|
||||||
- env: TARGET=mipsel-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc64-unknown-linux-gnu
|
|
||||||
- env: TARGET=powerpc64le-unknown-linux-gnu
|
|
||||||
- env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
|
||||||
- env: TARGET=x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# OSX
|
|
||||||
- env: TARGET=i686-apple-darwin
|
|
||||||
os: osx
|
|
||||||
- env: TARGET=x86_64-apple-darwin
|
|
||||||
os: osx
|
|
||||||
|
|
||||||
# *BSD
|
|
||||||
- env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1
|
|
||||||
- env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1
|
|
||||||
- env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1
|
|
||||||
|
|
||||||
# Windows
|
|
||||||
- env: TARGET=x86_64-pc-windows-gnu
|
|
||||||
|
|
||||||
# Nightly and Beta
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
|
||||||
rust: nightly
|
|
||||||
- env: TARGET=x86_64-apple-darwin
|
|
||||||
os: osx
|
|
||||||
rust: nightly
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
|
||||||
rust: beta
|
|
||||||
- env: TARGET=x86_64-apple-darwin
|
|
||||||
os: osx
|
|
||||||
rust: beta
|
|
||||||
|
|
||||||
# Historical Rust versions
|
|
||||||
- env: TARGET=x86_64-unknown-linux-gnu
|
|
||||||
rust: 1.28.0
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- set -e
|
|
||||||
- rustup self update
|
|
||||||
|
|
||||||
install:
|
|
||||||
- sh ci/install.sh
|
|
||||||
- source ~/.cargo/env || true
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- bash ci/script.sh
|
- if [[ "$DISABLE_TOOLS" == "" ]]; then cargo clippy --all && cargo fmt --all -- --check; fi
|
||||||
|
|
||||||
after_script: set +e
|
# For default build, split up compilation and tests so we can track build times
|
||||||
|
- cargo test --no-run
|
||||||
cache: cargo
|
- cargo test
|
||||||
before_cache:
|
- cargo test --release --no-run
|
||||||
# Travis can't cache files that are not readable by "others"
|
- cargo test --release
|
||||||
- chmod -R a+r $HOME/.cargo
|
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
# release tags
|
|
||||||
- /^v\d+\.\d+\.\d+.*$/
|
|
||||||
- master
|
- master
|
||||||
|
- staging
|
||||||
notifications:
|
- trying
|
||||||
email:
|
|
||||||
on_success: never
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dtparse"
|
name = "dtparse"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
authors = ["Bradlee Speice <bradlee@speice.io>"]
|
authors = ["Bradlee Speice <bradlee@speice.io>"]
|
||||||
description = "A dateutil-compatible timestamp parser for Rust"
|
description = "A dateutil-compatible timestamp parser for Rust"
|
||||||
repository = "https://github.com/bspeice/dtparse.git"
|
repository = "https://github.com/bspeice/dtparse.git"
|
||||||
|
@ -10,7 +10,6 @@ license = "Apache-2.0"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "bspeice/dtparse" }
|
travis-ci = { repository = "bspeice/dtparse" }
|
||||||
appveyor = { repository = "bspeice/dtparse" }
|
|
||||||
maintenance = { status = "passively-maintained" }
|
maintenance = { status = "passively-maintained" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# dtparse
|
# dtparse
|
||||||
|
|
||||||
[![travisci](https://travis-ci.org/bspeice/dtparse.svg?branch=master)](https://travis-ci.org/bspeice/dtparse)
|
[![travisci](https://travis-ci.org/bspeice/dtparse.svg?branch=master)](https://travis-ci.org/bspeice/dtparse)
|
||||||
[![appveyor](https://ci.appveyor.com/api/projects/status/r4de76tg9utfjva1/branch/master?svg=true)](https://ci.appveyor.com/project/bspeice/dtparse/branch/master)
|
|
||||||
[![crates.io](https://img.shields.io/crates/v/dtparse.svg)](https://crates.io/crates/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/)
|
[![docs.rs](https://docs.rs/dtparse/badge.svg)](https://docs.rs/dtparse/)
|
||||||
|
|
||||||
|
|
121
appveyor.yml
121
appveyor.yml
|
@ -1,121 +0,0 @@
|
||||||
# Appveyor configuration template for Rust using rustup for Rust installation
|
|
||||||
# https://github.com/starkat99/appveyor-rust
|
|
||||||
|
|
||||||
## Operating System (VM environment) ##
|
|
||||||
|
|
||||||
# Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets.
|
|
||||||
os: Visual Studio 2017
|
|
||||||
|
|
||||||
## Build Matrix ##
|
|
||||||
|
|
||||||
# This configuration will setup a build for each channel & target combination (12 windows
|
|
||||||
# combinations in all).
|
|
||||||
#
|
|
||||||
# There are 3 channels: stable, beta, and nightly.
|
|
||||||
#
|
|
||||||
# Alternatively, the full version may be specified for the channel to build using that specific
|
|
||||||
# version (e.g. channel: 1.5.0)
|
|
||||||
#
|
|
||||||
# The values for target are the set of windows Rust build targets. Each value is of the form
|
|
||||||
#
|
|
||||||
# ARCH-pc-windows-TOOLCHAIN
|
|
||||||
#
|
|
||||||
# Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker
|
|
||||||
# toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for
|
|
||||||
# a description of the toolchain differences.
|
|
||||||
# See https://github.com/rust-lang-nursery/rustup.rs/#toolchain-specification for description of
|
|
||||||
# toolchains and host triples.
|
|
||||||
#
|
|
||||||
# Comment out channel/target combos you do not wish to build in CI.
|
|
||||||
#
|
|
||||||
# You may use the `cargoflags` and `RUSTFLAGS` variables to set additional flags for cargo commands
|
|
||||||
# and rustc, respectively. For instance, you can uncomment the cargoflags lines in the nightly
|
|
||||||
# channels to enable unstable features when building for nightly. Or you could add additional
|
|
||||||
# matrix entries to test different combinations of features.
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
|
|
||||||
### MSVC Toolchains ###
|
|
||||||
|
|
||||||
# Stable 64-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
# Stable 32-bit MSVC
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
# Beta 64-bit MSVC
|
|
||||||
- channel: beta
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
# Beta 32-bit MSVC
|
|
||||||
- channel: beta
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
# Nightly 64-bit MSVC
|
|
||||||
- channel: nightly
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
#cargoflags: --features "unstable"
|
|
||||||
# Nightly 32-bit MSVC
|
|
||||||
- channel: nightly
|
|
||||||
target: i686-pc-windows-msvc
|
|
||||||
#cargoflags: --features "unstable"
|
|
||||||
|
|
||||||
### GNU Toolchains ###
|
|
||||||
|
|
||||||
# Stable 64-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
# Stable 32-bit GNU
|
|
||||||
- channel: stable
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
# Beta 64-bit GNU
|
|
||||||
- channel: beta
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
# Beta 32-bit GNU
|
|
||||||
- channel: beta
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
# Nightly 64-bit GNU
|
|
||||||
- channel: nightly
|
|
||||||
target: x86_64-pc-windows-gnu
|
|
||||||
#cargoflags: --features "unstable"
|
|
||||||
# Nightly 32-bit GNU
|
|
||||||
- channel: nightly
|
|
||||||
target: i686-pc-windows-gnu
|
|
||||||
#cargoflags: --features "unstable"
|
|
||||||
|
|
||||||
### Allowed failures ###
|
|
||||||
|
|
||||||
# See Appveyor documentation for specific details. In short, place any channel or targets you wish
|
|
||||||
# to allow build failures on (usually nightly at least is a wise choice). This will prevent a build
|
|
||||||
# or test failure in the matching channels/targets from failing the entire build.
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- channel: nightly
|
|
||||||
|
|
||||||
# If you only care about stable channel build failures, uncomment the following line:
|
|
||||||
#- channel: beta
|
|
||||||
|
|
||||||
## Install Script ##
|
|
||||||
|
|
||||||
# This is the most important part of the Appveyor configuration. This installs the version of Rust
|
|
||||||
# specified by the 'channel' and 'target' environment variables from the build matrix. This uses
|
|
||||||
# rustup to install Rust.
|
|
||||||
#
|
|
||||||
# For simple configurations, instead of using the build matrix, you can simply set the
|
|
||||||
# default-toolchain and default-host manually here.
|
|
||||||
install:
|
|
||||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
|
||||||
- rustup-init -yv --default-toolchain %channel% --default-host %target%
|
|
||||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
|
||||||
- rustc -vV
|
|
||||||
- cargo -vV
|
|
||||||
|
|
||||||
## Build Script ##
|
|
||||||
|
|
||||||
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
|
|
||||||
# the "directory does not contain a project or solution file" error.
|
|
||||||
build: false
|
|
||||||
|
|
||||||
# Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs
|
|
||||||
#directly or perform other testing commands. Rust will automatically be placed in the PATH
|
|
||||||
# environment variable.
|
|
||||||
test_script:
|
|
||||||
- cargo test --verbose %cargoflags%
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
status = [
|
||||||
|
"continuous-integration/travis-ci/push",
|
||||||
|
]
|
||||||
|
delete_merged_branches = true
|
|
@ -81,7 +81,7 @@ tests = {
|
||||||
'Thu Sep 25 10:36:28 BRST 2003', '1996.07.10 AD at 15:08:56 PDT',
|
'Thu Sep 25 10:36:28 BRST 2003', '1996.07.10 AD at 15:08:56 PDT',
|
||||||
'Tuesday, April 12, 1952 AD 3:30:42pm PST',
|
'Tuesday, April 12, 1952 AD 3:30:42pm PST',
|
||||||
'November 5, 1994, 8:15:30 am EST', '1994-11-05T08:15:30-05:00',
|
'November 5, 1994, 8:15:30 am EST', '1994-11-05T08:15:30-05:00',
|
||||||
'1994-11-05T08:15:30Z', '1976-07-04T00:01:02Z',
|
'1994-11-05T08:15:30Z', '1976-07-04T00:01:02Z', '1986-07-05T08:15:30z',
|
||||||
'Tue Apr 4 00:22:12 PDT 1995'
|
'Tue Apr 4 00:22:12 PDT 1995'
|
||||||
],
|
],
|
||||||
'test_fuzzy_tzinfo': [
|
'test_fuzzy_tzinfo': [
|
||||||
|
|
95
src/lib.rs
95
src/lib.rs
|
@ -1,4 +1,5 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
#![cfg_attr(test, allow(unknown_lints))]
|
||||||
#![cfg_attr(test, deny(warnings))]
|
#![cfg_attr(test, deny(warnings))]
|
||||||
|
|
||||||
//! # dtparse
|
//! # dtparse
|
||||||
|
@ -90,6 +91,8 @@ use rust_decimal::Decimal;
|
||||||
use rust_decimal::Error as DecimalError;
|
use rust_decimal::Error as DecimalError;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
@ -144,8 +147,18 @@ pub enum ParseError {
|
||||||
/// Parser unable to make sense of year/month/day parameters in the time string;
|
/// Parser unable to make sense of year/month/day parameters in the time string;
|
||||||
/// please report to maintainer as the timestring likely exposes a bug in implementation
|
/// please report to maintainer as the timestring likely exposes a bug in implementation
|
||||||
YearMonthDayError(&'static str),
|
YearMonthDayError(&'static str),
|
||||||
|
/// Parser unable to find any date/time-related content in the supplied string
|
||||||
|
NoDate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ParseError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ParseError {}
|
||||||
|
|
||||||
type ParseResult<I> = Result<I, ParseError>;
|
type ParseResult<I> = Result<I, ParseError>;
|
||||||
|
|
||||||
pub(crate) fn tokenize(parse_string: &str) -> Vec<String> {
|
pub(crate) fn tokenize(parse_string: &str) -> Vec<String> {
|
||||||
|
@ -155,16 +168,15 @@ pub(crate) fn tokenize(parse_string: &str) -> Vec<String> {
|
||||||
|
|
||||||
/// Utility function for `ParserInfo` that helps in constructing
|
/// Utility function for `ParserInfo` that helps in constructing
|
||||||
/// the attributes that make up the `ParserInfo` container
|
/// the attributes that make up the `ParserInfo` container
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(get_unwrap))] // Recommended suggestion of &vec[0] doesn't compile
|
|
||||||
pub fn parse_info(vec: Vec<Vec<&str>>) -> HashMap<String, usize> {
|
pub fn parse_info(vec: Vec<Vec<&str>>) -> HashMap<String, usize> {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
|
|
||||||
if vec.len() == 1 {
|
if vec.len() == 1 {
|
||||||
for (i, val) in vec.get(0).unwrap().into_iter().enumerate() {
|
for (i, val) in vec.get(0).unwrap().iter().enumerate() {
|
||||||
m.insert(val.to_lowercase(), i);
|
m.insert(val.to_lowercase(), i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i, val_vec) in vec.into_iter().enumerate() {
|
for (i, val_vec) in vec.iter().enumerate() {
|
||||||
for val in val_vec {
|
for val in val_vec {
|
||||||
m.insert(val.to_lowercase(), i);
|
m.insert(val.to_lowercase(), i);
|
||||||
}
|
}
|
||||||
|
@ -337,7 +349,9 @@ impl ParserInfo {
|
||||||
res.year = Some(self.convertyear(y, res.century_specified))
|
res.year = Some(self.convertyear(y, res.century_specified))
|
||||||
};
|
};
|
||||||
|
|
||||||
if res.tzoffset == Some(0) && res.tzname.is_none() || res.tzname == Some("Z".to_owned()) {
|
if (res.tzoffset == Some(0) && res.tzname.is_none())
|
||||||
|
|| (res.tzname == Some("Z".to_owned()) || res.tzname == Some("z".to_owned()))
|
||||||
|
{
|
||||||
res.tzname = Some("UTC".to_owned());
|
res.tzname = Some("UTC".to_owned());
|
||||||
res.tzoffset = Some(0);
|
res.tzoffset = Some(0);
|
||||||
} else if res.tzoffset != Some(0)
|
} else if res.tzoffset != Some(0)
|
||||||
|
@ -506,7 +520,7 @@ impl YMD {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(needless_return))]
|
#[allow(clippy::needless_return)]
|
||||||
fn resolve_ymd(
|
fn resolve_ymd(
|
||||||
&mut self,
|
&mut self,
|
||||||
yearfirst: bool,
|
yearfirst: bool,
|
||||||
|
@ -612,6 +626,31 @@ struct ParsingResult {
|
||||||
any_unused_tokens: Vec<String>,
|
any_unused_tokens: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! option_len {
|
||||||
|
($o:expr) => {{
|
||||||
|
if $o.is_some() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsingResult {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
option_len!(self.year)
|
||||||
|
+ option_len!(self.month)
|
||||||
|
+ option_len!(self.day)
|
||||||
|
+ option_len!(self.weekday)
|
||||||
|
+ option_len!(self.hour)
|
||||||
|
+ option_len!(self.minute)
|
||||||
|
+ option_len!(self.second)
|
||||||
|
+ option_len!(self.microsecond)
|
||||||
|
+ option_len!(self.tzname)
|
||||||
|
+ option_len!(self.ampm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parser is responsible for doing the actual work of understanding a time string.
|
/// Parser is responsible for doing the actual work of understanding a time string.
|
||||||
/// The root level `parse` function is responsible for constructing a default `Parser`
|
/// The root level `parse` function is responsible for constructing a default `Parser`
|
||||||
/// and triggering its behavior.
|
/// and triggering its behavior.
|
||||||
|
@ -660,7 +699,7 @@ impl Parser {
|
||||||
/// timezone name support (i.e. "EST", "BRST") is not available by default
|
/// timezone name support (i.e. "EST", "BRST") is not available by default
|
||||||
/// at the moment, they must be added through `tzinfos` at the moment in
|
/// at the moment, they must be added through `tzinfos` at the moment in
|
||||||
/// order to be resolved.
|
/// order to be resolved.
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] // Need to release a 2.0 for changing public API
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
&self,
|
&self,
|
||||||
timestr: &str,
|
timestr: &str,
|
||||||
|
@ -679,6 +718,10 @@ impl Parser {
|
||||||
let (res, tokens) =
|
let (res, tokens) =
|
||||||
self.parse_with_tokens(timestr, dayfirst, yearfirst, fuzzy, fuzzy_with_tokens)?;
|
self.parse_with_tokens(timestr, dayfirst, yearfirst, fuzzy, fuzzy_with_tokens)?;
|
||||||
|
|
||||||
|
if res.len() == 0 {
|
||||||
|
return Err(ParseError::NoDate);
|
||||||
|
}
|
||||||
|
|
||||||
let naive = self.build_naive(&res, &default_ts)?;
|
let naive = self.build_naive(&res, &default_ts)?;
|
||||||
|
|
||||||
if !ignoretz {
|
if !ignoretz {
|
||||||
|
@ -689,7 +732,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] // Imitating Python API is priority
|
#[allow(clippy::cognitive_complexity)] // Imitating Python API is priority
|
||||||
fn parse_with_tokens(
|
fn parse_with_tokens(
|
||||||
&self,
|
&self,
|
||||||
timestr: &str,
|
timestr: &str,
|
||||||
|
@ -736,11 +779,11 @@ impl Parser {
|
||||||
// Jan-01[-99]
|
// Jan-01[-99]
|
||||||
let sep = &l[i + 1];
|
let sep = &l[i + 1];
|
||||||
// TODO: This seems like a very unsafe unwrap
|
// TODO: This seems like a very unsafe unwrap
|
||||||
ymd.append(l[i + 2].parse::<i32>().unwrap(), &l[i + 2], None)?;
|
ymd.append(l[i + 2].parse::<i32>()?, &l[i + 2], None)?;
|
||||||
|
|
||||||
if i + 3 < len_l && &l[i + 3] == sep {
|
if i + 3 < len_l && &l[i + 3] == sep {
|
||||||
// Jan-01-99
|
// Jan-01-99
|
||||||
ymd.append(l[i + 4].parse::<i32>().unwrap(), &l[i + 4], None)?;
|
ymd.append(l[i + 4].parse::<i32>()?, &l[i + 4], None)?;
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,17 +846,17 @@ impl Parser {
|
||||||
// TODO: check that l[i + 1] is integer?
|
// TODO: check that l[i + 1] is integer?
|
||||||
if len_li == 4 {
|
if len_li == 4 {
|
||||||
// -0300
|
// -0300
|
||||||
hour_offset = Some(l[i + 1][..2].parse::<i32>().unwrap());
|
hour_offset = Some(l[i + 1][..2].parse::<i32>()?);
|
||||||
min_offset = Some(l[i + 1][2..4].parse::<i32>().unwrap());
|
min_offset = Some(l[i + 1][2..4].parse::<i32>()?);
|
||||||
} else if i + 2 < len_l && l[i + 2] == ":" {
|
} else if i + 2 < len_l && l[i + 2] == ":" {
|
||||||
// -03:00
|
// -03:00
|
||||||
hour_offset = Some(l[i + 1].parse::<i32>().unwrap());
|
hour_offset = Some(l[i + 1].parse::<i32>()?);
|
||||||
min_offset = Some(l[i + 3].parse::<i32>().unwrap());
|
min_offset = Some(l[i + 3].parse::<i32>()?);
|
||||||
i += 2;
|
i += 2;
|
||||||
} else if len_li <= 2 {
|
} else if len_li <= 2 {
|
||||||
// -[0]3
|
// -[0]3
|
||||||
let range_len = min(l[i + 1].len(), 2);
|
let range_len = min(l[i + 1].len(), 2);
|
||||||
hour_offset = Some(l[i + 1][..range_len].parse::<i32>().unwrap());
|
hour_offset = Some(l[i + 1][..range_len].parse::<i32>()?);
|
||||||
min_offset = Some(0);
|
min_offset = Some(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,9 +918,10 @@ impl Parser {
|
||||||
&& tzname.is_none()
|
&& tzname.is_none()
|
||||||
&& tzoffset.is_none()
|
&& tzoffset.is_none()
|
||||||
&& token.len() <= 5
|
&& token.len() <= 5
|
||||||
&& all_ascii_upper
|
&& (all_ascii_upper || self.info.utczone.contains_key(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_unwrap)]
|
||||||
fn ampm_valid(&self, hour: Option<i32>, ampm: Option<bool>, fuzzy: bool) -> ParseResult<bool> {
|
fn ampm_valid(&self, hour: Option<i32>, ampm: Option<bool>, fuzzy: bool) -> ParseResult<bool> {
|
||||||
let mut val_is_ampm = !(fuzzy && ampm.is_some());
|
let mut val_is_ampm = !(fuzzy && ampm.is_some());
|
||||||
|
|
||||||
|
@ -981,6 +1025,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_unwrap)]
|
||||||
fn parse_numeric_token(
|
fn parse_numeric_token(
|
||||||
&self,
|
&self,
|
||||||
tokens: &[String],
|
tokens: &[String],
|
||||||
|
@ -1017,9 +1062,9 @@ impl Parser {
|
||||||
let s = &tokens[idx];
|
let s = &tokens[idx];
|
||||||
|
|
||||||
if ymd.len() == 0 && tokens[idx].find('.') == None {
|
if ymd.len() == 0 && tokens[idx].find('.') == None {
|
||||||
ymd.append(s[0..2].parse::<i32>().unwrap(), &s[0..2], None)?;
|
ymd.append(s[0..2].parse::<i32>()?, &s[0..2], None)?;
|
||||||
ymd.append(s[2..4].parse::<i32>().unwrap(), &s[2..4], None)?;
|
ymd.append(s[2..4].parse::<i32>()?, &s[2..4], None)?;
|
||||||
ymd.append(s[4..6].parse::<i32>().unwrap(), &s[4..6], None)?;
|
ymd.append(s[4..6].parse::<i32>()?, &s[4..6], None)?;
|
||||||
} else {
|
} else {
|
||||||
// 19990101T235959[.59]
|
// 19990101T235959[.59]
|
||||||
res.hour = s[0..2].parse::<i32>().ok();
|
res.hour = s[0..2].parse::<i32>().ok();
|
||||||
|
@ -1032,13 +1077,9 @@ impl Parser {
|
||||||
} else if vec![8, 12, 14].contains(&len_li) {
|
} else if vec![8, 12, 14].contains(&len_li) {
|
||||||
// YYMMDD
|
// YYMMDD
|
||||||
let s = &tokens[idx];
|
let s = &tokens[idx];
|
||||||
ymd.append(
|
ymd.append(s[..4].parse::<i32>()?, &s[..4], Some(YMDLabel::Year))?;
|
||||||
s[..4].parse::<i32>().unwrap(),
|
ymd.append(s[4..6].parse::<i32>()?, &s[4..6], None)?;
|
||||||
&s[..4],
|
ymd.append(s[6..8].parse::<i32>()?, &s[6..8], None)?;
|
||||||
Some(YMDLabel::Year),
|
|
||||||
)?;
|
|
||||||
ymd.append(s[4..6].parse::<i32>().unwrap(), &s[4..6], None)?;
|
|
||||||
ymd.append(s[6..8].parse::<i32>().unwrap(), &s[6..8], None)?;
|
|
||||||
|
|
||||||
if len_li > 8 {
|
if len_li > 8 {
|
||||||
res.hour = Some(s[8..10].parse::<i32>()?);
|
res.hour = Some(s[8..10].parse::<i32>()?);
|
||||||
|
@ -1080,7 +1121,7 @@ impl Parser {
|
||||||
{
|
{
|
||||||
// TODO: There's got to be a better way of handling the condition above
|
// TODO: There's got to be a better way of handling the condition above
|
||||||
let sep = &tokens[idx + 1];
|
let sep = &tokens[idx + 1];
|
||||||
ymd.append(value_repr.parse::<i32>().unwrap(), &value_repr, None)?;
|
ymd.append(value_repr.parse::<i32>()?, &value_repr, None)?;
|
||||||
|
|
||||||
if idx + 2 < len_l && !info.jump_index(&tokens[idx + 2]) {
|
if idx + 2 < len_l && !info.jump_index(&tokens[idx + 2]) {
|
||||||
if let Ok(val) = tokens[idx + 2].parse::<i32>() {
|
if let Ok(val) = tokens[idx + 2].parse::<i32>() {
|
||||||
|
@ -1202,6 +1243,7 @@ impl Parser {
|
||||||
hms_idx
|
hms_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_unwrap)]
|
||||||
fn parse_hms(
|
fn parse_hms(
|
||||||
&self,
|
&self,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
|
@ -1261,7 +1303,6 @@ impl Parser {
|
||||||
(minute, second)
|
(minute, second)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] // Need Vec type because of mutability in the function that calls us
|
|
||||||
fn recombine_skipped(&self, skipped_idxs: Vec<usize>, tokens: Vec<String>) -> Vec<String> {
|
fn recombine_skipped(&self, skipped_idxs: Vec<usize>, tokens: Vec<String>) -> Vec<String> {
|
||||||
let mut skipped_tokens: Vec<String> = vec![];
|
let mut skipped_tokens: Vec<String> = vec![];
|
||||||
|
|
||||||
|
|
|
@ -17,26 +17,34 @@ fn test_fuzz() {
|
||||||
parse("2..\x00\x000d\x00+\x010d\x01\x00\x00\x00+"),
|
parse("2..\x00\x000d\x00+\x010d\x01\x00\x00\x00+"),
|
||||||
Err(ParseError::UnrecognizedFormat)
|
Err(ParseError::UnrecognizedFormat)
|
||||||
);
|
);
|
||||||
// OverflowError: Python int too large to convert to C long
|
|
||||||
// assert_eq!(parse("8888884444444888444444444881"), Err(ParseError::AmPmWithoutHour));
|
|
||||||
let default = NaiveDate::from_ymd(2016, 6, 29).and_hms(0, 0, 0);
|
let default = NaiveDate::from_ymd(2016, 6, 29).and_hms(0, 0, 0);
|
||||||
let p = Parser::default();
|
let p = Parser::default();
|
||||||
let res = p
|
let res = p.parse(
|
||||||
.parse(
|
"\x0D\x31",
|
||||||
"\x0D\x31",
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
Some(&default),
|
||||||
Some(&default),
|
false,
|
||||||
false,
|
&HashMap::new(),
|
||||||
&HashMap::new(),
|
);
|
||||||
)
|
assert_eq!(res, Err(ParseError::NoDate));
|
||||||
.unwrap();
|
|
||||||
assert_eq!(res.0, default);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("\x2D\x2D\x32\x31\x38\x6D"),
|
parse("\x2D\x2D\x32\x31\x38\x6D"),
|
||||||
Err(ParseError::ImpossibleTimestamp("Invalid minute"))
|
Err(ParseError::ImpossibleTimestamp("Invalid minute"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn large_int() {
|
||||||
|
let parse_result = parse("1412409095009.jpg");
|
||||||
|
assert!(parse_result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_string() {
|
||||||
|
assert_eq!(parse(""), Err(ParseError::NoDate))
|
||||||
|
}
|
||||||
|
|
|
@ -3348,6 +3348,33 @@ fn test_parse_ignoretz6() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_ignoretz7() {
|
fn test_parse_ignoretz7() {
|
||||||
|
let info = ParserInfo::default();
|
||||||
|
let pdt = PyDateTime {
|
||||||
|
year: 1986,
|
||||||
|
month: 7,
|
||||||
|
day: 5,
|
||||||
|
hour: 8,
|
||||||
|
minute: 15,
|
||||||
|
second: 30,
|
||||||
|
micros: 0,
|
||||||
|
tzo: None,
|
||||||
|
};
|
||||||
|
parse_and_assert(
|
||||||
|
pdt,
|
||||||
|
info,
|
||||||
|
"1986-07-05T08:15:30z",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
&HashMap::new(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_ignoretz8() {
|
||||||
let info = ParserInfo::default();
|
let info = ParserInfo::default();
|
||||||
let pdt = PyDateTime {
|
let pdt = PyDateTime {
|
||||||
year: 1995,
|
year: 1995,
|
||||||
|
|
Loading…
Reference in New Issue