mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
Merge pull request #7894 from drinkcat/jiff-date-ls
date/ls: Switch from chrono to jiff
This commit is contained in:
commit
dfc2e249ef
14 changed files with 399 additions and 325 deletions
4
.github/workflows/CICD.yml
vendored
4
.github/workflows/CICD.yml
vendored
|
@ -653,10 +653,6 @@ jobs:
|
|||
;;
|
||||
esac
|
||||
outputs CARGO_TEST_OPTIONS
|
||||
# ** pass needed environment into `cross` container (iff `cross` not already configured via "Cross.toml")
|
||||
if [ "${CARGO_CMD}" = 'cross' ] && [ ! -e "Cross.toml" ] ; then
|
||||
printf "[build.env]\npassthrough = [\"CI\", \"RUST_BACKTRACE\", \"CARGO_TERM_COLOR\"]\n" > Cross.toml
|
||||
fi
|
||||
# * executable for `strip`?
|
||||
STRIP="strip"
|
||||
case ${{ matrix.job.target }} in
|
||||
|
|
84
Cargo.lock
generated
84
Cargo.lock
generated
|
@ -314,27 +314,6 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
|
@ -1337,6 +1316,47 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27e77966151130221b079bcec80f1f34a9e414fa489d99152a201c07fd2182bc"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97265751f8a9a4228476f2fc17874a9e7e70e96b893368e42619880fe143b48a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb-platform"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
|
||||
dependencies = [
|
||||
"jiff-tzdb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
|
@ -1747,15 +1767,6 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.9.0"
|
||||
|
@ -1839,6 +1850,15 @@ version = "1.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||
|
||||
[[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 = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
@ -2808,6 +2828,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"jiff",
|
||||
"libc",
|
||||
"parse_datetime",
|
||||
"uucore",
|
||||
|
@ -3079,6 +3100,7 @@ dependencies = [
|
|||
"clap",
|
||||
"glob",
|
||||
"hostname",
|
||||
"jiff",
|
||||
"lscolors",
|
||||
"number_prefix",
|
||||
"selinux",
|
||||
|
@ -3675,7 +3697,6 @@ dependencies = [
|
|||
"blake2b_simd",
|
||||
"blake3",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"crc32fast",
|
||||
"data-encoding",
|
||||
|
@ -3687,7 +3708,6 @@ dependencies = [
|
|||
"fluent-bundle",
|
||||
"glob",
|
||||
"hex",
|
||||
"iana-time-zone",
|
||||
"itertools 0.14.0",
|
||||
"libc",
|
||||
"md-5",
|
||||
|
|
|
@ -282,7 +282,6 @@ chrono = { version = "0.4.41", default-features = false, features = [
|
|||
"alloc",
|
||||
"clock",
|
||||
] }
|
||||
chrono-tz = "0.10.0"
|
||||
clap = { version = "4.5", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.4"
|
||||
clap_mangen = "0.2"
|
||||
|
@ -300,9 +299,13 @@ gcd = "2.3"
|
|||
glob = "0.3.1"
|
||||
half = "2.4.1"
|
||||
hostname = "0.4"
|
||||
iana-time-zone = "0.1.57"
|
||||
indicatif = "0.17.8"
|
||||
itertools = "0.14.0"
|
||||
jiff = { version = "0.2.10", default-features = false, features = [
|
||||
"std",
|
||||
"alloc",
|
||||
"tz-system",
|
||||
] }
|
||||
libc = "0.2.172"
|
||||
linux-raw-sys = "0.9"
|
||||
lscolors = { version = "0.20.0", default-features = false, features = [
|
||||
|
|
7
Cross.toml
Normal file
7
Cross.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
# spell-checker:ignore (misc) dpkg noninteractive tzdata
|
||||
[build]
|
||||
pre-build = [
|
||||
"apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install tzdata",
|
||||
]
|
||||
[build.env]
|
||||
passthrough = ["CI", "RUST_BACKTRACE", "CARGO_TERM_COLOR"]
|
159
fuzz/Cargo.lock
generated
159
fuzz/Cargo.lock
generated
|
@ -219,27 +219,6 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
|
@ -644,6 +623,47 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27e77966151130221b079bcec80f1f34a9e414fa489d99152a201c07fd2182bc"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97265751f8a9a4228476f2fc17874a9e7e70e96b893368e42619880fe143b48a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524"
|
||||
|
||||
[[package]]
|
||||
name = "jiff-tzdb-platform"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
|
||||
dependencies = [
|
||||
"jiff-tzdb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
|
@ -831,15 +851,6 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.9.0"
|
||||
|
@ -851,50 +862,27 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||
|
||||
[[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 = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
|
@ -928,15 +916,6 @@ version = "5.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
|
@ -944,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -954,15 +933,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
|
@ -1133,12 +1106,6 @@ version = "2.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "sm3"
|
||||
version = "0.4.2"
|
||||
|
@ -1316,6 +1283,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"jiff",
|
||||
"libc",
|
||||
"parse_datetime",
|
||||
"uucore",
|
||||
|
@ -1385,7 +1353,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"memchr",
|
||||
"nix",
|
||||
"rand 0.9.1",
|
||||
"rand",
|
||||
"rayon",
|
||||
"self_cell",
|
||||
"tempfile",
|
||||
|
@ -1442,8 +1410,6 @@ dependencies = [
|
|||
"bigdecimal",
|
||||
"blake2b_simd",
|
||||
"blake3",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"clap",
|
||||
"crc32fast",
|
||||
"data-encoding",
|
||||
|
@ -1454,7 +1420,6 @@ dependencies = [
|
|||
"fluent-bundle",
|
||||
"glob",
|
||||
"hex",
|
||||
"iana-time-zone",
|
||||
"itertools",
|
||||
"libc",
|
||||
"md-5",
|
||||
|
@ -1481,7 +1446,7 @@ name = "uucore-fuzz"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"libfuzzer-sys",
|
||||
"rand 0.9.1",
|
||||
"rand",
|
||||
"uu_cksum",
|
||||
"uu_cut",
|
||||
"uu_date",
|
||||
|
@ -1514,7 +1479,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"console",
|
||||
"libc",
|
||||
"rand 0.9.1",
|
||||
"rand",
|
||||
"similar",
|
||||
"tempfile",
|
||||
"uucore",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# spell-checker:ignore datetime
|
||||
# spell-checker:ignore datetime tzdb zoneinfo
|
||||
[package]
|
||||
name = "uu_date"
|
||||
description = "date ~ (uutils) display or set the current time"
|
||||
|
@ -19,9 +19,14 @@ workspace = true
|
|||
path = "src/date.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["custom-tz-fmt", "parser"] }
|
||||
chrono = { workspace = true } # TODO: Eventually we'll want to remove this
|
||||
jiff = { workspace = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
uucore = { workspace = true, features = ["parser"] }
|
||||
parse_datetime = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
|
|
@ -3,19 +3,17 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
||||
// spell-checker:ignore strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
||||
|
||||
use chrono::format::{Item, StrftimeItems};
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, Utc};
|
||||
#[cfg(windows)]
|
||||
use chrono::{Datelike, Timelike};
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use jiff::fmt::strtime;
|
||||
use jiff::tz::TimeZone;
|
||||
use jiff::{SignedDuration, Timestamp, Zoned};
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||
use libc::{CLOCK_REALTIME, clock_settime, timespec};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use uucore::custom_tz_fmt::custom_time_format;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::FromIo;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
@ -75,7 +73,7 @@ struct Settings {
|
|||
utc: bool,
|
||||
format: Format,
|
||||
date_source: DateSource,
|
||||
set_to: Option<DateTime<FixedOffset>>,
|
||||
set_to: Option<Zoned>,
|
||||
}
|
||||
|
||||
/// Various ways of displaying the date
|
||||
|
@ -93,7 +91,7 @@ enum DateSource {
|
|||
Custom(String),
|
||||
File(PathBuf),
|
||||
Stdin,
|
||||
Human(TimeDelta),
|
||||
Human(SignedDuration),
|
||||
}
|
||||
|
||||
enum Iso8601Format {
|
||||
|
@ -167,9 +165,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
};
|
||||
|
||||
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
||||
let ref_time = Local::now();
|
||||
if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date.as_str()) {
|
||||
let duration = new_time.signed_duration_since(ref_time);
|
||||
if let Ok(duration) = parse_offset(date.as_str()) {
|
||||
DateSource::Human(duration)
|
||||
} else {
|
||||
DateSource::Custom(date.into())
|
||||
|
@ -203,39 +199,37 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
|
||||
if let Some(date) = settings.set_to {
|
||||
// All set time functions expect UTC datetimes.
|
||||
let date: DateTime<Utc> = if settings.utc {
|
||||
date.with_timezone(&Utc)
|
||||
let date = if settings.utc {
|
||||
date.with_time_zone(TimeZone::UTC)
|
||||
} else {
|
||||
date.into()
|
||||
date
|
||||
};
|
||||
|
||||
return set_system_datetime(date);
|
||||
} else {
|
||||
// Get the current time, either in the local time zone or UTC.
|
||||
let now: DateTime<FixedOffset> = if settings.utc {
|
||||
let now = Utc::now();
|
||||
now.with_timezone(&now.offset().fix())
|
||||
let now = if settings.utc {
|
||||
Timestamp::now().to_zoned(TimeZone::UTC)
|
||||
} else {
|
||||
let now = Local::now();
|
||||
now.with_timezone(now.offset())
|
||||
Zoned::now()
|
||||
};
|
||||
|
||||
// Iterate over all dates - whether it's a single date or a file.
|
||||
let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
|
||||
DateSource::Custom(ref input) => {
|
||||
let date = parse_date(input.clone());
|
||||
let date = parse_date(input);
|
||||
let iter = std::iter::once(date);
|
||||
Box::new(iter)
|
||||
}
|
||||
DateSource::Human(relative_time) => {
|
||||
// Double check the result is overflow or not of the current_time + relative_time
|
||||
// it may cause a panic of chrono::datetime::DateTime add
|
||||
match now.checked_add_signed(relative_time) {
|
||||
Some(date) => {
|
||||
match now.checked_add(relative_time) {
|
||||
Ok(date) => {
|
||||
let iter = std::iter::once(Ok(date));
|
||||
Box::new(iter)
|
||||
}
|
||||
None => {
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date {relative_time}"),
|
||||
|
@ -272,23 +266,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
// Format all the dates
|
||||
for date in dates {
|
||||
match date {
|
||||
Ok(date) => {
|
||||
let format_string = custom_time_format(format_string);
|
||||
// Hack to work around panic in chrono,
|
||||
// TODO - remove when a fix for https://github.com/chronotope/chrono/issues/623 is released
|
||||
let format_items = StrftimeItems::new(format_string.as_str());
|
||||
if format_items.clone().any(|i| i == Item::Error) {
|
||||
// TODO: Switch to lenient formatting.
|
||||
Ok(date) => match strtime::format(format_string, &date) {
|
||||
Ok(s) => println!("{s}"),
|
||||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid format {}", format_string.replace("%f", "%N")),
|
||||
format!("invalid format {} ({e})", format_string),
|
||||
));
|
||||
}
|
||||
let formatted = date
|
||||
.format_with_items(format_items)
|
||||
.to_string()
|
||||
.replace("%f", "%N");
|
||||
println!("{formatted}");
|
||||
}
|
||||
},
|
||||
Err((input, _err)) => show!(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date {}", input.quote())
|
||||
|
@ -388,13 +375,13 @@ fn make_format_string(settings: &Settings) -> &str {
|
|||
Iso8601Format::Hours => "%FT%H%:z",
|
||||
Iso8601Format::Minutes => "%FT%H:%M%:z",
|
||||
Iso8601Format::Seconds => "%FT%T%:z",
|
||||
Iso8601Format::Ns => "%FT%T,%f%:z",
|
||||
Iso8601Format::Ns => "%FT%T,%N%:z",
|
||||
},
|
||||
Format::Rfc5322 => "%a, %d %h %Y %T %z",
|
||||
Format::Rfc3339(ref fmt) => match *fmt {
|
||||
Rfc3339Format::Date => "%F",
|
||||
Rfc3339Format::Seconds => "%F %T%:z",
|
||||
Rfc3339Format::Ns => "%F %T.%f%:z",
|
||||
Rfc3339Format::Ns => "%F %T.%N%:z",
|
||||
},
|
||||
Format::Custom(ref fmt) => fmt,
|
||||
Format::Default => "%a %b %e %X %Z %Y",
|
||||
|
@ -403,19 +390,43 @@ fn make_format_string(settings: &Settings) -> &str {
|
|||
|
||||
/// Parse a `String` into a `DateTime`.
|
||||
/// If it fails, return a tuple of the `String` along with its `ParseError`.
|
||||
// TODO: Convert `parse_datetime` to jiff and remove wrapper from chrono to jiff structures.
|
||||
fn parse_date<S: AsRef<str> + Clone>(
|
||||
s: S,
|
||||
) -> Result<DateTime<FixedOffset>, (String, parse_datetime::ParseDateTimeError)> {
|
||||
parse_datetime::parse_datetime(s.as_ref()).map_err(|e| (s.as_ref().into(), e))
|
||||
) -> Result<Zoned, (String, parse_datetime::ParseDateTimeError)> {
|
||||
match parse_datetime::parse_datetime(s.as_ref()) {
|
||||
Ok(date) => {
|
||||
let timestamp =
|
||||
Timestamp::new(date.timestamp(), date.timestamp_subsec_nanos() as i32).unwrap();
|
||||
Ok(Zoned::new(timestamp, TimeZone::UTC))
|
||||
}
|
||||
Err(e) => Err((s.as_ref().into(), e)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Convert `parse_datetime` to jiff and remove wrapper from chrono to jiff structures.
|
||||
// Also, consider whether parse_datetime::parse_datetime_at_date can be renamed to something
|
||||
// like parse_datetime::parse_offset, instead of doing some addition/subtraction.
|
||||
fn parse_offset(date: &str) -> Result<SignedDuration, ()> {
|
||||
let ref_time = chrono::Local::now();
|
||||
if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date) {
|
||||
let duration = new_time.signed_duration_since(ref_time);
|
||||
Ok(SignedDuration::new(
|
||||
duration.num_seconds(),
|
||||
duration.subsec_nanos(),
|
||||
))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
unimplemented!("setting date not implemented (unsupported target)");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"setting the date is not supported by macOS".to_string(),
|
||||
|
@ -423,7 +434,7 @@ fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
|||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"setting the date is not supported by Redox".to_string(),
|
||||
|
@ -436,10 +447,11 @@ fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
|||
/// `<https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html>`
|
||||
/// `<https://linux.die.net/man/3/clock_settime>`
|
||||
/// `<https://www.gnu.org/software/libc/manual/html_node/Time-Types.html>`
|
||||
fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(date: Zoned) -> UResult<()> {
|
||||
let ts = date.timestamp();
|
||||
let timespec = timespec {
|
||||
tv_sec: date.timestamp() as _,
|
||||
tv_nsec: date.timestamp_subsec_nanos() as _,
|
||||
tv_sec: ts.as_second() as _,
|
||||
tv_nsec: ts.subsec_nanosecond() as _,
|
||||
};
|
||||
|
||||
let result = unsafe { clock_settime(CLOCK_REALTIME, ×pec) };
|
||||
|
@ -456,7 +468,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
|||
/// See here for more:
|
||||
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
|
||||
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
|
||||
fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(date: Zoned) -> UResult<()> {
|
||||
let system_time = SYSTEMTIME {
|
||||
wYear: date.year() as u16,
|
||||
wMonth: date.month() as u16,
|
||||
|
@ -467,7 +479,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
|||
wMinute: date.minute() as u16,
|
||||
wSecond: date.second() as u16,
|
||||
// TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
|
||||
wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as u16,
|
||||
wMilliseconds: ((date.subsec_nanosecond() / 1_000_000) % 1000) as u16,
|
||||
};
|
||||
|
||||
let result = unsafe { SetSystemTime(&system_time) };
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# spell-checker:ignore tzdb zoneinfo
|
||||
|
||||
[package]
|
||||
name = "uu_ls"
|
||||
description = "ls ~ (uutils) display directory contents"
|
||||
|
@ -23,6 +25,11 @@ chrono = { workspace = true }
|
|||
clap = { workspace = true, features = ["env"] }
|
||||
glob = { workspace = true }
|
||||
hostname = { workspace = true }
|
||||
jiff = { workspace = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
lscolors = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
selinux = { workspace = true, optional = true }
|
||||
|
@ -30,7 +37,6 @@ terminal_size = { workspace = true }
|
|||
thiserror = { workspace = true }
|
||||
uucore = { workspace = true, features = [
|
||||
"colors",
|
||||
"custom-tz-fmt",
|
||||
"entries",
|
||||
"format",
|
||||
"fs",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly nohash
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly nohash strtime
|
||||
|
||||
use std::iter;
|
||||
#[cfg(windows)]
|
||||
|
@ -16,24 +16,24 @@ use std::{
|
|||
fs::{self, DirEntry, FileType, Metadata, ReadDir},
|
||||
io::{BufWriter, ErrorKind, Stdout, Write, stdout},
|
||||
path::{Path, PathBuf},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
os::unix::fs::{FileTypeExt, MetadataExt},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::HashSet, io::IsTerminal};
|
||||
|
||||
use ansi_width::ansi_width;
|
||||
use chrono::format::{Item, StrftimeItems};
|
||||
use chrono::{DateTime, Local, TimeDelta};
|
||||
use clap::{
|
||||
Arg, ArgAction, Command,
|
||||
builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
|
||||
};
|
||||
use glob::{MatchOptions, Pattern};
|
||||
use jiff::fmt::StdIoWrite;
|
||||
use jiff::fmt::strtime::BrokenDownTime;
|
||||
use jiff::{Timestamp, Zoned};
|
||||
use lscolors::LsColors;
|
||||
use term_grid::{DEFAULT_SEPARATOR_SIZE, Direction, Filling, Grid, GridOptions, SPACES_IN_TAB};
|
||||
use thiserror::Error;
|
||||
|
@ -59,7 +59,6 @@ use uucore::libc::{dev_t, major, minor};
|
|||
use uucore::line_ending::LineEnding;
|
||||
use uucore::quoting_style::{self, QuotingStyle, escape_name};
|
||||
use uucore::{
|
||||
custom_tz_fmt,
|
||||
display::Quotable,
|
||||
error::{UError, UResult, set_exit_code},
|
||||
format_usage,
|
||||
|
@ -274,64 +273,37 @@ enum TimeStyle {
|
|||
Format(String),
|
||||
}
|
||||
|
||||
/// A struct/impl used to format a file DateTime, precomputing the format for performance reasons.
|
||||
struct TimeStyler {
|
||||
// default format, always specified.
|
||||
default: Vec<Item<'static>>,
|
||||
// format for a recent time, only specified it is is different from the default
|
||||
recent: Option<Vec<Item<'static>>>,
|
||||
// If `recent` is set, cache the threshold time when we switch from recent to default format.
|
||||
recent_time_threshold: Option<DateTime<Local>>,
|
||||
/// Whether the given date is considered recent (i.e., in the last 6 months).
|
||||
fn is_recent(time: Timestamp, state: &mut ListState) -> bool {
|
||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||
time > state.recent_time_threshold
|
||||
}
|
||||
|
||||
impl TimeStyler {
|
||||
/// Create a TimeStyler based on a TimeStyle specification.
|
||||
fn new(style: &TimeStyle) -> TimeStyler {
|
||||
let default: Vec<Item<'static>> = match style {
|
||||
TimeStyle::FullIso => StrftimeItems::new("%Y-%m-%d %H:%M:%S.%f %z").parse(),
|
||||
TimeStyle::LongIso => StrftimeItems::new("%Y-%m-%d %H:%M").parse(),
|
||||
TimeStyle::Iso => StrftimeItems::new("%Y-%m-%d ").parse(),
|
||||
// In this version of chrono translating can be done
|
||||
// The function is chrono::datetime::DateTime::format_localized
|
||||
// However it's currently still hard to get the current pure-rust-locale
|
||||
// So it's not yet implemented
|
||||
TimeStyle::Locale => StrftimeItems::new("%b %e %Y").parse(),
|
||||
TimeStyle::Format(fmt) => {
|
||||
StrftimeItems::new_lenient(custom_tz_fmt::custom_time_format(fmt).as_str())
|
||||
.parse_to_owned()
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
let recent = match style {
|
||||
TimeStyle::Iso => Some(StrftimeItems::new("%m-%d %H:%M")),
|
||||
// See comment above about locale
|
||||
TimeStyle::Locale => Some(StrftimeItems::new("%b %e %H:%M")),
|
||||
_ => None,
|
||||
}
|
||||
.map(|x| x.collect());
|
||||
let recent_time_threshold = if recent.is_some() {
|
||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||
Some(Local::now() - TimeDelta::try_seconds(31_556_952 / 2).unwrap())
|
||||
} else {
|
||||
None
|
||||
impl TimeStyle {
|
||||
/// Format the given time according to this time format style.
|
||||
fn format(
|
||||
&self,
|
||||
date: Zoned,
|
||||
out: &mut Vec<u8>,
|
||||
state: &mut ListState,
|
||||
) -> Result<(), jiff::Error> {
|
||||
let recent = is_recent(date.timestamp(), state);
|
||||
let tm = BrokenDownTime::from(&date);
|
||||
let mut out = StdIoWrite(out);
|
||||
let config = jiff::fmt::strtime::Config::new().lenient(true);
|
||||
|
||||
let fmt = match (self, recent) {
|
||||
(Self::FullIso, _) => "%Y-%m-%d %H:%M:%S.%f %z",
|
||||
(Self::LongIso, _) => "%Y-%m-%d %H:%M",
|
||||
(Self::Iso, true) => "%m-%d %H:%M",
|
||||
(Self::Iso, false) => "%Y-%m-%d ",
|
||||
// TODO: Using correct locale string is not implemented.
|
||||
(Self::Locale, true) => "%b %e %H:%M",
|
||||
(Self::Locale, false) => "%b %e %Y",
|
||||
(Self::Format(fmt), _) => fmt,
|
||||
};
|
||||
|
||||
TimeStyler {
|
||||
default,
|
||||
recent,
|
||||
recent_time_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a DateTime, using `recent` format if available, and the DateTime
|
||||
/// is recent enough.
|
||||
fn format(&self, time: DateTime<Local>) -> String {
|
||||
if self.recent.is_none() || time <= self.recent_time_threshold.unwrap() {
|
||||
time.format_with_items(self.default.iter())
|
||||
} else {
|
||||
time.format_with_items(self.recent.as_ref().unwrap().iter())
|
||||
}
|
||||
.to_string()
|
||||
tm.format_with_config(&config, fmt, &mut out)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2093,8 +2065,7 @@ struct ListState<'a> {
|
|||
uid_cache: HashMap<u32, String>,
|
||||
#[cfg(unix)]
|
||||
gid_cache: HashMap<u32, String>,
|
||||
|
||||
time_styler: TimeStyler,
|
||||
recent_time_threshold: Timestamp,
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
|
@ -2111,7 +2082,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
|||
uid_cache: HashMap::new(),
|
||||
#[cfg(unix)]
|
||||
gid_cache: HashMap::new(),
|
||||
time_styler: TimeStyler::new(&config.time_style),
|
||||
recent_time_threshold: Timestamp::now() - Duration::new(31_556_952 / 2, 0),
|
||||
};
|
||||
|
||||
for loc in locs {
|
||||
|
@ -2907,7 +2878,7 @@ fn display_item_long(
|
|||
};
|
||||
|
||||
output_display.extend(b" ");
|
||||
output_display.extend(display_date(md, config, state).as_bytes());
|
||||
display_date(md, config, state, &mut output_display)?;
|
||||
output_display.extend(b" ");
|
||||
|
||||
let item_name = display_item_name(
|
||||
|
@ -3106,15 +3077,27 @@ fn get_system_time(md: &Metadata, config: &Config) -> Option<SystemTime> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_time(md: &Metadata, config: &Config) -> Option<DateTime<Local>> {
|
||||
fn get_time(md: &Metadata, config: &Config) -> Option<Zoned> {
|
||||
let time = get_system_time(md, config)?;
|
||||
Some(time.into())
|
||||
time.try_into().ok()
|
||||
}
|
||||
|
||||
fn display_date(metadata: &Metadata, config: &Config, state: &mut ListState) -> String {
|
||||
fn display_date(
|
||||
metadata: &Metadata,
|
||||
config: &Config,
|
||||
state: &mut ListState,
|
||||
out: &mut Vec<u8>,
|
||||
) -> UResult<()> {
|
||||
match get_time(metadata, config) {
|
||||
Some(time) => state.time_styler.format(time),
|
||||
None => "???".into(),
|
||||
// TODO: Some fancier error conversion might be nice.
|
||||
Some(time) => config
|
||||
.time_style
|
||||
.format(time, out, state)
|
||||
.map_err(|x| USimpleError::new(1, x.to_string())),
|
||||
None => {
|
||||
out.extend(b"???");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ path = "src/lib/lib.rs"
|
|||
|
||||
[dependencies]
|
||||
chrono = { workspace = true, optional = true }
|
||||
chrono-tz = { workspace = true, optional = true }
|
||||
clap = { workspace = true }
|
||||
uucore_procs = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
|
@ -29,7 +28,6 @@ dns-lookup = { workspace = true, optional = true }
|
|||
dunce = { version = "1.0.4", optional = true }
|
||||
wild = "2.2.1"
|
||||
glob = { workspace = true, optional = true }
|
||||
iana-time-zone = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
time = { workspace = true, optional = true, features = [
|
||||
"formatting",
|
||||
|
@ -138,6 +136,5 @@ utf8 = []
|
|||
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
||||
version-cmp = []
|
||||
wide = []
|
||||
custom-tz-fmt = ["chrono", "chrono-tz", "iana-time-zone"]
|
||||
tty = []
|
||||
uptime = ["chrono", "libc", "windows-sys", "utmpx", "utmp-classic"]
|
||||
|
|
|
@ -14,8 +14,6 @@ pub mod buf_copy;
|
|||
pub mod checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub mod colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub mod custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub mod encoding;
|
||||
#[cfg(feature = "extendedbigdecimal")]
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono_tz::{OffsetName, Tz};
|
||||
use iana_time_zone::get_timezone;
|
||||
|
||||
/// Get the alphabetic abbreviation of the current timezone.
|
||||
///
|
||||
/// For example, "UTC" or "CET" or "PDT"
|
||||
fn timezone_abbreviation() -> String {
|
||||
let tz = match std::env::var("TZ") {
|
||||
// TODO Support other time zones...
|
||||
Ok(s) if s == "UTC0" || s.is_empty() => Tz::Etc__UTC,
|
||||
_ => match get_timezone() {
|
||||
Ok(tz_str) => tz_str.parse().unwrap(),
|
||||
Err(_) => Tz::Etc__UTC,
|
||||
},
|
||||
};
|
||||
|
||||
let offset = tz.offset_from_utc_date(&Utc::now().date_naive());
|
||||
offset.abbreviation().unwrap_or("UTC").to_string()
|
||||
}
|
||||
|
||||
/// Adapt the given string to be accepted by the chrono library crate.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// fmt: the format of the string
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// A string that can be used as parameter of the chrono functions that use formats
|
||||
pub fn custom_time_format(fmt: &str) -> String {
|
||||
// TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
|
||||
// chrono crashes on %#z, but it's the same as %z anyway.
|
||||
// GNU `date` uses `%N` for nano seconds, however the `chrono` crate uses `%f`.
|
||||
fmt.replace("%#z", "%z")
|
||||
.replace("%N", "%f")
|
||||
.replace("%Z", timezone_abbreviation().as_ref())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{custom_time_format, timezone_abbreviation};
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_format() {
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%d-%m-%Y %H-%M-%S"), "%d-%m-%Y %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(
|
||||
custom_time_format("%Y-%m-%d %H-%M-%S.%N"),
|
||||
"%Y-%m-%d %H-%M-%S.%f"
|
||||
);
|
||||
assert_eq!(custom_time_format("%Z"), timezone_abbreviation());
|
||||
}
|
||||
}
|
|
@ -41,8 +41,6 @@ pub use crate::features::buf_copy;
|
|||
pub use crate::features::checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub use crate::features::colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub use crate::features::custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub use crate::features::encoding;
|
||||
#[cfg(feature = "extendedbigdecimal")]
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
//
|
||||
// spell-checker: ignore: AEDT AEST EEST NZDT NZST
|
||||
|
||||
use chrono::{DateTime, Datelike, Duration, NaiveTime, Utc}; // spell-checker:disable-line
|
||||
use regex::Regex;
|
||||
|
@ -564,11 +566,153 @@ fn test_date_from_stdin() {
|
|||
);
|
||||
}
|
||||
|
||||
const JAN2: &str = "2024-01-02 12:00:00 +0000";
|
||||
const JUL2: &str = "2024-07-02 12:00:00 +0000";
|
||||
|
||||
#[test]
|
||||
fn test_date_empty_tz() {
|
||||
fn test_date_tz() {
|
||||
fn test_tz(tz: &str, date: &str, output: &str) {
|
||||
println!("Test with TZ={tz}, date=\"{date}\".");
|
||||
new_ucmd!()
|
||||
.env("TZ", tz)
|
||||
.arg("-d")
|
||||
.arg(date)
|
||||
.arg("+%Y-%m-%d %H:%M:%S %Z")
|
||||
.succeeds()
|
||||
.stdout_only(output);
|
||||
}
|
||||
|
||||
// Empty TZ, UTC0, invalid timezone.
|
||||
test_tz("", JAN2, "2024-01-02 12:00:00 UTC\n");
|
||||
test_tz("UTC0", JAN2, "2024-01-02 12:00:00 UTC\n");
|
||||
// TODO: We do not handle invalid timezones the same way as GNU coreutils
|
||||
//test_tz("Invalid/Timezone", JAN2, "2024-01-02 12:00:00 Invalid\n");
|
||||
|
||||
// Test various locations, some of them use daylight saving, some don't.
|
||||
test_tz("America/Vancouver", JAN2, "2024-01-02 04:00:00 PST\n");
|
||||
test_tz("America/Vancouver", JUL2, "2024-07-02 05:00:00 PDT\n");
|
||||
test_tz("Europe/Berlin", JAN2, "2024-01-02 13:00:00 CET\n");
|
||||
test_tz("Europe/Berlin", JUL2, "2024-07-02 14:00:00 CEST\n");
|
||||
test_tz("Africa/Cairo", JAN2, "2024-01-02 14:00:00 EET\n");
|
||||
// Egypt restored daylight saving in 2023, so if the database is outdated, this will fail.
|
||||
//test_tz("Africa/Cairo", JUL2, "2024-07-02 15:00:00 EEST\n");
|
||||
test_tz("Asia/Tokyo", JAN2, "2024-01-02 21:00:00 JST\n");
|
||||
test_tz("Asia/Tokyo", JUL2, "2024-07-02 21:00:00 JST\n");
|
||||
test_tz("Australia/Sydney", JAN2, "2024-01-02 23:00:00 AEDT\n");
|
||||
test_tz("Australia/Sydney", JUL2, "2024-07-02 22:00:00 AEST\n"); // Shifts the other way.
|
||||
test_tz("Pacific/Tahiti", JAN2, "2024-01-02 02:00:00 -10\n"); // No abbreviation.
|
||||
test_tz("Antarctica/South_Pole", JAN2, "2024-01-03 01:00:00 NZDT\n");
|
||||
test_tz("Antarctica/South_Pole", JUL2, "2024-07-03 00:00:00 NZST\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_tz_with_utc_flag() {
|
||||
new_ucmd!()
|
||||
.env("TZ", "")
|
||||
.env("TZ", "Europe/Berlin")
|
||||
.arg("-u")
|
||||
.arg("+%Z")
|
||||
.succeeds()
|
||||
.stdout_only("UTC\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_tz_various_formats() {
|
||||
fn test_tz(tz: &str, date: &str, output: &str) {
|
||||
println!("Test with TZ={tz}, date=\"{date}\".");
|
||||
new_ucmd!()
|
||||
.env("TZ", tz)
|
||||
.arg("-d")
|
||||
.arg(date)
|
||||
.arg("+%z %:z %::z %:::z %Z")
|
||||
.succeeds()
|
||||
.stdout_only(output);
|
||||
}
|
||||
|
||||
test_tz(
|
||||
"America/Vancouver",
|
||||
JAN2,
|
||||
"-0800 -08:00 -08:00:00 -08 PST\n",
|
||||
);
|
||||
// Half-hour timezone
|
||||
test_tz("Asia/Calcutta", JAN2, "+0530 +05:30 +05:30:00 +05:30 IST\n");
|
||||
test_tz("Europe/Berlin", JAN2, "+0100 +01:00 +01:00:00 +01 CET\n");
|
||||
test_tz(
|
||||
"Australia/Sydney",
|
||||
JAN2,
|
||||
"+1100 +11:00 +11:00:00 +11 AEDT\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_tz_with_relative_time() {
|
||||
new_ucmd!()
|
||||
.env("TZ", "America/Vancouver")
|
||||
.arg("-d")
|
||||
.arg("1 hour ago")
|
||||
.arg("+%Y-%m-%d %H:%M:%S %Z")
|
||||
.succeeds()
|
||||
.stdout_matches(&Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} P[DS]T\n$").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_utc_time() {
|
||||
// Test that -u flag shows correct UTC time
|
||||
// We get 2 UTC times just in case we're really unlucky and this runs around
|
||||
// an hour change.
|
||||
let utc_hour_1: i32 = new_ucmd!()
|
||||
.env("TZ", "Asia/Taipei")
|
||||
.arg("-u")
|
||||
.arg("+%-H")
|
||||
.succeeds()
|
||||
.stdout_str()
|
||||
.trim_end()
|
||||
.parse()
|
||||
.unwrap();
|
||||
let tpe_hour: i32 = new_ucmd!()
|
||||
.env("TZ", "Asia/Taipei")
|
||||
.arg("+%-H")
|
||||
.succeeds()
|
||||
.stdout_str()
|
||||
.trim_end()
|
||||
.parse()
|
||||
.unwrap();
|
||||
let utc_hour_2: i32 = new_ucmd!()
|
||||
.env("TZ", "Asia/Taipei")
|
||||
.arg("-u")
|
||||
.arg("+%-H")
|
||||
.succeeds()
|
||||
.stdout_str()
|
||||
.trim_end()
|
||||
.parse()
|
||||
.unwrap();
|
||||
// Taipei is always 8 hours ahead of UTC (no daylight savings)
|
||||
assert!(
|
||||
(tpe_hour - utc_hour_1 + 24) % 24 == 8 || (tpe_hour - utc_hour_2 + 24) % 24 == 8,
|
||||
"TPE: {tpe_hour} UTC: {utc_hour_1}/{utc_hour_2}"
|
||||
);
|
||||
|
||||
// Test that -u flag shows UTC timezone
|
||||
new_ucmd!()
|
||||
.arg("-u")
|
||||
.arg("+%Z")
|
||||
.succeeds()
|
||||
.stdout_only("UTC\n");
|
||||
|
||||
// Test that -u flag with specific timestamp shows correct UTC time
|
||||
new_ucmd!()
|
||||
.arg("-u")
|
||||
.arg("-d")
|
||||
.arg("@0")
|
||||
.succeeds()
|
||||
.stdout_only("Thu Jan 1 00:00:00 UTC 1970\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_empty_tz_time() {
|
||||
new_ucmd!()
|
||||
.env("TZ", "")
|
||||
.arg("-d")
|
||||
.arg("@0")
|
||||
.succeeds()
|
||||
.stdout_only("Thu Jan 1 00:00:00 UTC 1970\n");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue