1 /******************************************************************************* 2 3 copyright: Copyright (c) 2007 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Feb 2007: Initial release 8 9 author: Kris 10 11 *******************************************************************************/ 12 13 module tango.time.Clock; 14 15 public import tango.time.Time; 16 17 private import tango.sys.Common; 18 19 private import tango.core.Exception; 20 21 /****************************************************************************** 22 23 Exposes UTC time relative to Jan 1st, 1 AD. These values are 24 based upon a clock-tick of 100ns, giving them a span of greater 25 than 10,000 years. These units of time are the foundation of most 26 time and date functionality in Tango. 27 28 Interval is another type of time period, used for measuring a 29 much shorter duration; typically used for timeout periods and 30 for high-resolution timers. These intervals are measured in 31 units of 1 second, and support fractional units (0.001 = 1ms). 32 33 *******************************************************************************/ 34 35 struct Clock 36 { 37 // copied from Gregorian. Used while we rely on OS for toDate. 38 package __gshared immutable uint[] DaysToMonthCommon = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; 39 package static void setDoy(ref DateTime dt) 40 { 41 uint doy = dt.date.day + DaysToMonthCommon[dt.date.month - 1]; 42 uint year = dt.date.year; 43 44 if(dt.date.month > 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) 45 doy++; 46 47 dt.date.doy = doy; 48 } 49 50 version (Win32) 51 { 52 /*************************************************************** 53 54 Return the current time as UTC since the epoch 55 56 ***************************************************************/ 57 58 @property static Time now () 59 { 60 FILETIME fTime = void; 61 GetSystemTimeAsFileTime (&fTime); 62 return convert (fTime); 63 } 64 65 /*************************************************************** 66 67 Set Date fields to represent the current time. 68 69 ***************************************************************/ 70 71 static DateTime toDate () 72 { 73 return toDate (now); 74 } 75 76 77 /*************************************************************** 78 79 Set fields to represent the provided UTC time. Note 80 that the conversion is limited by the underlying OS, 81 and will fail to operate correctly with Time 82 values beyond the domain. On Win32 the earliest 83 representable date is 1601. On linux it is 1970. Both 84 systems have limitations upon future dates also. Date 85 is limited to millisecond accuracy at best. 86 87 ***************************************************************/ 88 89 static DateTime toDate (const(Time) time) 90 { 91 DateTime dt = void; 92 SYSTEMTIME sTime = void; 93 94 auto fTime = convert (time); 95 FileTimeToSystemTime (&fTime, &sTime); 96 97 dt.date.year = sTime.wYear; 98 dt.date.month = sTime.wMonth; 99 dt.date.day = sTime.wDay; 100 dt.date.dow = sTime.wDayOfWeek; 101 dt.date.era = 0; 102 dt.time.hours = sTime.wHour; 103 dt.time.minutes = sTime.wMinute; 104 dt.time.seconds = sTime.wSecond; 105 dt.time.millis = sTime.wMilliseconds; 106 107 // Calculate the day-of-year 108 setDoy(dt); 109 110 return dt; 111 } 112 113 /*************************************************************** 114 115 Convert Date fields to Time 116 117 Note that the conversion is limited by the underlying 118 OS, and will not operate correctly with Time 119 values beyond the domain. On Win32 the earliest 120 representable date is 1601. On linux it is 1970. Both 121 systems have limitations upon future dates also. Date 122 is limited to millisecond accuracy at best. 123 124 ***************************************************************/ 125 126 static Time fromDate (ref const(DateTime) dt) 127 { 128 SYSTEMTIME sTime = void; 129 FILETIME fTime = void; 130 131 sTime.wYear = cast(ushort) dt.date.year; 132 sTime.wMonth = cast(ushort) dt.date.month; 133 sTime.wDayOfWeek = 0; 134 sTime.wDay = cast(ushort) dt.date.day; 135 sTime.wHour = cast(ushort) dt.time.hours; 136 sTime.wMinute = cast(ushort) dt.time.minutes; 137 sTime.wSecond = cast(ushort) dt.time.seconds; 138 sTime.wMilliseconds = cast(ushort) dt.time.millis; 139 140 SystemTimeToFileTime (&sTime, &fTime); 141 return convert (fTime); 142 } 143 144 /*************************************************************** 145 146 Convert FILETIME to a Time 147 148 ***************************************************************/ 149 150 package static Time convert (const(FILETIME) time) 151 { 152 auto t = *cast(long*) &time; 153 t *= 100 / TimeSpan.NanosecondsPerTick; 154 return Time.epoch1601 + TimeSpan(t); 155 } 156 157 /*************************************************************** 158 159 Convert Time to a FILETIME 160 161 ***************************************************************/ 162 163 package static FILETIME convert (const(Time) dt) 164 { 165 FILETIME time = void; 166 167 TimeSpan span = dt - Time.epoch1601; 168 assert (span >= TimeSpan.zero); 169 *cast(long*) &time.dwLowDateTime = span.ticks; 170 return time; 171 } 172 } 173 174 version (Posix) 175 { 176 /*************************************************************** 177 178 Return the current time as UTC since the epoch 179 180 ***************************************************************/ 181 182 @property static Time now () 183 { 184 timeval tv = void; 185 if (gettimeofday (&tv, null)) 186 throw new PlatformException ("Clock.now :: Posix timer is not available"); 187 188 return convert (tv); 189 } 190 191 /*************************************************************** 192 193 Set Date fields to represent the current time. 194 195 ***************************************************************/ 196 197 static DateTime toDate () 198 { 199 return toDate (now); 200 } 201 202 /*************************************************************** 203 204 Set fields to represent the provided UTC time. Note 205 that the conversion is limited by the underlying OS, 206 and will fail to operate correctly with Time 207 values beyond the domain. On Win32 the earliest 208 representable date is 1601. On linux it is 1970. Both 209 systems have limitations upon future dates also. Date 210 is limited to millisecond accuracy at best. 211 212 **************************************************************/ 213 214 static DateTime toDate (const(Time) time) 215 { 216 DateTime dt = void; 217 auto timeval = convert (time); 218 dt.time.millis = cast(uint)timeval.tv_usec / 1000; 219 220 tm t = void; 221 gmtime_r (&timeval.tv_sec, &t); 222 223 dt.date.year = t.tm_year + 1900; 224 dt.date.month = t.tm_mon + 1; 225 dt.date.day = t.tm_mday; 226 dt.date.dow = t.tm_wday; 227 dt.date.era = 0; 228 dt.time.hours = t.tm_hour; 229 dt.time.minutes = t.tm_min; 230 dt.time.seconds = t.tm_sec; 231 232 // Calculate the day-of-year 233 setDoy(dt); 234 235 return dt; 236 } 237 238 /*************************************************************** 239 240 Convert Date fields to Time 241 242 Note that the conversion is limited by the underlying 243 OS, and will not operate correctly with Time 244 values beyond the domain. On Win32 the earliest 245 representable date is 1601. On linux it is 1970. Both 246 systems have limitations upon future dates also. Date 247 is limited to millisecond accuracy at best. 248 249 ***************************************************************/ 250 251 static Time fromDate (ref const(DateTime) dt) 252 { 253 tm t = void; 254 255 t.tm_year = dt.date.year - 1900; 256 t.tm_mon = dt.date.month - 1; 257 t.tm_mday = dt.date.day; 258 t.tm_hour = dt.time.hours; 259 t.tm_min = dt.time.minutes; 260 t.tm_sec = dt.time.seconds; 261 262 auto seconds = timegm (&t); 263 return Time.epoch1970 + 264 TimeSpan.fromSeconds(seconds) + 265 TimeSpan.fromMillis(dt.time.millis); 266 } 267 268 /*************************************************************** 269 270 Convert timeval to a Time 271 272 ***************************************************************/ 273 274 package static Time convert (ref const(timeval) tv) 275 { 276 return Time.epoch1970 + 277 TimeSpan.fromSeconds(tv.tv_sec) + 278 TimeSpan.fromMicros(tv.tv_usec); 279 } 280 281 /*************************************************************** 282 283 Convert Time to a timeval 284 285 ***************************************************************/ 286 287 package static timeval convert (const(Time) time) 288 { 289 timeval tv = void; 290 291 TimeSpan span = time - time.epoch1970; 292 assert (span >= TimeSpan.zero); 293 tv.tv_sec = cast(typeof(tv.tv_sec)) span.seconds; 294 tv.tv_usec = cast(typeof(tv.tv_usec)) (span.micros % 1_000_000L); 295 return tv; 296 } 297 } 298 } 299 300 301 302 debug (UnitTest) 303 { 304 unittest 305 { 306 auto time = Clock.now; 307 auto clock=Clock.convert(time); 308 assert (Clock.convert(clock) is time); 309 310 time -= TimeSpan(time.ticks % TimeSpan.TicksPerSecond); 311 auto date = Clock.toDate(time); 312 313 assert (time is Clock.fromDate(date)); 314 } 315 } 316 317 debug (Clock) 318 { 319 void main() 320 { 321 auto time = Clock.now; 322 } 323 }