You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b83d7a953c | |||
| 4b412e03d2 | |||
| c312311cf0 | |||
| 672a2acbc4 | |||
| 665eb95a13 | |||
| 0371374e7e | |||
| 1129eeaeaf | |||
| 400969043d | |||
| d464f3a2b1 | |||
| 025feb86f8 | |||
| 53ed79bcfc | |||
| d5bd1e8e4a | |||
| 7e9a8b502c |
@@ -0,0 +1,15 @@
|
|||||||
|
name: Security audit
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
jobs:
|
||||||
|
audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: rustsec/audit-check@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" --source-dir .
|
grcov . --binary-path="${COVERAGE_REPORT_DIR}" --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" --source-dir .
|
||||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||||
- name: Upload coverage results (to Codecov.io)
|
- name: Upload coverage results (to Codecov.io)
|
||||||
uses: codecov/codecov-action@v6
|
uses: codecov/codecov-action@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
files: ${{ steps.coverage.outputs.report }}
|
files: ${{ steps.coverage.outputs.report }}
|
||||||
|
|||||||
Generated
+17
-26
@@ -138,9 +138,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed"
|
name = "codspeed"
|
||||||
version = "4.4.1"
|
version = "4.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b684e94583e85a5ca7e1a6454a89d76a5121240f2fb67eb564129d9bafdb9db0"
|
checksum = "57af92d1db7f6871b7e82c79cd87f2501db66f36b0eab924be6ea83dd6b2f3f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -156,9 +156,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed-criterion-compat"
|
name = "codspeed-criterion-compat"
|
||||||
version = "4.4.1"
|
version = "4.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e65444156eb73ad7f57618188f8d4a281726d133ef55b96d1dcff89528609ab"
|
checksum = "1d31ae2e9ab23c29fa13bdfa06d012524176f5c0f4e25ec262cd829d947ebc5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"codspeed",
|
"codspeed",
|
||||||
@@ -169,9 +169,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codspeed-criterion-compat-walltime"
|
name = "codspeed-criterion-compat-walltime"
|
||||||
version = "4.4.1"
|
version = "4.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96389aaa4bbb872ea4924dc0335b2bb181bcf28d6eedbe8fea29afcc5bde36a6"
|
checksum = "cc8605e40bab5114dcb0f76268e18880082b5798dec10757b5b58d2c3bbc7a1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anes",
|
"anes",
|
||||||
"cast",
|
"cast",
|
||||||
@@ -201,7 +201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -364,7 +364,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -384,9 +384,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.23"
|
version = "0.2.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
|
checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
"jiff-tzdb-platform",
|
"jiff-tzdb-platform",
|
||||||
@@ -394,14 +394,14 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.61.2",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff-static"
|
name = "jiff-static"
|
||||||
version = "0.2.23"
|
version = "0.2.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
|
checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -498,7 +498,7 @@ dependencies = [
|
|||||||
"jiff",
|
"jiff",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rstest",
|
"rstest",
|
||||||
"winnow 1.0.1",
|
"winnow 1.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -896,7 +896,7 @@ version = "0.1.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -914,15 +914,6 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.61.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -998,9 +989,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "1.0.1"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
|
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|||||||
Generated
+9
-18
@@ -76,9 +76,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.23"
|
version = "0.2.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
|
checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
"jiff-tzdb-platform",
|
"jiff-tzdb-platform",
|
||||||
@@ -86,14 +86,14 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff-static"
|
name = "jiff-static"
|
||||||
version = "0.2.23"
|
version = "0.2.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4"
|
checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -133,9 +133,9 @@ checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.12"
|
version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d"
|
checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -268,20 +268,11 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.61.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.15"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|||||||
+13
-10
@@ -189,8 +189,8 @@ impl DateTimeBuilder {
|
|||||||
/// - b. Apply time. If time carries an explicit numeric offset, apply the
|
/// - b. Apply time. If time carries an explicit numeric offset, apply the
|
||||||
/// offset before setting time.
|
/// offset before setting time.
|
||||||
/// - c. Apply weekday (e.g., "next Friday" or "last Monday").
|
/// - c. Apply weekday (e.g., "next Friday" or "last Monday").
|
||||||
/// - d. Apply relative adjustments (e.g., "+3 days", "-2 months").
|
/// - d. Apply fixed offset if present (anchors the instant).
|
||||||
/// - e. Apply final fixed offset if present.
|
/// - e. Apply relative adjustments (e.g., "+3 days", "-2 months").
|
||||||
pub(super) fn build(self) -> Result<ParsedDateTime, error::Error> {
|
pub(super) fn build(self) -> Result<ParsedDateTime, error::Error> {
|
||||||
if let Some(date) = self.date.as_ref() {
|
if let Some(date) = self.date.as_ref() {
|
||||||
if date.year.unwrap_or(0) > 9999 {
|
if date.year.unwrap_or(0) > 9999 {
|
||||||
@@ -317,7 +317,17 @@ impl DateTimeBuilder {
|
|||||||
dt = dt.checked_add(Span::new().try_days(delta)?)?;
|
dt = dt.checked_add(Span::new().try_days(delta)?)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4d. Apply relative adjustments.
|
// 4d. Apply fixed offset. This anchors the instant before relative
|
||||||
|
// adjustments so that "1970/01/01 UTC N seconds" adds N seconds to the
|
||||||
|
// fixed-offset instant rather than to a local wall-clock that may drift
|
||||||
|
// across a DST boundary (see issue uutils/coreutils#12555).
|
||||||
|
if let Some(offset) = self.offset {
|
||||||
|
let (offset, hour_adjustment) = offset.normalize();
|
||||||
|
dt = dt.checked_add(Span::new().hours(hour_adjustment))?;
|
||||||
|
dt = dt.datetime().to_zoned((&offset).try_into()?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4e. Apply relative adjustments.
|
||||||
for rel in self.relative {
|
for rel in self.relative {
|
||||||
dt = match rel {
|
dt = match rel {
|
||||||
relative::Relative::Years(_) | relative::Relative::Months(_) => {
|
relative::Relative::Years(_) | relative::Relative::Months(_) => {
|
||||||
@@ -338,13 +348,6 @@ impl DateTimeBuilder {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4e. Apply final fixed offset.
|
|
||||||
if let Some(offset) = self.offset {
|
|
||||||
let (offset, hour_adjustment) = offset.normalize();
|
|
||||||
dt = dt.checked_add(Span::new().hours(hour_adjustment))?;
|
|
||||||
dt = dt.datetime().to_zoned((&offset).try_into()?)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(dt)
|
Ok(dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -285,3 +285,31 @@ fn test_multiple_month_skip(#[case] base: &str, #[case] input: &str, #[case] exp
|
|||||||
fn test_embedded_timezone(#[case] input: &str, #[case] expected: &str) {
|
fn test_embedded_timezone(#[case] input: &str, #[case] expected: &str) {
|
||||||
check_absolute(input, expected);
|
check_absolute(input, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for uutils/coreutils#12555.
|
||||||
|
// A fixed offset (e.g. the "UTC" keyword) must anchor the instant *before*
|
||||||
|
// relative adjustments are applied. Otherwise, when the base zone observes DST,
|
||||||
|
// adding a large relative offset drifts the wall-clock across a DST boundary
|
||||||
|
// and re-anchoring at the fixed offset bakes in the extra hour.
|
||||||
|
// "1970-01-01 UTC + N seconds" must equal exactly N seconds after the epoch,
|
||||||
|
// regardless of the base zone's DST rules.
|
||||||
|
#[test]
|
||||||
|
fn test_utc_keyword_plus_relative_seconds_across_dst() {
|
||||||
|
// Base in a DST-observing zone (Europe/Berlin); the value only matters as
|
||||||
|
// the default zone for interpreting the date, not the result.
|
||||||
|
let base = "2020-06-15 12:00:00"
|
||||||
|
.parse::<DateTime>()
|
||||||
|
.unwrap()
|
||||||
|
.to_zoned(TimeZone::get("Europe/Berlin").unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// A timestamp landing in summer (CEST, UTC+2) while the epoch base is in
|
||||||
|
// winter (CET, UTC+1), so the DST offset differs between base and result.
|
||||||
|
let seconds = 1_780_318_971_i64;
|
||||||
|
let input = format!("1970-01-01 UTC {seconds} seconds");
|
||||||
|
|
||||||
|
let parsed = parse_datetime::parse_datetime_at_date(base, input)
|
||||||
|
.unwrap()
|
||||||
|
.expect_in_range();
|
||||||
|
assert_eq!(parsed.timestamp().as_second(), seconds);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user