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