You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
6.6KB

  1. #![allow(unused)]
  2. use std::str::FromStr;
  3. use chrono::{DateTime, Utc, NaiveDateTime, TimeZone};
  4. const ONE_SECOND: u64 = 1_000_000_000_u64;
  5. fn nanos(utc: DateTime<Utc>) -> u64 {
  6. (utc.timestamp() as u64) * 1_000_000_000_u64 + (utc.timestamp_subsec_nanos() as u64)
  7. }
  8. fn get_time() -> (i64, i32) {
  9. let mut tv = libc::timespec { tv_sec: 0, tv_nsec: 0 };
  10. unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut tv); }
  11. (tv.tv_sec as i64, tv.tv_nsec as i32)
  12. }
  13. fn timespec_to_utc(sec: i64, nsec: i32) -> DateTime<Utc> {
  14. let naive = NaiveDateTime::from_timestamp(sec, nsec as u32);
  15. DateTime::from_utc(naive, Utc)
  16. }
  17. fn timespec_to_nanos(sec: i64, nsec: i32) -> u64 {
  18. (sec as u64) * ONE_SECOND + (nsec as u64)
  19. }
  20. fn nanos_to_timespec(nanos: u64) -> (i64, i32) {
  21. ((nanos / ONE_SECOND) as i64, (nanos % ONE_SECOND) as i32)
  22. }
  23. fn main() {
  24. let args: clap::ArgMatches = clap::App::new("utcnow")
  25. .author("Jonathan Strong <jonathan.strong@gmail.com>")
  26. .version(clap::crate_version!())
  27. .about("\ncurrent time in utc with non-cryptic interface.\n\n\
  28. default output format is rfc3339 with trailing 'Z', e.g. '1999-12-31T23:59:59.999999999Z'")
  29. .help_message("help")
  30. .version_message("version")
  31. .arg(clap::Arg::with_name("unix")
  32. .help("display elapsed nanoseconds since 1970-01-01T00:00:00Z")
  33. .long("unix")
  34. .short("u")
  35. .required(false)
  36. .takes_value(false))
  37. .arg(clap::Arg::with_name("rfc2822")
  38. .help("display in rfc2822 format")
  39. .long("rfc2822")
  40. .short("r")
  41. .required(false)
  42. .takes_value(false))
  43. .arg(clap::Arg::with_name("seconds")
  44. .help("display unix timestamp in seconds, instead of default nanoseconds")
  45. .long("seconds")
  46. .short("s")
  47. .conflicts_with_all(&["rfc2822", "timespec", "millis"])
  48. .required(false)
  49. .takes_value(false))
  50. .arg(clap::Arg::with_name("millis")
  51. .help("display unix timestamp in milliseconds, instead of default nanoseconds")
  52. .long("millis")
  53. .short("m")
  54. .conflicts_with_all(&["rfc2822", "timespec", "seconds"])
  55. .required(false)
  56. .takes_value(false))
  57. .arg(clap::Arg::with_name("timespec")
  58. .help("display as <sec>,<nsec>")
  59. .long("timespec")
  60. .short("t")
  61. .required(false)
  62. .takes_value(false))
  63. .arg(clap::Arg::with_name("null")
  64. .short("0")
  65. .long("null")
  66. .help("terminate displayed time with null character instead of newline")
  67. .takes_value(false)
  68. .required(false))
  69. .arg(clap::Arg::with_name("unix-to-rfc3339")
  70. .long("unix-to-rfc3339")
  71. .short("R")
  72. .alias("unix-to-utc")
  73. .help("convert integer unix timestamp (nanoseconds precision) to datetime \
  74. in rfc3339 format. combine with --seconds or --millis to parse a
  75. seconds timestamp with alternate precison")
  76. .takes_value(true)
  77. .required(false)
  78. .conflicts_with_all(&["unix"]))
  79. .arg(clap::Arg::with_name("rfc3339-to-unix")
  80. .long("rfc3339-to-unix")
  81. .short("U")
  82. .alias("utc-to-unix")
  83. .help("parse rfc3339 timestamp and display it as a unix timestamp")
  84. .takes_value(true)
  85. .required(false)
  86. .conflicts_with_all(&["unix", "unix-to-rfc3339", "rfc2822", "timespec"]))
  87. .arg(clap::Arg::with_name("est-tz")
  88. .long("est")
  89. .short("E")
  90. .help("display in us/eastern timezone")
  91. .takes_value(false)
  92. .conflicts_with_all(&["unix", "millis", "seconds", "timespec"]))
  93. .get_matches();
  94. let (sec, nsec): (i64, i32) =
  95. if let Some(ts_str) = args.value_of("unix-to-rfc3339") { // either parse --unix-to-rfc3339 input
  96. match u64::from_str(ts_str) {
  97. // check alternate precisions --seconds or --millis
  98. Ok(seconds) if args.is_present("seconds") => nanos_to_timespec(seconds * ONE_SECOND),
  99. Ok(millis) if args.is_present("millis") => nanos_to_timespec(millis * 1_000_000),
  100. // otherwise go with default nanos
  101. Ok(nanos) => nanos_to_timespec(nanos),
  102. Err(e) => {
  103. eprintln!("failed to parse timestamp (expected integer): {}", e);
  104. std::process::exit(1);
  105. }
  106. }
  107. } else if let Some(rfc_str) = args.value_of("rfc3339-to-unix") { // or --rfc3339-to-unix
  108. //match DateTime::<Utc>::parse_from_rfc3339(rfc_str) {
  109. match rfc_str.parse::<DateTime<Utc>>() {
  110. Ok(utc) => nanos_to_timespec(nanos(utc)),
  111. Err(e) => {
  112. eprintln!("failed to parse timestamp (expected integer): {}", e);
  113. std::process::exit(1);
  114. }
  115. }
  116. } else { // or else get current time
  117. get_time()
  118. };
  119. let endline = if args.is_present("null") { "\u{0}" } else { "\n" }; // pick endline - \n unless -0
  120. if args.is_present("timespec") { // display timespec
  121. print!("{},{}{}", sec, nsec, endline);
  122. } else if args.is_present("unix") || args.is_present("rfc3339-to-unix") { // display unix
  123. if args.is_present("seconds") { // unix seconds
  124. print!("{}{}", sec, endline);
  125. } else if args.is_present("millis") { // unix millis
  126. print!("{}{}", timespec_to_nanos(sec, nsec) / 1_000_000, endline);
  127. } else { // unix nanos
  128. print!("{}{}", timespec_to_nanos(sec, nsec), endline);
  129. }
  130. } else {
  131. let time = timespec_to_utc(sec, nsec);
  132. let time = if args.is_present("est-tz") {
  133. time.with_timezone(&chrono_tz::US::Eastern)
  134. } else {
  135. time.with_timezone(&chrono_tz::UTC)
  136. };
  137. let time_str = if args.is_present("rfc2822") { // display rfc2822
  138. time.to_rfc2822()
  139. } else {
  140. //time.to_rfc3339()
  141. format!("{:?}", time)
  142. };
  143. print!("{}{}", time_str, endline);
  144. }
  145. }