1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*!
# build-time

Simple proc-macros to generate build timestamp string literals.

Based on Jasen Borisov's [build_timestamp](https://crates.io/crates/build_timestamp) crate.

Two function like procedures are provided: `build_time_utc` and `build_time_local`.

They take an optional [`strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html)
date and time format string as input, and return a string literal.
If the input is empty, they will return a string literal in
[RFC 3339 date and time format](https://en.wikipedia.org/wiki/ISO_8601#RFCs),
e.g., `"2021-05-29T06:55:50.418437046+00:00"`.

Requires Rust 1.45+ because these macros are used in expression positions.

## Usage

```rust
use build_time::{build_time_utc, build_time_local};

// Returns the UTC build timestamp in RFC3339 date and time format.
let utc_build_time = build_time_utc!();

// Returns the local build timestamp in the specified format.
let local_build_time = build_time_local!("%Y-%m-%dT%H:%M:%S%.f%:z");
```
*/

use chrono::{DateTime, Local, Utc};
use once_cell::sync::Lazy;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse_macro_input, LitStr};

static BUILD_TIME: Lazy<DateTime<Utc>> = Lazy::new(Utc::now);

/// Build time in UTC.
///
/// It takes an optional [`strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html)
/// date and time format string as input, and returns a string literal.
/// If the input is empty, it will return a string literal in
/// [RFC 3339 date and time format](https://en.wikipedia.org/wiki/ISO_8601#RFCs),
/// e.g., `"2021-05-29T06:55:50.418437046+00:00"`.
///
/// # Example
///
/// ```rust
/// use build_time::build_time_utc;
///
/// // Returns the UTC build timestamp in RFC3339 date and time format.
/// let build_time_rfc3339 = build_time_utc!();
///
/// // Returns the UTC build timestamp in the specified format.
/// let build_time_formatted = build_time_utc!("%Y-%m-%dT%H:%M:%S%.f%:z");
/// ```
#[proc_macro]
pub fn build_time_utc(input: TokenStream) -> TokenStream {
    let time_str = if input.is_empty() {
        BUILD_TIME.to_rfc3339()
    } else {
        let format = parse_macro_input!(input as LitStr);
        BUILD_TIME.format(&format.value()).to_string()
    };

    let lit = LitStr::new(&time_str, Span::call_site());

    quote!(#lit).into()
}

/// Build time in the local timescale.
///
/// It takes an optional [`strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html)
/// date and time format string as input, and returns a string literal.
/// If the input is empty, it will return a string literal in
/// [RFC 3339 date and time format](https://en.wikipedia.org/wiki/ISO_8601#RFCs),
/// e.g., `"2021-05-29T06:55:50.418437046+00:00"`.
///
/// # Example
///
/// ```rust
/// use build_time::build_time_local;
///
/// // Returns the local build timestamp in RFC3339 date and time format.
/// let build_time_rfc3339 = build_time_local!();
///
/// // Returns the local build timestamp in the specified format.
/// let build_time_formatted = build_time_local!("%Y-%m-%dT%H:%M:%S%.f%:z");
/// ```
#[proc_macro]
pub fn build_time_local(input: TokenStream) -> TokenStream {
    let local_time = BUILD_TIME.with_timezone(&Local);
    let time_str = if input.is_empty() {
        local_time.to_rfc3339()
    } else {
        let format = parse_macro_input!(input as LitStr);
        local_time.format(&format.value()).to_string()
    };

    let lit = LitStr::new(&time_str, Span::call_site());

    quote!(#lit).into()
}