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 }