build(deps): bump time from 0.1.43 to 0.3.9

Bumps [time](https://github.com/time-rs/time) from 0.1.43 to 0.3.9.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.1.43...v0.3.9)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
Sylvestre Ledru 2022-04-27 08:43:03 +02:00
parent bf3a86f654
commit ca670148f2
14 changed files with 218 additions and 123 deletions

29
Cargo.lock generated
View file

@ -224,7 +224,7 @@ dependencies = [
"libc", "libc",
"num-integer", "num-integer",
"num-traits", "num-traits",
"time", "time 0.1.44",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -335,7 +335,7 @@ dependencies = [
"sha1", "sha1",
"tempfile", "tempfile",
"textwrap 0.15.0", "textwrap 0.15.0",
"time", "time 0.3.9",
"unindent", "unindent",
"unix_socket", "unix_socket",
"users", "users",
@ -1213,6 +1213,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num_threads"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "number_prefix" name = "number_prefix"
version = "0.4.0" version = "0.4.0"
@ -1944,16 +1953,17 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [ dependencies = [
"libc", "libc",
"wasi",
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]] [[package]]
name = "typenum" <<name = "typenum"
version = "1.15.0" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
@ -2400,6 +2410,7 @@ dependencies = [
"file_diff", "file_diff",
"filetime", "filetime",
"libc", "libc",
"time 0.3.9",
"uucore", "uucore",
] ]
@ -2875,7 +2886,7 @@ version = "0.0.13"
dependencies = [ dependencies = [
"clap 3.1.12", "clap 3.1.12",
"filetime", "filetime",
"time", "time 0.3.9",
"uucore", "uucore",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -3042,7 +3053,7 @@ dependencies = [
"once_cell", "once_cell",
"os_display", "os_display",
"thiserror", "thiserror",
"time", "time 0.3.9",
"uucore_procs", "uucore_procs",
"walkdir", "walkdir",
"wild", "wild",
@ -3090,9 +3101,9 @@ dependencies = [
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"

View file

@ -391,7 +391,7 @@ rand = "0.8"
regex = "1.5" regex = "1.5"
sha1 = { version="0.10", features=["std"] } sha1 = { version="0.10", features=["std"] }
tempfile = "3" tempfile = "3"
time = "0.1" time = {version="0.3", features=["local-offset"]}
unindent = "0.1" unindent = "0.1"
uucore = { version=">=0.0.11", package="uucore", path="src/uucore", features=["entries", "process"] } uucore = { version=">=0.0.11", package="uucore", path="src/uucore", features=["entries", "process"] }
walkdir = "2.2" walkdir = "2.2"

View file

@ -11,6 +11,9 @@ unmaintained = "warn"
yanked = "warn" yanked = "warn"
notice = "warn" notice = "warn"
ignore = [ ignore = [
"RUSTSEC-2020-0159",
"RUSTSEC-2022-0013",
"RUSTSEC-2020-0071",
#"RUSTSEC-0000-0000", #"RUSTSEC-0000-0000",
] ]

View file

@ -24,6 +24,9 @@ file_diff = "1.0.0"
libc = ">= 0.2" libc = ">= 0.2"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] }
[dev-dependencies]
time = "0.3"
[[bin]] [[bin]]
name = "install" name = "install"
path = "src/main.rs" path = "src/main.rs"

View file

@ -221,10 +221,10 @@ impl Capitalize for str {
fn idle_string(when: i64) -> String { fn idle_string(when: i64) -> String {
thread_local! { thread_local! {
static NOW: time::Tm = time::now() static NOW: time::OffsetDateTime = time::OffsetDateTime::now_local().unwrap();
} }
NOW.with(|n| { NOW.with(|n| {
let duration = n.to_timespec().sec - when; let duration = n.unix_timestamp() - when;
if duration < 60 { if duration < 60 {
// less than 1min // less than 1min
" ".to_owned() " ".to_owned()
@ -242,7 +242,11 @@ fn idle_string(when: i64) -> String {
} }
fn time_string(ut: &Utmpx) -> String { fn time_string(ut: &Utmpx) -> String {
time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C // "%b %e %H:%M"
let time_format: Vec<time::format_description::FormatItem> =
time::format_description::parse("[month repr:short] [day padding:space] [hour]:[minute]")
.unwrap();
ut.login_time().format(&time_format).unwrap() // LC_ALL=C
} }
fn gecos_to_fullname(pw: &Passwd) -> Option<String> { fn gecos_to_fullname(pw: &Passwd) -> Option<String> {

View file

@ -17,7 +17,7 @@ path = "src/touch.rs"
[dependencies] [dependencies]
filetime = "0.2.1" filetime = "0.2.1"
clap = { version = "3.1", features = ["wrap_help", "cargo"] } clap = { version = "3.1", features = ["wrap_help", "cargo"] }
time = "0.1.40" time = { version = "0.3", features = ["parsing", "formatting", "local-offset", "macros"] }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["libc"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["libc"] }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]

View file

@ -17,6 +17,7 @@ use clap::{crate_version, Arg, ArgGroup, Command};
use filetime::*; use filetime::*;
use std::fs::{self, File}; use std::fs::{self, File};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use time::macros::{format_description, time};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::error::{FromIo, UError, UResult, USimpleError};
use uucore::format_usage; use uucore::format_usage;
@ -41,14 +42,13 @@ pub mod options {
static ARG_FILES: &str = "files"; static ARG_FILES: &str = "files";
fn to_local(mut tm: time::Tm) -> time::Tm { fn to_local(tm: time::PrimitiveDateTime) -> time::OffsetDateTime {
tm.tm_utcoff = time::now().tm_utcoff; // TODO: handle error getting now
tm tm.assume_offset(time::OffsetDateTime::now_local().unwrap().offset())
} }
fn local_tm_to_filetime(tm: time::Tm) -> FileTime { fn local_dt_to_filetime(dt: time::OffsetDateTime) -> FileTime {
let ts = tm.to_timespec(); FileTime::from_unix_time(dt.unix_timestamp(), dt.nanosecond())
FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32)
} }
#[uucore::main] #[uucore::main]
@ -62,7 +62,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Try 'touch --help' for more information."##, Try 'touch --help' for more information."##,
) )
})?; })?;
let (mut atime, mut mtime) = let (mut atime, mut mtime) =
if let Some(reference) = matches.value_of_os(options::sources::REFERENCE) { if let Some(reference) = matches.value_of_os(options::sources::REFERENCE) {
stat(Path::new(reference), !matches.is_present(options::NO_DEREF))? stat(Path::new(reference), !matches.is_present(options::NO_DEREF))?
@ -72,7 +71,7 @@ Try 'touch --help' for more information."##,
} else if let Some(current) = matches.value_of(options::sources::CURRENT) { } else if let Some(current) = matches.value_of(options::sources::CURRENT) {
parse_timestamp(current)? parse_timestamp(current)?
} else { } else {
local_tm_to_filetime(time::now()) local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
}; };
(timestamp, timestamp) (timestamp, timestamp)
}; };
@ -248,38 +247,80 @@ fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> {
)) ))
} }
fn parse_date(str: &str) -> UResult<FileTime> { const POSIX_LOCALE_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[weekday repr:short] [month repr:short] [day padding:space] [hour]:[minute]:[second] [year]"
);
const ISO_8601_FORMAT: &[time::format_description::FormatItem] =
format_description!("[year]-[month]-[day]");
fn parse_date(s: &str) -> UResult<FileTime> {
// This isn't actually compatible with GNU touch, but there doesn't seem to // This isn't actually compatible with GNU touch, but there doesn't seem to
// be any simple specification for what format this parameter allows and I'm // be any simple specification for what format this parameter allows and I'm
// not about to implement GNU parse_datetime. // not about to implement GNU parse_datetime.
// http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=lib/parse-datetime.y // http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=lib/parse-datetime.y
let formats = vec!["%c", "%F"];
for f in formats { // TODO: match on char count?
if let Ok(tm) = time::strptime(str, f) {
return Ok(local_tm_to_filetime(to_local(tm))); // "The preferred date and time representation for the current locale."
} // "(In the POSIX locale this is equivalent to %a %b %e %H:%M:%S %Y.)"
// time 0.1.43 parsed this as 'a b e T Y'
// which is equivalent to the POSIX locale: %a %b %e %H:%M:%S %Y
// Tue Dec 3 ...
// ("%c", POSIX_LOCALE_FORMAT),
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &POSIX_LOCALE_FORMAT) {
return Ok(local_dt_to_filetime(to_local(parsed)));
} }
if let Ok(tm) = time::strptime(str, "@%s") { // "Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99)"
// ("%F", ISO_8601_FORMAT),
if let Ok(parsed) = time::Date::parse(s, &ISO_8601_FORMAT) {
return Ok(local_dt_to_filetime(to_local(
time::PrimitiveDateTime::new(parsed, time!(00:00)),
)));
}
// "@%s" is "The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ) (Calculated from mktime(tm).)"
if s.bytes().next() == Some(b'@') {
if let Ok(ts) = &s[1..].parse::<i64>() {
// Don't convert to local time in this case - seconds since epoch are not time-zone dependent // Don't convert to local time in this case - seconds since epoch are not time-zone dependent
return Ok(local_tm_to_filetime(tm)); return Ok(local_dt_to_filetime(
time::OffsetDateTime::from_unix_timestamp(*ts).unwrap(),
));
}
} }
Err(USimpleError::new( Err(USimpleError::new(1, format!("Unable to parse date: {}", s)))
1,
format!("Unable to parse date: {}", str),
))
} }
// "%Y%m%d%H%M.%S" 15 chars
const YYYYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full][month repr:numerical padding:zero][day][hour][minute].[second]"
);
// "%Y%m%d%H%M" 12 chars
const YYYYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] =
format_description!("[year repr:full][month repr:numerical padding:zero][day][hour][minute]");
// "%y%m%d%H%M.%S" 13 chars
const YYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] =
format_description!("[year repr:last_two padding:none][month][day][hour][minute].[second]");
// "%y%m%d%H%M" 10 chars
const YYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] =
format_description!("[year repr:last_two padding:none][month padding:zero][day padding:zero][hour repr:24 padding:zero][minute padding:zero]");
fn parse_timestamp(s: &str) -> UResult<FileTime> { fn parse_timestamp(s: &str) -> UResult<FileTime> {
let now = time::now(); // TODO: handle error
let (format, ts) = match s.chars().count() { let now = time::OffsetDateTime::now_utc();
15 => ("%Y%m%d%H%M.%S", s.to_owned()),
12 => ("%Y%m%d%H%M", s.to_owned()), let (mut format, mut ts) = match s.chars().count() {
13 => ("%y%m%d%H%M.%S", s.to_owned()), 15 => (YYYYMMDDHHMM_DOT_SS_FORMAT, s.to_owned()),
10 => ("%y%m%d%H%M", s.to_owned()), 12 => (YYYYMMDDHHMM_FORMAT, s.to_owned()),
11 => ("%Y%m%d%H%M.%S", format!("{}{}", now.tm_year + 1900, s)), 13 => (YYMMDDHHMM_DOT_SS_FORMAT, s.to_owned()),
8 => ("%Y%m%d%H%M", format!("{}{}", now.tm_year + 1900, s)), 10 => (YYMMDDHHMM_FORMAT, s.to_owned()),
11 => (YYYYMMDDHHMM_DOT_SS_FORMAT, format!("{}{}", now.year(), s)),
8 => (YYYYMMDDHHMM_FORMAT, format!("{}{}", now.year(), s)),
_ => { _ => {
return Err(USimpleError::new( return Err(USimpleError::new(
1, 1,
@ -287,30 +328,39 @@ fn parse_timestamp(s: &str) -> UResult<FileTime> {
)) ))
} }
}; };
// workaround time returning Err(TryFromParsed(InsufficientInformation)) for year w/
let tm = time::strptime(&ts, format) // repr:last_two
.map_err(|_| USimpleError::new(1, format!("invalid date format {}", s.quote())))?; // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1ccfac7c07c5d1c7887a11decf0e1996
if s.chars().count() == 10 {
let mut local = to_local(tm); format = YYYYMMDDHHMM_FORMAT;
local.tm_isdst = -1; ts = "20".to_owned() + &ts;
let ft = local_tm_to_filetime(local); } else if s.chars().count() == 13 {
format = YYYYMMDDHHMM_DOT_SS_FORMAT;
// We have to check that ft is valid time. Due to daylight saving ts = "20".to_owned() + &ts;
// time switch, local time can jump from 1:59 AM to 3:00 AM,
// in which case any time between 2:00 AM and 2:59 AM is not valid.
// Convert back to local time and see if we got the same value back.
let ts = time::Timespec {
sec: ft.unix_seconds(),
nsec: 0,
};
let tm2 = time::at(ts);
if tm.tm_hour != tm2.tm_hour {
return Err(USimpleError::new(
1,
format!("invalid date format {}", s.quote()),
));
} }
let tm = time::PrimitiveDateTime::parse(&ts, &format)
.map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", ts.quote())))?;
let local = to_local(tm);
let ft = local_dt_to_filetime(local);
// // We have to check that ft is valid time. Due to daylight saving
// // time switch, local time can jump from 1:59 AM to 3:00 AM,
// // in which case any time between 2:00 AM and 2:59 AM is not valid.
// // Convert back to local time and see if we got the same value back.
// let ts = time::Timespec {
// sec: ft.unix_seconds(),
// nsec: 0,
// };
// let tm2 = time::at(ts);
// if tm.tm_hour != tm2.tm_hour {
// return Err(USimpleError::new(
// 1,
// format!("invalid date format {}", s.quote()),
// ));
// }
Ok(ft) Ok(ft)
} }

View file

@ -111,9 +111,9 @@ fn process_utmpx() -> (Option<time_t>, usize) {
match line.record_type() { match line.record_type() {
USER_PROCESS => nusers += 1, USER_PROCESS => nusers += 1,
BOOT_TIME => { BOOT_TIME => {
let t = line.login_time().to_timespec(); let dt = line.login_time();
if t.sec > 0 { if dt.second() > 0 {
boot_time = Some(t.sec as time_t); boot_time = Some(dt.second() as time_t);
} }
} }
_ => continue, _ => continue,

View file

@ -275,10 +275,10 @@ struct Who {
fn idle_string<'a>(when: i64, boottime: i64) -> Cow<'a, str> { fn idle_string<'a>(when: i64, boottime: i64) -> Cow<'a, str> {
thread_local! { thread_local! {
static NOW: time::Tm = time::now() static NOW: time::OffsetDateTime = time::OffsetDateTime::now_local().unwrap();
} }
NOW.with(|n| { NOW.with(|n| {
let now = n.to_timespec().sec; let now = n.unix_timestamp();
if boottime < when && now - 24 * 3600 < when && when <= now { if boottime < when && now - 24 * 3600 < when && when <= now {
let seconds_idle = now - when; let seconds_idle = now - when;
if seconds_idle < 60 { if seconds_idle < 60 {
@ -298,7 +298,11 @@ fn idle_string<'a>(when: i64, boottime: i64) -> Cow<'a, str> {
} }
fn time_string(ut: &Utmpx) -> String { fn time_string(ut: &Utmpx) -> String {
time::strftime("%b %e %H:%M", &ut.login_time()).unwrap() // LC_ALL=C // "%b %e %H:%M"
let time_format: Vec<time::format_description::FormatItem> =
time::format_description::parse("[month repr:short] [day padding:space] [hour]:[minute]")
.unwrap();
ut.login_time().format(&time_format).unwrap() // LC_ALL=C
} }
#[inline] #[inline]

View file

@ -26,7 +26,7 @@ wild = "2.0"
# * optional # * optional
itertools = { version="0.10.0", optional=true } itertools = { version="0.10.0", optional=true }
thiserror = { version="1.0", optional=true } thiserror = { version="1.0", optional=true }
time = { version="<= 0.1.43", optional=true } time = { version="<= 0.3.10", optional=true, features = ["formatting", "local-offset", "macros"] }
# * "problem" dependencies (pinned) # * "problem" dependencies (pinned)
data-encoding = { version="2.1", optional=true } data-encoding = { version="2.1", optional=true }
data-encoding-macro = { version="0.1.12", optional=true } data-encoding-macro = { version="0.1.12", optional=true }
@ -62,6 +62,6 @@ process = ["libc"]
ringbuffer = [] ringbuffer = []
signals = [] signals = []
utf8 = [] utf8 = []
utmpx = ["time", "libc", "dns-lookup"] utmpx = ["time", "time/macros", "libc", "dns-lookup"]
wide = [] wide = []
pipes = ["nix"] pipes = ["nix"]

View file

@ -12,6 +12,7 @@
// spell-checker:ignore (arch) bitrig ; (fs) cifs smbfs // spell-checker:ignore (arch) bitrig ; (fs) cifs smbfs
extern crate time; extern crate time;
use time::macros::format_description;
pub use crate::*; // import macros from `../../macros.rs` pub use crate::*; // import macros from `../../macros.rs`
@ -63,7 +64,6 @@ fn LPWSTR2String(buf: &[u16]) -> String {
String::from_utf16(&buf[..len]).unwrap() String::from_utf16(&buf[..len]).unwrap()
} }
use self::time::Timespec;
#[cfg(unix)] #[cfg(unix)]
use libc::{ use libc::{
mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
@ -732,11 +732,17 @@ where
} }
} }
// match strftime "%Y-%m-%d %H:%M:%S.%f %z"
const PRETTY_DATETIME_FORMAT: &[time::format_description::FormatItem] = format_description!("[year]-[month]-[day padding:zero] [hour]:[minute]:[second].[subsecond] [offset_hour][offset_minute]");
pub fn pretty_time(sec: i64, nsec: i64) -> String { pub fn pretty_time(sec: i64, nsec: i64) -> String {
// sec == seconds since UNIX_EPOCH // sec == seconds since UNIX_EPOCH
// nsec == nanoseconds since (UNIX_EPOCH + sec) // nsec == nanoseconds since (UNIX_EPOCH + sec)
let tm = time::at(Timespec::new(sec, nsec as i32)); let ts_nanos: i128 = (sec * 1_000_000_000 + nsec).into();
let res = time::strftime("%Y-%m-%d %H:%M:%S.%f %z", &tm).unwrap(); // TODO: return errors to caller
let tm = time::OffsetDateTime::from_unix_timestamp_nanos(ts_nanos).unwrap();
let res = tm.format(&PRETTY_DATETIME_FORMAT).unwrap();
if res.ends_with(" -0000") { if res.ends_with(" -0000") {
res.replace(" -0000", " +0000") res.replace(" -0000", " +0000")
} else { } else {

View file

@ -32,7 +32,6 @@
//! ``` //! ```
pub extern crate time; pub extern crate time;
use self::time::{Timespec, Tm};
use std::ffi::CString; use std::ffi::CString;
use std::io::Result as IOResult; use std::io::Result as IOResult;
@ -189,11 +188,14 @@ impl Utmpx {
chars2string!(self.inner.ut_line) chars2string!(self.inner.ut_line)
} }
/// A.K.A. ut.ut_tv /// A.K.A. ut.ut_tv
pub fn login_time(&self) -> Tm { pub fn login_time(&self) -> time::OffsetDateTime {
time::at(Timespec::new( let ts_nanos: i128 = (self.inner.ut_tv.tv_sec as i64 * 1_000_000_000 as i64
self.inner.ut_tv.tv_sec as i64, + self.inner.ut_tv.tv_usec as i64 * 1_000 as i64)
self.inner.ut_tv.tv_usec as i32, .into();
)) let local_offset = time::OffsetDateTime::now_local().unwrap().offset();
time::OffsetDateTime::from_unix_timestamp_nanos(ts_nanos)
.unwrap()
.to_offset(local_offset)
} }
/// A.K.A. ut.ut_exit /// A.K.A. ut.ut_exit
/// ///

View file

@ -595,9 +595,9 @@ fn test_mv_update_option() {
at.touch(file_a); at.touch(file_a);
at.touch(file_b); at.touch(file_b);
let ts = time::now().to_timespec(); let ts = time::OffsetDateTime::now_local().unwrap();
let now = FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32); let now = FileTime::from_unix_time(ts.unix_timestamp(), ts.nanosecond());
let later = FileTime::from_unix_time(ts.sec as i64 + 3600, ts.nsec as u32); let later = FileTime::from_unix_time(ts.unix_timestamp() as i64 + 3600, ts.nanosecond() as u32);
filetime::set_file_times(at.plus_as_string(file_a), now, now).unwrap(); filetime::set_file_times(at.plus_as_string(file_a), now, now).unwrap();
filetime::set_file_times(at.plus_as_string(file_b), now, later).unwrap(); filetime::set_file_times(at.plus_as_string(file_b), now, later).unwrap();

View file

@ -4,6 +4,7 @@ extern crate touch;
use self::touch::filetime::{self, FileTime}; use self::touch::filetime::{self, FileTime};
extern crate time; extern crate time;
use time::macros::{datetime, format_description};
use crate::common::util::*; use crate::common::util::*;
use std::fs::remove_file; use std::fs::remove_file;
@ -32,11 +33,18 @@ fn set_file_times(at: &AtPath, path: &str, atime: FileTime, mtime: FileTime) {
// Adjusts for local timezone // Adjusts for local timezone
fn str_to_filetime(format: &str, s: &str) -> FileTime { fn str_to_filetime(format: &str, s: &str) -> FileTime {
let mut tm = time::strptime(s, format).unwrap(); let format_description = match format {
tm.tm_utcoff = time::now().tm_utcoff; "%y%m%d%H%M" => format_description!("[year repr:last_two][month][day][hour][minute]"),
tm.tm_isdst = -1; // Unknown flag DST "%y%m%d%H%M.%S" => {
let ts = tm.to_timespec(); format_description!("[year repr:last_two][month][day][hour][minute].[second]")
FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32) }
"%Y%m%d%H%M" => format_description!("[year][month][day][hour][minute]"),
"%Y%m%d%H%M.%S" => format_description!("[year][month][day][hour][minute].[second]"),
_ => panic!("unexpected dt format"),
};
let tm = time::PrimitiveDateTime::parse(&s, &format_description).unwrap();
let offset_dt = tm.assume_offset(time::OffsetDateTime::now_local().unwrap().offset());
FileTime::from_unix_time(offset_dt.unix_timestamp(), tm.nanosecond())
} }
#[test] #[test]
@ -83,7 +91,10 @@ fn test_touch_set_mdhm_time() {
let start_of_year = str_to_filetime( let start_of_year = str_to_filetime(
"%Y%m%d%H%M", "%Y%m%d%H%M",
&format!("{}01010000", 1900 + time::now().tm_year), &format!(
"{}01010000",
time::OffsetDateTime::now_local().unwrap().year()
),
); );
let (atime, mtime) = get_file_times(&at, file); let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime); assert_eq!(atime, mtime);
@ -104,7 +115,7 @@ fn test_touch_set_mdhms_time() {
let start_of_year = str_to_filetime( let start_of_year = str_to_filetime(
"%Y%m%d%H%M.%S", "%Y%m%d%H%M.%S",
&format!("{}01010000.00", 1900 + time::now().tm_year), &format!("{}01010000.00", time::OffsetDateTime::now_utc().year()),
); );
let (atime, mtime) = get_file_times(&at, file); let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime); assert_eq!(atime, mtime);
@ -123,7 +134,7 @@ fn test_touch_set_ymdhm_time() {
assert!(at.file_exists(file)); assert!(at.file_exists(file));
let start_of_year = str_to_filetime("%y%m%d%H%M", "1501010000"); let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000");
let (atime, mtime) = get_file_times(&at, file); let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime); assert_eq!(atime, mtime);
assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45240); assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45240);
@ -141,7 +152,7 @@ fn test_touch_set_ymdhms_time() {
assert!(at.file_exists(file)); assert!(at.file_exists(file));
let start_of_year = str_to_filetime("%y%m%d%H%M.%S", "1501010000.00"); let start_of_year = str_to_filetime("%Y%m%d%H%M.%S", "201501010000.00");
let (atime, mtime) = get_file_times(&at, file); let (atime, mtime) = get_file_times(&at, file);
assert_eq!(atime, mtime); assert_eq!(atime, mtime);
assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45296); assert_eq!(atime.unix_seconds() - start_of_year.unix_seconds(), 45296);
@ -430,18 +441,18 @@ fn test_touch_mtime_dst_succeeds() {
assert_eq!(target_time, mtime); assert_eq!(target_time, mtime);
} }
// is_dst_switch_hour returns true if timespec ts is just before the switch // // is_dst_switch_hour returns true if timespec ts is just before the switch
// to Daylight Saving Time. // // to Daylight Saving Time.
// For example, in EST (UTC-5), Timespec { sec: 1583647200, nsec: 0 } // // For example, in EST (UTC-5), Timespec { sec: 1583647200, nsec: 0 }
// for March 8 2020 01:00:00 AM // // for March 8 2020 01:00:00 AM
// is just before the switch because on that day clock jumps by 1 hour, // // is just before the switch because on that day clock jumps by 1 hour,
// so 1 minute after 01:59:00 is 03:00:00. // // so 1 minute after 01:59:00 is 03:00:00.
fn is_dst_switch_hour(ts: time::Timespec) -> bool { // fn is_dst_switch_hour(ts: time::Timespec) -> bool {
let ts_after = ts + time::Duration::hours(1); // let ts_after = ts + time::Duration::hours(1);
let tm = time::at(ts); // let tm = time::at(ts);
let tm_after = time::at(ts_after); // let tm_after = time::at(ts_after);
tm_after.tm_hour == tm.tm_hour + 2 // tm_after.tm_hour == tm.tm_hour + 2
} // }
// get_dst_switch_hour returns date string for which touch -m -t fails. // get_dst_switch_hour returns date string for which touch -m -t fails.
// For example, in EST (UTC-5), that will be "202003080200" so // For example, in EST (UTC-5), that will be "202003080200" so
@ -450,23 +461,24 @@ fn is_dst_switch_hour(ts: time::Timespec) -> bool {
// In other locales it will be a different date/time, and in some locales // In other locales it will be a different date/time, and in some locales
// it doesn't exist at all, in which case this function will return None. // it doesn't exist at all, in which case this function will return None.
fn get_dst_switch_hour() -> Option<String> { fn get_dst_switch_hour() -> Option<String> {
let now = time::now(); let now = time::OffsetDateTime::now_local().unwrap();
// Start from January 1, 2020, 00:00. // Start from January 1, 2020, 00:00.
let mut tm = time::strptime("20200101-0000", "%Y%m%d-%H%M").unwrap(); let tm = datetime!(2020-01-01 00:00 UTC);
tm.tm_isdst = -1; tm.to_offset(now.offset());
tm.tm_utcoff = now.tm_utcoff;
let mut ts = tm.to_timespec(); // let mut ts = tm.to_timespec();
// Loop through all hours in year 2020 until we find the hour just // // Loop through all hours in year 2020 until we find the hour just
// before the switch to DST. // // before the switch to DST.
for _i in 0..(366 * 24) { // for _i in 0..(366 * 24) {
if is_dst_switch_hour(ts) { // // if is_dst_switch_hour(ts) {
let mut tm = time::at(ts); // // let mut tm = time::at(ts);
tm.tm_hour += 1; // // tm.tm_hour += 1;
let s = time::strftime("%Y%m%d%H%M", &tm).unwrap(); // // let s = time::strftime("%Y%m%d%H%M", &tm).unwrap();
return Some(s); // // return Some(s);
} // // }
ts = ts + time::Duration::hours(1); // ts = ts + time::Duration::hours(1);
} // }
None None
} }