Skip to content

Commit af52e1e

Browse files
committed
move more stuff - TODO
1 parent d3ae31e commit af52e1e

File tree

6 files changed

+23
-222
lines changed

6 files changed

+23
-222
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/touch/src/touch.rs

Lines changed: 7 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ use time::macros::{format_description, offset, time};
1818
use time::Duration;
1919
use uucore::display::Quotable;
2020
use uucore::error::{FromIo, UError, UResult, USimpleError};
21+
use uucore::parse_date;
22+
use uucore::parse_date_common::local_dt_to_filetime;
2123
use uucore::parse_relative_time;
24+
use uucore::parse_timestamp;
25+
2226
use uucore::{format_usage, help_about, help_usage, show};
2327

2428
const ABOUT: &str = help_about!("touch.md");
@@ -42,29 +46,6 @@ pub mod options {
4246

4347
static ARG_FILES: &str = "files";
4448

45-
// Convert a date/time to a date with a TZ offset
46-
fn to_local(tm: time::PrimitiveDateTime) -> time::OffsetDateTime {
47-
let offset = match time::OffsetDateTime::now_local() {
48-
Ok(lo) => lo.offset(),
49-
Err(e) => {
50-
panic!("error: {e}");
51-
}
52-
};
53-
tm.assume_offset(offset)
54-
}
55-
56-
// Convert a date/time with a TZ offset into a FileTime
57-
fn local_dt_to_filetime(dt: time::OffsetDateTime) -> FileTime {
58-
FileTime::from_unix_time(dt.unix_timestamp(), dt.nanosecond())
59-
}
60-
61-
// Convert a date/time, considering that the input is in UTC time
62-
// Used for touch -d 1970-01-01 18:43:33.023456789 for example
63-
fn dt_to_filename(tm: time::PrimitiveDateTime) -> FileTime {
64-
let dt = tm.assume_offset(offset!(UTC));
65-
local_dt_to_filetime(dt)
66-
}
67-
6849
#[uucore::main]
6950
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
7051
let matches = uu_app().try_get_matches_from(args)?;
@@ -108,21 +89,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
10889

10990
(atime, mtime)
11091
} else {
111-
let timestamp = parse_date(date)?;
92+
let timestamp = parse_date::from_str(date)?;
11293
(timestamp, timestamp)
11394
}
11495
}
11596
(Some(reference), None) => {
11697
stat(Path::new(reference), !matches.get_flag(options::NO_DEREF))?
11798
}
11899
(None, Some(date)) => {
119-
let timestamp = parse_date(date)?;
100+
let timestamp = parse_date::from_str(date)?;
120101
(timestamp, timestamp)
121102
}
122103
(None, None) => {
123104
let timestamp =
124105
if let Some(current) = matches.get_one::<String>(options::sources::CURRENT) {
125-
parse_timestamp(current)?
106+
parse_timestamp::from_str(current)?
126107
} else {
127108
local_dt_to_filetime(time::OffsetDateTime::now_local().unwrap())
128109
};
@@ -320,192 +301,6 @@ fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> {
320301
))
321302
}
322303

