1 /******************************************************************************
2 
3     Real time clock, obtains the current UNIX wall clock time in µs.
4 
5     Copyright:
6         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14  ******************************************************************************/
15 
16 module ocean.time.MicrosecondsClock;
17 
18 
19 import ocean.time.Time;
20 import core.sys.posix.time: gmtime_r, localtime_r;
21 import core.stdc.time: tm, time_t;
22 import core.sys.posix.sys.time : timeval, gettimeofday;
23 import ocean.core.TypeConvert;
24 
25 version (unittest)
26 {
27     import ocean.core.Test;
28 }
29 
30 /******************************************************************************/
31 
32 class MicrosecondsClock
33 {
34     /**************************************************************************
35 
36         timeval struct alias, defined as
37 
38         ---
39         struct timeval
40         {
41             time_t tv_sec;  // UNIX time in s
42             int    tv_usec; // µs in the current second
43         }
44         ---
45 
46      **************************************************************************/
47 
48     alias .timeval timeval;
49 
50     /**************************************************************************
51 
52         Returns:
53             the current UNIX wall clock time in µs.
54 
55      **************************************************************************/
56 
57     static public ulong now_us ( )
58     {
59         return us(now);
60     }
61 
62     /**************************************************************************
63 
64         Usage tips: use
65 
66         ---
67             MicrosecondsClock.now.tv_sec
68         ---
69 
70         to obtain the UNIX timestamp of the current wall clock time or
71 
72         ---
73             with (MicrosecondsClock.now.tv_sec)
74             {
75                 // tv_sec:  UNIX timestamp of the current wall clock time
76                 // tv_usec: µs in the current second
77             }
78         ---
79 
80         to get the current UNIX time split into seconds and microseconds.
81 
82         Returns:
83             the current UNIX wall clock time.
84 
85      **************************************************************************/
86 
87     static public timeval now ( )
88     {
89         timeval t;
90 
91         gettimeofday(&t, null);
92 
93         return t;
94     }
95 
96     /**************************************************************************
97 
98         Converts t to a single integer value representing the number of
99         microseconds.
100 
101         Params:
102             t = timeval value to convert to single microseconds value
103 
104         Returns:
105             number of microseconds
106 
107      **************************************************************************/
108 
109     static public ulong us ( timeval t )
110     in
111     {
112         static if (is (t.tv_sec : int))
113         {
114             static assert (cast (ulong) t.tv_sec.max <                          // overflow check
115                           (cast (ulong) t.tv_sec.max + 1) * 1_000_000);
116         }
117     }
118     do
119     {
120         return t.tv_sec * 1_000_000UL + t.tv_usec;
121     }
122 
123     /***************************************************************************
124 
125         Converts `t` to a tm struct, specifying the year, months, days, hours,
126         minutes, and seconds.
127 
128         Params:
129             t = time in seconds to convert
130             local = true: return local time, false: return GMT.
131 
132         Returns:
133             the t as tm struct.
134 
135         Out:
136             DST can be enabled with local time only.
137 
138     ***************************************************************************/
139 
140     static public tm toTm ( time_t t, bool local = false )
141     out (datetime)
142     {
143         assert (local || datetime.tm_isdst <= 0, "DST enabled with GMT");
144     }
145     do
146     {
147         tm datetime;
148 
149         // actually one should check the return value of localtime_r() and
150         // gmtime_r(), but in this usage they should never fail
151         (local? &localtime_r : &gmtime_r)(&t, &datetime);
152 
153         return datetime;
154     }
155 
156     unittest
157     {
158         time_t sec = 1460103457;
159         auto t = toTm(sec);
160 
161         // Just compare the raw time values
162         t.tm_gmtoff = t.tm_gmtoff.init;
163         t.tm_zone = t.tm_zone.init;
164 
165         test!("==")(t, tm(37, 17, 8, 8, 3, 116, 5, 98, 0));
166     }
167 
168     /***************************************************************************
169 
170         Converts `t` to a tm struct, specifying the year, months, days, hours,
171         minutes, and seconds, plus the microseconds via an out parameter.
172 
173         Params:
174             t = timeval struct to convert
175             us = receives the remainder number of microseconds (not stored in
176                 the returned tm struct)
177 
178         Returns:
179             tm struct containing everything.
180 
181     ***************************************************************************/
182 
183     static public tm toTm ( timeval t, out ulong us )
184     {
185         us = t.tv_usec;
186         return toTm(t.tv_sec);
187     }
188 
189     unittest
190     {
191         timeval tv;
192         tv.tv_sec = 1460103457;
193         tv.tv_usec = 1095;
194         ulong us;
195         auto t = toTm(tv, us);
196 
197         // Just compare the raw time values
198         t.tm_gmtoff = t.tm_gmtoff.init;
199         t.tm_zone = t.tm_zone.init;
200 
201         test!("==")(t, tm(37, 17, 8, 8, 3, 116, 5, 98, 0));
202         test!("==")(us, 1095);
203     }
204 
205     /***************************************************************************
206 
207         Converts `t` to a DateTime struct, specifying the year, months, days,
208         hours, minutes, seconds, and milliseconds, plus the microseconds via an
209         out parameter.
210 
211         Params:
212             t = timeval struct to convert
213             us = receives the remainder number of microseconds (not stored in
214                 the returned DateTime struct)
215 
216         Returns:
217             DateTime struct containing everything.
218 
219     ***************************************************************************/
220 
221     static public DateTime toDateTime ( timeval t, out ulong us )
222     {
223         with (t) with (toTm(tv_sec))
224         {
225             DateTime dt;
226 
227             dt.date.day   = tm_mday;
228             dt.date.year  = tm_year + 1900;
229             dt.date.month = tm_mon  + 1;
230             dt.date.dow   = tm_wday;
231             dt.date.doy   = tm_yday + 1;
232 
233             dt.time.hours   = tm_hour;
234             dt.time.minutes = tm_min;
235             dt.time.seconds = tm_sec;
236 
237             auto usec = tv_usec / 1000;
238             assert (usec <= uint.max);
239             assert (usec >= 0);
240             dt.time.millis = castFrom!(long).to!(uint)(usec);
241 
242             us = tv_usec % 1000;
243 
244             return dt;
245         }
246     }
247 
248     unittest
249     {
250         timeval tv;
251         tv.tv_sec = 1460103457;
252         tv.tv_usec = 1095;
253         ulong us;
254         auto dt = toDateTime(tv, us);
255         test!("==")(dt,
256             DateTime(Date(0, 8, 2016, 4, 5, 99), TimeOfDay(8, 17, 37, 1)));
257         test!("==")(us, 95);
258     }
259 }