1 module logdefer.time.duration; 2 3 4 import core.checkedint : muls; 5 import std.conv : to; 6 import std.traits : isIntegral; 7 8 9 alias Nanos = Duration!(TimeUnit.NANOS); 10 alias Micros = Duration!(TimeUnit.MICROS); 11 alias Millis = Duration!(TimeUnit.MILLIS); 12 alias Seconds = Duration!(TimeUnit.SECONDS); 13 14 enum TimeUnit 15 { 16 NANOS=1, 17 MICROS=1_000, 18 MILLIS=1_000_000, 19 SECONDS=1_000_000_000 20 } 21 22 @safe 23 struct Duration(TimeUnit timeUnit) 24 { 25 public: 26 long value; 27 static immutable TimeUnit units = timeUnit; 28 static immutable string displayUnits = getDisplayUnits(); 29 30 @nogc 31 nothrow this(long value) 32 { 33 this.value = value; 34 } 35 36 @nogc 37 nothrow this(OtherDuration)(const OtherDuration otherDuration) 38 { 39 this.value = toDuration!(Duration!timeUnit)(otherDuration).value; 40 } 41 42 @nogc 43 nothrow ToDuration toDuration(ToDuration)() const 44 { 45 return toDuration!(ToDuration)(this); 46 } 47 48 @nogc 49 nothrow double toUnits(ToUnits)() const 50 { 51 return toUnits!(ToUnits)(this); 52 } 53 54 nothrow string toString(ToUnits = TimeUnit)() const 55 { 56 //XXX 57 return to!string(value) ~ " " ~ displayUnits; 58 } 59 60 @nogc 61 nothrow bool opEquals(OtherDuration)(const OtherDuration otherDuration) 62 { 63 return opEquals(otherDuration); 64 } 65 66 @nogc 67 nothrow bool opEquals(OtherDuration)(const ref OtherDuration otherDuration) 68 { 69 return toDuration!Nanos(this).value == toDuration!Nanos(otherDuration).value; 70 } 71 72 @nogc 73 nothrow void opAssign(OtherDuration)(const OtherDuration otherDuration) 74 { 75 this.value = toDuration!(Duration!timeUnit)(otherDuration).value; 76 } 77 78 @nogc 79 pure nothrow int opCmp(OtherDuration)(const OtherDuration other) const 80 { 81 return opCmp(other); 82 } 83 84 @nogc 85 pure nothrow int opCmp(OtherDuration)(const ref OtherDuration other) const 86 { 87 static if (isIntegral!OtherDuration) 88 { 89 return this.value > other ? 1 90 : this.value < other ? -1 91 : 0; 92 } 93 else 94 { 95 long thisNanos = toDuration!Nanos(this).value; 96 long otherNanos = toDuration!Nanos(other).value; 97 98 return thisNanos > otherNanos ? 1 99 : thisNanos < otherNanos ? -1 100 : 0; 101 } 102 } 103 104 Duration!timeUnit opBinary(string op)(long operand) const 105 if (op == "*") 106 { 107 bool overflow; 108 auto value = muls(this.value, operand, overflow); 109 110 if (overflow) 111 { 112 throw new Exception("Overflow"); 113 } 114 115 return Duration!timeUnit(value); 116 } 117 118 Duration!timeUnit opBinary(string op)(long operand) const 119 if (op == "/") 120 { 121 if (operand == 0) 122 { 123 throw new Exception("Divide by 0"); 124 } 125 126 return Duration!timeUnit(this.value / operand); 127 } 128 129 @nogc 130 pure static nothrow ToDuration toDuration(ToDuration, FromDuration)(const FromDuration fromDuration) 131 { 132 static if (ToDuration.units > FromDuration.units) 133 { 134 return ToDuration(fromDuration.value / (ToDuration.units / FromDuration.units)); 135 } 136 else static if (ToDuration.units < FromDuration.units) 137 { 138 return ToDuration(fromDuration.value * (FromDuration.units / ToDuration.units)); 139 } 140 else 141 { 142 return ToDuration(fromDuration.value); 143 } 144 } 145 146 @nogc 147 pure static nothrow double toUnits(ToUnits, FromDuration)(const FromDuration fromDuration) 148 { 149 static if (ToUnits.units > FromDuration.units) 150 { 151 return cast(double)(fromDuration.value) / (ToUnits.units / FromDuration.units); 152 } 153 else static if (ToUnits.units < FromDuration.units) 154 { 155 return cast(double)(fromDuration.value) * (FromDuration.units / ToUnits.units); 156 } 157 else 158 { 159 return cast(double)fromDuration.value; 160 } 161 } 162 163 private: 164 static string getDisplayUnits() 165 { 166 final switch(timeUnit) 167 { 168 case TimeUnit.SECONDS: return "seconds"; 169 case TimeUnit.MILLIS: return "milliseconds"; 170 case TimeUnit.MICROS: return "microseconds"; 171 case TimeUnit.NANOS: return "nanoseconds"; 172 } 173 } 174 } 175 176 177 version(unittest) 178 { 179 import std.stdio : writeln; 180 } 181 182 unittest 183 { 184 writeln("[UnitTest toDuration Seconds]"); 185 186 Nanos nanos = Seconds(1); 187 Micros micros = Seconds(1); 188 Millis millis = Seconds(1); 189 Seconds seconds = Seconds(1); 190 191 assert(nanos == Nanos(1_000_000_000)); 192 assert(micros == Micros(1_000_000)); 193 assert(millis == Millis(1_000)); 194 assert(seconds == Seconds(1)); 195 } 196 197 unittest 198 { 199 writeln("[UnitTest toDuration Millis]"); 200 201 Nanos nanos = Millis(1); 202 Micros micros = Millis(1); 203 Millis millis = Millis(1); 204 Seconds seconds = Millis(1); 205 206 assert(nanos == Nanos(1_000_000)); 207 assert(micros == Micros(1_000)); 208 assert(millis == Millis(1)); 209 assert(seconds == Seconds(0)); 210 } 211 212 unittest 213 { 214 writeln("[UnitTest toDuration Micros]"); 215 216 Nanos nanos = Micros(1); 217 Micros micros = Micros(1); 218 Millis millis = Micros(1); 219 Seconds seconds = Micros(1); 220 221 assert(nanos == Nanos(1_000)); 222 assert(micros == Micros(1)); 223 assert(millis == Millis(0)); 224 assert(seconds == Seconds(0)); 225 } 226 227 unittest 228 { 229 writeln("[UnitTest toDuration Nanos]"); 230 231 Nanos nanos = Nanos(1); 232 Micros micros = Nanos(1); 233 Millis millis = Nanos(1); 234 Seconds seconds = Nanos(1); 235 236 assert(nanos == Nanos(1)); 237 assert(micros == Micros(0)); 238 assert(millis == Millis(0)); 239 assert(seconds == Seconds(0)); 240 } 241 242 unittest 243 { 244 writeln("[UnitTest toUnits Seconds]"); 245 246 auto nanos = Seconds(1).toUnits!Nanos; 247 auto micros = Seconds(1).toUnits!Micros; 248 auto millis = Seconds(1).toUnits!Millis; 249 auto seconds = Seconds(1).toUnits!Seconds; 250 251 assert(nanos == 1_000_000_000f); 252 assert(micros == 1_000_000f); 253 assert(millis == 1_000f); 254 assert(seconds == 1f); 255 } 256 257 unittest 258 { 259 writeln("[UnitTest toUnits Millis]"); 260 261 auto nanos = Millis(1).toUnits!Nanos; 262 auto micros = Millis(1).toUnits!Micros; 263 auto millis = Millis(1).toUnits!Millis; 264 auto seconds = Millis(1).toUnits!Seconds; 265 266 assert(nanos == 1_000_000f); 267 assert(micros == 1_000f); 268 assert(millis == 1f); 269 assert(seconds == 0.001f); 270 } 271 272 unittest 273 { 274 writeln("[UnitTest toUnits Micros]"); 275 276 auto nanos = Micros(1).toUnits!Nanos; 277 auto micros = Micros(1).toUnits!Micros; 278 auto millis = Micros(1).toUnits!Millis; 279 auto seconds = Micros(1).toUnits!Seconds; 280 281 assert(nanos == 1_000f); 282 assert(micros == 1f); 283 assert(millis == 0.001f); 284 assert(seconds == 0.000001f); 285 } 286 287 unittest 288 { 289 writeln("[UnitTest toUnits Nanos]"); 290 291 auto nanos = Nanos(1).toUnits!Nanos; 292 auto micros = Nanos(1).toUnits!Micros; 293 auto millis = Nanos(1).toUnits!Millis; 294 auto seconds = Nanos(1).toUnits!Seconds; 295 296 assert(nanos == 1f); 297 assert(micros == 0.001f); 298 assert(millis == 0.000001f); 299 assert(seconds == 0.000000001f); 300 } 301 302 unittest 303 { 304 writeln("[UnitTest opAssign]"); 305 306 Nanos nanos; 307 Micros micros; 308 Millis millis; 309 Seconds seconds; 310 311 nanos = Micros(10); 312 micros = Nanos(1_000); 313 millis = Seconds(1); 314 seconds = Millis(5_999); 315 316 assert(nanos == Nanos(10_000)); 317 assert(micros == Micros(1)); 318 assert(millis == Millis(1_000)); 319 assert(seconds == Seconds(5)); 320 } 321 322 unittest 323 { 324 writeln("[UnitTest opCmp]"); 325 326 assert(Seconds(1) > Seconds(0)); 327 assert(Seconds(1) == Seconds(1)); 328 assert(Seconds(1) < Seconds(2)); 329 330 assert(Seconds(1) > Millis(999)); 331 assert(Seconds(1) == Millis(1_000)); 332 assert(Seconds(1) < Millis(1_001)); 333 334 assert(Seconds(1) > Micros(999_999)); 335 assert(Seconds(1) == Micros(1_000_000)); 336 assert(Seconds(1) < Micros(1_000_001)); 337 338 assert(Seconds(1) > Nanos(999_999_999)); 339 assert(Seconds(1) == Nanos(1_000_000_000)); 340 assert(Seconds(1) < Nanos(1_000_000_001)); 341 342 assert(Nanos(1) < Micros(1)); 343 assert(Nanos(1_000) == Micros(1)); 344 assert(Nanos(1_001) > Micros(1)); 345 } 346 347 unittest 348 { 349 writeln("[UnitTest Duration]"); 350 351 Seconds seconds = Seconds(100); 352 Millis millis = Millis(100); 353 Micros micros = Micros(100); 354 Nanos nanos = Nanos(100); 355 356 assert(seconds.units == TimeUnit.SECONDS); 357 assert(seconds.toString() == "100 seconds"); 358 359 assert(millis.units == TimeUnit.MILLIS); 360 assert(millis.toString() == "100 milliseconds"); 361 362 assert(micros.units == TimeUnit.MICROS); 363 assert(micros.toString() == "100 microseconds"); 364 365 assert(nanos.units == TimeUnit.NANOS); 366 assert(nanos.toString() == "100 nanoseconds"); 367 }