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 106 107 108 109 110
//! # unix-ts-macros: A macro to quickly generate unix-ts timestamps.
//!
//! unix-ts-macros simplifies the creation of timestamps into a procedural
//! macro: `ts`.
//!
//! ## Usage
//!
//! Add the create as well as unix-ts to your `Cargo.toml` file like usual:
//!
//! ```toml
//! [dependencies]
//! unix-ts = "0.1"
//! unix-ts-macros = "0.1"
//! ```
//!
//! You can create a timestamp with the `ts!` macro, which takes the Unix
//! timestamp as an argument:
//!
//! ```
//! use unix_ts_macros::ts;
//!
//! // The argument is the number of seconds since the Unix epoch.
//! let t = ts!(1335020400);
//!
//! // Fractional seconds are also allowed.
//! let t2 = ts!(1335020400.25);
//! ```
#![crate_name = "unix_ts_macros"]
extern crate proc_macro;
use proc_macro::TokenStream;
/// Create a timestamp from the given Unix timestamp.
///
/// # Examples
///
/// ```
/// use unix_ts_macros::ts;
///
/// let t = ts!(1335020400);
/// assert_eq!(t.seconds(), 1335020400);
///
/// let t = ts!(1335020400.25);
/// assert_eq!(t.seconds(), 1335020400);
/// assert_eq!(t.subsec(3), 250);
///
/// let t = ts!(-86400);
/// assert_eq!(t.seconds(), -86400);
/// ```
#[proc_macro]
pub fn ts(input: TokenStream) -> TokenStream {
let mut src = input.to_string().trim_start().trim_end().to_owned();
if src.len() == 0 {
panic!("No input to ts! macro.");
}
// If we have a sign bit, deal with it.
let neg = src.starts_with('-');
src = src.trim_start_matches('-').trim_start().to_owned();
// If there is no decimal point, this is an integer;
// return a timestamp from it.
if !src.contains('.') {
return format!(
"::unix_ts::Timestamp::from({}{})",
if neg { '-' } else { ' ' },
src
)
.parse()
.unwrap();
}
// If we start with a decimal point, prepend a zero.
if src.starts_with('.') {
src = format!("0{}", src);
}
// Split into two strings for whole seconds and nanos and return the
// appropriate Timestamp.
let src: Vec<&str> = src.split('.').collect();
if src.len() > 2 {
panic!("Unrecognized input to ts! macro.");
}
let mut seconds = src[0].parse::<i64>().unwrap();
let mut nanos = src[1].to_owned();
while nanos.len() < 9 {
nanos += "0";
}
// If nanos is anything other than zero, we actually need to decrement
// the seconds by one. This is because the nanos is always positive;
// otherwise representing -0.5 seconds would be impossible.
//
// Note: This counter-intuitively means *adding* one here because we are
// tracking our sign bit separately.
if neg && nanos != "000000000" {
seconds += 1;
}
// Return the new timestamp.
return format!(
"::unix_ts::Timestamp::new({}{}, {})",
if neg { '-' } else { ' ' },
seconds,
nanos[0..9].to_string(),
)
.parse()
.unwrap();
}