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 }