Browse Source

add gmtime and difftime

Marat Safin 7 years ago
parent
commit
2b21dca567
8 changed files with 181 additions and 32 deletions
  1. 39 0
      src/time/src/constants.rs
  2. 40 0
      src/time/src/helpers.rs
  3. 62 31
      src/time/src/lib.rs
  4. 13 0
      src/time/src/linux.rs
  5. 4 0
      src/time/src/redox.rs
  6. 1 0
      tests/.gitignore
  7. 2 1
      tests/Makefile
  8. 20 0
      tests/gmtime.c

+ 39 - 0
src/time/src/constants.rs

@@ -0,0 +1,39 @@
+#[cfg(target_os = "linux")]
+#[path = "linux.rs"]
+pub mod sys;
+
+#[cfg(target_os = "redox")]
+#[path = "redox.rs"]
+pub mod sys;
+
+pub use sys::*;
+use platform::types::*;
+
+// Move epoch from 01.01.1970 to 01.03.0000 (yes, Year 0) - this is the first
+// day of a 400-year long "era", right after additional day of leap year.
+// This adjustment is required only for date calculation, so instead of
+// modifying time_t value (which would require 64-bit operations to work
+// correctly) it's enough to adjust the calculated number of days since epoch.
+pub(crate) const EPOCH_ADJUSTMENT_DAYS: c_long = 719468;
+// year to which the adjustment was made
+pub(crate) const ADJUSTED_EPOCH_YEAR: c_int = 0;
+// 1st March of year 0 is Wednesday
+pub(crate) const ADJUSTED_EPOCH_WDAY: c_long = 3;
+pub(crate) const DAYS_PER_ERA: c_long = (400 - 97) * 365 + 97 * 366;
+pub(crate) const DAYS_PER_CENTURY: c_ulong = (100 - 24) * 365 + 24 * 366;
+pub(crate) const DAYS_PER_4_YEARS: c_ulong = 3 * 365 + 366;
+pub(crate) const DAYS_PER_YEAR: c_int = 365;
+pub(crate) const DAYS_IN_JANUARY: c_int = 31;
+pub(crate) const DAYS_IN_FEBRUARY: c_int = 28;
+pub(crate) const YEARS_PER_ERA: c_int = 400;
+
+pub(crate) const SECSPERMIN: c_long = 60;
+pub(crate) const MINSPERHOUR: c_long = 60;
+pub(crate) const HOURSPERDAY: c_long = 24;
+pub(crate) const SECSPERHOUR: c_long = SECSPERMIN * MINSPERHOUR;
+pub(crate) const SECSPERDAY: c_long = SECSPERHOUR * HOURSPERDAY;
+pub(crate) const DAYSPERWEEK: c_int = 7;
+
+pub(crate) const YEAR_BASE: c_int = 1900;
+
+pub(crate) const UTC: *const c_char = b"UTC\0" as *const u8 as *const c_char;

+ 40 - 0
src/time/src/helpers.rs

@@ -0,0 +1,40 @@
+use constants::*;
+use platform::types::*;
+
+// compute year, month, day & day of year
+// for description of this algorithm see
+// http://howardhinnant.github.io/date_algorithms.html#civil_from_days
+#[inline(always)]
+pub(crate) fn civil_from_days(days: c_long) -> (c_int, c_int, c_int, c_int) {
+    let (era, year): (c_int, c_int);
+    let (erayear, mut yearday, mut month, day): (c_int, c_int, c_int, c_int);
+    let eraday: c_ulong;
+
+    era = (if days >= 0 {
+        days
+    } else {
+        days - (DAYS_PER_ERA - 1)
+    } / DAYS_PER_ERA) as c_int;
+    eraday = (days - era as c_long * DAYS_PER_ERA) as c_ulong;
+    let a = eraday / (DAYS_PER_4_YEARS - 1);
+    let b = eraday / DAYS_PER_CENTURY;
+    let c = eraday / (DAYS_PER_ERA as c_ulong - 1);
+    erayear = ((eraday - a + b - c) / 365) as c_int;
+    let d = DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100;
+    yearday = (eraday - d as c_ulong) as c_int;
+    month = (5 * yearday + 2) / 153;
+    day = yearday - (153 * month + 2) / 5 + 1;
+    month += if month < 10 { 2 } else { -10 };
+    year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1) as c_int;
+    yearday += if yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY {
+        -(DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY)
+    } else {
+        DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + is_leap(erayear)
+    };
+    return (year, month, day, yearday);
+}
+
+#[inline(always)]
+fn is_leap(y: c_int) -> c_int {
+    ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) as c_int
+}

