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.

164 lines
6.5KB

  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. for your own safety and well-being, local time functionality is not provided.\n\n\
  29. default output format is rfc3339 with trailing 'Z', e.g. '1999-12-31T23:59:59.999999999Z'")
  30. .help_message("help")
  31. .version_message("version")
  32. .arg(clap::Arg::with_name("unix")
  33. .help("display elapsed nanoseconds since 1970-01-01T00:00:00Z")
  34. .long("unix")
  35. .short("u")
  36. .required(false)
  37. .takes_value(false))
  38. .arg(clap::Arg::with_name("rfc2822")
  39. .help("display in rfc2822 format")
  40. .long("rfc2822")
  41. .short("r")
  42. .required(false)
  43. .takes_value(false))
  44. .arg(clap::Arg::with_name("seconds")
  45. .help("display unix timestamp in seconds, instead of default nanoseconds")
  46. .long("seconds")
  47. .short("s")
  48. .conflicts_with_all(&["rfc2822", "timespec", "millis"])
  49. .required(false)
  50. .takes_value(false))
  51. .arg(clap::Arg::with_name("millis")
  52. .help("display unix timestamp in milliseconds, instead of default nanoseconds")
  53. .long("millis")
  54. .short("m")
  55. .conflicts_with_all(&["rfc2822", "timespec", "seconds"])
  56. .required(false)
  57. .takes_value(false))
  58. .arg(clap::Arg::with_name("timespec")
  59. .help("display as <sec>,<nsec>")
  60. .long("timespec")
  61. .short("t")
  62. .required(false)
  63. .takes_value(false))
  64. .arg(clap::Arg::with_name("null")
  65. .short("0")
  66. .long("null")
  67. .help("terminate displayed time with null character instead of newline")
  68. .takes_value(false)
  69. .required(false))
  70. .arg(clap::Arg::with_name("unix-to-rfc3339")
  71. .long("unix-to-rfc3339")
  72. .short("R")
  73. .alias("unix-to-utc")
  74. .help("convert integer unix timestamp (nanoseconds precision) to datetime \
  75. in rfc3339 format. combine with --seconds or --millis to parse a
  76. seconds timestamp with alternate precison")
  77. .takes_value(true)
  78. .required(false)
  79. .conflicts_with_all(&["unix"]))
  80. .arg(clap::Arg::with_name("rfc3339-to-unix")
  81. .long("rfc3339-to-unix")
  82. .short("U")
  83. .alias("utc-to-unix")
  84. .help("parse rfc3339 timestamp and display it as a unix timestamp")
  85. .takes_value(true)
  86. .required(false)
  87. .conflicts_with_all(&["unix", "unix-to-rfc3339", "rfc2822", "timespec"]))
  88. .get_matches();
  89. let (sec, nsec): (i64, i32) =
  90. if let Some(ts_str) = args.value_of("unix-to-rfc3339") { // either parse --unix-to-rfc3339 input
  91. match u64::from_str(ts_str) {
  92. // check alternate precisions --seconds or --millis
  93. Ok(seconds) if args.is_present("seconds") => nanos_to_timespec(seconds * ONE_SECOND),
  94. Ok(millis) if args.is_present("millis") => nanos_to_timespec(millis * 1_000_000),
  95. // otherwise go with default nanos
  96. Ok(nanos) => nanos_to_timespec(nanos),
  97. Err(e) => {
  98. eprintln!("failed to parse timestamp (expected integer): {}", e);
  99. std::process::exit(1);
  100. }
  101. }
  102. } else if let Some(rfc_str) = args.value_of("rfc3339-to-unix") { // or --rfc3339-to-unix
  103. //match DateTime::<Utc>::parse_from_rfc3339(rfc_str) {
  104. match rfc_str.parse::<DateTime<Utc>>() {
  105. Ok(utc) => nanos_to_timespec(nanos(utc)),
  106. Err(e) => {
  107. eprintln!("failed to parse timestamp (expected integer): {}", e);
  108. std::process::exit(1);
  109. }
  110. }
  111. } else { // or else get current time
  112. get_time()
  113. };
  114. let endline = if args.is_present("null") { "\u{0}" } else { "\n" }; // pick endline - \n unless -0
  115. if args.is_present("timespec") { // display timespec
  116. print!("{},{}{}", sec, nsec, endline);
  117. } else if args.is_present("unix") || args.is_present("rfc3339-to-unix") { // display unix
  118. if args.is_present("seconds") { // unix seconds
  119. print!("{}{}", sec, endline);
  120. } else if args.is_present("millis") { // unix millis
  121. print!("{}{}", timespec_to_nanos(sec, nsec) / 1_000_000, endline);
  122. } else { // unix nanos
  123. print!("{}{}", timespec_to_nanos(sec, nsec), endline);
  124. }
  125. } else if args.is_present("rfc2822") { // display rfc2822
  126. print!("{}{}", timespec_to_utc(sec, nsec).to_rfc2822(), endline);
  127. } else { // display rfc3339
  128. // Debug view is iso format / rfc3339 that we want
  129. // Display has spaces - no go
  130. // to_rfc3339 uses +00:00 instead of Z, which I don't like
  131. print!("{:?}{}", timespec_to_utc(sec, nsec), endline);
  132. }
  133. }