summaryrefslogtreecommitdiff
path: root/meap/meap-code/ch9/ch9-clock2
diff options
context:
space:
mode:
authorAdam Carpenter <53hornet@gmail.com>2019-03-27 15:32:37 -0400
committerAdam Carpenter <53hornet@gmail.com>2019-03-27 15:32:37 -0400
commit67cdcc2e12118becb823e20a40cc2687f2b8425a (patch)
treeed92c3234b89079e6d4cf36f5e80c5ffa79def48 /meap/meap-code/ch9/ch9-clock2
parente25482fca375d318a39c3b54db396b0db6e0b263 (diff)
downloadlearning-rust-67cdcc2e12118becb823e20a40cc2687f2b8425a.tar.xz
learning-rust-67cdcc2e12118becb823e20a40cc2687f2b8425a.zip
Started Rust in Action MEAP.
Diffstat (limited to 'meap/meap-code/ch9/ch9-clock2')
-rw-r--r--meap/meap-code/ch9/ch9-clock2/Cargo.toml19
-rw-r--r--meap/meap-code/ch9/ch9-clock2/src/main.rs132
2 files changed, 151 insertions, 0 deletions
diff --git a/meap/meap-code/ch9/ch9-clock2/Cargo.toml b/meap/meap-code/ch9/ch9-clock2/Cargo.toml
new file mode 100644
index 0000000..04cf8df
--- /dev/null
+++ b/meap/meap-code/ch9/ch9-clock2/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "clock"
+version = "0.1.2"
+authors = ["Tim McNamara <code@timmcnamara.co.nz>"]
+
+[dependencies]
+byteorder = "1"
+chrono = "0.4"
+clap = "2.32"
+
+[target.'cfg(windows)'.dependencies]
+winapi = "0.2"
+kernel32-sys = "0.2"
+
+[target.'cfg(not(windows))'.dependencies]
+libc = "0.2"
+
+[profile.release]
+lto = true
diff --git a/meap/meap-code/ch9/ch9-clock2/src/main.rs b/meap/meap-code/ch9/ch9-clock2/src/main.rs
new file mode 100644
index 0000000..9b6968b
--- /dev/null
+++ b/meap/meap-code/ch9/ch9-clock2/src/main.rs
@@ -0,0 +1,132 @@
+extern crate chrono;
+extern crate clap;
+#[cfg(windows)]
+extern crate kernel32;
+#[cfg(not(windows))]
+extern crate libc;
+#[cfg(windows)]
+extern crate winapi;
+
+use chrono::DateTime; // date type
+use chrono::Weekday;
+use chrono::{Datelike, Timelike}; // traits that provides .year(), .month(), .second() ... methods
+use chrono::{FixedOffset, Local, NaiveDateTime, Utc}; // timezone types
+use clap::{App, Arg};
+use std::mem::zeroed;
+use std::thread::sleep;
+struct Clock;
+
+impl Clock {
+ fn get() -> DateTime<Local> {
+ Local::now()
+ }
+
+ #[cfg(windows)]
+ fn set<Tz: TimeZone>(t: DateTime<Tz>) -> () {
+ use chrono::Weekday;
+ use kernel32::SetSystemTime;
+ use winapi::{SYSTEMTIME, WORD};
+
+ let t = t.with_timezone(&Local);
+
+ let mut systime: SYSTEMTIME = unsafe { zeroed() };
+
+ let dow = match t.weekday() {
+ Weekday::Mon => 1,
+ Weekday::Tue => 2,
+ Weekday::Wed => 3,
+ Weekday::Thu => 4,
+ Weekday::Fri => 5,
+ Weekday::Sat => 6,
+ Weekday::Sun => 0,
+ };
+
+ let mut ns = t.nanosecond();
+ let is_leap_second = ns > 1_000_000_000;
+
+ if is_leap_second {
+ ns -= 1_000_000_000;
+ }
+
+ systime.wYear = t.year() as WORD;
+ systime.wMonth = t.month() as WORD;
+ systime.wDayOfWeek = dow as WORD;
+ systime.wDay = t.day() as WORD;
+ systime.wHour = t.hour() as WORD;
+ systime.wMinute = t.minute() as WORD;
+ systime.wSecond = t.second() as WORD;
+ systime.wMilliseconds = (ns / 1_000_000) as WORD;
+
+ let systime_ptr = &systime as *const SYSTEMTIME; // as *mut SYSTEMTIME; // convert to a pointer, then change its mutability
+
+ unsafe {
+ SetSystemTime(systime_ptr); // giving a pointer to a value to something outside of Rust's control is unsafe
+ }
+ }
+
+ #[cfg(not(windows))]
+ fn set<Tz: TimeZone>(t: DateTime<Tz>) -> () {
+ use libc::settimeofday;
+ use libc::{suseconds_t, time_t, timeval, timezone};
+
+ let t = t.with_timezone(&Local); // variable names indicate the data's progression
+ let mut u: timeval = unsafe { zeroed() }; // through the function
+
+ u.tv_sec = t.timestamp() as time_t;
+ u.tv_usec = t.timestamp_subsec_micros() as suseconds_t;
+
+ unsafe {
+ let mock_tz: *const timezone = std::ptr::null();
+ settimeofday(&u as *const timeval, mock_tz);
+ }
+ }
+}
+
+fn main() {
+ let app = App::new("clock")
+ .version("0.1.2")
+ .about("Gets and (aspirationally) sets the time.")
+ .after_help("Note: UNIX timestamps are parsed as whole seconds since 1st January 1970 0:00:00 UTC. For more accuracy, use another format.")
+ .arg(Arg::with_name("action")
+ .takes_value(true)
+ .possible_values(&["get", "set"])
+ .default_value("get"))
+ .arg(Arg::with_name("std")
+ .short("s")
+ .long("use-standard")
+ .takes_value(true)
+ .possible_values(&["rfc2822", "rfc3339", "timestamp"])
+ .default_value("rfc3339"))
+ .arg(Arg::with_name("datetime")
+ .help("When <action> is 'set', apply <datetime>. Otherwise, ignore."));
+
+ let args = app.get_matches();
+
+ let action = args.value_of("std").unwrap(); // default_value() has been supplied,
+ let std = args.value_of("std").unwrap(); // so it's safe to use .unwrap()
+
+ if action == "set" {
+ let t_ = args.value_of("datetime").unwrap();
+
+ let parser = match std {
+ "rfc2822" => DateTime::parse_from_rfc2822,
+ "rfc3339" => DateTime::parse_from_rfc3339,
+ //"rfc3339" => |secs:| NaiveDateTime,
+ _ => unimplemented!(),
+ };
+
+ let err_msg = format!("Unable to parse {} according to {}", t_, std);
+ let t = parser(t_).expect(&err_msg);
+
+ Clock::set(t)
+ }
+
+ let now = Clock::get();
+
+ match std {
+ "timestamp" => println!("{}", now.timestamp()),
+ "rfc2822" => println!("{}", now.to_rfc2822()),
+ "rfc3339" => println!("{}", now.to_rfc3339()),
+ _ => unreachable!(),
+ }
+}