1 /******************************************************************************* 2 3 Copyright: 4 Copyright (c) 2007 Kris Bell. 5 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 6 All rights reserved. 7 8 License: 9 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 10 See LICENSE_TANGO.txt for details. 11 12 Version: Feb 2007: Initial release 13 14 Authors: Kris 15 16 *******************************************************************************/ 17 18 module ocean.time.Clock; 19 20 import core.sys.posix.sys.time; 21 import core.sys.posix.time; 22 import ocean.core.ExceptionDefinitions; 23 public import ocean.time.Time; 24 25 version (unittest) import ocean.core.Test; 26 27 /****************************************************************************** 28 29 Exposes UTC time relative to Jan 1st, 1 AD. These values are 30 based upon a clock-tick of 100ns, giving them a span of greater 31 than 10,000 years. These units of time are the foundation of most 32 time and date functionality in Tango contributors. 33 34 Interval is another type of time period, used for measuring a 35 much shorter duration; typically used for timeout periods and 36 for high-resolution timers. These intervals are measured in 37 units of 1 second, and support fractional units (0.001 = 1ms). 38 39 *******************************************************************************/ 40 41 struct Clock 42 { 43 /// Time at which the program started 44 private static Time start_time_; 45 46 /// Returns: Time at which the application started 47 public static Time startTime () 48 { 49 return start_time_; 50 } 51 52 static this () 53 { 54 start_time_ = Clock.now; 55 } 56 57 // copied from Gregorian. Used while we rely on OS for toDate. 58 package static uint[] DaysToMonthCommon = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; 59 package static void setDoy(ref DateTime dt) 60 { 61 uint doy = dt.date.day + DaysToMonthCommon[dt.date.month - 1]; 62 uint year = dt.date.year; 63 64 if(dt.date.month > 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) 65 doy++; 66 67 dt.date.doy = doy; 68 } 69 70 71 /*************************************************************************** 72 73 Returns: 74 the current time as UTC since the epoch 75 76 ***************************************************************************/ 77 78 static Time now () 79 { 80 timeval tv = void; 81 if (gettimeofday (&tv, null)) 82 throw new PlatformException ("Clock.now :: Posix timer is not available"); 83 84 return convert (tv); 85 } 86 87 /*************************************************************************** 88 89 Set Date fields to represent the current time. 90 91 ***************************************************************************/ 92 93 static DateTime toDate () 94 { 95 return toDate(now); 96 } 97 98 /*************************************************************************** 99 100 Set fields to represent the provided UTC time. 101 102 Note that the conversion is limited by the underlying OS, and will fail 103 to operate correctly with Time values beyond the domain, which is 104 01-01-1970 on Linux. 105 Date is limited to millisecond accuracy at best. 106 107 ***************************************************************************/ 108 109 static DateTime toDate (Time time) 110 { 111 DateTime dt = void; 112 auto timeval = convert (time); 113 dt.time.millis = cast(uint) (timeval.tv_usec / 1000); 114 115 tm t = void; 116 gmtime_r (&timeval.tv_sec, &t); 117 118 dt.date.year = t.tm_year + 1900; 119 dt.date.month = t.tm_mon + 1; 120 dt.date.day = t.tm_mday; 121 dt.date.dow = t.tm_wday; 122 dt.date.era = 0; 123 dt.time.hours = t.tm_hour; 124 dt.time.minutes = t.tm_min; 125 dt.time.seconds = t.tm_sec; 126 127 // Calculate the day-of-year 128 setDoy(dt); 129 130 return dt; 131 } 132 133 /*************************************************************************** 134 135 Convert Date fields to Time 136 137 Note that the conversion is limited by the underlying OS, and will fail 138 to operate correctly with Time values beyond the domain, which is 139 01-01-1970 on Linux. 140 Date is limited to millisecond accuracy at best. 141 142 ***************************************************************************/ 143 144 static Time fromDate (ref DateTime dt) 145 { 146 tm t = void; 147 148 t.tm_year = dt.date.year - 1900; 149 t.tm_mon = dt.date.month - 1; 150 t.tm_mday = dt.date.day; 151 t.tm_hour = dt.time.hours; 152 t.tm_min = dt.time.minutes; 153 t.tm_sec = dt.time.seconds; 154 155 auto seconds = timegm (&t); 156 return Time.epoch1970 + 157 TimeSpan.fromSeconds(seconds) + 158 TimeSpan.fromMillis(dt.time.millis); 159 } 160 161 /*************************************************************************** 162 163 Convert timeval to a Time 164 165 ***************************************************************************/ 166 167 package static Time convert (ref timeval tv) 168 { 169 return Time.epoch1970 + 170 TimeSpan.fromSeconds(tv.tv_sec) + 171 TimeSpan.fromMicros(tv.tv_usec); 172 } 173 174 /*************************************************************************** 175 176 Convert Time to a timeval 177 178 ***************************************************************************/ 179 180 package static timeval convert (Time time) 181 { 182 timeval tv = void; 183 184 TimeSpan span = time - time.epoch1970; 185 assert (span >= TimeSpan.zero); 186 tv.tv_sec = cast(typeof(tv.tv_sec)) span.seconds; 187 tv.tv_usec = cast(typeof(tv.tv_usec)) (span.micros % 1_000_000L); 188 return tv; 189 } 190 } 191 192 193 194 unittest 195 { 196 auto time = Clock.now; 197 auto clock=Clock.convert(time); 198 test (Clock.convert(clock) is time); 199 200 time -= TimeSpan(time.ticks % TimeSpan.TicksPerSecond); 201 auto date = Clock.toDate(time); 202 203 test (time is Clock.fromDate(date)); 204 }