Skip to main content

nu_command/date/
humanize.rs

1use crate::date::utils::parse_date_from_string;
2use chrono::{DateTime, FixedOffset, Local};
3use nu_engine::command_prelude::*;
4
5#[derive(Clone)]
6pub struct DateHumanize;
7
8impl Command for DateHumanize {
9    fn name(&self) -> &str {
10        "date humanize"
11    }
12
13    fn signature(&self) -> Signature {
14        Signature::build("date humanize")
15            .input_output_types(vec![
16                (Type::Date, Type::String),
17                (Type::String, Type::String),
18                (
19                    Type::List(Box::new(Type::Date)),
20                    Type::List(Box::new(Type::String)),
21                ),
22                (
23                    Type::List(Box::new(Type::String)),
24                    Type::List(Box::new(Type::String)),
25                ),
26            ])
27            .allow_variants_without_examples(true)
28            .category(Category::Date)
29    }
30
31    fn description(&self) -> &str {
32        "Print a 'humanized' format for the date, relative to now."
33    }
34
35    fn search_terms(&self) -> Vec<&str> {
36        vec![
37            "relative",
38            "now",
39            "today",
40            "tomorrow",
41            "yesterday",
42            "weekday",
43            "weekday_name",
44            "timezone",
45        ]
46    }
47
48    fn run(
49        &self,
50        engine_state: &EngineState,
51        _stack: &mut Stack,
52        call: &Call,
53        input: PipelineData,
54    ) -> Result<PipelineData, ShellError> {
55        let head = call.head;
56        // This doesn't match explicit nulls
57        if let PipelineData::Empty = input {
58            return Err(ShellError::PipelineEmpty { dst_span: head });
59        }
60        input.map(move |value| helper(value, head), engine_state.signals())
61    }
62
63    fn examples(&self) -> Vec<Example<'_>> {
64        vec![
65            Example {
66                description: "Print a 'humanized' format for the date, relative to now.",
67                example: r#""2021-10-22 20:00:12 +01:00" | date humanize"#,
68                result: None,
69            },
70            Example {
71                description: "Humanize a list of datetimes.",
72                example: "[2021-10-22T20:00:12+01:00, 2021-10-23T20:00:00+01:00] | date humanize",
73                result: None,
74            },
75            Example {
76                description: "Humanize a list of date strings.",
77                example: r#"["2021-10-22 20:00:12 +01:00", "2021-10-22 21:00:00 +01:00"] | date humanize"#,
78                result: None,
79            },
80        ]
81    }
82}
83
84fn helper(value: Value, head: Span) -> Value {
85    let span = value.span();
86    match value {
87        Value::Nothing { .. } => {
88            let dt = Local::now();
89            Value::string(humanize_date(dt.with_timezone(dt.offset())), head)
90        }
91        Value::String { val, .. } => {
92            let dt = parse_date_from_string(&val, span);
93            match dt {
94                Ok(x) => Value::string(humanize_date(x), head),
95                Err(e) => e,
96            }
97        }
98        Value::Date { val, .. } => Value::string(humanize_date(val), head),
99        _ => Value::error(
100            ShellError::OnlySupportsThisInputType {
101                exp_input_type: "date, string (that represents datetime), or nothing".into(),
102                wrong_type: value.get_type().to_string(),
103                dst_span: head,
104                src_span: span,
105            },
106            head,
107        ),
108    }
109}
110
111fn humanize_date(dt: DateTime<FixedOffset>) -> String {
112    nu_protocol::human_time_from_now(&dt).to_string()
113}
114
115#[cfg(test)]
116mod test {
117    use super::*;
118
119    #[test]
120    fn test_examples() -> nu_test_support::Result {
121        nu_test_support::test().examples(DateHumanize)
122    }
123}