323-
const POSIX_LOCALE_FORMAT: &[time::format_description::FormatItem] = format_description!(
324-
"[weekday repr:short] [month repr:short] [day padding:space] \
325-
[hour]:[minute]:[second] [year]"
326-
);
327-
328-
const ISO_8601_FORMAT: &[time::format_description::FormatItem] =
329-
format_description!("[year]-[month]-[day]");
330-
331-
// "%Y%m%d%H%M.%S" 15 chars
332-
const YYYYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
333-
"[year repr:full][month repr:numerical padding:zero]\
334-
[day][hour][minute].[second]"
335-
);
336-
337-
// "%Y-%m-%d %H:%M:%S.%SS" 12 chars
338-
const YYYYMMDDHHMMSS_FORMAT: &[time::format_description::FormatItem] = format_description!(
339-
"[year repr:full]-[month repr:numerical padding:zero]-\
340-
[day] [hour]:[minute]:[second].[subsecond]"
341-
);
342-
343-
// "%Y-%m-%d %H:%M:%S" 12 chars
344-
const YYYYMMDDHHMMS_FORMAT: &[time::format_description::FormatItem] = format_description!(
345-
"[year repr:full]-[month repr:numerical padding:zero]-\
346-
[day] [hour]:[minute]:[second]"
347-
);
348-
349-
// "%Y-%m-%d %H:%M" 12 chars
350-
// Used for example in tests/touch/no-rights.sh
351-
const YYYY_MM_DD_HH_MM_FORMAT: &[time::format_description::FormatItem] = format_description!(
352-
"[year repr:full]-[month repr:numerical padding:zero]-\
353-
[day] [hour]:[minute]"
354-
);
355-
356-
// "%Y%m%d%H%M" 12 chars
357-
const YYYYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
358-
"[year repr:full][month repr:numerical padding:zero]\
359-
[day][hour][minute]"
360-
);
361-
362-
// "%y%m%d%H%M.%S" 13 chars
363-
const YYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
364-
"[year repr:last_two padding:none][month][day]\
365-
[hour][minute].[second]"
366-
);
367-
368-
// "%y%m%d%H%M" 10 chars
369-
const YYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
370-
"[year repr:last_two padding:none][month padding:zero][day padding:zero]\
371-
[hour repr:24 padding:zero][minute padding:zero]"
372-
);
373-
374-
// "%Y-%m-%d %H:%M +offset"
375-
// Used for example in tests/touch/relative.sh
376-
const YYYYMMDDHHMM_OFFSET_FORMAT: &[time::format_description::FormatItem] = format_description!(
377-
"[year]-[month]-[day] [hour repr:24]:[minute] \
378-
[offset_hour sign:mandatory][offset_minute]"
379-
);
380-
381-
fn parse_date(s: &str) -> UResult<FileTime> {
382-
// This isn't actually compatible with GNU touch, but there doesn't seem to
383-
// be any simple specification for what format this parameter allows and I'm
384-
// not about to implement GNU parse_datetime.
385-
// http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=lib/parse-datetime.y
386-
387-
// TODO: match on char count?
388-
389-
// "The preferred date and time representation for the current locale."
390-
// "(In the POSIX locale this is equivalent to %a %b %e %H:%M:%S %Y.)"
391-
// time 0.1.43 parsed this as 'a b e T Y'
392-
// which is equivalent to the POSIX locale: %a %b %e %H:%M:%S %Y
393-
// Tue Dec 3 ...
394-
// ("%c", POSIX_LOCALE_FORMAT),
395-
//
396-
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &POSIX_LOCALE_FORMAT) {
397-
return Ok(local_dt_to_filetime(to_local(parsed)));
398-
}
399-
400-
// Also support other formats found in the GNU tests like
401-
// in tests/misc/stat-nanoseconds.sh
402-
// or tests/touch/no-rights.sh
403-
for fmt in [
404-
YYYYMMDDHHMMS_FORMAT,
405-
YYYYMMDDHHMMSS_FORMAT,
406-
YYYY_MM_DD_HH_MM_FORMAT,
407-
YYYYMMDDHHMM_OFFSET_FORMAT,
408-
] {
409-
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &fmt) {
410-
return Ok(dt_to_filename(parsed));
411-
}
412-
}
413-
414-
// "Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99)"
415-
// ("%F", ISO_8601_FORMAT),
416-
if let Ok(parsed) = time::Date::parse(s, &ISO_8601_FORMAT) {
417-
return Ok(local_dt_to_filetime(to_local(
418-
time::PrimitiveDateTime::new(parsed, time!(00:00)),
419-
)));
420-
}
421-
422-
// "@%s" is "The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). (TZ) (Calculated from mktime(tm).)"
423-
if s.bytes().next() == Some(b'@') {
424-
if let Ok(ts) = &s[1..].parse::<i64>() {
425-
// Don't convert to local time in this case - seconds since epoch are not time-zone dependent
426-
return Ok(local_dt_to_filetime(
427-
time::OffsetDateTime::from_unix_timestamp(*ts).unwrap(),
428-
));
429-
}
430-
}
431-
432-
if let Some(duration) = parse_relative_time::from_str(s) {
433-
let now_local = time::OffsetDateTime::now_local().unwrap();
434-
let diff = now_local.checked_add(duration).unwrap();
435-
return Ok(local_dt_to_filetime(diff));
436-
}
437-
438-
Err(USimpleError::new(1, format!("Unable to parse date: {s}")))
439-
}
440-
441-
fn parse_timestamp(s: &str) -> UResult<FileTime> {
442-
// TODO: handle error
443-
let now = time::OffsetDateTime::now_utc();
444-
445-
let (mut format, mut ts) = match s.chars().count() {
446-
15 => (YYYYMMDDHHMM_DOT_SS_FORMAT, s.to_owned()),
447-
12 => (YYYYMMDDHHMM_FORMAT, s.to_owned()),
448-
13 => (YYMMDDHHMM_DOT_SS_FORMAT, s.to_owned()),
449-
10 => (YYMMDDHHMM_FORMAT, s.to_owned()),
450-
11 => (YYYYMMDDHHMM_DOT_SS_FORMAT, format!("{}{}", now.year(), s)),
451-
8 => (YYYYMMDDHHMM_FORMAT, format!("{}{}", now.year(), s)),
452-
_ => {
453-
return Err(USimpleError::new(
454-
1,
455-
format!("invalid date format {}", s.quote()),
456-
))
457-
}
458-
};
459-
// workaround time returning Err(TryFromParsed(InsufficientInformation)) for year w/
460-
// repr:last_two
461-
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1ccfac7c07c5d1c7887a11decf0e1996
462-
if s.chars().count() == 10 {
463-
format = YYYYMMDDHHMM_FORMAT;
464-
ts = "20".to_owned() + &ts;
465-
} else if s.chars().count() == 13 {
466-
format = YYYYMMDDHHMM_DOT_SS_FORMAT;
467-
ts = "20".to_owned() + &ts;
468-
}
469-
470-
let leap_sec = if (format == YYYYMMDDHHMM_DOT_SS_FORMAT || format == YYMMDDHHMM_DOT_SS_FORMAT)
471-
&& ts.ends_with(".60")
472-
{
473-
// Work around to disable leap seconds
474-
// Used in gnu/tests/touch/60-seconds
475-
ts = ts.replace(".60", ".59");
476-
true
477-
} else {
478-
false
479-
};
480-
481-
let tm = time::PrimitiveDateTime::parse(&ts, &format)
482-
.map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", ts.quote())))?;
483-
let mut local = to_local(tm);
484-
if leap_sec {
485-
// We are dealing with a leap second, add it
486-
local = local.saturating_add(Duration::SECOND);
487-
}
488-
let ft = local_dt_to_filetime(local);
489-
490-
// // We have to check that ft is valid time. Due to daylight saving
491-
// // time switch, local time can jump from 1:59 AM to 3:00 AM,
492-
// // in which case any time between 2:00 AM and 2:59 AM is not valid.
493-
// // Convert back to local time and see if we got the same value back.
494-
// let ts = time::Timespec {
495-
// sec: ft.unix_seconds(),
496-
// nsec: 0,
497-
// };
498-
// let tm2 = time::at(ts);
499-
// if tm.tm_hour != tm2.tm_hour {
500-
// return Err(USimpleError::new(
501-
// 1,
502-
// format!("invalid date format {}", s.quote()),
503-
// ));
504-
// }
505-
506-
Ok(ft)
507-
}
508-
509304
// TODO: this may be a good candidate to put in fsext.rs
510305
/// Returns a PathBuf to stdout.
511306
///