+ 62 - 31
src/time/src/lib.rs

@@ -4,34 +4,12 @@
 
 extern crate platform;
 
-use platform::types::*;
+pub mod constants;
+mod helpers;
 
-#[cfg(target_os = "redox")]
-pub const CLOCK_REALTIME: c_int = 1;
-#[cfg(target_os = "redox")]
-pub const CLOCK_MONOTONIC: c_int = 4;
-#[cfg(target_os = "linux")]
-pub const CLOCK_REALTIME: c_int = 0;
-#[cfg(target_os = "linux")]
-pub const CLOCK_MONOTONIC: c_int = 1;
-#[cfg(target_os = "linux")]
-pub const CLOCK_PROCESS_CPUTIME_ID: c_int = 2;
-#[cfg(target_os = "linux")]
-pub const CLOCK_THREAD_CPUTIME_ID: c_int = 3;
-#[cfg(target_os = "linux")]
-pub const CLOCK_MONOTONIC_RAW: c_int = 4;
-#[cfg(target_os = "linux")]
-pub const CLOCK_REALTIME_COARSE: c_int = 5;
-#[cfg(target_os = "linux")]
-pub const CLOCK_MONOTONIC_COARSE: c_int = 6;
-#[cfg(target_os = "linux")]
-pub const CLOCK_BOOTTIME: c_int = 7;
-#[cfg(target_os = "linux")]
-pub const CLOCK_REALTIME_ALARM: c_int = 8;
-#[cfg(target_os = "linux")]
-pub const CLOCK_BOOTTIME_ALARM: c_int = 9;
-#[cfg(target_os = "linux")]
-pub const CLOCK_TAI: c_int = 11;
+use platform::types::*;
+use constants::*;
+use helpers::*;
 
 /*
  *#[repr(C)]
@@ -56,6 +34,24 @@ pub struct tm {
     pub tm_zone: *const c_char,
 }
 
+impl Default for tm {
+    fn default() -> tm {
+        tm {
+            tm_sec: 0,
+            tm_min: 0,
+            tm_hour: 0,
+            tm_mday: 0,
+            tm_mon: 0,
+            tm_year: 0,
+            tm_wday: 0,
+            tm_yday: 0,
+            tm_isdst: 0,
+            tm_gmtoff: 0,
+            tm_zone: UTC,
+        }
+    }
+}
+
 #[repr(C)]
 pub struct itimerspec {
     pub it_interval: timespec,
@@ -105,8 +101,8 @@ pub extern "C" fn ctime_r(clock: *const time_t, buf: *mut c_char) -> *mut c_char
 }
 
 #[no_mangle]
-pub extern "C" fn difftime(time1: time_t, time0: time_t) -> f64 {
-    unimplemented!();
+pub extern "C" fn difftime(time1: time_t, time0: time_t) -> c_double {
+    (time1 - time0) as c_double
 }
 
 #[no_mangle]
@@ -116,12 +112,47 @@ pub extern "C" fn getdate(string: *const c_char) -> tm {
 
 #[no_mangle]
 pub extern "C" fn gmtime(timer: *const time_t) -> *mut tm {
-    unimplemented!();
+    let mut result: tm = Default::default();
+    return gmtime_r(timer, &mut result);
 }
 
 #[no_mangle]
 pub extern "C" fn gmtime_r(clock: *const time_t, result: *mut tm) -> *mut tm {
-    unimplemented!();
+    let (mut days, mut rem): (c_long, c_long);
+    let mut weekday: c_int;
+    let lcltime = unsafe { *clock };
+
+    days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
+    rem = lcltime % SECSPERDAY;
+    if rem < 0 {
+        rem += SECSPERDAY;
+        days -= 1;
+    }
+    unsafe {
+        (*result).tm_hour = (rem / SECSPERHOUR) as c_int;
+        rem %= SECSPERHOUR;
+        (*result).tm_min = (rem / SECSPERMIN) as c_int;
+        (*result).tm_sec = (rem % SECSPERMIN) as c_int;
+    }
+
+    weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK as c_long) as c_int;
+    if weekday < 0 {
+        weekday += DAYSPERWEEK;
+    }
+    unsafe { (*result).tm_wday = weekday };
+
+    let (year, month, day, yearday) = civil_from_days(days);
+    unsafe {
+        (*result).tm_yday = yearday;
+        (*result).tm_year = year - YEAR_BASE;
+        (*result).tm_mon = month;
+        (*result).tm_mday = day;
+
+        (*result).tm_isdst = 0;
+        (*result).tm_gmtoff = 0;
+        (*result).tm_zone = UTC;
+    }
+    result
 }
 
 #[no_mangle]

+ 13 - 0
src/time/src/linux.rs

@@ -0,0 +1,13 @@
+use platform::types::*;
+
+pub const CLOCK_REALTIME: c_int = 0;
+pub const CLOCK_MONOTONIC: c_int = 1;
+pub const CLOCK_PROCESS_CPUTIME_ID: c_int = 2;
+pub const CLOCK_THREAD_CPUTIME_ID: c_int = 3;
+pub const CLOCK_MONOTONIC_RAW: c_int = 4;
+pub const CLOCK_REALTIME_COARSE: c_int = 5;
+pub const CLOCK_MONOTONIC_COARSE: c_int = 6;
+pub const CLOCK_BOOTTIME: c_int = 7;
+pub const CLOCK_REALTIME_ALARM: c_int = 8;
+pub const CLOCK_BOOTTIME_ALARM: c_int = 9;
+pub const CLOCK_TAI: c_int = 11;

+ 4 - 0
src/time/src/redox.rs

@@ -0,0 +1,4 @@
+use platform::types::*;
+
+pub const CLOCK_REALTIME: c_int = 1;
+pub const CLOCK_MONOTONIC: c_int = 4;

+ 1 - 0
tests/.gitignore

@@ -49,3 +49,4 @@
 /waitpid
 /write
 /time
+/gmtime

+ 2 - 1
tests/Makefile

@@ -43,7 +43,8 @@ EXPECT_BINS=\
 	unlink \
 	waitpid \
 	write \
-	time
+	time \
+	gmtime
 
 # Binaries that may generate varied output
 BINS=\

+ 20 - 0
tests/gmtime.c

@@ -0,0 +1,20 @@
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char** argv) {
+    time_t a = 0;
+    tm expected = { .tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 1, .tm_year = 70,
+                    .tm_wday = 4, .tm_yday = 0, .tm_isdst = 0, .tm_gmtoff = 0, .tm_zone = "UTC" };
+
+    tm *info = gmtime(&a);
+    if (info->tm_sec != expected.tm_sec || info->tm_min != expected.tm_min ||
+        info->tm_hour != expected.tm_hour || info->tm_mday != expected.tm_mday ||
+        info->tm_year != expected.tm_year || info->tm_wday != expected.tm_wday ||
+        info->tm_yday != expected.tm_yday || info->tm_isdst != expected.tm_isdst ||
+        info->tm_gmtoff != expected.tm_gmtoff || strcmp(info->tm_zone, expected.tm_zone) != 0) {
+            exit(1);
+    }
+    return 0;
+}