1 /** 2 * Some nice "polyfills" for standard std.datetime types. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.time.types; 15 16 // *************************************************************************** 17 18 import std.datetime; 19 20 /// `typeof(SysTime.stdTime)`, the numeric type used to store absolute time in D. 21 alias StdTime = typeof(SysTime.init.stdTime); // long 22 23 /// Convert from `StdTime` to `Duration`. 24 alias stdDur = hnsecs; 25 26 /// Like `SysTime.stdTime`. 27 @property StdTime stdTime(Duration d) pure @safe nothrow @nogc 28 { 29 return d.total!"hnsecs"(); 30 } 31 32 /// `true` when the duration `d` is zero. 33 @property bool empty(Duration d) pure @safe nothrow @nogc 34 { 35 return !d.stdTime; 36 } 37 38 /// Workaround SysTime.fracSecs only being available in 2.067, 39 /// and SysTime.fracSec becoming deprecated in the same version. 40 static if (!is(typeof(SysTime.init.fracSecs))) 41 @property Duration fracSecs(SysTime s) 42 { 43 enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); 44 return hnsecs(s.stdTime % hnsecsPerSecond); 45 } 46 47 /// As above, for Duration.split and Duration.get 48 static if (!is(typeof(Duration.init.split!()))) 49 @property auto split(units...)(Duration d) 50 { 51 static struct Result 52 { 53 mixin("long " ~ [units].join(", ") ~ ";"); 54 } 55 56 Result result; 57 foreach (unit; units) 58 { 59 static if (is(typeof(d.get!unit))) // unit == "msecs" || unit == "usecs" || unit == "hnsecs" || unit == "nsecs") 60 long value = d.get!unit(); 61 else 62 long value = mixin("d.fracSec." ~ unit); 63 mixin("result." ~ unit ~ " = value;"); 64 } 65 return result; 66 } 67 68 // *************************************************************************** 69 70 /// An absolute, timezone-less point in time. 71 /// Like `SysTime`, but does not carry timezone information. 72 /// Zero-overhead wrapper around `StdTime` which attempts to 73 /// be compatible with `std.datetime`. 74 struct AbsTime 75 { 76 StdTime stdTime; 77 78 this(StdTime stdTime) pure @safe nothrow @nogc { this.stdTime = stdTime; } 79 this(SysTime sysTime) pure @safe nothrow @nogc { this.stdTime = sysTime.stdTime; } 80 81 this(DateTime dateTime, Duration fracSecs = Duration.zero) pure @safe nothrow @nogc 82 { 83 assert(fracSecs >= Duration.zero && fracSecs < seconds(1), "Invalid fracSecs"); 84 85 immutable dateDiff = dateTime.date - Date.init; 86 immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; 87 88 auto t = dateDiff + todDiff + fracSecs; 89 this(t.stdTime); 90 } 91 92 private static immutable epochDate = Date(1, 1, 1); 93 this(Date date) pure @safe nothrow @nogc { this((date - epochDate).stdTime); } 94 95 @property SysTime sysTime(immutable TimeZone tz = null) const pure @safe nothrow /*@nogc*/ { return SysTime(stdTime, tz); } 96 97 /// The Xth day of the Gregorian Calendar (in the UTC time zone) that this AbsTime is on. 98 @property int dayOfGregorianCal() const pure @safe nothrow @nogc 99 { 100 // As in SysTime: 101 auto days = cast(int) stdTime.stdDur.total!"days"; 102 if (stdTime > 0 || stdTime == days.days.stdTime) 103 days++; 104 return days; 105 } 106 107 @safe unittest // As in SysTime 108 { 109 import std.datetime.date : DateTime; 110 111 assert(AbsTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); 112 assert(AbsTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); 113 assert(AbsTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); 114 115 assert(AbsTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); 116 assert(AbsTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); 117 assert(AbsTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); 118 119 assert(AbsTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); 120 assert(AbsTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); 121 } 122 123 Date opCast(T : Date)() const pure @safe nothrow @nogc { return Date(dayOfGregorianCal); } 124 125 int opCmp(const AbsTime b) const pure @safe nothrow @nogc { return this.stdTime < b.stdTime ? -1 : this.stdTime > b.stdTime ? +1 : 0; } 126 int opCmp(const SysTime b) const pure @safe nothrow @nogc { return this.opCmp(AbsTime(b)); } 127 128 Duration opBinary(string op : "-")(AbsTime b) const pure @safe nothrow @nogc { return (this.stdTime - b.stdTime).stdDur; } 129 Duration opBinary(string op : "-")(SysTime b) const pure @safe nothrow @nogc { return (this.stdTime - b.stdTime).stdDur; } 130 Duration opBinaryRight(string op : "-")(SysTime a) const pure @safe nothrow @nogc { return (a.stdTime - this.stdTime).stdDur; } 131 132 AbsTime opBinary(string op : "-")(Duration d) const pure @safe nothrow @nogc { return AbsTime(this.stdTime - d.stdTime); } 133 AbsTime opBinary(string op : "+")(Duration d) const pure @safe nothrow @nogc { return AbsTime(this.stdTime + d.stdTime); } 134 AbsTime opBinaryRight(string op : "+")(Duration d) const pure @safe nothrow @nogc { return AbsTime(d.stdTime + this.stdTime); } 135 136 string toString() const @safe nothrow { return sysTime.toString(); } 137 138 static enum min = AbsTime(SysTime.min.stdTime); 139 static enum max = AbsTime(SysTime.max.stdTime); 140 } 141 142 AbsTime absTime(StdTime stdTime) { return AbsTime(stdTime); } 143 AbsTime absTime(SysTime sysTime) { return AbsTime(sysTime); }