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.

144 lines
5.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. .requires("unix")
  49. .conflicts_with_all(&["rfc2822", "timespec"])
  50. .required(false)
  51. .takes_value(false))
  52. .arg(clap::Arg::with_name("timespec")
  53. .help("display as <sec>,<nsec>")
  54. .long("timespec")
  55. .short("t")
  56. .required(false)
  57. .takes_value(false))
  58. .arg(clap::Arg::with_name("null")
  59. .short("0")
  60. .long("null")
  61. .help("terminate displayed time with null character instead of newline")
  62. .takes_value(false)
  63. .required(false))
  64. .arg(clap::Arg::with_name("unix-to-rfc3339")
  65. .long("unix-to-rfc3339")
  66. .alias("unix-to-utc")
  67. .help("convert integer unix timestamp (nanoseconds precision) to datetime in rfc3339 format")
  68. .takes_value(true)
  69. .required(false)
  70. .conflicts_with_all(&["unix", "seconds"]))
  71. .arg(clap::Arg::with_name("rfc3339-to-unix")
  72. .long("rfc3339-to-unix")
  73. .alias("utc-to-unix")
  74. .help("parse rfc3339 timestamp and display it as a unix timestamp")
  75. .takes_value(true)
  76. .required(false)
  77. .conflicts_with_all(&["unix", "unix-to-rfc3339", "rfc2822", "timespec"]))
  78. .get_matches();
  79. let (sec, nsec): (i64, i32) =
  80. if let Some(ts_str) = args.value_of("unix-to-rfc3339") { // either parse --unix-to-rfc3339 input
  81. match u64::from_str(ts_str) {
  82. Ok(nanos) => nanos_to_timespec(nanos),
  83. Err(e) => {
  84. eprintln!("failed to parse timestamp (expected integer): {}", e);
  85. std::process::exit(1);
  86. }
  87. }
  88. } else if let Some(rfc_str) = args.value_of("rfc3339-to-unix") { // or --rfc3339-to-unix
  89. //match DateTime::<Utc>::parse_from_rfc3339(rfc_str) {
  90. match rfc_str.parse::<DateTime<Utc>>() {
  91. Ok(utc) => nanos_to_timespec(nanos(utc)),
  92. Err(e) => {
  93. eprintln!("failed to parse timestamp (expected integer): {}", e);
  94. std::process::exit(1);
  95. }
  96. }
  97. } else { // or else get current time
  98. get_time()
  99. };
  100. let endline = if args.is_present("null") { "\u{0}" } else { "\n" }; // pick endline - \n unless -0
  101. if args.is_present("timespec") { // display timespec
  102. print!("{},{}{}", sec, nsec, endline);
  103. } else if args.is_present("unix") || args.is_present("rfc3339-to-unix") { // display unix
  104. if args.is_present("seconds") { // unix seconds
  105. print!("{}{}", sec, endline);
  106. } else { // unix nanos
  107. print!("{}{}", timespec_to_nanos(sec, nsec), endline);
  108. }
  109. } else if args.is_present("rfc2822") { // display rfc2822
  110. print!("{}{}", timespec_to_utc(sec, nsec).to_rfc2822(), endline);
  111. } else { // display rfc3339
  112. // Debug view is iso format / rfc3339 that we want
  113. // Display has spaces - no go
  114. // to_rfc3339 uses +00:00 instead of Z, which I don't like
  115. print!("{:?}{}", timespec_to_utc(sec, nsec), endline);
  116. }
  117. }