src/uucore/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ glob = "0.3.1"
2727
# * optional
2828
itertools = { version="0.10.5", optional=true }
2929
thiserror = { workspace=true, optional=true }
30+
filetime = { workspace=true }
3031
time = { workspace=true, optional=true, features = ["formatting", "local-offset", "macros"] }
3132
# * "problem" dependencies (pinned)
3233
data-encoding = { version="2.3", optional=true }

src/uucore/src/lib/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ pub use crate::mods::ranges;
2929
pub use crate::mods::version_cmp;
3030

3131
// * string parsing modules
32+
pub use crate::parser::parse_date;
33+
pub use crate::parser::parse_date_common;
3234
pub use crate::parser::parse_glob;
3335
pub use crate::parser::parse_relative_time;
3436
pub use crate::parser::parse_size;
3537
pub use crate::parser::parse_time;
38+
pub use crate::parser::parse_timestamp;
3639

3740
// * feature-gated modules
3841
#[cfg(feature = "encoding")]

src/uucore/src/lib/parser.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
pub mod parse_date;
2+
pub mod parse_date_common;
13
pub mod parse_glob;
24
pub mod parse_relative_time;
35
pub mod parse_size;
46
pub mod parse_time;
7+
pub mod parse_timestamp;

src/uucore/src/lib/parser/parse_date.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ pub fn from_str(s: &str) -> UResult<FileTime> {
2929
// Tue Dec 3 ...
3030
// ("%c", POSIX_LOCALE_FORMAT),
3131
//
32-
if let Ok(parsed) = time::OffsetDateTime::parse(s, &POSIX_LOCALE_FORMAT) {
32+
33+
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &POSIX_LOCALE_FORMAT) {
3334
return Ok(local_dt_to_filetime(to_local(parsed)));
3435
}
3536

@@ -109,21 +110,18 @@ mod tests {
109110
#[test]
110111
fn test_from_str_iso_8601_format() {
111112
let s = "2023-04-23";
112-
let expected = local_dt_to_filetime(
113-
to_local(time::PrimitiveDateTime::new(
114-
time::Date::parse(s, &ISO_8601_FORMAT).unwrap(),
115-
time!(00:00),
116-
)),
117-
);
113+
let expected = local_dt_to_filetime(to_local(time::PrimitiveDateTime::new(
114+
time::Date::parse(s, &ISO_8601_FORMAT).unwrap(),
115+
time!(00:00),
116+
)));
118117
assert_file_time_eq(s, expected);
119118
}
120119

121120
#[test]
122121
fn test_from_str_seconds_since_epoch() {
123122
let s = "@1609459200";
124-
let expected = local_dt_to_filetime(
125-
time::OffsetDateTime::from_unix_timestamp(1609459200).unwrap(),
126-
);
123+
let expected =
124+
local_dt_to_filetime(time::OffsetDateTime::from_unix_timestamp(1609459200).unwrap());
127125
assert_file_time_eq(s, expected);
128126
}
129127

0 commit comments

Comments
 (0)