1pub mod approx;
4pub(crate) mod compat;
5pub mod parse;
6pub mod show;
7pub mod tm;
8
9use show::{parse_date_format, DateMode, DateModeType};
10use std::mem::size_of;
11use tm::{atoi_bytes, parse_timestamp_prefix, Timestamp};
12
13pub enum TestToolDateResult {
15 Output(Vec<String>),
16 Exit(i32),
17}
18
19pub fn test_tool_date(args: &[String]) -> Result<TestToolDateResult, String> {
21 if std::env::var_os("GIT_TEST_DATE_NOW").is_some() && std::env::var_os("TZ").is_none() {
24 std::env::set_var("TZ", "UTC");
25 unsafe extern "C" {
27 fn tzset();
28 }
29 unsafe {
30 tzset();
31 }
32 }
33 if args.is_empty() {
34 return Err("test-tool date: missing subcommand".to_string());
35 }
36 let sub = args[0].as_str();
37 let rest = &args[1..];
38
39 match sub {
40 "is64bit" => {
41 let code = if size_of::<Timestamp>() == 8 { 0 } else { 1 };
43 Ok(TestToolDateResult::Exit(code))
44 }
45 "time_t-is64bit" => {
46 let code = if size_of::<compat::time_t>() == 8 { 0 } else { 1 };
47 Ok(TestToolDateResult::Exit(code))
48 }
49 "relative" => {
50 let mut lines = Vec::new();
51 for a in rest {
52 let t: u64 = a
53 .parse()
54 .map_err(|_| format!("test-tool date relative: bad integer {a}"))?;
55 let s = show::show_date_relative(t, tm::get_time_sec());
56 lines.push(format!("{a} -> {s}"));
57 }
58 Ok(TestToolDateResult::Output(lines))
59 }
60 "human" => {
61 let mut lines = Vec::new();
62 for a in rest {
63 let t: u64 = a
64 .parse()
65 .map_err(|_| format!("test-tool date human: bad integer {a}"))?;
66 let mut mode = DateMode::from_type(DateModeType::Human);
67 let s = show::show_date(t, 0, &mut mode);
68 show::date_mode_release(&mut mode);
69 lines.push(format!("{a} -> {s}"));
70 }
71 Ok(TestToolDateResult::Output(lines))
72 }
73 "parse" => {
74 let mut lines = Vec::new();
75 for a in rest {
76 match parse::parse_date(a) {
77 Ok(ds) => {
78 let parts: Vec<&str> = ds.split_whitespace().collect();
79 if parts.len() >= 2 {
80 let t: u64 = parts[0].parse().map_err(|_| "bad parse output")?;
81 let tz = atoi_bytes(parts[1].as_bytes());
82 let mut mode = DateMode::from_type(DateModeType::Iso8601);
83 let out = show::show_date(t, tz, &mut mode);
84 show::date_mode_release(&mut mode);
85 lines.push(format!("{a} -> {out}"));
86 } else {
87 lines.push(format!("{a} -> bad"));
88 }
89 }
90 Err(()) => lines.push(format!("{a} -> bad")),
91 }
92 }
93 Ok(TestToolDateResult::Output(lines))
94 }
95 "approxidate" => {
96 let mut lines = Vec::new();
97 for a in rest {
98 let mut err = 0;
99 let t = approx::approxidate_careful(a, Some(&mut err));
100 let mut mode = DateMode::from_type(DateModeType::Iso8601);
101 let out = show::show_date(t, 0, &mut mode);
102 show::date_mode_release(&mut mode);
103 lines.push(format!("{a} -> {out}"));
104 }
105 Ok(TestToolDateResult::Output(lines))
106 }
107 s if s.starts_with("show:") => {
108 let format = s.strip_prefix("show:").unwrap_or("");
109 let mut mode = parse_date_format(format).map_err(|e| e.to_string())?;
110 let mut lines = Vec::new();
111 for a in rest {
112 let b = a.as_bytes();
113 let (t, n) = parse_timestamp_prefix(b);
114 let mut rest_b = &b[n..];
115 while rest_b.first() == Some(&b' ') {
116 rest_b = &rest_b[1..];
117 }
118 let tz = atoi_bytes(rest_b);
119 let s = show::show_date(t, tz, &mut mode);
120 lines.push(format!("{a} -> {s}"));
121 }
122 show::date_mode_release(&mut mode);
123 Ok(TestToolDateResult::Output(lines))
124 }
125 _ => Err(format!("test-tool date: unknown subcommand '{sub}'")),
126 }
127}