From bfb003cd8e36dc80c4aa3ae152c83bc15f1f477d Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Mon, 12 Dec 2016 14:58:06 -0500 Subject: [PATCH 001/262] Replace use of onRangeError --- std/format.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/format.d b/std/format.d index 4218f0cdfb9..f9aeeb76502 100644 --- a/std/format.d +++ b/std/format.d @@ -6534,7 +6534,7 @@ immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar */ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { - import core.exception : onRangeError; + import core.exception : onRangeError, RangeError; import std.utf : encode; import std.format : formattedWrite, FormatException; @@ -6548,7 +6548,7 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) auto n = encode(enc, c); if (buf.length < i + n) - onRangeError("std.string.sformat", 0); + throw new RangeError(__FILE__, __LINE__); buf[i .. i + n] = enc[0 .. n]; i += n; @@ -6556,7 +6556,7 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) void put(const(char)[] s) { if (buf.length < i + s.length) - onRangeError("std.string.sformat", 0); + throw new RangeError(__FILE__, __LINE__); buf[i .. i + s.length] = s[]; i += s.length; From db2bf8fcfb494bdf62c4d7b6ff1199e1cfa5c06f Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Sat, 27 Feb 2016 20:12:25 +0200 Subject: [PATCH 002/262] Fix issue 6621 - add sliding window iterator --- changelog/std-range-slides.dd | 33 ++ std/range/package.d | 987 ++++++++++++++++++++++++++++++++++ 2 files changed, 1020 insertions(+) create mode 100644 changelog/std-range-slides.dd diff --git a/changelog/std-range-slides.dd b/changelog/std-range-slides.dd new file mode 100644 index 00000000000..c2898806eb6 --- /dev/null +++ b/changelog/std-range-slides.dd @@ -0,0 +1,33 @@ +`std.range.slides` (a fixed-size sliding window range) was added + +$(REF slides, std, range) allows to iterate a range in sliding windows: + +--- +import std.array : array; +import std.algorithm.comparison : equal; + +assert([0, 1, 2, 3].slides(2).equal!equal( + [[0, 1], [1, 2], [2, 3]] +)); +assert(5.iota.slides(3).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4]] +)); + +assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); +assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + +// set a custom stepsize (default 1) +assert(6.iota.slides(1, 2).equal!equal( + [[0], [2], [4]] +)); + +assert(6.iota.slides(2, 4).equal!equal( + [[0, 1], [4, 5]] +)); + +// allow slides with less elements than the window size +assert(3.iota.slides!(No.slidesWithLessElements)(4).empty); +assert(3.iota.slides!(Yes.slidesWithLessElements)(4).equal!equal( + [[0, 1, 2]] +)); +--- diff --git a/std/range/package.d b/std/range/package.d index a177b18d1a4..7224134ae66 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -125,6 +125,12 @@ $(BOOKTABLE , $(TD Similar to $(D recurrence), except that a random-access _range is created. )) + $(TR $(TD $(D $(LREF slides))) + $(TD Creates a _range that returns a fixed-size sliding window + over the original _range. Unlike chunks, + it advances a configurable number of items at a time, + not one chunk at a time). + )) $(TR $(TD $(D $(LREF stride))) $(TD Iterates a _range with stride $(I n). )) @@ -6810,6 +6816,15 @@ greater than zero. If $(D !isInfinite!Source) and $(D source.walkLength) is not evenly divisible by $(D chunkSize), the back element of this range will contain fewer than $(D chunkSize) elements. + +Params: + r = Range from which the chunks will be selected + chunkSize = Chunk size + +See_Also: $(LREF slides) + +Returns: Forward range of all chunks with propagated bidirectionality, + conditional random access and slicing. */ struct Chunks(Source) if (isForwardRange!Source) @@ -7299,6 +7314,978 @@ if (isForwardRange!Source && hasLength!Source) assert(equal(chunks, [[1], [2], [3], [], []])); } +/** +A fixed-sized sliding window iteration +of size `windowSize` over a `source` range by a custom `stepSize`. + +The `Source` range must be at least an `ForwardRange` and +the `windowSize` must be greater than zero. + +For `windowSize = 1` it splits the range into single element groups (aka `unflatten`) +For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. + +Params: + f = If `Yes.slidesWithLessElements` slides with fewer + elements than `windowSize`. This can only happen if the initial range + contains less elements than `windowSize`. In this case + if `No.slidesWithLessElements` an empty range will be returned. + r = Range from which the slides will be selected + windowSize = Sliding window size + stepSize = Steps between the windows (by default 1) + +Returns: Range of all sliding windows with propagated bidirectionality, + forwarding, conditional random access and slicing. + +See_Also: $(LREF chunks) +*/ +auto slides(Flag!"slidesWithLessElements" f = Yes.slidesWithLessElements, + Source)(Source source, size_t windowSize, size_t stepSize = 1) + if (isForwardRange!Source) +{ + return Slides!(f, Source)(source, windowSize, stepSize); +} + +private struct Slides(Flag!"slidesWithLessElements" slidesWithLessElements = Yes.slidesWithLessElements, Source) + if (isForwardRange!Source) +{ +private: + Source _source; + size_t _windowSize; + size_t _stepSize; + + static if (hasLength!Source) + { + enum needsEndTracker = false; + } + else + { + // if there's no information about the length, track needs to be kept manually + private Source _nextSource; + enum needsEndTracker = true; + } + + private bool _empty; + + static if (hasSlicing!Source) + { + private enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); + } + +public: + /// Standard constructor + this(Source source, size_t windowSize, size_t stepSize) + { + assert(windowSize > 0, "windowSize must be greater than zero"); + assert(stepSize > 0, "stepSize must be greater than zero"); + _source = source; + _windowSize = windowSize; + _stepSize = stepSize; + + static if (needsEndTracker) + { + // _nextSource is used to "look into the future" and check for the end + _nextSource = source.save; + _nextSource.popFrontN(windowSize); + } + + static if (!slidesWithLessElements) + { + // empty source range is needed, s.t. length, slicing etc. works properly + static if (needsEndTracker) + { + if (_nextSource.empty) + _source = _nextSource; + } + else + { + if (_source.length < windowSize) + { + static if (hasSlicing!Source) + { + // if possible use the faster opDollar overload + static if (hasSliceToEnd) + _source = _source[$ .. $]; + else + _source = _source[_source.length .. _source.length]; + } + else + { + _source.popFrontN(_source.length); + } + } + } + } + + _empty = _source.empty; + } + + /// Forward range primitives. Always present. + @property auto front() + { + assert(!empty, "Attempting to access front on an empty range"); + static if (hasSlicing!Source && hasLength!Source) + { + import std.algorithm.comparison : min; + return _source[0 .. min(_windowSize, _source.length)]; + } + else + { + return _source.save.take(_windowSize); + } + } + + /// Ditto + void popFront() + { + assert(!empty, "Attempting to call popFront() on an empty range"); + _source.popFrontN(_stepSize); + + // if the range has less elements than its window size, + // we have seen the last full window (i.e. its empty) + static if (needsEndTracker) + { + if (_nextSource.empty) + _empty = true; + else + _nextSource.popFrontN(_stepSize); + } + else + { + if (_source.length < _windowSize) + _empty = true; + } + } + + static if (!isInfinite!Source) + { + /// Ditto + @property bool empty() const + { + return _empty; + } + } + else + { + // undocumented + enum empty = false; + } + + /// Ditto + @property typeof(this) save() + { + return typeof(this)(_source.save, _windowSize, _stepSize); + } + + static if (hasLength!Source) + { + /// Length. Only if $(D hasLength!Source) is $(D true) + @property size_t length() + { + if (_source.length < _windowSize) + { + static if (slidesWithLessElements) + return 1; + else + return 0; + } + else + { + return (_source.length - _windowSize + _stepSize) / _stepSize; + } + } + } + + static if (hasSlicing!Source) + { + /** + Indexing and slicing operations. Provided only if + $(D hasSlicing!Source) is $(D true). + */ + auto opIndex(size_t index) + { + immutable start = index * _stepSize; + + static if (isInfinite!Source) + { + immutable end = start + _windowSize; + } + else + { + import std.algorithm.comparison : min; + + immutable len = _source.length; + assert(start < len, "slides index out of bounds"); + immutable end = min(start + _windowSize, len); + } + + return _source[start .. end]; + } + + static if (!isInfinite!Source) + { + typeof(this) opSlice(size_t lower, size_t upper) + { + import std.algorithm.comparison : min; + assert(lower <= upper && upper <= length, "slides slicing index out of bounds"); + + lower *= _stepSize; + upper *= _stepSize; + + immutable len = _source.length; + + /* + * Notice that we only need to move for windowSize - 1 to the right: + * source = [0, 1, 2, 3] (length: 4) + * - source.slides(2) -> s = [[0, 1], [1, 2], [2, 3]] + * right pos for s[0..3]: 3 (upper) + 2 (windowSize) - 1 = 4 + * + * - source.slides(3) -> s = [[0, 1, 2], [1, 2, 3]] + * right pos for s[0..2]: 2 (upper) + 3 (windowSize) - 1 = 4 + * + * source = [0, 1, 2, 3, 4] (length: 5) + * - source.slides(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] + * right pos for s[0..2]: 2 (upper) + 4 (windowSize) - 1 = 5 + */ + return typeof(this) + (_source[min(lower, len) .. min(upper + _windowSize - 1, len)], + _windowSize, _stepSize); + } + } + else static if (hasSliceToEnd) + { + //For slicing an infinite chunk, we need to slice the source to the infinite end. + auto opSlice(size_t lower, size_t upper) + { + assert(lower <= upper, "slides slicing index out of bounds"); + return typeof(this)(_source[lower * _stepSize .. $], + _windowSize, _stepSize).takeExactly(upper - lower); + } + } + + static if (isInfinite!Source) + { + static if (hasSliceToEnd) + { + private static struct DollarToken{} + DollarToken opDollar() + { + return DollarToken(); + } + //Slice to dollar + typeof(this) opSlice(size_t lower, DollarToken) + { + return typeof(this)(_source[lower * _stepSize .. $], _windowSize, _stepSize); + } + } + } + else + { + //Dollar token carries a static type, with no extra information. + //It can lazily transform into _source.length on algorithmic + //operations such as : slides[$/2, $-1]; + private static struct DollarToken + { + private size_t _length; + alias _length this; + } + + DollarToken opDollar() + { + return DollarToken(this.length); + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(DollarToken, DollarToken) + { + static if (hasSliceToEnd) + { + return typeof(this)(_source[$ .. $], _windowSize, _stepSize); + } + else + { + immutable len = _source.length; + return typeof(this)(_source[len .. len], _windowSize, _stepSize); + } + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(size_t lower, DollarToken) + { + import std.algorithm.comparison : min; + assert(lower <= length, "slides slicing index out of bounds"); + lower *= _stepSize; + static if (hasSliceToEnd) + { + return typeof(this)(_source[min(lower, _source.length) .. $], _windowSize, _stepSize); + } + else + { + immutable len = _source.length; + return typeof(this)(_source[min(lower, len) .. len], _windowSize, _stepSize); + } + } + + // Optimized slice overloads optimized for using dollar. + typeof(this) opSlice(DollarToken, size_t upper) + { + assert(upper == length, "slides slicing index out of bounds"); + return this[$ .. $]; + } + } + + // Bidirectional range primitives + static if (!isInfinite!Source) + { + /** + Bidirectional range primitives. Provided only if both + `hasSlicing!Source` and `!isInfinite!Source` are `true`. + */ + @property auto back() + { + import std.algorithm.comparison : max; + + assert(!empty, "Attempting to access front on an empty slice"); + + immutable len = _source.length; + /* + * Note: + * - `end` in the following is the exclusive end as used in opSlice + * - For the trivial case with `stepSize = 1` `end` is at `len`: + * + * iota(4).slides(2) = [[0, 1], [1, 2], [2, 3] (end = 4) + * iota(4).slides(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) + * + * - For the non-trivial cases, we need to calculate the gap + * between `len` and `end` - this is the number of missing elements + * from the input range: + * + * iota(7).slides(2, 3) = [[0, 1], [3, 4]] || 6 + * iota(7).slides(2, 4) = [[0, 1], [4, 5]] || 6 + * iota(7).slides(1, 5) = [[0], [5]] || 6 + * + * As it can be seen `gap` can be at most `stepSize - 1` + * More generally the elements of the sliding window with + * `w = windowSize` and `s = stepSize` are: + * + * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w] + * + * We can thus calculate the gap between the `end` and `len` as: + * + * gap = len - (n * s + w) = len - w - (n * s) + * + * As we aren't interested in exact value of `n`, but the best + * minimal `gap` value, we can use modulo to "cut" `len - w` optimally: + * + * gap = len - w - (s - s ... - s) = (len - w) % s + * + * So for example: + * + * iota(7).slides(2, 3) = [[0, 1], [3, 4]] + * gap: (7 - 2) % 3 = 5 % 3 = 2 + * end: 7 - 2 = 5 + * + * iota(7).slides(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] + * gap: (7 - 4) % 2 = 3 % 2 = 1 + * end: 7 - 1 = 6 + */ + size_t gap = (len - _windowSize) % _stepSize; + + // check for underflow + immutable start = (len > _windowSize + gap) ? len - _windowSize - gap : 0; + + return _source[start .. len - gap]; + } + + /// Ditto + void popBack() + { + assert(!empty, "Attempting to call popBack() on an empty range"); + + immutable end = _source.length > _stepSize ? _source.length - _stepSize : 0; + _source = _source[0 .. end]; + + if (_source.length < _windowSize) + _empty = true; + } + } + } +} + +/// +@safe pure nothrow unittest +{ + import std.array : array; + import std.algorithm.comparison : equal; + + assert([0, 1, 2, 3].slides(2).equal!equal( + [[0, 1], [1, 2], [2, 3]] + )); + assert(5.iota.slides(3).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4]] + )); + + assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + + // set a custom stepsize (default 1) + assert(6.iota.slides(1, 2).equal!equal( + [[0], [2], [4]] + )); + + assert(6.iota.slides(2, 4).equal!equal( + [[0, 1], [4, 5]] + )); + + // allow slides with less elements than the window size + assert(3.iota.slides!(No.slidesWithLessElements)(4).empty); + assert(3.iota.slides!(Yes.slidesWithLessElements)(4).equal!equal( + [[0, 1, 2]] + )); +} + +/// count k-mers +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : each; + + int[dstring] d; + "AGAGA"d.slides(2).each!(a => d[a]++); + assert(d == ["AG"d: 2, "GA"d: 2]); +} + +// @nogc +@safe pure nothrow @nogc unittest +{ + import std.algorithm.comparison : equal; + + static immutable res1 = [[0], [1], [2], [3]]; + assert(4.iota.slides(1).equal!equal(res1)); + + static immutable res2 = [[0, 1], [1, 2], [2, 3]]; + assert(4.iota.slides(2).equal!equal(res2)); +} + +// different window sizes +@safe pure nothrow unittest +{ + import std.array : array; + import std.algorithm.comparison : equal; + + assert([0, 1, 2, 3].slides(1).array == [[0], [1], [2], [3]]); + assert([0, 1, 2, 3].slides(2).array == [[0, 1], [1, 2], [2, 3]]); + assert([0, 1, 2, 3].slides(3).array == [[0, 1, 2], [1, 2, 3]]); + assert([0, 1, 2, 3].slides(4).array == [[0, 1, 2, 3]]); + assert([0, 1, 2, 3].slides(5).array == [[0, 1, 2, 3]]); + + + assert(iota(2).slides(2).front.equal([0, 1])); + assert(iota(3).slides(2).equal!equal([[0, 1],[1, 2]])); + assert(iota(3).slides(3).equal!equal([[0, 1, 2]])); + assert(iota(3).slides(4).equal!equal([[0, 1, 2]])); + assert(iota(1, 4).slides(1).equal!equal([[1], [2], [3]])); + assert(iota(1, 4).slides(3).equal!equal([[1, 2, 3]])); +} + +unittest +{ + import std.algorithm.comparison : equal; + + assert(6.iota.slides(1, 1).equal!equal( + [[0], [1], [2], [3], [4], [5]] + )); + assert(6.iota.slides(1, 2).equal!equal( + [[0], [2], [4]] + )); + assert(6.iota.slides(1, 3).equal!equal( + [[0], [3]] + )); + assert(6.iota.slides(1, 4).equal!equal( + [[0], [4]] + )); + assert(6.iota.slides(1, 5).equal!equal( + [[0], [5]] + )); + assert(6.iota.slides(2, 1).equal!equal( + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]] + )); + assert(6.iota.slides(2, 2).equal!equal( + [[0, 1], [2, 3], [4, 5]] + )); + assert(6.iota.slides(2, 3).equal!equal( + [[0, 1], [3, 4]] + )); + assert(6.iota.slides(2, 4).equal!equal( + [[0, 1], [4, 5]] + )); + assert(6.iota.slides(2, 5).equal!equal( + [[0, 1]] + )); + assert(6.iota.slides(3, 1).equal!equal( + [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]] + )); + assert(6.iota.slides(3, 2).equal!equal( + [[0, 1, 2], [2, 3, 4]] + )); + assert(6.iota.slides(3, 3).equal!equal( + [[0, 1, 2], [3, 4, 5]] + )); + assert(6.iota.slides(3, 4).equal!equal( + [[0, 1, 2]] + )); + assert(6.iota.slides(4, 1).equal!equal( + [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] + )); + assert(6.iota.slides(4, 2).equal!equal( + [[0, 1, 2, 3], [2, 3, 4, 5]] + )); + assert(6.iota.slides(4, 3).equal!equal( + [[0, 1, 2, 3]] + )); + assert(6.iota.slides(5, 1).equal!equal( + [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]] + )); + assert(6.iota.slides(5, 2).equal!equal( + [[0, 1, 2, 3, 4]] + )); + assert(6.iota.slides(5, 3).equal!equal( + [[0, 1, 2, 3, 4]] + )); +} + +// emptyness, copyability, strings +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : each, map; + + // check with empty input + int[] d; + assert(d.slides(2).empty); + assert(d.slides(2, 2).empty); + + // is copyable? + auto e = iota(5).slides(2); + e.popFront; + assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); + assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); + assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]); + + // test with strings + int[dstring] f; + "AGAGA"d.slides(3).each!(a => f[a]++); + assert(f == ["AGA"d: 2, "GAG"d: 1]); + + int[dstring] g; + "ABCDEFG"d.slides(3, 3).each!(a => g[a]++); + assert(g == ["ABC"d:1, "DEF"d:1]); +} + +// test slicing, length +@safe pure nothrow unittest +{ + import std.array : array; + import std.algorithm.comparison : equal; + + // test index + assert(iota(3).slides(4)[0].equal([0, 1, 2])); + assert(iota(5).slides(4)[1].equal([1, 2, 3, 4])); + assert(iota(3).slides(4, 2)[0].equal([0, 1, 2])); + assert(iota(5).slides(4, 2)[1].equal([2, 3, 4])); + assert(iota(3).slides(4, 3)[0].equal([0, 1, 2])); + assert(iota(5).slides(4, 3)[1].equal([3, 4,])); + + // test slicing + assert(iota(3).slides(4)[0 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slides(2)[1 .. $].equal!equal([[1, 2]])); + assert(iota(1, 5).slides(2)[0 .. 1].equal!equal([[1, 2]])); + assert(iota(1, 5).slides(2)[0 .. 2].equal!equal([[1, 2], [2, 3]])); + assert(iota(1, 5).slides(3)[0 .. 1].equal!equal([[1, 2, 3]])); + assert(iota(1, 5).slides(3)[0 .. 2].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(1, 6).slides(3)[2 .. 3].equal!equal([[3, 4, 5]])); + assert(iota(1, 5).slides(4)[0 .. 1].equal!equal([[1, 2, 3, 4]])); + + // length + assert(iota(3).slides(1).length == 3); + assert(iota(3).slides(1, 2).length == 2); + assert(iota(3).slides(1, 3).length == 1); + assert(iota(3).slides(1, 4).length == 1); + assert(iota(3).slides(2).length == 2); + assert(iota(3).slides(2, 2).length == 1); + assert(iota(3).slides(2, 3).length == 1); + assert(iota(3).slides(3).length == 1); + assert(iota(3).slides(3, 2).length == 1); + + // opDollar + assert(iota(3).slides(4)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slides(4)[$ .. $].empty); + assert(iota(3).slides(4)[$ .. 1].empty); + + assert(iota(5).slides(3, 1)[$/2 .. $].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(5).slides(3, 2)[$/2 .. $].equal!equal([[2, 3, 4]])); + assert(iota(5).slides(3, 3)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slides(4, 3)[$ .. $].empty); + assert(iota(3).slides(4, 3)[$ .. 1].empty); +} + +// test No.slidesWithLessElements +@safe pure nothrow unittest +{ + assert(iota(3).slides(4).length == 1); + assert(iota(3).slides(4, 4).length == 1); + + assert(iota(3).slides!(No.slidesWithLessElements)(4).empty); + assert(iota(3, 3).slides!(No.slidesWithLessElements)(4).empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4).length == 0); + assert(iota(3).slides!(No.slidesWithLessElements)(4, 4).length == 0); + + assert(iota(3).slides!(No.slidesWithLessElements)(400).empty); + assert(iota(3).slides!(No.slidesWithLessElements)(400).length == 0); + assert(iota(3).slides!(No.slidesWithLessElements)(400, 10).length == 0); + + assert(iota(3).slides!(No.slidesWithLessElements)(4)[0 .. $].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4)[$ .. $].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4)[$ .. 0].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4)[$/2 .. $].empty); + + // with different step sizes + assert(iota(3).slides!(No.slidesWithLessElements)(4, 5)[0 .. $].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4, 6)[$ .. $].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4, 7)[$ .. 0].empty); + assert(iota(3).slides!(No.slidesWithLessElements)(4, 8)[$/2 .. $].empty); +} + +// test with infinite ranges +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + // InfiniteRange without RandomAccess + auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); + assert(fibs.slides(2).take(2).equal!equal([[1, 1], [1, 2]])); + assert(fibs.slides(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); + + // InfiniteRange with RandomAccess and slicing + auto odds = sequence!("a[0] + n * a[1]")(1, 2); + auto oddsByPairs = odds.slides(2); + assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); + assert(oddsByPairs[1].equal([3, 5])); + assert(oddsByPairs[4].equal([9, 11])); + + static assert(hasSlicing!(typeof(odds))); + assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); + assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); + + auto oddsWithGaps = odds.slides(2, 4); + assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); + assert(oddsWithGaps[2].equal([17, 19])); + assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); + assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]])); +} + +// test reverse +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + auto e = iota(3).slides(2); + assert(e.retro.equal!equal([[1, 2], [0, 1]])); + assert(e.retro.array.equal(e.array.retro)); + + auto e2 = iota(5).slides(3); + assert(e2.retro.equal!equal([[2, 3, 4], [1, 2, 3], [0, 1, 2]])); + assert(e2.retro.array.equal(e2.array.retro)); + + auto e3 = iota(3).slides(4); + assert(e3.retro.equal!equal([[0, 1, 2]])); + assert(e3.retro.array.equal(e3.array.retro)); +} + +// test reverse with different steps +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + assert(iota(7).slides(2, 1).retro.equal!equal( + [[5, 6], [4, 5], [3, 4], [2, 3], [1, 2], [0, 1]] + )); + assert(iota(7).slides(2, 2).retro.equal!equal( + [[4, 5], [2, 3], [0, 1]] + )); + assert(iota(7).slides(2, 3).retro.equal!equal( + [[3, 4], [0, 1]] + )); + assert(iota(7).slides(2, 4).retro.equal!equal( + [[4, 5], [0, 1]] + )); + assert(iota(7).slides(2, 5).retro.equal!equal( + [[5, 6], [0, 1]] + )); + assert(iota(7).slides(3, 1).retro.equal!equal( + [[4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] + )); + assert(iota(7).slides(3, 2).retro.equal!equal( + [[4, 5, 6], [2, 3, 4], [0, 1, 2]] + )); + assert(iota(7).slides(4, 1).retro.equal!equal( + [[3, 4, 5, 6], [2, 3, 4, 5], [1, 2, 3, 4], [0, 1, 2, 3]] + )); + assert(iota(7).slides(4, 2).retro.equal!equal( + [[2, 3, 4, 5], [0, 1, 2, 3]] + )); + assert(iota(7).slides(4, 3).retro.equal!equal( + [[3, 4, 5, 6], [0, 1, 2, 3]] + )); + assert(iota(7).slides(4, 4).retro.equal!equal( + [[0, 1, 2, 3]] + )); + assert(iota(7).slides(5, 1).retro.equal!equal( + [[2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [0, 1, 2, 3, 4]] + )); + assert(iota(7).slides(5, 2).retro.equal!equal( + [[2, 3, 4, 5, 6], [0, 1, 2, 3, 4]] + )); + assert(iota(7).slides(5, 3).retro.equal!equal( + [[0, 1, 2, 3, 4]] + )); + assert(iota(7).slides(5, 4).retro.equal!equal( + [[0, 1, 2, 3, 4]] + )); +} + +// step size +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + + assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(8).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(9).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + assert(iota(13).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); +} + +// test with dummy ranges +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy, AllDummyRanges; + import std.meta : AliasSeq; + + alias AllForwardDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional), + //DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), + //DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), + DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional) + ); + + foreach (Range; AliasSeq!AllForwardDummyRanges) + { + Range r; + assert(r.slides(1).equal!equal( + [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] + )); + assert(r.slides(2).equal!equal( + [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] + )); + assert(r.slides(3).equal!equal( + [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], + [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] + )); + assert(r.slides(6).equal!equal( + [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], + [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] + )); + assert(r.slides(15).equal!equal( + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] + )); + + assert(r.slides!(No.slidesWithLessElements)(15).empty); + } + + alias BackwardsDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), + ); + + foreach (Range; AliasSeq!BackwardsDummyRanges) + { + Range r; + assert(r.slides(1).retro.equal!equal( + [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]] + )); + assert(r.slides(2).retro.equal!equal( + [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]] + )); + assert(r.slides(5).retro.equal!equal( + [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], + [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]] + )); + assert(r.slides(15).retro.equal!equal( + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] + )); + + // different step sizes + assert(r.slides(2, 4)[2].equal([9, 10])); + assert(r.slides(2, 1).equal!equal( + [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] + )); + assert(r.slides(2, 2).equal!equal( + [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] + )); + assert(r.slides(2, 3).equal!equal( + [[1, 2], [4, 5], [7, 8]] + )); + assert(r.slides(2, 4).equal!equal( + [[1, 2], [5, 6], [9, 10]] + )); + + // front = back + foreach (windowSize; 1..10) + foreach (stepSize; 1..10) + { + auto slider = r.slides(windowSize, stepSize); + assert(slider.retro.retro.equal!equal(slider)); + } + } + + assert(iota(1, 12).slides(2, 4)[0..3].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slides(2, 4)[0..$].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slides(2, 4)[$/2..$].equal!equal([[5, 6], [9, 10]])); + + // reverse + assert(iota(1, 12).slides(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]])); +} + +// test different sliceable ranges +@safe pure nothrow unittest +{ + import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; + import std.meta : AliasSeq; + + struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar, + Flag!"withInfiniteness" withInfiniteness = No.withInfiniteness) + { + Range arr = 10.iota.array; // similar to DummyRange + @property auto save() { return typeof(this)(arr); } + @property auto front() { return arr[0]; } + void popFront() { arr.popFront(); } + auto opSlice(size_t i, size_t j) + { + // subslices can't be infinite + return SliceableRange!(Range, withOpDollar, No.withInfiniteness)(arr[i .. j]); + } + + static if (withInfiniteness) + { + enum empty = false; + } + else + { + @property bool empty() { return arr.empty; } + @property auto length() { return arr.length; } + } + + static if (withOpDollar) + { + static if (withInfiniteness) + { + struct Dollar {} + Dollar opDollar() const { return Dollar.init; } + + //Slice to dollar + typeof(this) opSlice(size_t lower, Dollar) + { + return typeof(this)(arr[lower .. $]); + } + + } + else + { + alias opDollar = length; + } + } + } + + alias T = int[]; + + alias SliceableDummyRanges = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), + SliceableRange!(T, No.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness), + ); + + foreach (Range; AliasSeq!SliceableDummyRanges) + { + Range r; + r.arr = 10.iota.array; // for clarity + + static assert (isForwardRange!Range); + enum hasSliceToEnd = hasSlicing!Range && is(typeof(Range.init[0 .. $]) == Range); + + assert(r.slides(2)[0].equal([0, 1])); + assert(r.slides(2)[1].equal([1, 2])); + + // saveable + auto s = r.slides(2); + assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); + s.save.popFront; + assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); + + assert(r.slides(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); + } + + alias SliceableDummyRangesWithoutInfinity = AliasSeq!( + DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random, T), + DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random, T), + SliceableRange!(T, No.withOpDollar, No.withInfiniteness), + SliceableRange!(T, Yes.withOpDollar, No.withInfiniteness), + ); + + foreach (Range; AliasSeq!SliceableDummyRangesWithoutInfinity) + { + static assert (hasSlicing!Range); + static assert (hasLength!Range); + + Range r; + r.arr = 10.iota.array; // for clarity + + assert(r.slides!(No.slidesWithLessElements)(6).equal!equal( + [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], + [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] + )); + assert(r.slides!(No.slidesWithLessElements)(16).empty); + + assert(r.slides(4)[0 .. $].equal(r.slides(4))); + assert(r.slides(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); + assert(r.slides(2)[$ .. $].empty); + + assert(r.slides(3).retro.equal!equal( + [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] + )); + } + + // separate checks for infinity + auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); + assert(infIndex.slides(2)[0].equal([0, 1])); + assert(infIndex.slides(2)[1].equal([1, 2])); + + auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); + assert(infDollar.slides(2)[1 .. $].front.equal([1, 2])); + assert(infDollar.slides(4)[0 .. $].front.equal([0, 1, 2, 3])); + assert(infDollar.slides(4)[2 .. $].front.equal([2, 3, 4, 5])); +} private struct OnlyResult(T, size_t arity) { From 1dfbb9ad16e822fe240baa1e668795e921117cb7 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Sat, 4 Mar 2017 05:07:14 +0100 Subject: [PATCH 003/262] [BOOKTABLES]: Add BOOKTABLE to stdx.allocator --- std/experimental/allocator/package.d | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 91fff66f9c9..fc504ed2bbe 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -6,6 +6,33 @@ and destruction/deallocation of data including `struct`s and `class`es, and also array primitives related to allocation. This module is the entry point for both making use of allocators and for their documentation. +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Make) $(TD + $(LREF make) + $(LREF makeArray) + $(LREF makeMultidimensionalArray) +)) +$(TR $(TD Dispose) $(TD + $(LREF dispose) + $(LREF disposeMultidimensionalArray) +)) +$(TR $(TD Modify) $(TD + $(LREF expandArray) + $(LREF shrinkArray) +)) +$(TR $(TD Global) $(TD + $(LREF processAllocator) + $(LREF theAllocator) +)) +$(TR $(TD Class interface) $(TD + $(LREF allocatorObject) + $(LREF CAllocatorImpl) + $(LREF IAllocator) +)) +) + Synopsis: --- // Allocate an int, initialize it with 42 From 19966e57b375ccdf2bc1e3547c1eb78437403523 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Fri, 3 Mar 2017 10:51:40 -0500 Subject: [PATCH 004/262] Remove unnessesary call to std.conv.text from std.conv.convError --- std/conv.d | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/std/conv.d b/std/conv.d index 3c3f1b9d11d..c07c0fbc331 100644 --- a/std/conv.d +++ b/std/conv.d @@ -73,26 +73,32 @@ class ConvException : Exception mixin basicExceptionCtors; } -private string convError_unexpected(S)(S source) -{ - return source.empty ? "end of input" : text("'", source.front, "'"); -} - private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__) { - return new ConvException( - text("Unexpected ", convError_unexpected(source), - " when converting from type "~S.stringof~" to type "~T.stringof), - fn, ln); + string msg; + + if (source.empty) + msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof; + else + msg = text("Unexpected '", source.front, + "' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof); + + return new ConvException(msg, fn, ln); } private auto convError(S, T)(S source, int radix, string fn = __FILE__, size_t ln = __LINE__) { - return new ConvException( - text("Unexpected ", convError_unexpected(source), - " when converting from type "~S.stringof~" base ", radix, - " to type "~T.stringof), - fn, ln); + string msg; + + if (source.empty) + msg = text("Unexpected end of input when converting from type " ~ S.stringof ~ " base ", radix, + " to type " ~ T.stringof); + else + msg = text("Unexpected '", source.front, + "' when converting from type " ~ S.stringof ~ " base ", radix, + " to type " ~ T.stringof); + + return new ConvException(msg, fn, ln); } @safe pure/* nothrow*/ // lazy parameter bug From 6f01592b21473b74b0c6c88cbcacf529c49b6a2a Mon Sep 17 00:00:00 2001 From: Superstar64 Date: Thu, 9 Mar 2017 22:20:43 -0500 Subject: [PATCH 005/262] make InputAssignable work with isInputRange --- std/range/interfaces.d | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/range/interfaces.d b/std/range/interfaces.d index fa7435e39da..3a69077586f 100644 --- a/std/range/interfaces.d +++ b/std/range/interfaces.d @@ -208,6 +208,13 @@ interface RandomAccessInfinite(E) : ForwardRange!E { interface InputAssignable(E) : InputRange!E { /// @property void front(E newVal); + + @property E front(); +} + +@safe unittest +{ + static assert(isInputRange!(InputAssignable!int)); } /**Adds assignable elements to ForwardRange.*/ From 516c74bf0b71c8c84132daae0b42538a0e725ad7 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 14 Mar 2017 14:38:56 -0400 Subject: [PATCH 006/262] Added some type qualifiers to std.parallelism --- std/parallelism.d | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/std/parallelism.d b/std/parallelism.d index 99cdd95e2ed..7a5bebaeae0 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -2093,7 +2093,7 @@ public: else { - bool empty() @property + bool empty() const @property { // popFront() sets this when source is empty return buf1.length == 0; @@ -2889,11 +2889,11 @@ public: } } - ref T opIndex(size_t index) + ref opIndex(this Qualified)(size_t index) { import std.conv : text; assert(index < size, text(index, '\t', uint.max)); - return *(cast(T*) (data + elemSize * index)); + return *(cast(CopyTypeQualifiers!(Qualified, T)*) (data + elemSize * index)); } void opIndexAssign(T val, size_t index) @@ -2916,7 +2916,7 @@ public: failure will result when calling this method. This is not checked when assertions are disabled for performance reasons. */ - ref T get() @property + ref get(this Qualified)() @property { assert(*stillThreadLocal, "Cannot call get() on this instance of WorkerLocalStorage " ~ @@ -2996,12 +2996,12 @@ public: } public: - ref T front() @property + ref front(this Qualified)() @property { return this[0]; } - ref T back() @property + ref back(this Qualified)() @property { return this[_length - 1]; } @@ -3028,7 +3028,7 @@ public: return this; } - ref T opIndex(size_t index) + ref opIndex(this Qualified)(size_t index) { assert(index < _length); return workerLocalStorage[index + beginOffset]; @@ -3049,12 +3049,12 @@ public: return typeof(this)(newWl); } - bool empty() @property + bool empty() const @property { return length == 0; } - size_t length() @property + size_t length() const @property { return _length; } From af4bb44baa17f5a4aafda4f7bedcb4d0704fe514 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 8 Mar 2017 13:05:36 -0500 Subject: [PATCH 007/262] Use dfmt on std/concurrency.d --- std/concurrency.d | 976 +++++++++++++++++++--------------------------- 1 file changed, 393 insertions(+), 583 deletions(-) diff --git a/std/concurrency.d b/std/concurrency.d index cf2e17c9f94..44b9d8f8285 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -63,24 +63,21 @@ */ module std.concurrency; +public import std.variant; + +import core.atomic; +import core.sync.condition; +import core.sync.mutex; +import core.thread; +import std.concurrencybase; +import std.range.primitives; +import std.traits; -public -{ - import std.variant; -} private { - import core.atomic; - import core.thread; - import core.sync.mutex; - import core.sync.condition; - import std.range.primitives; - import std.traits; - import std.concurrencybase; - template hasLocalAliasing(T...) { - static if ( !T.length ) + static if (!T.length) enum hasLocalAliasing = false; else enum hasLocalAliasing = (std.traits.hasUnsharedAliasing!(T[0]) && !is(T[0] == Tid)) || @@ -117,10 +114,9 @@ private @property auto convertsTo(T...)() { - static if ( T.length == 1 ) + static if (T.length == 1) { - return is( T[0] == Variant ) || - data.convertsTo!(T); + return is(T[0] == Variant) || data.convertsTo!(T); } else { @@ -131,9 +127,9 @@ private @property auto get(T...)() { - static if ( T.length == 1 ) + static if (T.length == 1) { - static if ( is( T[0] == Variant ) ) + static if (is(T[0] == Variant)) return data; else return data.get!(T); @@ -145,47 +141,46 @@ private } } - auto map(Op)( Op op ) + auto map(Op)(Op op) { alias Args = Parameters!(Op); - static if ( Args.length == 1 ) + static if (Args.length == 1) { - static if ( is( Args[0] == Variant ) ) - return op( data ); + static if (is(Args[0] == Variant)) + return op(data); else - return op( data.get!(Args) ); + return op(data.get!(Args)); } else { import std.typecons : Tuple; - return op( data.get!(Tuple!(Args)).expand ); + return op(data.get!(Tuple!(Args)).expand); } } } - void checkops(T...)( T ops ) + void checkops(T...)(T ops) { - foreach ( i, t1; T ) + foreach (i, t1; T) { - static assert( isFunctionPointer!t1 || isDelegate!t1 ); + static assert(isFunctionPointer!t1 || isDelegate!t1); alias a1 = Parameters!(t1); alias r1 = ReturnType!(t1); - static if ( i < T.length - 1 && is( r1 == void ) ) + static if (i < T.length - 1 && is(r1 == void)) { - static assert( a1.length != 1 || !is( a1[0] == Variant ), - "function with arguments " ~ a1.stringof ~ - " occludes successive function" ); + static assert(a1.length != 1 || !is(a1[0] == Variant), + "function with arguments " ~ a1.stringof ~ + " occludes successive function"); - foreach ( t2; T[i+1 .. $] ) + foreach (t2; T[i + 1 .. $]) { - static assert( isFunctionPointer!t2 || isDelegate!t2 ); + static assert(isFunctionPointer!t2 || isDelegate!t2); alias a2 = Parameters!(t2); - static assert( !is( a1 == a2 ), - "function with arguments " ~ a1.stringof ~ - " occludes successive function" ); + static assert(!is(a1 == a2), + "function with arguments " ~ a1.stringof ~ " occludes successive function"); } } } @@ -193,22 +188,19 @@ private @property ref ThreadInfo thisInfo() nothrow { - if ( scheduler is null ) + if (scheduler is null) return ThreadInfo.thisInfo; return scheduler.thisInfo; } } - static ~this() { thisInfo.cleanup(); } - // Exceptions - /** * Thrown on calls to $(D receiveOnly) if a message other than the type * the receiving thread expected is sent. @@ -216,13 +208,12 @@ static ~this() class MessageMismatch : Exception { /// - this( string msg = "Unexpected message type" ) @safe pure nothrow @nogc + this(string msg = "Unexpected message type") @safe pure nothrow @nogc { - super( msg ); + super(msg); } } - /** * Thrown on calls to $(D receive) if the thread that spawned the receiving * thread has terminated and no more messages exist. @@ -230,32 +221,30 @@ class MessageMismatch : Exception class OwnerTerminated : Exception { /// - this( Tid t, string msg = "Owner terminated" ) @safe pure nothrow @nogc + this(Tid t, string msg = "Owner terminated") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown if a linked thread has terminated. */ class LinkTerminated : Exception { /// - this( Tid t, string msg = "Link terminated" ) @safe pure nothrow @nogc + this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown if a message was sent to a thread via * $(REF prioritySend, std,concurrency) and the receiver does not have a handler @@ -264,9 +253,9 @@ class LinkTerminated : Exception class PriorityMessageException : Exception { /// - this( Variant vals ) + this(Variant vals) { - super( "Priority message" ); + super("Priority message"); message = vals; } @@ -276,7 +265,6 @@ class PriorityMessageException : Exception Variant message; } - /** * Thrown on mailbox crowding if the mailbox is configured with * $(D OnCrowding.throwException). @@ -284,16 +272,15 @@ class PriorityMessageException : Exception class MailboxFull : Exception { /// - this( Tid t, string msg = "Mailbox full" ) @safe pure nothrow @nogc + this(Tid t, string msg = "Mailbox full") @safe pure nothrow @nogc { - super( msg ); + super(msg); tid = t; } Tid tid; } - /** * Thrown when a Tid is missing, e.g. when $(D ownerTid) doesn't * find an owner thread. @@ -315,13 +302,12 @@ class TidMissingException : Exception struct Tid { private: - this( MessageBox m ) @safe pure nothrow @nogc + this(MessageBox m) @safe pure nothrow @nogc { mbox = m; } - - MessageBox mbox; + MessageBox mbox; public: @@ -352,7 +338,6 @@ public: assert(text(tid2) == text(tid3)); } - /** * Returns: The $(LREF Tid) of the caller's thread. */ @@ -361,9 +346,9 @@ public: // TODO: remove when concurrency is safe static auto trus() @trusted { - if ( thisInfo.ident != Tid.init ) + if (thisInfo.ident != Tid.init) return thisInfo.ident; - thisInfo.ident = Tid( new MessageBox ); + thisInfo.ident = Tid(new MessageBox); return thisInfo.ident; } @@ -380,8 +365,7 @@ public: { import std.exception : enforce; - enforce!TidMissingException(thisInfo.owner.mbox !is null, - "Error: Thread has no owner thread."); + enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread."); return thisInfo.owner; } @@ -407,7 +391,7 @@ public: private template isSpawnable(F, T...) { - template isParamsImplicitlyConvertible(F1, F2, int i=0) + template isParamsImplicitlyConvertible(F1, F2, int i = 0) { alias param1 = Parameters!F1; alias param2 = Parameters!F2; @@ -416,15 +400,15 @@ private template isSpawnable(F, T...) else static if (param1.length == i) enum isParamsImplicitlyConvertible = true; else static if (isImplicitlyConvertible!(param2[i], param1[i])) - enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, F2, i+1); + enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, + F2, i + 1); else enum isParamsImplicitlyConvertible = false; } - enum isSpawnable = isCallable!F - && is(ReturnType!F == void) - && isParamsImplicitlyConvertible!(F, void function(T)) - && ( isFunctionPointer!F - || !hasUnsharedAliasing!F); + + enum isSpawnable = isCallable!F && is(ReturnType!F == void) + && isParamsImplicitlyConvertible!(F, void function(T)) + && (isFunctionPointer!F || !hasUnsharedAliasing!F); } /** @@ -478,15 +462,12 @@ private template isSpawnable(F, T...) * } * --- */ -Tid spawn(F, T...)( F fn, T args ) -if ( isSpawnable!(F, T) ) +Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T)) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - return _spawn( false, fn, args ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + return _spawn(false, fn, args); } - /** * Starts fn(args) in a logical thread and will receive a LinkTerminated * message when the operation terminates. @@ -506,38 +487,34 @@ if ( isSpawnable!(F, T) ) * Returns: * A Tid representing the new thread. */ -Tid spawnLinked(F, T...)( F fn, T args ) -if ( isSpawnable!(F, T) ) +Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T)) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - return _spawn( true, fn, args ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + return _spawn(true, fn, args); } - /* * */ -private Tid _spawn(F, T...)( bool linked, F fn, T args ) -if ( isSpawnable!(F, T) ) +private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T)) { // TODO: MessageList and &exec should be shared. - auto spawnTid = Tid( new MessageBox ); + auto spawnTid = Tid(new MessageBox); auto ownerTid = thisTid; void exec() { thisInfo.ident = spawnTid; thisInfo.owner = ownerTid; - fn( args ); + fn(args); } // TODO: MessageList and &exec should be shared. - if ( scheduler !is null ) - scheduler.spawn( &exec ); + if (scheduler !is null) + scheduler.spawn(&exec); else { - auto t = new Thread( &exec ); + auto t = new Thread(&exec); t.start(); } thisInfo.links[spawnTid] = linked; @@ -546,24 +523,24 @@ if ( isSpawnable!(F, T) ) @system unittest { - void function() fn1; - void function(int) fn2; - static assert( __traits(compiles, spawn(fn1))); - static assert( __traits(compiles, spawn(fn2, 2))); + void function() fn1; + void function(int) fn2; + static assert(__traits(compiles, spawn(fn1))); + static assert(__traits(compiles, spawn(fn2, 2))); static assert(!__traits(compiles, spawn(fn1, 1))); static assert(!__traits(compiles, spawn(fn2))); - void delegate(int) shared dg1; - shared(void delegate(int)) dg2; - shared(void delegate(long) shared) dg3; - shared(void delegate(real, int , long) shared) dg4; - void delegate(int) immutable dg5; - void delegate(int) dg6; - static assert( __traits(compiles, spawn(dg1, 1))); - static assert( __traits(compiles, spawn(dg2, 2))); - static assert( __traits(compiles, spawn(dg3, 3))); - static assert( __traits(compiles, spawn(dg4, 4, 4, 4))); - static assert( __traits(compiles, spawn(dg5, 5))); + void delegate(int) shared dg1; + shared(void delegate(int)) dg2; + shared(void delegate(long) shared) dg3; + shared(void delegate(real, int, long) shared) dg4; + void delegate(int) immutable dg5; + void delegate(int) dg6; + static assert(__traits(compiles, spawn(dg1, 1))); + static assert(__traits(compiles, spawn(dg2, 2))); + static assert(__traits(compiles, spawn(dg3, 3))); + static assert(__traits(compiles, spawn(dg4, 4, 4, 4))); + static assert(__traits(compiles, spawn(dg5, 5))); static assert(!__traits(compiles, spawn(dg6, 6))); auto callable1 = new class{ void opCall(int) shared {} }; @@ -590,24 +567,18 @@ if ( isSpawnable!(F, T) ) static assert( __traits(compiles, spawn(callable11, 11))); } - -// Sending and Receiving Messages - - /** * Places the values as a message at the back of tid's message queue. * * Sends the supplied value to the thread represented by tid. As with * $(REF spawn, std,concurrency), $(D T) must not have unshared aliasing. */ -void send(T...)( Tid tid, T vals ) +void send(T...)(Tid tid, T vals) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - _send( tid, vals ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + _send(tid, vals); } - /** * Places the values as a message on the front of tid's message queue. * @@ -615,34 +586,30 @@ void send(T...)( Tid tid, T vals ) * queue instead of at the back. This function is typically used for * out-of-band communication, to signal exceptional conditions, etc. */ -void prioritySend(T...)( Tid tid, T vals ) +void prioritySend(T...)(Tid tid, T vals) { - static assert( !hasLocalAliasing!(T), - "Aliases to mutable thread-local data not allowed." ); - _send( MsgType.priority, tid, vals ); + static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed."); + _send(MsgType.priority, tid, vals); } - /* * ditto */ -private void _send(T...)( Tid tid, T vals ) +private void _send(T...)(Tid tid, T vals) { - _send( MsgType.standard, tid, vals ); + _send(MsgType.standard, tid, vals); } - /* * Implementation of send. This allows parameter checking to be different for * both Tid.send() and .send(). */ -private void _send(T...)( MsgType type, Tid tid, T vals ) +private void _send(T...)(MsgType type, Tid tid, T vals) { - auto msg = Message( type, vals ); - tid.mbox.put( msg ); + auto msg = Message(type, vals); + tid.mbox.put(msg); } - /** * Receives a message from another thread. * @@ -771,8 +738,7 @@ receiveOnlyRet!(T) receiveOnly(T...)() in { assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned " - ~ "or thisTid was passed to a running thread."); + "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); } body { @@ -781,32 +747,22 @@ body Tuple!(T) ret; - thisInfo.ident.mbox.get( - ( T val ) - { - static if ( T.length ) - ret.field = val; - }, - ( LinkTerminated e ) - { - throw e; - }, - ( OwnerTerminated e ) - { - throw e; - }, - ( Variant val ) - { - static if (T.length > 1) - string exp = T.stringof; - else - string exp = T[0].stringof; + thisInfo.ident.mbox.get((T val) { + static if (T.length) + ret.field = val; + }, + (LinkTerminated e) { throw e; }, + (OwnerTerminated e) { throw e; }, + (Variant val) { + static if (T.length > 1) + string exp = T.stringof; + else + string exp = T[0].stringof; - throw new MessageMismatch( - format("Unexpected message type: expected '%s', got '%s'", - exp, val.type.toString())); - } ); - static if ( T.length == 1 ) + throw new MessageMismatch( + format("Unexpected message type: expected '%s', got '%s'", exp, val.type.toString())); + }); + static if (T.length == 1) return ret[0]; else return ret; @@ -842,80 +798,69 @@ body * $(REF Duration, core,time) has passed. It returns $(D true) if it received a * message and $(D false) if it timed out waiting for one. */ -bool receiveTimeout(T...)( Duration duration, T ops ) +bool receiveTimeout(T...)(Duration duration, T ops) in { assert(thisInfo.ident.mbox !is null, - "Cannot receive a message until a thread was spawned " - ~ "or thisTid was passed to a running thread."); + "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread."); } body { - checkops( ops ); + checkops(ops); - return thisInfo.ident.mbox.get( duration, ops ); + return thisInfo.ident.mbox.get(duration, ops); } @safe unittest { - static assert( __traits( compiles, - { - receiveTimeout( msecs(0), (Variant x) {} ); - receiveTimeout( msecs(0), (int x) {}, (Variant x) {} ); - } ) ); + static assert(__traits(compiles, { + receiveTimeout(msecs(0), (Variant x) {}); + receiveTimeout(msecs(0), (int x) {}, (Variant x) {}); + })); - static assert( !__traits( compiles, - { - receiveTimeout( msecs(0), (Variant x) {}, (int x) {} ); - } ) ); + static assert(!__traits(compiles, { + receiveTimeout(msecs(0), (Variant x) {}, (int x) {}); + })); - static assert( !__traits( compiles, - { - receiveTimeout( msecs(0), (int x) {}, (int x) {} ); - } ) ); + static assert(!__traits(compiles, { + receiveTimeout(msecs(0), (int x) {}, (int x) {}); + })); - static assert( __traits( compiles, - { - receiveTimeout( msecs(10), (int x) {}, (Variant x) {} ); - } ) ); + static assert(__traits(compiles, { + receiveTimeout(msecs(10), (int x) {}, (Variant x) {}); + })); } - // MessageBox Limits - /** * These behaviors may be specified when a mailbox is full. */ enum OnCrowding { - block, /// Wait until room is available. + block, /// Wait until room is available. throwException, /// Throw a MailboxFull exception. - ignore /// Abort the send and return. + ignore /// Abort the send and return. } - private { - bool onCrowdingBlock( Tid tid ) @safe pure nothrow @nogc + bool onCrowdingBlock(Tid tid) @safe pure nothrow @nogc { return true; } - - bool onCrowdingThrow( Tid tid ) @safe pure + bool onCrowdingThrow(Tid tid) @safe pure { - throw new MailboxFull( tid ); + throw new MailboxFull(tid); } - - bool onCrowdingIgnore( Tid tid ) @safe pure nothrow @nogc + bool onCrowdingIgnore(Tid tid) @safe pure nothrow @nogc { return false; } } - /** * Sets a maximum mailbox size. * @@ -930,20 +875,19 @@ private * doThis = The behavior executed when a message is sent to a full * mailbox. */ -void setMaxMailboxSize( Tid tid, size_t messages, OnCrowding doThis ) @safe pure +void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure { - final switch ( doThis ) + final switch (doThis) { case OnCrowding.block: - return tid.mbox.setMaxMsgs( messages, &onCrowdingBlock ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingBlock); case OnCrowding.throwException: - return tid.mbox.setMaxMsgs( messages, &onCrowdingThrow ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingThrow); case OnCrowding.ignore: - return tid.mbox.setMaxMsgs( messages, &onCrowdingIgnore ); + return tid.mbox.setMaxMsgs(messages, &onCrowdingIgnore); } } - /** * Sets a maximum mailbox size. * @@ -957,47 +901,40 @@ void setMaxMailboxSize( Tid tid, size_t messages, OnCrowding doThis ) @safe pure * onCrowdingDoThis = The routine called when a message is sent to a full * mailbox. */ -void setMaxMailboxSize( Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis ) +void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis) { - tid.mbox.setMaxMsgs( messages, onCrowdingDoThis ); + tid.mbox.setMaxMsgs(messages, onCrowdingDoThis); } - -// Name Registration - - private { - __gshared Tid[string] tidByName; + __gshared Tid[string] tidByName; __gshared string[][Tid] namesByTid; - __gshared Mutex registryLock; + __gshared Mutex registryLock; } - extern (C) void std_concurrency_static_this() { registryLock = new Mutex; } - private void unregisterMe() { auto me = thisInfo.ident; if (thisInfo.ident != Tid.init) { - synchronized( registryLock ) + synchronized (registryLock) { - if ( auto allNames = me in namesByTid ) + if (auto allNames = me in namesByTid) { - foreach ( name; *allNames ) - tidByName.remove( name ); - namesByTid.remove( me ); + foreach (name; *allNames) + tidByName.remove(name); + namesByTid.remove(me); } } } } - /** * Associates name with tid. * @@ -1013,13 +950,13 @@ private void unregisterMe() * true if the name is available and tid is not known to represent a * defunct thread. */ -bool register( string name, Tid tid ) +bool register(string name, Tid tid) { - synchronized( registryLock ) + synchronized (registryLock) { - if ( name in tidByName ) + if (name in tidByName) return false; - if ( tid.mbox.isClosed ) + if (tid.mbox.isClosed) return false; namesByTid[tid] ~= name; tidByName[name] = tid; @@ -1027,7 +964,6 @@ bool register( string name, Tid tid ) } } - /** * Removes the registered name associated with a tid. * @@ -1037,26 +973,25 @@ bool register( string name, Tid tid ) * Returns: * true if the name is registered, false if not. */ -bool unregister( string name ) +bool unregister(string name) { import std.algorithm.searching : countUntil; import std.algorithm.mutation : remove, SwapStrategy; - synchronized( registryLock ) + synchronized (registryLock) { - if ( auto tid = name in tidByName ) + if (auto tid = name in tidByName) { auto allNames = *tid in namesByTid; - auto pos = countUntil( *allNames, name ); - remove!(SwapStrategy.unstable)( *allNames, pos ); - tidByName.remove( name ); + auto pos = countUntil(*allNames, name); + remove!(SwapStrategy.unstable)(*allNames, pos); + tidByName.remove(name); return true; } return false; } } - /** * Gets the Tid associated with name. * @@ -1066,20 +1001,16 @@ bool unregister( string name ) * Returns: * The associated Tid or Tid.init if name is not registered. */ -Tid locate( string name ) +Tid locate(string name) { - synchronized( registryLock ) + synchronized (registryLock) { - if ( auto tid = name in tidByName ) + if (auto tid = name in tidByName) return *tid; return Tid.init; } } - -// Scheduler - - /** * Encapsulates all implementation-level data needed for scheduling. * @@ -1089,9 +1020,9 @@ Tid locate( string name ) */ struct ThreadInfo { - Tid ident; + Tid ident; bool[Tid] links; - Tid owner; + Tid owner; /** * Gets a thread-local instance of ThreadInfo. @@ -1106,7 +1037,6 @@ struct ThreadInfo return val; } - /** * Cleans up this ThreadInfo. * @@ -1116,17 +1046,16 @@ struct ThreadInfo */ void cleanup() { - if ( ident.mbox !is null ) + if (ident.mbox !is null) ident.mbox.close(); - foreach ( tid; links.keys ) - _send( MsgType.linkDead, tid, ident ); - if ( owner != Tid.init ) - _send( MsgType.linkDead, owner, ident ); + foreach (tid; links.keys) + _send(MsgType.linkDead, tid, ident); + if (owner != Tid.init) + _send(MsgType.linkDead, owner, ident); unregisterMe(); // clean up registry entries } } - /** * A Scheduler controls how threading is performed by spawn. * @@ -1175,7 +1104,7 @@ interface Scheduler * absence of a custom scheduler. It will be automatically executed * via a call to spawn by the Scheduler. */ - void start( void delegate() op ); + void start(void delegate() op); /** * Assigns a logical thread to execute the supplied op. @@ -1190,7 +1119,7 @@ interface Scheduler * op = The function to execute. This may be the actual function passed * by the user to spawn itself, or may be a wrapper function. */ - void spawn( void delegate() op ); + void spawn(void delegate() op); /** * Yields execution to another logical thread. @@ -1227,10 +1156,9 @@ interface Scheduler * cases a Scheduler may need to hold this reference and unlock the * mutex before yielding execution to another logical thread. */ - Condition newCondition( Mutex m ) nothrow; + Condition newCondition(Mutex m) nothrow; } - /** * An example Scheduler using kernel threads. * @@ -1239,29 +1167,26 @@ interface Scheduler * and may be instantiated and used, but is not a necessary part of the * default functioning of this module. */ -class ThreadScheduler : - Scheduler +class ThreadScheduler : Scheduler { /** * This simply runs op directly, since no real scheduling is needed by * this approach. */ - void start( void delegate() op ) + void start(void delegate() op) { op(); } - /** * Creates a new kernel thread and assigns it to run the supplied op. */ - void spawn( void delegate() op ) + void spawn(void delegate() op) { - auto t = new Thread( op ); + auto t = new Thread(op); t.start(); } - /** * This scheduler does no explicit multiplexing, so this is a no-op. */ @@ -1270,7 +1195,6 @@ class ThreadScheduler : // no explicit yield needed } - /** * Returns ThreadInfo.thisInfo, since it is a thread-local instance of * ThreadInfo, which is the correct behavior for this scheduler. @@ -1280,48 +1204,43 @@ class ThreadScheduler : return ThreadInfo.thisInfo; } - /** * Creates a new Condition variable. No custom behavior is needed here. */ - Condition newCondition( Mutex m ) nothrow + Condition newCondition(Mutex m) nothrow { - return new Condition( m ); + return new Condition(m); } } - /** * An example Scheduler using Fibers. * * This is an example scheduler that creates a new Fiber per call to spawn * and multiplexes the execution of all fibers within the main thread. */ -class FiberScheduler : - Scheduler +class FiberScheduler : Scheduler { /** * This creates a new Fiber for the supplied op and then starts the * dispatcher. */ - void start( void delegate() op ) + void start(void delegate() op) { - create( op ); + create(op); dispatch(); } - /** * This created a new Fiber for the supplied op and adds it to the * dispatch list. */ - void spawn( void delegate() op ) nothrow + void spawn(void delegate() op) nothrow { - create( op ); + create(op); yield(); } - /** * If the caller is a scheduled Fiber, this yields execution to another * scheduled Fiber. @@ -1335,7 +1254,6 @@ class FiberScheduler : Fiber.yield(); } - /** * Returns an appropriate ThreadInfo instance. * @@ -1347,38 +1265,33 @@ class FiberScheduler : { auto f = cast(InfoFiber) Fiber.getThis(); - if ( f !is null ) + if (f !is null) return f.info; return ThreadInfo.thisInfo; } - /** * Returns a Condition analog that yields when wait or notify is called. */ - Condition newCondition( Mutex m ) nothrow + Condition newCondition(Mutex m) nothrow { - return new FiberCondition( m ); + return new FiberCondition(m); } - private: - static class InfoFiber : - Fiber + static class InfoFiber : Fiber { ThreadInfo info; - this( void delegate() op ) nothrow + this(void delegate() op) nothrow { - super( op ); + super(op); } } - - class FiberCondition : - Condition + class FiberCondition : Condition { - this( Mutex m ) nothrow + this(Mutex m) nothrow { super(m); notified = false; @@ -1386,20 +1299,21 @@ private: override void wait() nothrow { - scope(exit) notified = false; + scope (exit) notified = false; - while ( !notified ) + while (!notified) switchContext(); } - override bool wait( Duration period ) nothrow + override bool wait(Duration period) nothrow { import core.time : MonoTime; - scope(exit) notified = false; - for ( auto limit = MonoTime.currTime + period; + scope (exit) notified = false; + + for (auto limit = MonoTime.currTime + period; !notified && !period.isNegative; - period = limit - MonoTime.currTime ) + period = limit - MonoTime.currTime) { yield(); } @@ -1422,57 +1336,56 @@ private: void switchContext() nothrow { mutex_nothrow.unlock_nothrow(); - scope(exit) mutex_nothrow.lock_nothrow(); + scope (exit) mutex_nothrow.lock_nothrow(); yield(); } private bool notified; } - private: void dispatch() { import std.algorithm.mutation : remove; - while ( m_fibers.length > 0 ) + while (m_fibers.length > 0) { - auto t = m_fibers[m_pos].call( Fiber.Rethrow.no ); + auto t = m_fibers[m_pos].call(Fiber.Rethrow.no); if (t !is null && !(cast(OwnerTerminated) t)) + { throw t; - if ( m_fibers[m_pos].state == Fiber.State.TERM ) + } + if (m_fibers[m_pos].state == Fiber.State.TERM) { - if ( m_pos >= (m_fibers = remove( m_fibers, m_pos )).length ) + if (m_pos >= (m_fibers = remove(m_fibers, m_pos)).length) m_pos = 0; } - else if ( m_pos++ >= m_fibers.length - 1 ) + else if (m_pos++ >= m_fibers.length - 1) { m_pos = 0; } } } - - void create( void delegate() op ) nothrow + void create(void delegate() op) nothrow { void wrap() { - scope(exit) + scope (exit) { thisInfo.cleanup(); } op(); } - m_fibers ~= new InfoFiber( &wrap ); - } + m_fibers ~= new InfoFiber(&wrap); + } private: Fiber[] m_fibers; - size_t m_pos; + size_t m_pos; } - @system unittest { static void receive(Condition cond, ref size_t received) @@ -1504,7 +1417,7 @@ private: auto cond = fs.newCondition(mtx); size_t received, sent; - auto waiter = new Fiber({receive(cond, received);}), notifier = new Fiber({send(cond, sent);}); + auto waiter = new Fiber({ receive(cond, received); }), notifier = new Fiber({ send(cond, sent); }); waiter.call(); assert(received == 0); notifier.call(); @@ -1516,7 +1429,6 @@ private: assert(received == 1); } - /** * Sets the Scheduler behavior within the program. * @@ -1526,10 +1438,8 @@ private: */ __gshared Scheduler scheduler; - // Generator - /** * If the caller is a Fiber and is not a Generator, this function will call * scheduler.yield() or Fiber.yield(), as appropriate. @@ -1544,11 +1454,11 @@ void yield() nothrow if (fiber) return Fiber.yield(); } - else scheduler.yield(); + else + scheduler.yield(); } } - /// Used to determine whether a Generator is running. private interface IsGenerator {} @@ -1586,8 +1496,7 @@ private interface IsGenerator {} * } * --- */ -class Generator(T) : - Fiber, IsGenerator +class Generator(T) : Fiber, IsGenerator { /** * Initializes a generator object which is associated with a static @@ -1606,7 +1515,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a static * D function. The function will be called once to prepare the range @@ -1625,7 +1533,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a dynamic * D function. The function will be called once to prepare the range @@ -1643,7 +1550,6 @@ class Generator(T) : call(); } - /** * Initializes a generator object which is associated with a dynamic * D function. The function will be called once to prepare the range @@ -1662,7 +1568,6 @@ class Generator(T) : call(); } - /** * Returns true if the generator is empty. */ @@ -1671,7 +1576,6 @@ class Generator(T) : return m_value is null || state == State.TERM; } - /** * Obtains the next value from the underlying function. */ @@ -1680,7 +1584,6 @@ class Generator(T) : call(); } - /** * Returns the most recently generated value. */ @@ -1689,12 +1592,10 @@ class Generator(T) : return *m_value; } - private: - T* m_value; + T* m_value; } - /** * Yields a value of type T to the caller of the currently executing * generator. @@ -1713,7 +1614,6 @@ void yield(T)(ref T value) throw new Exception("yield(T) called with no active generator for the supplied type"); } - /// ditto void yield(T)(T value) { @@ -1728,18 +1628,15 @@ void yield(T)(T value) static void testScheduler(Scheduler s) { scheduler = s; - scheduler.start( - { - auto tid = spawn( - { + scheduler.start({ + auto tid = spawn({ int i; try { for (i = 1; i < 10; i++) { - assertNotThrown!AssertError( - assert(receiveOnly!int() == i)); + assertNotThrown!AssertError(assert(receiveOnly!int() == i)); } } catch (OwnerTerminated e) @@ -1751,8 +1648,7 @@ void yield(T)(T value) assert(i == 4); }); - auto r = new Generator!int( - { + auto r = new Generator!int({ assertThrown!Exception(yield(2.0)); yield(); // ensure this is a no-op yield(1); @@ -1773,10 +1669,6 @@ void yield(T)(T value) testScheduler(new FiberScheduler); } - -// MessageBox Implementation - - private { /* @@ -1790,25 +1682,25 @@ private { this() @trusted nothrow /* TODO: make @safe after relevant druntime PR gets merged */ { - m_lock = new Mutex; - m_closed = false; + m_lock = new Mutex; + m_closed = false; - if ( scheduler is null ) + if (scheduler is null) { - m_putMsg = new Condition( m_lock ); - m_notFull = new Condition( m_lock ); + m_putMsg = new Condition(m_lock); + m_notFull = new Condition(m_lock); } else { - m_putMsg = scheduler.newCondition( m_lock ); - m_notFull = scheduler.newCondition( m_lock ); + m_putMsg = scheduler.newCondition(m_lock); + m_notFull = scheduler.newCondition(m_lock); } } /// final @property bool isClosed() @safe @nogc pure { - synchronized( m_lock ) + synchronized (m_lock) { return m_closed; } @@ -1825,16 +1717,15 @@ private * unbounded. * call = The routine to call when the queue is full. */ - final void setMaxMsgs( size_t num, bool function(Tid) call ) @safe @nogc pure + final void setMaxMsgs(size_t num, bool function(Tid) call) @safe @nogc pure { - synchronized( m_lock ) + synchronized (m_lock) { - m_maxMsgs = num; + m_maxMsgs = num; m_onMaxMsgs = call; } } - /* * If maxMsgs is not set, the message is added to the queue and the * owner is notified. If the queue is full, the message will still be @@ -1849,29 +1740,29 @@ private * Throws: * An exception if the queue is full and onCrowdingDoThis throws. */ - final void put( ref Message msg ) + final void put(ref Message msg) { - synchronized( m_lock ) + synchronized (m_lock) { // TODO: Generate an error here if m_closed is true, or maybe // put a message in the caller's queue? - if ( !m_closed ) + if (!m_closed) { - while ( true ) + while (true) { - if ( isPriorityMsg( msg ) ) + if (isPriorityMsg(msg)) { - m_sharedPty.put( msg ); + m_sharedPty.put(msg); m_putMsg.notify(); return; } - if ( !mboxFull() || isControlMsg( msg ) ) + if (!mboxFull() || isControlMsg(msg)) { - m_sharedBox.put( msg ); + m_sharedBox.put(msg); m_putMsg.notify(); return; } - if ( m_onMaxMsgs !is null && !m_onMaxMsgs( thisTid ) ) + if (m_onMaxMsgs !is null && !m_onMaxMsgs(thisTid)) { return; } @@ -1883,7 +1774,6 @@ private } } - /* * Matches ops against each message in turn until a match is found. * @@ -1900,13 +1790,13 @@ private * if the owner thread terminates and no existing messages match the * supplied ops. */ - bool get(T...)( scope T vals ) + bool get(T...)(scope T vals) { import std.meta : AliasSeq; - static assert( T.length ); + static assert(T.length); - static if ( isImplicitlyConvertible!(T[0], Duration) ) + static if (isImplicitlyConvertible!(T[0], Duration)) { alias Ops = AliasSeq!(T[1 .. $]); alias ops = vals[1 .. $]; @@ -1920,22 +1810,22 @@ private enum timedWait = false; } - bool onStandardMsg( ref Message msg ) + bool onStandardMsg(ref Message msg) { - foreach ( i, t; Ops ) + foreach (i, t; Ops) { alias Args = Parameters!(t); - auto op = ops[i]; + auto op = ops[i]; - if ( msg.convertsTo!(Args) ) + if (msg.convertsTo!(Args)) { - static if ( is( ReturnType!(t) == bool ) ) + static if (is(ReturnType!(t) == bool)) { - return msg.map( op ); + return msg.map(op); } else { - msg.map( op ); + msg.map(op); return true; } } @@ -1943,59 +1833,60 @@ private return false; } - bool onLinkDeadMsg( ref Message msg ) + bool onLinkDeadMsg(ref Message msg) { - assert( msg.convertsTo!(Tid) ); + assert(msg.convertsTo!(Tid)); auto tid = msg.get!(Tid); - if ( bool* pDepends = tid in thisInfo.links ) + if (bool* pDepends = tid in thisInfo.links) { auto depends = *pDepends; - thisInfo.links.remove( tid ); + thisInfo.links.remove(tid); // Give the owner relationship precedence. - if ( depends && tid != thisInfo.owner ) + if (depends && tid != thisInfo.owner) { - auto e = new LinkTerminated( tid ); - auto m = Message( MsgType.standard, e ); - if ( onStandardMsg( m ) ) + auto e = new LinkTerminated(tid); + auto m = Message(MsgType.standard, e); + if (onStandardMsg(m)) return true; throw e; } } - if ( tid == thisInfo.owner ) + if (tid == thisInfo.owner) { thisInfo.owner = Tid.init; - auto e = new OwnerTerminated( tid ); - auto m = Message( MsgType.standard, e ); - if ( onStandardMsg( m ) ) + auto e = new OwnerTerminated(tid); + auto m = Message(MsgType.standard, e); + if (onStandardMsg(m)) return true; throw e; } return false; } - bool onControlMsg( ref Message msg ) + bool onControlMsg(ref Message msg) { - switch ( msg.type ) + switch (msg.type) { case MsgType.linkDead: - return onLinkDeadMsg( msg ); + return onLinkDeadMsg(msg); default: return false; } } - bool scan( ref ListT list ) + bool scan(ref ListT list) { - for ( auto range = list[]; !range.empty; ) + for (auto range = list[]; !range.empty;) { // Only the message handler will throw, so if this occurs // we can be certain that the message was handled. - scope(failure) list.removeAt( range ); + scope (failure) + list.removeAt(range); - if ( isControlMsg( range.front ) ) + if (isControlMsg(range.front)) { - if ( onControlMsg( range.front ) ) + if (onControlMsg(range.front)) { // Although the linkDead message is a control message, // it can be handled by the user. Since the linkDead @@ -2003,12 +1894,12 @@ private // it has been handled and we can return from receive. // This is a weird special case that will have to be // handled in a more general way if more are added. - if ( !isLinkDeadMsg( range.front ) ) + if (!isLinkDeadMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); continue; } - list.removeAt( range ); + list.removeAt(range); return true; } range.popFront(); @@ -2016,9 +1907,9 @@ private } else { - if ( onStandardMsg( range.front ) ) + if (onStandardMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); return true; } range.popFront(); @@ -2028,47 +1919,46 @@ private return false; } - - bool pty( ref ListT list ) + bool pty(ref ListT list) { - if ( !list.empty ) + if (!list.empty) { auto range = list[]; - if ( onStandardMsg( range.front ) ) + if (onStandardMsg(range.front)) { - list.removeAt( range ); + list.removeAt(range); return true; } - if ( range.front.convertsTo!(Throwable) ) + if (range.front.convertsTo!(Throwable)) throw range.front.get!(Throwable); - else if ( range.front.convertsTo!(shared(Throwable)) ) + else if (range.front.convertsTo!(shared(Throwable))) throw range.front.get!(shared(Throwable)); - else throw new PriorityMessageException( range.front.data ); + else + throw new PriorityMessageException(range.front.data); } return false; } - static if ( timedWait ) + static if (timedWait) { import core.time : MonoTime; auto limit = MonoTime.currTime + period; } - while ( true ) + while (true) { ListT arrived; - if ( pty( m_localPty ) || - scan( m_localBox ) ) + if (pty(m_localPty) || scan(m_localBox)) { return true; } yield(); - synchronized( m_lock ) + synchronized (m_lock) { updateMsgCount(); - while ( m_sharedPty.empty && m_sharedBox.empty ) + while (m_sharedPty.empty && m_sharedBox.empty) { // NOTE: We're notifying all waiters here instead of just // a few because the onCrowding behavior may have @@ -2076,11 +1966,11 @@ private // unnecessarily if the new behavior is not to block. // This will admittedly result in spurious wakeups // in other situations, but what can you do? - if ( m_putQueue && !mboxFull() ) + if (m_putQueue && !mboxFull()) m_notFull.notifyAll(); - static if ( timedWait ) + static if (timedWait) { - if ( period <= Duration.zero || !m_putMsg.wait( period ) ) + if (period <= Duration.zero || !m_putMsg.wait(period)) return false; } else @@ -2088,30 +1978,31 @@ private m_putMsg.wait(); } } - m_localPty.put( m_sharedPty ); - arrived.put( m_sharedBox ); + m_localPty.put(m_sharedPty); + arrived.put(m_sharedBox); } - if ( m_localPty.empty ) + if (m_localPty.empty) { - scope(exit) m_localBox.put( arrived ); - if ( scan( arrived ) ) + scope (exit) m_localBox.put(arrived); + if (scan(arrived)) + { return true; + } else { - static if ( timedWait ) + static if (timedWait) { period = limit - MonoTime.currTime; } continue; } } - m_localBox.put( arrived ); - pty( m_localPty ); + m_localBox.put(arrived); + pty(m_localPty); return true; } } - /* * Called on thread termination. This routine processes any remaining * control messages, clears out message queues, and sets a flag to @@ -2119,111 +2010,83 @@ private */ final void close() { - static void onLinkDeadMsg( ref Message msg ) + static void onLinkDeadMsg(ref Message msg) { - assert( msg.convertsTo!(Tid) ); + assert(msg.convertsTo!(Tid)); auto tid = msg.get!(Tid); - thisInfo.links.remove( tid ); - if ( tid == thisInfo.owner ) + thisInfo.links.remove(tid); + if (tid == thisInfo.owner) thisInfo.owner = Tid.init; } - static void sweep( ref ListT list ) + static void sweep(ref ListT list) { - for ( auto range = list[]; !range.empty; range.popFront() ) + for (auto range = list[]; !range.empty; range.popFront()) { - if ( range.front.type == MsgType.linkDead ) - onLinkDeadMsg( range.front ); + if (range.front.type == MsgType.linkDead) + onLinkDeadMsg(range.front); } } ListT arrived; - sweep( m_localBox ); - synchronized( m_lock ) + sweep(m_localBox); + synchronized (m_lock) { - arrived.put( m_sharedBox ); + arrived.put(m_sharedBox); m_closed = true; } m_localBox.clear(); - sweep( arrived ); + sweep(arrived); } - private: - // Routines involving shared data, m_lock must be held. - + // Routines involving local data only, no lock needed. bool mboxFull() @safe @nogc pure nothrow { - return m_maxMsgs && - m_maxMsgs <= m_localMsgs + m_sharedBox.length; + return m_maxMsgs && m_maxMsgs <= m_localMsgs + m_sharedBox.length; } - void updateMsgCount() @safe @nogc pure nothrow { m_localMsgs = m_localBox.length; } - - private: - // Routines involving local data only, no lock needed. - - - bool isControlMsg( ref Message msg ) @safe @nogc pure nothrow + bool isControlMsg(ref Message msg) @safe @nogc pure nothrow { - return msg.type != MsgType.standard && - msg.type != MsgType.priority; + return msg.type != MsgType.standard && msg.type != MsgType.priority; } - - bool isPriorityMsg( ref Message msg ) @safe @nogc pure nothrow + bool isPriorityMsg(ref Message msg) @safe @nogc pure nothrow { return msg.type == MsgType.priority; } - - bool isLinkDeadMsg( ref Message msg ) @safe @nogc pure nothrow + bool isLinkDeadMsg(ref Message msg) @safe @nogc pure nothrow { return msg.type == MsgType.linkDead; } - - private: - // Type declarations. - - alias OnMaxFn = bool function(Tid); - alias ListT = List!(Message); - - private: - // Local data, no lock needed. - - - ListT m_localBox; - ListT m_localPty; + alias ListT = List!(Message); + ListT m_localBox; + ListT m_localPty; - private: - // Shared data, m_lock must be held on access. - - - Mutex m_lock; - Condition m_putMsg; - Condition m_notFull; - size_t m_putQueue; - ListT m_sharedBox; - ListT m_sharedPty; - OnMaxFn m_onMaxMsgs; - size_t m_localMsgs; - size_t m_maxMsgs; - bool m_closed; - + Mutex m_lock; + Condition m_putMsg; + Condition m_notFull; + size_t m_putQueue; + ListT m_sharedBox; + ListT m_sharedPty; + OnMaxFn m_onMaxMsgs; + size_t m_localMsgs; + size_t m_maxMsgs; + bool m_closed; } - /* * */ @@ -2240,29 +2103,23 @@ private @property ref T front() { - enforce( m_prev.next, "invalid list node" ); + enforce(m_prev.next, "invalid list node"); return m_prev.next.val; } - @property void front( T val ) + @property void front(T val) { - enforce( m_prev.next, "invalid list node" ); + enforce(m_prev.next, "invalid list node"); m_prev.next.val = val; } void popFront() { - enforce( m_prev.next, "invalid list node" ); + enforce(m_prev.next, "invalid list node"); m_prev = m_prev.next; } - //T moveFront() - //{ - // enforce( m_prev.next ); - // return move( m_prev.next.val ); - //} - - private this( Node* p ) + private this(Node* p) { m_prev = p; } @@ -2270,102 +2127,73 @@ private private Node* m_prev; } - - /* - * - */ - void put( T val ) + void put(T val) { - put( newNode( val ) ); + put(newNode(val)); } - - /* - * - */ - void put( ref List!(T) rhs ) + void put(ref List!(T) rhs) { - if ( !rhs.empty ) + if (!rhs.empty) { - put( rhs.m_first ); - while ( m_last.next !is null ) + put(rhs.m_first); + while (m_last.next !is null) { m_last = m_last.next; m_count++; } rhs.m_first = null; - rhs.m_last = null; + rhs.m_last = null; rhs.m_count = 0; } } - - /* - * - */ Range opSlice() { - return Range( cast(Node*) &m_first ); + return Range(cast(Node*)&m_first); } - - /* - * - */ - void removeAt( Range r ) + void removeAt(Range r) { import std.exception : enforce; - assert( m_count ); + assert(m_count); Node* n = r.m_prev; - enforce( n && n.next, "attempting to remove invalid list node" ); + enforce(n && n.next, "attempting to remove invalid list node"); - if ( m_last is m_first ) + if (m_last is m_first) m_last = null; - else if ( m_last is n.next ) + else if (m_last is n.next) m_last = n; Node* to_free = n.next; n.next = n.next.next; - freeNode( to_free ); + freeNode(to_free); m_count--; } - - /* - * - */ @property size_t length() { return m_count; } - - /* - * - */ void clear() { m_first = m_last = null; m_count = 0; } - - /* - * - */ @property bool empty() { return m_first is null; } - private: struct Node { - Node* next; - T val; + Node* next; + T val; - this( T v ) + this(T v) { val = v; } @@ -2377,12 +2205,13 @@ private void unlock() { atomicStore!(MemoryOrder.rel)(locked, false); } bool locked; } + static shared SpinLock sm_lock; static shared Node* sm_head; Node* newNode(T v) { - Node *n; + Node* n; { sm_lock.lock(); scope (exit) sm_lock.unlock(); @@ -2413,19 +2242,15 @@ private sm_lock.lock(); scope (exit) sm_lock.unlock(); - auto sn = cast(shared(Node)*)n; + auto sn = cast(shared(Node)*) n; sn.next = sm_head; sm_head = sn; } - - /* - * - */ - void put( Node* n ) + void put(Node* n) { m_count++; - if ( !empty ) + if (!empty) { m_last.next = n; m_last = n; @@ -2435,73 +2260,57 @@ private m_last = n; } - - Node* m_first; - Node* m_last; - size_t m_count; + Node* m_first; + Node* m_last; + size_t m_count; } } - -version( unittest ) +version (unittest) { import std.stdio; import std.typecons : tuple, Tuple; - void testfn( Tid tid ) - { - receive( (float val) { assert(0); }, - (int val, int val2) - { - assert( val == 42 && val2 == 86 ); - } ); - receive( (Tuple!(int, int) val) - { - assert( val[0] == 42 && - val[1] == 86 ); - } ); - receive( (Variant val) {} ); - receive( (string val) - { - if ( "the quick brown fox" != val ) - return false; - return true; - }, - (string val) - { - assert( false ); - } ); - prioritySend( tid, "done" ); - } - - void runTest( Tid tid ) - { - send( tid, 42, 86 ); - send( tid, tuple(42, 86) ); - send( tid, "hello", "there" ); - send( tid, "the quick brown fox" ); - receive( (string val) { assert(val == "done"); } ); + void testfn(Tid tid) + { + receive((float val) { assert(0); }, (int val, int val2) { + assert(val == 42 && val2 == 86); + }); + receive((Tuple!(int, int) val) { assert(val[0] == 42 && val[1] == 86); }); + receive((Variant val) { }); + receive((string val) { + if ("the quick brown fox" != val) + return false; + return true; + }, (string val) { assert(false); }); + prioritySend(tid, "done"); } + void runTest(Tid tid) + { + send(tid, 42, 86); + send(tid, tuple(42, 86)); + send(tid, "hello", "there"); + send(tid, "the quick brown fox"); + receive((string val) { assert(val == "done"); }); + } void simpleTest() { - auto tid = spawn( &testfn, thisTid ); - runTest( tid ); + auto tid = spawn(&testfn, thisTid); + runTest(tid); // Run the test again with a limited mailbox size. - tid = spawn( &testfn, thisTid ); - setMaxMailboxSize( tid, 2, OnCrowding.block ); - runTest( tid ); + tid = spawn(&testfn, thisTid); + setMaxMailboxSize(tid, 2, OnCrowding.block); + runTest(tid); } - @system unittest { simpleTest(); } - @system unittest { scheduler = new ThreadScheduler; @@ -2510,8 +2319,6 @@ version( unittest ) } } -// initOnce - private @property Mutex initOnceLock() { __gshared Mutex lock; @@ -2555,6 +2362,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init) return initOnce!inst(new MySingleton); } } + assert(MySingleton.instance !is null); } @@ -2567,6 +2375,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init) static __gshared MySingleton inst; return initOnce!inst(new MySingleton); } + private: this() { val = ++cnt; } size_t val; @@ -2574,7 +2383,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init) } foreach (_; 0 .. 10) - spawn({ownerTid.send(MySingleton.instance.val);}); + spawn({ ownerTid.send(MySingleton.instance.val); }); foreach (_; 0 .. 10) assert(receiveOnly!size_t == MySingleton.instance.val); assert(MySingleton.cnt == 1); @@ -2599,7 +2408,8 @@ auto ref initOnce(alias var)(lazy typeof(var) init) auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) { // check that var is global, can't take address of a TLS variable - static assert(is(typeof({__gshared p = &var;})), "var must be 'static shared' or '__gshared'."); + static assert(is(typeof({ __gshared p = &var; })), + "var must be 'static shared' or '__gshared'."); import core.atomic : atomicLoad, MemoryOrder, atomicStore; static shared bool flag; @@ -2639,8 +2449,8 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) @system unittest { - static shared bool a; - __gshared bool b; + static shared bool a; + __gshared bool b; static bool c; bool d; initOnce!a(true); From 3aa8011a043a9bcfd52d43255f3c0a8885e143e9 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 17 Mar 2017 12:20:36 +0000 Subject: [PATCH 008/262] Issue 13568: Add CT-checked format string overloads to std.stdio * Add overloads for readf, writef, writefln. * Separate fmt argument from args for writef[ln] to improve docs. --- changelog/std-format-formattedWrite.dd | 15 ++-- std/stdio.d | 111 +++++++++++++++++++++---- 2 files changed, 102 insertions(+), 24 deletions(-) diff --git a/changelog/std-format-formattedWrite.dd b/changelog/std-format-formattedWrite.dd index d79cccf3747..4e8cac1f6fd 100644 --- a/changelog/std-format-formattedWrite.dd +++ b/changelog/std-format-formattedWrite.dd @@ -6,14 +6,13 @@ specifiers to be matched against the argument types passed. Any mismatch or orphaned specifiers/arguments will cause a compile-time error: ------- -import std.format; +import std.format, std.stdio; -void main() { - auto s = format!"%s is %s"("Pi", 3.14); - assert(s == "Pi is 3.14"); +auto s = format!"%s is %s"("Pi", 3.14); +assert(s == "Pi is 3.14"); +writefln!"%c is %s"('e', 1.61); - static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg - static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg - static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg -} +static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg +static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg +static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg ------- diff --git a/std/stdio.d b/std/stdio.d index eb3e699b277..59900a0f50a 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -1448,11 +1448,22 @@ Throws: $(D Exception) if the file is not opened. /** Writes its arguments in text format to the file, according to the -format in the first argument. +format string fmt. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) on an error writing to the file. */ + void writef(alias fmt, A...)(A args) + if (isSomeString!(typeof(fmt))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return this.writef(fmt, args); + } + + /// ditto void writef(Char, A...)(in Char[] fmt, A args) { import std.format : formattedWrite; @@ -1462,11 +1473,22 @@ Throws: $(D Exception) if the file is not opened. /** Writes its arguments in text format to the file, according to the -format in the first argument, followed by a newline. +format string fmt, followed by a newline. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) on an error writing to the file. */ + void writefln(alias fmt, A...)(A args) + if (isSomeString!(typeof(fmt))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return this.writefln(fmt, args); + } + + /// ditto void writefln(Char, A...)(in Char[] fmt, A args) { import std.format : formattedWrite; @@ -1764,7 +1786,7 @@ is recommended if you want to process a complete file. } /** - * Read data from the file according to the specified + * Reads data from the file according to the specified * $(LINK2 std_format.html#_format-string, _format specifier) using * $(REF formattedRead, std,_format). * Example: @@ -1777,7 +1799,7 @@ void main() foreach (_; 0 .. 3) { int a; - f.readf(" %d", a); + f.readf!" %d"(a); writeln(++a); } } @@ -1790,6 +1812,17 @@ $(CONSOLE 4 ) */ + uint readf(alias format, Data...)(auto ref Data data) + if (isSomeString!(typeof(format))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(format, Data); + static assert(!e, e.msg); + return this.readf(format, data); + } + + /// ditto uint readf(Data...)(in char[] format, auto ref Data data) { import std.format : formattedRead; @@ -1809,7 +1842,7 @@ $(CONSOLE scope(exit) std.file.remove(deleteme); string s; auto f = File(deleteme); - f.readf("%s\n", s); + f.readf!"%s\n"(s); assert(s == "hello", "["~s~"]"); f.readf("%s\n", s); assert(s == "world", "["~s~"]"); @@ -3668,11 +3701,12 @@ void writeln(T...)(T args) Writes formatted data to standard output (without a trailing newline). Params: -args = The first argument $(D args[0]) should be the format string, specifying +fmt = The format string, specifying how to format the rest of the arguments. For a full description of the syntax of the format string and how it controls the formatting of the rest of the arguments, please refer to the documentation for $(REF formattedWrite, std,format). +args = Items to write. Note: In older versions of Phobos, it used to be possible to write: @@ -3688,10 +3722,20 @@ stderr.writef("%s", "message"); ------ */ +void writef(alias fmt, A...)(A args) +if (isSomeString!(typeof(fmt))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return .writef(fmt, args); +} -void writef(T...)(T args) +/// ditto +void writef(Char, A...)(in Char[] fmt, A args) { - trustedStdout.writef(args); + trustedStdout.writef(fmt, args); } @system unittest @@ -3704,24 +3748,35 @@ void writef(T...)(T args) auto deleteme = testFilename(); auto f = File(deleteme, "w"); scope(exit) { std.file.remove(deleteme); } - f.writef("Hello, %s world number %s!", "nice", 42); + f.writef!"Hello, %s world number %s!"("nice", 42); f.close(); assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); // test write on stdout auto saveStdout = stdout; scope(exit) stdout = saveStdout; stdout.open(deleteme, "w"); - writef("Hello, %s world number %s!", "nice", 42); + writef!"Hello, %s world number %s!"("nice", 42); stdout.close(); assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!"); } /*********************************** - * Equivalent to $(D writef(args, '\n')). + * Equivalent to $(D writef(fmt, args, '\n')). */ -void writefln(T...)(T args) +void writefln(alias fmt, A...)(A args) +if (isSomeString!(typeof(fmt))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(fmt, A); + static assert(!e, e.msg); + return .writefln(fmt, args); +} + +/// ditto +void writefln(Char, A...)(in Char[] fmt, A args) { - trustedStdout.writefln(args); + trustedStdout.writefln(fmt, args); } @system unittest @@ -3730,11 +3785,11 @@ void writefln(T...)(T args) scope(failure) printf("Failed test at line %d\n", __LINE__); - // test writefln + // test File.writefln auto deleteme = testFilename(); auto f = File(deleteme, "w"); scope(exit) { std.file.remove(deleteme); } - f.writefln("Hello, %s world number %s!", "nice", 42); + f.writefln!"Hello, %s world number %s!"("nice", 42); f.close(); version (Windows) assert(cast(char[]) std.file.read(deleteme) == @@ -3743,6 +3798,19 @@ void writefln(T...)(T args) assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!\n", cast(char[]) std.file.read(deleteme)); + + // test writefln + auto saveStdout = stdout; + scope(exit) stdout = saveStdout; + stdout.open(deleteme, "w"); + writefln!"Hello, %s world number %s!"("nice", 42); + stdout.close(); + version (Windows) + assert(cast(char[]) std.file.read(deleteme) == + "Hello, nice world number 42!\r\n"); + else + assert(cast(char[]) std.file.read(deleteme) == + "Hello, nice world number 42!\n"); } /** @@ -3758,7 +3826,7 @@ void main() foreach (_; 0 .. 3) { int a; - readf(" %d", a); + readf!" %d"(a); writeln(++a); } } @@ -3770,6 +3838,17 @@ $(CONSOLE 4 ) */ +uint readf(alias format, A...)(auto ref A args) +if (isSomeString!(typeof(format))) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(format, A); + static assert(!e, e.msg); + return .readf(format, args); +} + +/// ditto uint readf(A...)(in char[] format, auto ref A args) { return stdin.readf(format, args); From 9ba4bd195851b8bd972413cdcb52fe9049b10875 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 20 Mar 2017 15:57:30 +0000 Subject: [PATCH 009/262] std.format: Improve FormatException messages --- std/format.d | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/std/format.d b/std/format.d index 61458950ad1..7767e9cc4bc 100644 --- a/std/format.d +++ b/std/format.d @@ -1678,7 +1678,7 @@ void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) { enforceFmt(f.spec == 's', - "null"); + "null literal cannot match %" ~ f.spec); put(w, "null"); } @@ -1742,7 +1742,7 @@ if (is(IntegralTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) f.spec == 's' || f.spec == 'd' || f.spec == 'u' ? 10 : 0; enforceFmt(base > 0, - "integral"); + "incompatible format character for integral argument: %" ~ f.spec); // Forward on to formatIntegral to handle both U and const(U) // Saves duplication of code for both versions. @@ -1990,7 +1990,7 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) return; } enforceFmt(find("fgFGaAeEs", fs.spec).length, - "incompatible format character for floating point type"); + "incompatible format character for floating point argument: %" ~ fs.spec); enforceFmt(!__ctfe, ctfpMessage); version (CRuntime_Microsoft) @@ -2959,7 +2959,7 @@ if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) AssocArrayTypeOf!T val = obj; enforceFmt(f.spec == 's' || f.spec == '(', - "associative"); + "incompatible format character for associative array argument: %" ~ f.spec); enum const(Char)[] defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator; auto fmtSpec = f.spec == '(' ? f.nested : defSpec; @@ -3822,6 +3822,7 @@ private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f private int getNthInt(A...)(uint index, A args) { import std.conv : to; + static if (A.length) { if (index) @@ -3834,7 +3835,8 @@ private int getNthInt(A...)(uint index, A args) } else { - throw new FormatException("int expected"); + throw new FormatException("width/precision must be an integer, not " + ~ typeof(args[0]).stringof); } } else From d37b8ad6d8027a96f80bf593862f961cc1bd1398 Mon Sep 17 00:00:00 2001 From: dukc Date: Mon, 20 Mar 2017 19:34:37 +0200 Subject: [PATCH 010/262] Simplified overloads of std.range.cycle. --- std/range/package.d | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 629bedb2e1b..e7e67e1207e 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -3643,11 +3643,12 @@ nothrow: } /// Ditto -Cycle!R cycle(R)(R input) -if (isForwardRange!R && !isInfinite!R) +auto cycle(R)(R input) +if (isForwardRange!R) { assert(!input.empty, "Attempting to pass an empty input to cycle"); - return Cycle!R(input); + static if (isInfinite!R) return input; + else return Cycle!R(input); } /// @@ -3671,13 +3672,6 @@ if (isRandomAccessRange!R && !isInfinite!R) return Cycle!R(input, index); } -/// Ditto -Cycle!R cycle(R)(R input) -if (isInfinite!R) -{ - return input; -} - /// Ditto Cycle!R cycle(R)(ref R input, size_t index = 0) @system if (isStaticArray!R) @@ -3769,6 +3763,13 @@ if (isStaticArray!R) @safe unittest // For infinite ranges { struct InfRange + { + void popFront() { } + @property int front() { return 0; } + enum empty = false; + auto save() { return this; } + } + struct NonForwardInfRange { void popFront() { } @property int front() { return 0; } @@ -3776,8 +3777,11 @@ if (isStaticArray!R) } InfRange i; + NonForwardInfRange j; auto c = cycle(i); assert(c == i); + //make sure it requires a forward range even when infinite + static assert(!is(typeof(j.cycle))); } @safe unittest From 165195936cf5b2f43a84562064f815e01bd25ebb Mon Sep 17 00:00:00 2001 From: byebye Date: Mon, 13 Mar 2017 02:43:52 +0100 Subject: [PATCH 011/262] std.container.Array - documentation cleanup - add missing details, e.g. about exception being thrown - fix wrong descriptions - fix wrong complexity specifications --- std/container/array.d | 810 +++++++++++++++++++++--------------------- 1 file changed, 407 insertions(+), 403 deletions(-) diff --git a/std/container/array.d b/std/container/array.d index fab078d75e7..9bd349c6b38 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -1,21 +1,21 @@ /** -This module provides an $(D Array) type with deterministic memory usage not -reliant on the GC, as an alternative to the built-in arrays. - -This module is a submodule of $(MREF std, container). - -Source: $(PHOBOSSRC std/container/_array.d) - -Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(HTTP -boost.org/LICENSE_1_0.txt)). - -Authors: $(HTTP erdani.com, Andrei Alexandrescu) - -$(SCRIPT inhibitQuickIndex = 1;) -*/ + * This module provides an `Array` type with deterministic memory usage not + * reliant on the GC, as an alternative to the built-in arrays. + * + * This module is a submodule of $(MREF std, container). + * + * Source: $(PHOBOSSRC std/container/_array.d) + * + * Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. + * + * License: Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at $(HTTP + * boost.org/LICENSE_1_0.txt)). + * + * Authors: $(HTTP erdani.com, Andrei Alexandrescu) + * + * $(SCRIPT inhibitQuickIndex = 1;) + */ module std.container.array; import std.range.primitives; @@ -232,21 +232,21 @@ private struct RangeT(A) } /** -Array type with deterministic control of memory. The memory allocated -for the array is reclaimed as soon as possible; there is no reliance -on the garbage collector. $(D Array) uses $(D malloc) and $(D free) -for managing its own memory. - -This means that pointers to elements of an $(D Array) will become -dangling as soon as the element is removed from the $(D Array). On the other hand -the memory allocated by an $(D Array) will be scanned by the GC and -GC managed objects referenced from an $(D Array) will be kept alive. - -Note: - -When using $(D Array) with range-based functions like those in $(D std.algorithm), -$(D Array) must be sliced to get a range (for example, use $(D array[].map!) -instead of $(D array.map!)). The container itself is not a range. + * _Array type with deterministic control of memory. The memory allocated + * for the array is reclaimed as soon as possible; there is no reliance + * on the garbage collector. `Array` uses `malloc`, `realloc` and `free` + * for managing its own memory. + * + * This means that pointers to elements of an `Array` will become + * dangling as soon as the element is removed from the `Array`. On the other hand + * the memory allocated by an `Array` will be scanned by the GC and + * GC managed objects referenced from an `Array` will be kept alive. + * + * Note: + * + * When using `Array` with range-based functions like those in `std.algorithm`, + * `Array` must be sliced to get a range (for example, use `array[].map!` + * instead of `array.map!`). The container itself is not a range. */ struct Array(T) if (!is(Unqual!T == bool)) @@ -265,14 +265,13 @@ if (!is(Unqual!T == bool)) size_t _capacity; T[] _payload; - // Convenience constructor this(T[] p) { _capacity = p.length; _payload = p; } // Destructor releases array memory ~this() { - //Warning: destroy will also destroy class instances. - //The hasElaborateDestructor protects us here. + // Warning: destroy would destroy also class instances. + // The hasElaborateDestructor protects us here. static if (hasElaborateDestructor!T) foreach (ref e; _payload) .destroy(e); @@ -283,33 +282,15 @@ if (!is(Unqual!T == bool)) free(_payload.ptr); } - this(this) - { - assert(0); - } + this(this) @disable; - void opAssign(Payload rhs) - { - assert(false); - } + void opAssign(Payload rhs) @disable; - // Duplicate data - // @property Payload dup() - // { - // Payload result; - // result._payload = _payload.dup; - // // Conservatively assume initial capacity == length - // result._capacity = result._payload.length; - // return result; - // } - - // length @property size_t length() const { return _payload.length; } - // length @property void length(size_t newLength) { import std.algorithm.mutation : initializeAll; @@ -336,13 +317,11 @@ if (!is(Unqual!T == bool)) _capacity = newLength; } - // capacity @property size_t capacity() const { return _capacity; } - // reserve void reserve(size_t elements) { if (elements <= capacity) return; @@ -350,7 +329,7 @@ if (!is(Unqual!T == bool)) bool overflow; const sz = mulu(elements, T.sizeof, overflow); if (overflow) assert(0); - static if (hasIndirections!T) // should use hasPointers instead + static if (hasIndirections!T) { /* Because of the transactional nature of this * relative to the garbage collector, ensure no @@ -365,8 +344,7 @@ if (!is(Unqual!T == bool)) // copy old data over to new array memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength); - // Zero out unused capacity to prevent gc from seeing - // false pointers + // Zero out unused capacity to prevent gc from seeing false pointers memset(newPayload.ptr + oldLength, 0, (elements - oldLength) * T.sizeof); @@ -377,9 +355,7 @@ if (!is(Unqual!T == bool)) } else { - /* These can't have pointers, so no need to zero - * unused region - */ + // These can't have pointers, so no need to zero unused region auto newPayloadPtr = cast(T*) realloc(_payload.ptr, sz); newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory"); auto newPayload = newPayloadPtr[0 .. length]; @@ -389,8 +365,8 @@ if (!is(Unqual!T == bool)) } // Insert one item - size_t insertBack(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) + size_t insertBack(Elem)(Elem elem) + if (isImplicitlyConvertible!(Elem, T)) { import std.conv : emplace; if (_capacity == length) @@ -398,29 +374,29 @@ if (!is(Unqual!T == bool)) reserve(1 + capacity * 3 / 2); } assert(capacity > length && _payload.ptr); - emplace(_payload.ptr + _payload.length, stuff); + emplace(_payload.ptr + _payload.length, elem); _payload = _payload.ptr[0 .. _payload.length + 1]; return 1; } - /// Insert a range of items - size_t insertBack(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + // Insert a range of items + size_t insertBack(Range)(Range r) + if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T)) { - static if (hasLength!Stuff) + static if (hasLength!Range) { immutable oldLength = length; - reserve(oldLength + stuff.length); + reserve(oldLength + r.length); } size_t result; - foreach (item; stuff) + foreach (item; r) { insertBack(item); ++result; } - static if (hasLength!Stuff) + static if (hasLength!Range) { - assert(length == oldLength + stuff.length); + assert(length == oldLength + r.length); } return result; } @@ -428,10 +404,11 @@ if (!is(Unqual!T == bool)) private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no); private Data _data; -/** -Constructor taking a number of items + /** + * Constructor taking a number of items. */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + this(U)(U[] values...) + if (isImplicitlyConvertible!(U, T)) { import std.conv : emplace; import core.checkedint : mulu; @@ -452,18 +429,17 @@ Constructor taking a number of items _data = Data(p[0 .. values.length]); } -/** -Constructor taking an input range + /** + * Constructor taking an input range */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T) && !is(Stuff == T[])) + this(Range)(Range r) + if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[])) { - insertBack(stuff); + insertBack(r); } - -/** -Comparison for equality. + /** + * Comparison for equality. */ bool opEquals(const Array rhs) const { @@ -478,21 +454,25 @@ Comparison for equality. return _data._payload == rhs._data._payload; } -/** - Defines the container's primary range, which is a random-access range. - - ConstRange is a variant with const elements. - ImmutableRange is a variant with immutable elements. -*/ + /** + * Defines the array's primary range, which is a random-access range. + * + * `ConstRange` is a variant with `const` elements. + * `ImmutableRange` is a variant with `immutable` elements. + */ alias Range = RangeT!Array; - alias ConstRange = RangeT!(const Array); /// ditto - alias ImmutableRange = RangeT!(immutable Array); /// ditto -/** -Duplicates the container. The elements themselves are not transitively -duplicated. + /// ditto + alias ConstRange = RangeT!(const Array); -Complexity: $(BIGOH n). + /// ditto + alias ImmutableRange = RangeT!(immutable Array); + + /** + * Duplicates the array. The elements themselves are not transitively + * duplicated. + * + * Complexity: $(BIGOH length). */ @property Array dup() { @@ -500,21 +480,20 @@ Complexity: $(BIGOH n). return Array(_data._payload); } -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) + /** + * Returns: `true` if and only if the array has no elements. + * + * Complexity: $(BIGOH 1) */ @property bool empty() const { return !_data.refCountedStore.isInitialized || _data._payload.empty; } -/** -Returns the number of elements in the container. - -Complexity: $(BIGOH 1). + /** + * Returns: The number of elements in the array. + * + * Complexity: $(BIGOH 1). */ @property size_t length() const { @@ -527,23 +506,27 @@ Complexity: $(BIGOH 1). return length; } -/** -Returns the maximum number of elements the container can store without - (a) allocating memory, (b) invalidating iterators upon insertion. - -Complexity: $(BIGOH 1) + /** + * Returns: The maximum number of elements the array can store without + * reallocating memory and invalidating iterators upon insertion. + * + * Complexity: $(BIGOH 1) */ @property size_t capacity() { return _data.refCountedStore.isInitialized ? _data._capacity : 0; } -/** -Ensures sufficient capacity to accommodate $(D e) elements. - -Postcondition: $(D capacity >= e) - -Complexity: $(BIGOH 1) + /** + * Ensures sufficient capacity to accommodate `e` _elements. + * If `e < capacity`, this method does nothing. + * + * Postcondition: `capacity >= e` + * + * Note: If the capacity is increased, one should assume that all + * iterators to the elements are invalidated. + * + * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). */ void reserve(size_t elements) { @@ -569,55 +552,59 @@ Complexity: $(BIGOH 1) } } -/** -Returns a range that iterates over elements of the container, in -forward order. - -Complexity: $(BIGOH 1) + /** + * Returns: A range that iterates over elements of the array in + * forward order. + * + * Complexity: $(BIGOH 1) */ Range opSlice() { return typeof(return)(this, 0, length); } + ConstRange opSlice() const { return typeof(return)(this, 0, length); } + ImmutableRange opSlice() immutable { return typeof(return)(this, 0, length); } -/** -Returns a range that iterates over elements of the container from -index $(D i) up to (excluding) index $(D j). - -Precondition: $(D i <= j && j <= length) - -Complexity: $(BIGOH 1) -*/ + /** + * Returns: A range that iterates over elements of the array from + * index `i` up to (excluding) index `j`. + * + * Precondition: `i <= j && j <= length` + * + * Complexity: $(BIGOH 1) + */ Range opSlice(size_t i, size_t j) { assert(i <= j && j <= length); return typeof(return)(this, i, j); } + ConstRange opSlice(size_t i, size_t j) const { assert(i <= j && j <= length); return typeof(return)(this, i, j); } + ImmutableRange opSlice(size_t i, size_t j) immutable { assert(i <= j && j <= length); return typeof(return)(this, i, j); } -/** -Forward to $(D opSlice().front) and $(D opSlice().back), respectively. - -Precondition: $(D !empty) - -Complexity: $(BIGOH 1) + /** + * Returns: The first element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) */ @property ref inout(T) front() inout { @@ -625,19 +612,25 @@ Complexity: $(BIGOH 1) return _data._payload[0]; } - /// ditto + /** + * Returns: The last element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + */ @property ref inout(T) back() inout { assert(_data.refCountedStore.isInitialized); return _data._payload[$ - 1]; } -/** -Indexing operators yield or modify the value at a specified index. - -Precondition: $(D i < length) - -Complexity: $(BIGOH 1) + /** + * Returns: The element or a reference to the element at the specified index. + * + * Precondition: `i < length` + * + * Complexity: $(BIGOH 1) */ ref inout(T) opIndex(size_t i) inout { @@ -645,12 +638,12 @@ Complexity: $(BIGOH 1) return _data._payload[i]; } -/** -Slicing operations execute an operation on an entire slice. - -Precondition: $(D i < j && j < length) - -Complexity: $(BIGOH slice.length) + /** + * Slicing operators executing the specified operation on the entire slice. + * + * Precondition: `i < j && j < length` + * + * Complexity: $(BIGOH slice.length) */ void opSliceAssign(T value) { @@ -669,7 +662,7 @@ Complexity: $(BIGOH slice.length) /// ditto void opSliceUnary(string op)() - if (op == "++" || op == "--") + if (op == "++" || op == "--") { if (!_data.refCountedStore.isInitialized) return; mixin(op~"_data._payload[];"); @@ -677,7 +670,7 @@ Complexity: $(BIGOH slice.length) /// ditto void opSliceUnary(string op)(size_t i, size_t j) - if (op == "++" || op == "--") + if (op == "++" || op == "--") { auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; mixin(op~"slice[i .. j];"); @@ -697,16 +690,14 @@ Complexity: $(BIGOH slice.length) mixin("slice[i .. j] "~op~"= value;"); } -/** -Returns a new container that's the concatenation of $(D this) and its -argument. $(D opBinaryRight) is only defined if $(D Stuff) does not -define $(D opBinary). - -Complexity: $(BIGOH n + m), where m is the number of elements in $(D -stuff) + /** + * Returns: A new array which is a concatenation of `this` and its argument. + * + * Complexity: + * $(BIGOH length + m), where `m` is the number of elements in `stuff`. */ Array opBinary(string op, Stuff)(Stuff stuff) - if (op == "~") + if (op == "~") { // TODO: optimize Array result; @@ -716,11 +707,11 @@ stuff) return result; } -/** -Forwards to $(D insertBack(stuff)). + /** + * Forwards to `insertBack`. */ void opOpAssign(string op, Stuff)(Stuff stuff) - if (op == "~") + if (op == "~") { static if (is(typeof(stuff[]))) { @@ -732,28 +723,28 @@ Forwards to $(D insertBack(stuff)). } } -/** -Removes all contents from the container. The container decides how $(D -capacity) is affected. - -Postcondition: $(D empty) - -Complexity: $(BIGOH n) + /** + * Removes all the elements from the array and releases allocated memory. + * + * Postcondition: `empty == true && capacity == 0` + * + * Complexity: $(BIGOH length) */ void clear() { _data = Data.init; } -/** -Sets the number of elements in the container to $(D newSize). If $(D -newSize) is greater than $(D length), the added elements are added to -unspecified positions in the container and initialized with $(D -T.init). - -Complexity: $(BIGOH abs(n - newLength)) - -Postcondition: $(D length == newLength) + /** + * Sets the number of elements in the array to `newLength`. If `newLength` + * is greater than `length`, the new elements are added to the end of the + * array and initialized with `T.init`. + * + * Complexity: + * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. + * If `capacity < newLength` the worst case is $(BIGOH newLength). + * + * Postcondition: `length == newLength` */ @property void length(size_t newLength) { @@ -761,16 +752,18 @@ Postcondition: $(D length == newLength) _data.length = newLength; } -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. The stable version behaves the same, -but guarantees that ranges iterating over the container are never invalidated. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH log(n)). + /** + * Removes the last element from the array and returns it. + * Both stable and non-stable versions behave the same and guarantee + * that ranges iterating over the array are never invalidated. + * + * Precondition: `empty == false` + * + * Returns: The element removed. + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ T removeAny() { @@ -778,19 +771,19 @@ Complexity: $(BIGOH log(n)). removeBack(); return result; } + /// ditto alias stableRemoveAny = removeAny; -/** -Inserts $(D value) to the front or back of the container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements in $(D stuff) + /** + * Inserts the specified elements at the back of the array. `stuff` can be + * a value convertible to `T` or a range of objects convertible to `T`. + * + * Returns: The number of elements inserted. + * + * Complexity: + * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), + * where `m` is the number of elements in `stuff`. */ size_t insertBack(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, T) || @@ -799,17 +792,20 @@ elements in $(D stuff) _data.refCountedStore.ensureInitialized(); return _data.insertBack(stuff); } + /// ditto alias insert = insertBack; -/** -Removes the value at the back of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: $(D !empty) - -Complexity: $(BIGOH log(n)). + /** + * Removes the value from the back of the array. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over the + * array are never invalidated. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ void removeBack() { @@ -819,21 +815,22 @@ Complexity: $(BIGOH log(n)). _data._payload = _data._payload[0 .. $ - 1]; } + /// ditto alias stableRemoveBack = removeBack; -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany). + /** + * Removes `howMany` values from the back of the array. + * Unlike the unparameterized versions above, these functions + * do not throw if they could not remove `howMany` elements. Instead, + * if `howMany > n`, all elements are removed. The returned value is + * the effective number of elements removed. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over + * the array are never invalidated. + * + * Returns: The number of elements removed. + * + * Complexity: $(BIGOH howMany). */ size_t removeBack(size_t howMany) { @@ -845,19 +842,22 @@ Complexity: $(BIGOH howMany). _data._payload = _data._payload[0 .. $ - howMany]; return howMany; } + /// ditto alias stableRemoveBack = removeBack; -/** -Inserts $(D stuff) before, after, or instead range $(D r), which must -be a valid range previously extracted from this container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + /** + * Inserts `stuff` before, after, or instead range `r`, which must + * be a valid range previously extracted from this array. `stuff` + * can be a value convertible to `T` or a range of objects convertible + * to `T`. Both stable and non-stable version behave the same and + * guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of values inserted. + * + * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. + * + * Throws: `Exception` if `r` is not a range extracted from this array. */ size_t insertBefore(Stuff)(Range r, Stuff stuff) if (isImplicitlyConvertible!(Stuff, T)) @@ -915,6 +915,9 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) } } + /// ditto + alias stableInsertBefore = insertBefore; + /// ditto size_t insertAfter(Stuff)(Range r, Stuff stuff) { @@ -969,17 +972,16 @@ Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) return 1; } -/** -Removes all elements belonging to $(D r), which must be a range -obtained originally from this container. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH n - m), where $(D m) is the number of elements in -$(D r) + /** + * Removes all elements belonging to `r`, which must be a range + * obtained originally from this array. + * + * Returns: A range spanning the remaining elements in the array that + * initially were right after `r`. + * + * Complexity: $(BIGOH length) + * + * Throws: `Exception` if `r` is not a valid range extracted from this array. */ Range linearRemove(Range r) { @@ -1456,13 +1458,14 @@ $(D r) ai.insertBack(arr); } + //////////////////////////////////////////////////////////////////////////////// // Array!bool //////////////////////////////////////////////////////////////////////////////// /** -_Array specialized for $(D bool). Packs together values efficiently by -allocating one bit per element. + * _Array specialized for `bool`. Packs together values efficiently by + * allocating one bit per element. */ struct Array(T) if (is(Unqual!T == bool)) @@ -1485,7 +1488,7 @@ if (is(Unqual!T == bool)) } /** - Defines the container's primary range. + * Defines the array's primary range. */ struct Range { @@ -1591,10 +1594,10 @@ if (is(Unqual!T == bool)) } /** - Property returning $(D true) if and only if the container has - no elements. - - Complexity: $(BIGOH 1) + * Property returning `true` if and only if the array has + * no elements. + * + * Complexity: $(BIGOH 1) */ @property bool empty() { @@ -1604,17 +1607,15 @@ if (is(Unqual!T == bool)) @system unittest { Array!bool a; - //a._store._refCountedDebug = true; assert(a.empty); a.insertBack(false); assert(!a.empty); } /** - Returns a duplicate of the container. The elements themselves - are not transitively duplicated. - - Complexity: $(BIGOH n). + * Returns: A duplicate of the array. + * + * Complexity: $(BIGOH length). */ @property Array dup() { @@ -1634,10 +1635,10 @@ if (is(Unqual!T == bool)) } /** - Returns the number of elements in the container. - - Complexity: $(BIGOH log(n)). - */ + * Returns the number of elements in the array. + * + * Complexity: $(BIGOH 1). + */ @property size_t length() const { return _store.refCountedStore.isInitialized ? _store._length : 0; @@ -1657,11 +1658,10 @@ if (is(Unqual!T == bool)) } /** - Returns the maximum number of elements the container can store - without (a) allocating memory, (b) invalidating iterators upon - insertion. - - Complexity: $(BIGOH log(n)). + * Returns: The maximum number of elements the array can store without + * reallocating memory and invalidating iterators upon insertion. + * + * Complexity: $(BIGOH 1). */ @property size_t capacity() { @@ -1683,12 +1683,15 @@ if (is(Unqual!T == bool)) } /** - Ensures sufficient capacity to accommodate $(D n) elements. - - Postcondition: $(D capacity >= n) - - Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), - otherwise $(BIGOH 1). + * Ensures sufficient capacity to accommodate `e` _elements. + * If `e < capacity`, this method does nothing. + * + * Postcondition: `capacity >= e` + * + * Note: If the capacity is increased, one should assume that all + * iterators to the elements are invalidated. + * + * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1). */ void reserve(size_t e) { @@ -1706,12 +1709,9 @@ if (is(Unqual!T == bool)) } /** - Returns a range that iterates over all elements of the - container, in a container-defined order. The container should - choose the most convenient and fast method of iteration for $(D - opSlice()). - - Complexity: $(BIGOH log(n)) + * Returns: A range that iterates over all elements of the array in forward order. + * + * Complexity: $(BIGOH 1) */ Range opSlice() { @@ -1726,10 +1726,9 @@ if (is(Unqual!T == bool)) } /** - Returns a range that iterates the container between two - specified positions. - - Complexity: $(BIGOH log(n)) + * Returns: A range that iterates the array between two specified positions. + * + * Complexity: $(BIGOH 1) */ Range opSlice(size_t a, size_t b) { @@ -1745,10 +1744,13 @@ if (is(Unqual!T == bool)) } /** - Equivalent to $(D opSlice().front) and $(D opSlice().back), - respectively. - - Complexity: $(BIGOH log(n)) + * Returns: The first element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + * + * Throws: `Exception` if the array is empty. */ @property bool front() { @@ -1773,7 +1775,15 @@ if (is(Unqual!T == bool)) assert(!a.front); } - /// Ditto + /** + * Returns: The last element of the array. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1) + * + * Throws: `Exception` if the array is empty. + */ @property bool back() { enforce(!empty); @@ -1805,7 +1815,11 @@ if (is(Unqual!T == bool)) } /** - Indexing operators yield or modify the value at a specified index. + * Indexing operators yielding or modifyng the value at the specified index. + * + * Precondition: `i < length` + * + * Complexity: $(BIGOH 1) */ bool opIndex(size_t i) { @@ -1814,6 +1828,7 @@ if (is(Unqual!T == bool)) enforce(div < data.length); return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem)); } + /// ditto void opIndexAssign(bool value, size_t i) { @@ -1823,6 +1838,7 @@ if (is(Unqual!T == bool)) if (value) data.ptr[div] |= (cast(size_t) 1 << rem); else data.ptr[div] &= ~(cast(size_t) 1 << rem); } + /// ditto void opIndexOpAssign(string op)(bool value, size_t i) { @@ -1839,6 +1855,7 @@ if (is(Unqual!T == bool)) else data.ptr[div] &= ~(cast(size_t) 1 << rem); } } + /// Ditto T moveAt(size_t i) { @@ -1855,13 +1872,13 @@ if (is(Unqual!T == bool)) } /** - Returns a new container that's the concatenation of $(D this) - and its argument. - - Complexity: $(BIGOH n + m), where m is the number of elements - in $(D stuff) + * Returns: A new array which is a concatenation of `this` and its argument. + * + * Complexity: + * $(BIGOH length + m), where `m` is the number of elements in `stuff`. */ - Array!bool opBinary(string op, Stuff)(Stuff rhs) if (op == "~") + Array!bool opBinary(string op, Stuff)(Stuff rhs) + if (op == "~") { auto result = this; return result ~= rhs; @@ -1878,18 +1895,11 @@ if (is(Unqual!T == bool)) [true, false, true, true, true, true, false, true])); } - // /// ditto - // TotalContainer opBinaryRight(Stuff, string op)(Stuff lhs) if (op == "~") - // { - // assert(0); - // } - /** - Forwards to $(D insertAfter(this[], stuff)). + * Forwards to `insertBack`. */ - // @@@BUG@@@ - //ref Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") - Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") + Array!bool opOpAssign(string op, Stuff)(Stuff stuff) + if (op == "~") { static if (is(typeof(stuff[]))) insertBack(stuff[]); else insertBack(stuff); @@ -1910,12 +1920,11 @@ if (is(Unqual!T == bool)) } /** - Removes all contents from the container. The container decides - how $(D capacity) is affected. - - Postcondition: $(D empty) - - Complexity: $(BIGOH n) + * Removes all the elements from the array and releases allocated memory. + * + * Postcondition: `empty == true && capacity == 0` + * + * Complexity: $(BIGOH length) */ void clear() { @@ -1931,14 +1940,15 @@ if (is(Unqual!T == bool)) } /** - Sets the number of elements in the container to $(D - newSize). If $(D newSize) is greater than $(D length), the - added elements are added to the container and initialized with - $(D ElementType.init). - - Complexity: $(BIGOH abs(n - newLength)) - - Postcondition: $(D _length == newLength) + * Sets the number of elements in the array to `newLength`. If `newLength` + * is greater than `length`, the new elements are added to the end of the + * array and initialized with `false`. + * + * Complexity: + * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`. + * If `capacity < newLength` the worst case is $(BIGOH newLength). + * + * Postcondition: `length == newLength` */ @property void length(size_t newLength) { @@ -1960,48 +1970,25 @@ if (is(Unqual!T == bool)) { assert(!e); } + immutable cap = a.capacity; a.length = 100; assert(a.length == 100); - assert(a.capacity >= a.length); + // do not realloc if length decreases + assert(a.capacity == cap); } /** - Inserts $(D stuff) in the container. $(D stuff) can be a value - convertible to $(D ElementType) or a range of objects - convertible to $(D ElementType). - - The $(D stable) version guarantees that ranges iterating over - the container are never invalidated. Client code that counts on - non-invalidating insertion should use $(D stableInsert). - - Returns: The number of elements added. - - Complexity: $(BIGOH m * log(n)), where $(D m) is the number of - elements in $(D stuff) - */ - alias insert = insertBack; - ///ditto - alias stableInsert = insertBack; - - /** - Same as $(D insert(stuff)) and $(D stableInsert(stuff)) - respectively, but relax the complexity constraint to linear. - */ - alias linearInsert = insertBack; - ///ditto - alias stableLinearInsert = insertBack; - - /** - Picks one value in the container, removes it from the - container, and returns it. The stable version behaves the same, - but guarantees that ranges iterating over the container are - never invalidated. - - Precondition: $(D !empty) - - Returns: The element removed. - - Complexity: $(BIGOH log(n)) + * Removes the last element from the array and returns it. + * Both stable and non-stable versions behave the same and guarantee + * that ranges iterating over the array are never invalidated. + * + * Precondition: `empty == false` + * + * Returns: The element removed. + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ T removeAny() { @@ -2009,6 +1996,7 @@ if (is(Unqual!T == bool)) removeBack(); return result; } + /// ditto alias stableRemoveAny = removeAny; @@ -2025,17 +2013,17 @@ if (is(Unqual!T == bool)) } /** - Inserts $(D value) to the back of the container. $(D stuff) can - be a value convertible to $(D ElementType) or a range of - objects convertible to $(D ElementType). The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: The number of elements inserted - - Complexity: $(BIGOH log(n)) + * Inserts the specified elements at the back of the array. `stuff` can be + * a value convertible to `bool` or a range of objects convertible to `bool`. + * + * Returns: The number of elements inserted. + * + * Complexity: + * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m), + * where `m` is the number of elements in `stuff`. */ - size_t insertBack(Stuff)(Stuff stuff) if (is(Stuff : bool)) + size_t insertBack(Stuff)(Stuff stuff) + if (is(Stuff : bool)) { _store.refCountedStore.ensureInitialized(); auto rem = _store._length % bitsPerWord; @@ -2059,7 +2047,8 @@ if (is(Unqual!T == bool)) ++_store._length; return 1; } - /// Ditto + + /// ditto size_t insertBack(Stuff)(Stuff stuff) if (isInputRange!Stuff && is(ElementType!Stuff : bool)) { @@ -2072,9 +2061,22 @@ if (is(Unqual!T == bool)) static if (!hasLength!Stuff) return result; else return stuff.length; } + /// ditto alias stableInsertBack = insertBack; + /// ditto + alias insert = insertBack; + + /// ditto + alias stableInsert = insertBack; + + /// ditto + alias linearInsert = insertBack; + + /// ditto + alias stableLinearInsert = insertBack; + @system unittest { Array!bool a; @@ -2085,16 +2087,15 @@ if (is(Unqual!T == bool)) } /** - Removes the value at the front or back of the container. The - stable version behaves the same, but guarantees that ranges - iterating over the container are never invalidated. The - optional parameter $(D howMany) instructs removal of that many - elements. If $(D howMany > n), all elements are removed and no - exception is thrown. - - Precondition: $(D !empty) - - Complexity: $(BIGOH log(n)). + * Removes the value from the back of the array. Both stable and non-stable + * versions behave the same and guarantee that ranges iterating over the + * array are never invalidated. + * + * Precondition: `empty == false` + * + * Complexity: $(BIGOH 1). + * + * Throws: `Exception` if the array is empty. */ void removeBack() { @@ -2111,23 +2112,22 @@ if (is(Unqual!T == bool)) _store._backend.length = _store._backend.length - 1; } } + /// ditto alias stableRemoveBack = removeBack; /** - Removes $(D howMany) values at the front or back of the - container. Unlike the unparameterized versions above, these - functions do not throw if they could not remove $(D howMany) - elements. Instead, if $(D howMany > n), all elements are - removed. The returned value is the effective number of elements - removed. The stable version behaves the same, but guarantees - that ranges iterating over the container are never invalidated. - - Returns: The number of elements removed - - Complexity: $(BIGOH howMany * log(n)). + * Removes `howMany` values from the back of the array. Unlike the + * unparameterized versions above, these functions do not throw if + * they could not remove `howMany` elements. Instead, if `howMany > n`, + * all elements are removed. The returned value is the effective number + * of elements removed. Both stable and non-stable versions behave the same + * and guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of elements removed. + * + * Complexity: $(BIGOH howMany). */ - /// ditto size_t removeBack(size_t howMany) { if (howMany >= length) @@ -2142,6 +2142,9 @@ if (is(Unqual!T == bool)) return howMany; } + /// ditto + alias stableRemoveBack = removeBack; + @system unittest { Array!bool a; @@ -2155,17 +2158,15 @@ if (is(Unqual!T == bool)) } /** - Inserts $(D stuff) before, after, or instead range $(D r), - which must be a valid range previously extracted from this - container. $(D stuff) can be a value convertible to $(D - ElementType) or a range of objects convertible to $(D - ElementType). The stable version behaves the same, but - guarantees that ranges iterating over the container are never - invalidated. - - Returns: The number of values inserted. - - Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + * Inserts `stuff` before, after, or instead range `r`, which must + * be a valid range previously extracted from this array. `stuff` + * can be a value convertible to `bool` or a range of objects convertible + * to `bool`. Both stable and non-stable version behave the same and + * guarantee that ranges iterating over the array are never invalidated. + * + * Returns: The number of values inserted. + * + * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`. */ size_t insertBefore(Stuff)(Range r, Stuff stuff) { @@ -2178,6 +2179,7 @@ if (is(Unqual!T == bool)) this[tailLength .. length]); return inserted; } + /// ditto alias stableInsertBefore = insertBefore; @@ -2207,6 +2209,7 @@ if (is(Unqual!T == bool)) this[tailLength .. length]); return inserted; } + /// ditto alias stableInsertAfter = insertAfter; @@ -2219,8 +2222,10 @@ if (is(Unqual!T == bool)) assert(a.length == 11, to!string(a.length)); assert(a[5]); } + /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) if (is(Stuff : bool)) + size_t replace(Stuff)(Range r, Stuff stuff) + if (is(Stuff : bool)) { if (!r.empty) { @@ -2236,6 +2241,7 @@ if (is(Unqual!T == bool)) } return 1; } + /// ditto alias stableReplace = replace; @@ -2250,15 +2256,13 @@ if (is(Unqual!T == bool)) } /** - Removes all elements belonging to $(D r), which must be a range - obtained originally from this container. The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: A range spanning the remaining elements in the container that - initially were right after $(D r). - - Complexity: $(BIGOH n) + * Removes all elements belonging to `r`, which must be a range + * obtained originally from this array. + * + * Returns: A range spanning the remaining elements in the array that + * initially were right after `r`. + * + * Complexity: $(BIGOH length) */ Range linearRemove(Range r) { From 250d972364081a30006a09e803e21a304da567f6 Mon Sep 17 00:00:00 2001 From: Superstar64 Date: Mon, 20 Mar 2017 23:06:31 -0400 Subject: [PATCH 012/262] changed front declaration to an alias --- std/range/interfaces.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/interfaces.d b/std/range/interfaces.d index 3a69077586f..e519a667e90 100644 --- a/std/range/interfaces.d +++ b/std/range/interfaces.d @@ -209,7 +209,7 @@ interface InputAssignable(E) : InputRange!E { /// @property void front(E newVal); - @property E front(); + alias front = InputRange!E.front; // overload base interface method } @safe unittest From d20056974aa1f7932e8c46151d284bc2dfb28b19 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Tue, 21 Mar 2017 12:22:09 +0000 Subject: [PATCH 013/262] Document compile-time checking for format strings --- std/format.d | 2 +- std/stdio.d | 40 ++++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/std/format.d b/std/format.d index 61458950ad1..7d1cc539401 100644 --- a/std/format.d +++ b/std/format.d @@ -579,7 +579,7 @@ can match the expected number of readings or fewer, even zero, if a matching failure happens. Throws: - An `Exception` if `S.length == 0` and `fmt` has format specifiers + An `Exception` if `S.length == 0` and `fmt` has format specifiers. */ uint formattedRead(alias fmt, R, S...)(ref R r, auto ref S args) if (isSomeString!(typeof(fmt))) diff --git a/std/stdio.d b/std/stdio.d index 59900a0f50a..8fc8f15eebd 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -1450,6 +1450,12 @@ Throws: $(D Exception) if the file is not opened. Writes its arguments in text format to the file, according to the format string fmt. +Params: +fmt = The $(LINK2 std_format.html#format-string, format string). +When passed as a compile-time argument, the string will be statically checked +against the argument types passed. +args = Items to write. + Throws: $(D Exception) if the file is not opened. $(D ErrnoException) on an error writing to the file. */ @@ -1471,13 +1477,7 @@ Throws: $(D Exception) if the file is not opened. formattedWrite(lockingTextWriter(), fmt, args); } -/** -Writes its arguments in text format to the file, according to the -format string fmt, followed by a newline. - -Throws: $(D Exception) if the file is not opened. - $(D ErrnoException) on an error writing to the file. -*/ + /// Equivalent to `file.writef(fmt, args, '\n')`. void writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt))) { @@ -1786,9 +1786,12 @@ is recommended if you want to process a complete file. } /** - * Reads data from the file according to the specified - * $(LINK2 std_format.html#_format-string, _format specifier) using - * $(REF formattedRead, std,_format). + * Reads formatted _data from the file using $(REF formattedRead, std,_format). + * Params: + * format = The $(LINK2 std_format.html#_format-string, _format string). + * When passed as a compile-time argument, the string will be statically checked + * against the argument types passed. + * data = Items to be read. * Example: ---- // test.d @@ -3701,11 +3704,9 @@ void writeln(T...)(T args) Writes formatted data to standard output (without a trailing newline). Params: -fmt = The format string, specifying -how to format the rest of the arguments. For a full description of the syntax -of the format string and how it controls the formatting of the rest of the -arguments, please refer to the documentation for $(REF formattedWrite, -std,format). +fmt = The $(LINK2 std_format.html#format-string, format string). +When passed as a compile-time argument, the string will be statically checked +against the argument types passed. args = Items to write. Note: In older versions of Phobos, it used to be possible to write: @@ -3814,9 +3815,12 @@ void writefln(Char, A...)(in Char[] fmt, A args) } /** - * Read data from $(D stdin) according to the specified - * $(LINK2 std_format.html#_format-string, _format specifier) using - * $(REF formattedRead, std,_format). + * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format). + * Params: + * format = The $(LINK2 std_format.html#_format-string, _format string). + * When passed as a compile-time argument, the string will be statically checked + * against the argument types passed. + * args = Items to be read. * Example: ---- // test.d From 749c56c3008c6952290a3ec55ab846eac6127fee Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Wed, 22 Mar 2017 12:10:56 +0100 Subject: [PATCH 014/262] split std.algorithm tests to avoid OOM - as seen on win-farm-1 tester --- win64.mak | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/win64.mak b/win64.mak index 14288f7cb1c..b8c3ccb5380 100644 --- a/win64.mak +++ b/win64.mak @@ -201,13 +201,16 @@ SRC_STD_ALGO_1= \ SRC_STD_ALGO_2= \ std\algorithm\searching.d \ - std\algorithm\setops.d \ + std\algorithm\setops.d + +SRC_STD_ALGO_3= \ std\algorithm\sorting.d \ std\algorithm\internal.d SRC_STD_ALGO= \ $(SRC_STD_ALGO_1) \ - $(SRC_STD_ALGO_2) + $(SRC_STD_ALGO_2) \ + $(SRC_STD_ALGO_3) SRC_STD_CONTAINER= \ std\container\array.d \ @@ -574,6 +577,7 @@ UNITTEST_OBJS= \ unittest4.obj \ unittest5a.obj \ unittest5b.obj \ + unittest5c.obj \ unittest6a.obj \ unittest6c.obj \ unittest6e.obj \ @@ -601,6 +605,7 @@ unittest : $(LIB) $(DMD) $(UDFLAGS) -c -unittest -ofunittest4.obj $(SRC_STD_4) $(SRC_STD_DIGEST) $(DMD) $(UDFLAGS) -c -unittest -ofunittest5a.obj $(SRC_STD_ALGO_1) $(DMD) $(UDFLAGS) -c -unittest -ofunittest5b.obj $(SRC_STD_ALGO_2) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest5c.obj $(SRC_STD_ALGO_3) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6a.obj $(SRC_STD_6a) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6c.obj $(SRC_STD_6c) $(DMD) $(UDFLAGS) -c -unittest -ofunittest6e.obj $(SRC_STD_6e) From a4f570fd4f262a5ea4b99c44d7ade239d3efc3c1 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Fri, 17 Mar 2017 09:47:17 -0400 Subject: [PATCH 015/262] Improve many docs in std.array --- std/array.d | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/std/array.d b/std/array.d index 934cf61a679..b3378680cbe 100644 --- a/std/array.d +++ b/std/array.d @@ -1983,7 +1983,7 @@ if (isInputRange!RoR && `sink`. Params: - sink = an output range + sink = an $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) subject = the array to scan from = the item to replace to = the item to replace all instances of `from` with @@ -1992,6 +1992,9 @@ if (isInputRange!RoR && If `sink` isn't defined, a new array without changing the contents of `subject`, or the original array if no match is found. + + See_Also: + $(REF map, std,algorithm,iteration) which can act as a lazy replace +/ E[] replace(E, R1, R2)(E[] subject, R1 from, R2 to) if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2 @@ -2240,9 +2243,15 @@ if (isInputRange!Range && } /++ - Replaces elements from $(D array) with indices ranging from $(D from) - (inclusive) to $(D to) (exclusive) with the range $(D stuff). Expands or + Replaces elements from `array` with indices ranging from `from` + (inclusive) to `to` (exclusive) with the range `stuff`. Expands or shrinks the array as needed. + + Params: + array = the _array to scan + from = the starting index + to = the ending index + stuff = the items to replace in-between `from` and `to` +/ void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) if (is(typeof(replace(array, from, to, stuff)))) @@ -2652,8 +2661,18 @@ if (isDynamicArray!(E[]) && } /++ - Returns a new array that is $(D s) with $(D slice) replaced by - $(D replacement[]). + Creates a new array such that the items in `slice` are replaced with the + items in `replacement`. `slice` and `replacement` do not need to be the + same length. The result will grow or shrink based on the items given. + + Params: + s = the base of the new array + slice = the slice of `s` to be replaced + replacement = the items to replace `slice` with + + Returns: + A new array that is `s` with `slice` replaced by + `replacement[]`. +/ inout(T)[] replaceSlice(T)(inout(T)[] s, in T[] slice, in T[] replacement) in @@ -2750,7 +2769,7 @@ if (isDynamicArray!A) /** * Reserve at least newCapacity elements for appending. Note that more elements - * may be reserved than requested. If `newCapacity <= capacity`, then nothing is + * may be reserved than requested. If `newCapacity <= capacity`, then nothing is * done. */ void reserve(size_t newCapacity) @safe pure nothrow @@ -2767,9 +2786,9 @@ if (isDynamicArray!A) } /** - * Returns the capacity of the array (the maximum number of elements the - * managed array can accommodate before triggering a reallocation). If any - * appending will reallocate, $(D capacity) returns $(D 0). + * Returns: the capacity of the array (the maximum number of elements the + * managed array can accommodate before triggering a reallocation). If any + * appending will reallocate, `0` will be returned. */ @property size_t capacity() const @safe pure nothrow { @@ -2777,7 +2796,7 @@ if (isDynamicArray!A) } /** - * Returns the managed array. + * Returns: The managed array. */ @property inout(ElementEncodingType!A)[] data() inout @trusted pure nothrow { From 9c8a71ef6e74b25daff9af6019f7bcfee4ca95b5 Mon Sep 17 00:00:00 2001 From: cjoan Date: Wed, 22 Mar 2017 13:04:29 -0400 Subject: [PATCH 016/262] Clarify deallocation done by typecons.RefCounted. I had trouble understanding how RefCounted!T was supposed to help me manage resources. These document changes should clarify the process and make it easier for others to understand. This is follow-up after this post on the D learn forum: https://forum.dlang.org/post/qctmkqpqnreysxcjmrgm@forum.dlang.org and after commits cd86cc25b60950dfd26f8f0f8840e576e0da1fc0 and 33217eb46f4bb5668cc5b763e9e428f93218545a "Clarify deallocation done by std.typecons.Unique." --- std/typecons.d | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/std/typecons.d b/std/typecons.d index 43d03b6325e..92f065488e5 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -5121,9 +5121,28 @@ enum RefCountedAutoInitialize /** Defines a reference-counted object containing a $(D T) value as -payload. $(D RefCounted) keeps track of all references of an object, -and when the reference count goes down to zero, frees the underlying -store. $(D RefCounted) uses $(D malloc) and $(D free) for operation. +payload. + +$(D RefCounted) is implemented as a pointer to a data structure, which +is refered to as the $(I store) or $(I storage implentation struct) in +this documentation. The store contains a reference count and the +$(D T) payload. $(D RefCounted) uses $(D malloc) to allocate the +store. As instances of $(D RefCounted) are copied or go out of scope, +they will automatically increment or decrement the reference count on +the store that they point to. When the reference count goes down to +zero, $(D RefCounted) will call $(D destroy) against the payload and +call $(D free) to deallocate the store. If the $(D T) payload contains +any references to GC-allocated memory, then $(RefCounted) will add it +to the GC memory that is scanned for pointers, and remove it from GC +scanning before $(D free) is called on the store. + +One important consequence of $(D destroy) is that it will call the +destructor of the $(D T) payload. GC-managed references are not +guaranteed to be valid during a destructor call, but other members of +$(D T), such as file handles or pointers to $(D malloc) memory, will +still be valid during the destructor call. This allows the $(D T) to +deallocate or clean up any non-GC resources immediately after the +reference count has reached zero. $(D RefCounted) is unsafe and should be used with care. No references to the payload should be escaped outside the $(D RefCounted) object. From 05620ead4b7b80984afb44fb5aa64528baa2fdca Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Thu, 23 Mar 2017 01:14:51 +0100 Subject: [PATCH 017/262] Fix issue 17270 --- std/experimental/typecons.d | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index b7743abeab5..75941c6b752 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -875,7 +875,7 @@ else alias final_get this; /// Ditto - T opUnary(string op)() + auto opUnary(string op)() if (__traits(compiles, mixin(op ~ "T.init"))) { return mixin(op ~ "this.final_value"); @@ -1024,3 +1024,10 @@ pure nothrow @safe unittest static assert(!__traits(compiles, arr ~= 4)); assert((arr ~ 4) == [1, 2, 3, 4]); } + +pure nothrow @nogc @system unittest +{ + int i = 1; + Final!(int*) fp = &i; + assert(*fp == 1); +} From 9fddec58c910bd9e636760bc69e2ae6c715bc0a9 Mon Sep 17 00:00:00 2001 From: byebye Date: Mon, 13 Mar 2017 02:49:56 +0100 Subject: [PATCH 018/262] std.container.Array - optimize concatenation --- std/container/array.d | 65 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/std/container/array.d b/std/container/array.d index 9bd349c6b38..47a00e24bd2 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -690,6 +690,8 @@ if (!is(Unqual!T == bool)) mixin("slice[i .. j] "~op~"= value;"); } + private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; })); + /** * Returns: A new array which is a concatenation of `this` and its argument. * @@ -699,14 +701,48 @@ if (!is(Unqual!T == bool)) Array opBinary(string op, Stuff)(Stuff stuff) if (op == "~") { - // TODO: optimize Array result; - result ~= this[]; - assert(result.length == length); - result ~= stuff[]; + + static if (hasLength!Stuff || isNarrowString!Stuff) + result.reserve(length + stuff.length); + else static if (hasSliceWithLength!Stuff) + result.reserve(length + stuff[].length); + else static if (isImplicitlyConvertible!(Stuff, T)) + result.reserve(length + 1); + + result.insertBack(this[]); + result ~= stuff; return result; } + @nogc @system unittest + { + auto a = Array!int(0, 1, 2); + int[3] b = [3, 4, 5]; + short[3] ci = [0, 1, 0]; + auto c = Array!short(ci); + assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); + assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); + assert(Array!int(0, 1, 2, 3) == a ~ 3); + assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); + } + + @nogc @system unittest + { + auto a = Array!char('a', 'b'); + assert(Array!char("abc") == a ~ 'c'); + import std.utf : byCodeUnit; + assert(Array!char("abcd") == a ~ "cd".byCodeUnit); + } + + @nogc @system unittest + { + auto a = Array!dchar("ąćę"d); + assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); + wchar x = 'Ϣ'; + assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); + } + /** * Forwards to `insertBack`. */ @@ -1880,8 +1916,18 @@ if (is(Unqual!T == bool)) Array!bool opBinary(string op, Stuff)(Stuff rhs) if (op == "~") { - auto result = this; - return result ~= rhs; + Array!bool result; + + static if (hasLength!Stuff) + result.reserve(length + rhs.length); + else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[]))) + result.reserve(length + rhs[].length); + else static if (isImplicitlyConvertible!(Stuff, bool)) + result.reserve(length + 1); + + result.insertBack(this[]); + result ~= rhs; + return result; } @system unittest @@ -1893,6 +1939,10 @@ if (is(Unqual!T == bool)) b.insertBack([true, true, false, true]); assert(equal((a ~ b)[], [true, false, true, true, true, true, false, true])); + assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); + Array!bool c; + c.insertBack(true); + assert((c ~ false)[].equal([true, false])); } /** @@ -2195,6 +2245,9 @@ if (is(Unqual!T == bool)) assert(a.length == 1, to!string(a.length)); a.insertBefore(a[], false); assert(a.length == 2, to!string(a.length)); + a.insertBefore(a[1 .. $], true); + import std.algorithm.comparison : equal; + assert(a[].equal([false, true, true])); } /// ditto From e7086e82549c13aac9b7dd05afd51699d2c90844 Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Thu, 23 Mar 2017 20:39:32 +0100 Subject: [PATCH 019/262] fix * assignment and improved unit test --- std/experimental/typecons.d | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 75941c6b752..53c4d7ccaf6 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -875,7 +875,7 @@ else alias final_get this; /// Ditto - auto opUnary(string op)() + auto ref opUnary(string op)() if (__traits(compiles, mixin(op ~ "T.init"))) { return mixin(op ~ "this.final_value"); @@ -1025,9 +1025,17 @@ pure nothrow @safe unittest assert((arr ~ 4) == [1, 2, 3, 4]); } +// issue 17270 pure nothrow @nogc @system unittest { int i = 1; Final!(int*) fp = &i; assert(*fp == 1); + static assert(!__traits(compiles, + fp = &i // direct assignment + )); + *fp = 2; // indirect assignment + assert(*fp == 2); + int* p = fp; + assert(*p == 2); } From 633a67714489909648a791fafc257dad8c4e1028 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Thu, 23 Mar 2017 16:19:56 -0700 Subject: [PATCH 020/262] Nicer error messages for std.typecons.experimental.Final. --- std/experimental/typecons.d | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index b7743abeab5..03b0b6ffd2f 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -855,10 +855,32 @@ else + underlying value of type `T` except for these mutating operators, + which are disabled. +/ - void opAssign(Other)(Other other); - void opOpAssign(string op, Other)(Other other); /// Ditto - void opUnary(string op : "--")(); /// Ditto - void opUnary(string op : "++")(); /// Ditto + void opAssign(Other)(Other other) + { + static assert(0, typeof(this).stringof ~ + " cannot be reassigned."); + } + + /// Ditto + void opOpAssign(string op, Other)(Other other) + { + static assert(0, typeof(this).stringof ~ + " cannot be reassigned."); + } + + /// Ditto + void opUnary(string op : "--")() + { + static assert(0, typeof(this).stringof ~ + " cannot be mutated."); + } + + /// Ditto + void opUnary(string op : "++")() + { + static assert(0, typeof(this).stringof ~ + " cannot be mutated."); + } } /** From c07db5334f1ab2a74c3aa30768d6204edc2bedd7 Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Fri, 24 Mar 2017 00:58:17 +0100 Subject: [PATCH 021/262] added static assert for * type --- std/experimental/typecons.d | 1 + 1 file changed, 1 insertion(+) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 53c4d7ccaf6..78ceab0164d 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -1034,6 +1034,7 @@ pure nothrow @nogc @system unittest static assert(!__traits(compiles, fp = &i // direct assignment )); + static assert(is(typeof(*fp) == int)); *fp = 2; // indirect assignment assert(*fp == 2); int* p = fp; From da17fdc038f6c13d6417183eb03e86923e448e1c Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Fri, 24 Mar 2017 01:51:32 +0100 Subject: [PATCH 022/262] added static asserts for arrays and pointers --- std/experimental/typecons.d | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 78ceab0164d..2795cbf649c 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -1040,3 +1040,18 @@ pure nothrow @nogc @system unittest int* p = fp; assert(*p == 2); } + +pure nothrow @system unittest +{ + Final!(int[]) arr; + static assert(!__traits(compiles, + arr.length++ + )); + arr.length = 10; + static assert(!__traits(compiles, + arr.ptr = null + )); + static assert(!__traits(compiles, + arr.ptr++ + )); +} \ No newline at end of file From c770fb481218e6eb2cd32363ce96de816dc6bdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Fri, 10 Mar 2017 18:50:53 +0100 Subject: [PATCH 023/262] Fix issue 17251 - Appender.put doesn't accept const input range elements. The two overloads taking an element and a const range were conflicting because canPutConstRange is overlapping the definition of canPutItem. --- std/array.d | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/std/array.d b/std/array.d index b3378680cbe..05075190569 100644 --- a/std/array.d +++ b/std/array.d @@ -2880,7 +2880,8 @@ if (isDynamicArray!A) { enum bool canPutConstRange = isInputRange!(Unqual!Range) && - !isInputRange!Range; + !isInputRange!Range && + is(typeof(Appender.init.put(Range.init.front))); } private template canPutRange(Range) { @@ -3072,6 +3073,21 @@ if (isDynamicArray!A) assert("%s".format(app) == "Appender!(int[])(%s)".format([1,2,3])); } +@safe unittest // issue 17251 +{ + static struct R + { + int front() const { return 0; } + bool empty() const { return true; } + void popFront() {} + } + + auto app = appender!(R[]); + const(R)[1] r; + app.put(r[0]); + app.put(r[]); +} + //Calculates an efficient growth scheme based on the old capacity //of data, and the minimum requested capacity. //arg curLen: The current length From bdb626f5ff8c7f3774a4aef7a1bb0383d4ff919d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 25 Mar 2017 19:09:50 +0100 Subject: [PATCH 024/262] Remove trailing whitespace. --- std/array.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/array.d b/std/array.d index 05075190569..4d923776be4 100644 --- a/std/array.d +++ b/std/array.d @@ -3081,7 +3081,7 @@ if (isDynamicArray!A) bool empty() const { return true; } void popFront() {} } - + auto app = appender!(R[]); const(R)[1] r; app.put(r[0]); From e632c58dbeaa45d84a1a962b392f2a8204c93ec8 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Sun, 26 Mar 2017 21:54:17 -0700 Subject: [PATCH 025/262] Remove @disable, which is now redundant. --- std/experimental/typecons.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 03b0b6ffd2f..7b2373b779b 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -848,7 +848,7 @@ else } // Attaching function attributes gives less noisy error messages - pure nothrow @safe @nogc @disable + pure nothrow @safe @nogc { /++ + All operators, including member access, are forwarded to the From ba8ad89b4578d5d84f7f9606624709ebfe699072 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Sun, 26 Mar 2017 21:48:40 -0700 Subject: [PATCH 026/262] Add disclaimer to std.random about unsuitability for cryptographical purposes. --- std/random.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/std/random.d b/std/random.d index 6d98d6ce55b..c8fcb3801f5 100644 --- a/std/random.d +++ b/std/random.d @@ -3,6 +3,12 @@ /** Facilities for random number generation. +$(RED Disclaimer:) The _random number generators and API provided in this +module are not designed to be cryptographically secure, and are therefore +unsuitable for cryptographic or security-related purposes such as generating +authentication tokens or network sequence numbers. For such needs, please use a +reputable cryptographic library instead. + The new-style generator objects hold their own state so they are immune of threading issues. The generators feature a number of well-known and well-documented methods of generating random From 94b31ce55a49d797c3ed7da3a38473b44ebf5556 Mon Sep 17 00:00:00 2001 From: dukc Date: Tue, 28 Mar 2017 07:46:41 +0300 Subject: [PATCH 027/262] It now still accepts inputRanges when they're infinite. --- std/range/package.d | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index e7e67e1207e..b229efe2ff2 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -3377,7 +3377,8 @@ public: Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make $(D Cycle) the identity application), $(D Cycle) detects that and aliases itself to the range type -itself. If the original range has random access, $(D Cycle) offers +itself. That works for non-forward ranges too. +If the original range has random access, $(D Cycle) offers random access and also offers a constructor taking an initial position $(D index). $(D Cycle) works with static arrays in addition to ranges, mostly for performance reasons. @@ -3644,8 +3645,11 @@ nothrow: /// Ditto auto cycle(R)(R input) -if (isForwardRange!R) +if (isInputRange!R) { + static assert(isForwardRange!R || isInfinite!R, + "Cycle requires a forward range argument unless it's statically known" + ~ " to be infinite"); assert(!input.empty, "Attempting to pass an empty input to cycle"); static if (isInfinite!R) return input; else return Cycle!R(input); @@ -3780,8 +3784,8 @@ if (isStaticArray!R) NonForwardInfRange j; auto c = cycle(i); assert(c == i); - //make sure it requires a forward range even when infinite - static assert(!is(typeof(j.cycle))); + //make sure it can alias out even non-forward infinite ranges + static assert(is(typeof(j.cycle) == typeof(j))); } @safe unittest From abddd888f2a18c1d2fb3e3cd11740f254918fe62 Mon Sep 17 00:00:00 2001 From: cjoan Date: Wed, 29 Mar 2017 00:06:51 -0400 Subject: [PATCH 028/262] Fixed typos in RefCounted documentation. --- std/typecons.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/typecons.d b/std/typecons.d index 92f065488e5..5d958e6e13e 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -5124,7 +5124,7 @@ Defines a reference-counted object containing a $(D T) value as payload. $(D RefCounted) is implemented as a pointer to a data structure, which -is refered to as the $(I store) or $(I storage implentation struct) in +is referred to as the $(I store) or $(I storage implementation struct) in this documentation. The store contains a reference count and the $(D T) payload. $(D RefCounted) uses $(D malloc) to allocate the store. As instances of $(D RefCounted) are copied or go out of scope, From 37e1bbea3f21e390e921cc1a3bbf4be796d2eb5a Mon Sep 17 00:00:00 2001 From: cjoan Date: Wed, 29 Mar 2017 01:44:38 -0400 Subject: [PATCH 029/262] More edits to typecons.RefCounted --- std/typecons.d | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/std/typecons.d b/std/typecons.d index 5d958e6e13e..a0c05ef8949 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -5123,18 +5123,18 @@ enum RefCountedAutoInitialize Defines a reference-counted object containing a $(D T) value as payload. -$(D RefCounted) is implemented as a pointer to a data structure, which -is referred to as the $(I store) or $(I storage implementation struct) in -this documentation. The store contains a reference count and the -$(D T) payload. $(D RefCounted) uses $(D malloc) to allocate the -store. As instances of $(D RefCounted) are copied or go out of scope, -they will automatically increment or decrement the reference count on -the store that they point to. When the reference count goes down to -zero, $(D RefCounted) will call $(D destroy) against the payload and -call $(D free) to deallocate the store. If the $(D T) payload contains -any references to GC-allocated memory, then $(RefCounted) will add it -to the GC memory that is scanned for pointers, and remove it from GC -scanning before $(D free) is called on the store. +An instance of $(D RefCounted) is a reference to a structure, +which is referred to as the $(I store), or $(I storage implementation +struct) in this documentation. The store contains a reference count +and the $(D T) payload. $(D RefCounted) uses $(D malloc) to allocate +the store. As instances of $(D RefCounted) are copied or go out of +scope, they will automatically increment or decrement the reference +count. When the reference count goes down to zero, $(D RefCounted) +will call $(D destroy) against the payload and call $(D free) to +deallocate the store. If the $(D T) payload contains any references +to GC-allocated memory, then $(RefCounted) will add it to the GC memory +that is scanned for pointers, and remove it from GC scanning before +$(D free) is called on the store. One important consequence of $(D destroy) is that it will call the destructor of the $(D T) payload. GC-managed references are not From e6936d12ae913e3d3612ab39f4643207c05c4a56 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 29 Mar 2017 10:19:41 -0400 Subject: [PATCH 030/262] Mention the extra checks in the std.string.assumeUTF docs --- std/string.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std/string.d b/std/string.d index f2751e9b23f..e5e7f53b597 100644 --- a/std/string.d +++ b/std/string.d @@ -6922,6 +6922,9 @@ return it typed as a UTF string. $(D ubyte) becomes $(D char), $(D ushort) becomes $(D wchar) and $(D uint) becomes $(D dchar). Type qualifiers are preserved. +When compiled with debug mode, this function performs an extra check to make +sure the return value is a valid Unicode string. + Params: arr = array of bytes, ubytes, shorts, ushorts, ints, or uints From 75d12127669d1f3b98fa16d46482b8a7443f33d2 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 29 Mar 2017 16:58:16 -0400 Subject: [PATCH 031/262] Documented undocumented hmac helpers --- std/digest/hmac.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/digest/hmac.d b/std/digest/hmac.d index 45c9ee6eb52..0e6158e0b59 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -208,12 +208,14 @@ if (hashBlockSize % 8 == 0) } } +/// Convenience constructor for $(LREF HMAC). template hmac(H) if (isDigest!H && hasBlockSize!H) { alias hmac = hmac!(H, H.blockSize); } +/// ditto template hmac(H, size_t blockSize) if (isDigest!H) { @@ -224,7 +226,6 @@ if (isDigest!H) * An instance of HMAC that can be fed data as desired, and finished * to compute the final hash when done. */ - auto hmac(scope const(ubyte)[] secret) { return HMAC!(H, blockSize)(secret); @@ -254,7 +255,6 @@ if (isDigest!H) * Returns: * The final _HMAC hash. */ - DigestType!H hmac(T...)(scope T data, scope const(ubyte)[] secret) if (allSatisfy!(isDigestibleRange, typeof(data))) { From b09ad9aeeaed177ca1fa2721d36064b4c00d3fe6 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 20 Mar 2017 17:02:19 +0000 Subject: [PATCH 032/262] Fix Issue 17288 - formattedWrite with width/precision and no value Previously an RangeError at runtime or assert(0) at CT would fire from formatNth with message e.g. "n = 1". --- std/format.d | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/std/format.d b/std/format.d index 7767e9cc4bc..21f56493efa 100644 --- a/std/format.d +++ b/std/format.d @@ -471,13 +471,6 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) uint currentArg = 0; while (spec.writeUpToNextSpec(w)) { - if (currentArg == funs.length && !spec.indexStart) - { - // leftover spec? - enforceFmt(fmt.length == 0, - text("Orphan format specifier: %", spec.spec)); - break; - } if (spec.width == spec.DYNAMIC) { auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); @@ -524,6 +517,13 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) // else negative precision is same as no precision else spec.precision = spec.UNSPECIFIED; } + if (currentArg == A.length && !spec.indexStart) + { + // leftover spec? + enforceFmt(fmt.length == 0, + text("Orphan format specifier: %", spec.spec)); + break; + } // Format! if (spec.indexStart > 0) { @@ -534,7 +534,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) { foreach (i; spec.indexStart - 1 .. spec.indexEnd) { - if (funs.length <= i) break; + if (A.length <= i) break; if (__ctfe) formatNth(w, spec, i, args); else @@ -3841,10 +3841,18 @@ private int getNthInt(A...)(uint index, A args) } else { - throw new FormatException("int expected"); + throw new FormatException("missing integer width/precision argument"); } } +@safe unittest +{ + assertThrown!FormatException(format("%*.d", 5.1, 2)); // float width + assertThrown!FormatException(format("%.*d", 5.1, 2)); // float precision + assertThrown!FormatException(format("%*.d", 5)); // missing arg + assertThrown!FormatException(format("%*.*d", 5, 4)); // missing arg +} + /* ======================== Unit Tests ====================================== */ version(unittest) From 290447ead429608c818db8c263c4df9b722c37c2 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 29 Mar 2017 16:50:01 -0400 Subject: [PATCH 033/262] Fix Issue 17286 - A function for comparing two digests securely --- std/digest/digest.d | 148 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/std/digest/digest.d b/std/digest/digest.d index b53589af588..48cbe35da43 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -65,6 +65,7 @@ module std.digest.digest; import std.meta : allSatisfy; import std.traits; +import std.range.primitives; public import std.ascii : LetterCase; @@ -1021,3 +1022,150 @@ if (isDigest!T) : Digest ubyte[5] buf; assert(hash.peek(buf).toHexString() == "39A34F41"); } + +/** + * Securely compares two digest representations while protecting against timing + * attacks. Do not use `==` to compare digest representations. + * + * The attack happens as follows: + * + * $(OL + * $(LI An attacker wants to send harmful data to your server, which + * requires a integrity HMAC SHA1 token signed with a secret.) + * $(LI The length of the token is known to be 40 characters long due to its format, + * so the attacker first sends `"0000000000000000000000000000000000000000"`, + * then `"1000000000000000000000000000000000000000"`, and so on.) + * $(LI The given HMAC token is compared with the expected token using the + * `==` string comparison, which returns `false` as soon as the first wrong + * element is found. If a wrong element is found, then a rejection is sent + * back to the sender.) + * $(LI Eventually, the attacker is able to determine the first character in + * the correct token because the sever takes slightly longer to return a + * rejection. This is due to the comparison moving on to second item in + * the two arrays, seeing they are different, and then sending the rejection.) + * $(LI It may seem like too small of a difference in time for the attacker + * to notice, but security researchers have shown that differences as + * small as $(LINK2 http://www.cs.rice.edu/~dwallach/pub/crosby-timing2009.pdf, + * 20µs can be reliably distinguished) even with network inconsistencies.) + * $(LI Repeat the process for each character until the attacker has the whole + * correct token and the server accepts the harmful data. This can be done + * in a week with the attacker pacing the attack to 10 requests per second + * with only one client.) + * ) + * + * This function defends against this attack by always comparing every single + * item in the array if the two arrays are the same length. Therefore, this + * function is always $(BIGOH n) for ranges of the same length. + * + * This attack can also be mitigated via rate limiting and banning IPs which have too + * many rejected requests. However, this does not completely solve the problem, + * as the attacker could be in control of a bot net. To fully defend against + * the timing attack, rate limiting, banning IPs, and using this function + * should be used together. + * + * Params: + * r1 = A digest representation + * r2 = A digest representation + * Returns: + * `true` if both representations are equal, `false` otherwise + * See_Also: + * $(LINK2 https://en.wikipedia.org/wiki/Timing_attack, The Wikipedia article + * on timing attacks). + */ +bool secureEqual(R1, R2)(R1 r1, R2 r2) +if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && + (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && + !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void)) +{ + static if (hasLength!R1 && hasLength!R2) + if (r1.length != r2.length) + return false; + + int result; + + static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && + hasLength!R1 && hasLength!R2) + { + foreach (i; 0 .. r1.length) + result |= r1[i] ^ r2[i]; + } + else static if (hasLength!R1 && hasLength!R2) + { + // Lengths are the same so we can squeeze out a bit of performance + // by not checking if r2 is empty + for (; !r1.empty; r1.popFront(), r2.popFront()) + { + result |= r1.front ^ r2.front; + } + } + else + { + // Generic case, walk both ranges + for (; !r1.empty; r1.popFront(), r2.popFront()) + { + if (r2.empty) return false; + result |= r1.front ^ r2.front; + } + if (!r2.empty) return false; + } + + return result == 0; +} + +/// +@system pure unittest +{ + import std.digest.hmac : hmac; + import std.digest.sha : SHA1; + import std.string : representation; + + // a typical HMAC data integrity verification + auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; + auto data = "data".representation; + + string hex1 = data.hmac!SHA1(secret).toHexString; + string hex2 = data.hmac!SHA1(secret).toHexString; + string hex3 = "data1".representation.hmac!SHA1(secret).toHexString; + + assert( secureEqual(hex1, hex2)); + assert(!secureEqual(hex1, hex3)); +} + +@system pure unittest +{ + import std.internal.test.dummyrange : ReferenceInputRange; + import std.range : takeExactly; + import std.string : representation; + import std.utf : byWchar, byDchar; + + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".representation; + assert(!secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018"w.representation; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018"d.representation; + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byWchar; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".byWchar; + auto hex2 = "02CA3484C375EDD3C0F08D3F50D119E610779018".byDchar; + assert(!secureEqual(hex1, hex2)); + } + { + auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + assert(secureEqual(hex1, hex2)); + } + { + auto hex1 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 8]).takeExactly(9); + auto hex2 = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6, 7, 9]).takeExactly(9); + assert(!secureEqual(hex1, hex2)); + } +} From d1471d95e0e963d019d6efa87773cd8a99f668a7 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 31 Mar 2017 09:43:37 +0100 Subject: [PATCH 034/262] Use collectExceptionMsg, fix coverage for missing width/precision --- std/format.d | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/std/format.d b/std/format.d index 21f56493efa..eb8cd01aa0d 100644 --- a/std/format.d +++ b/std/format.d @@ -3847,10 +3847,14 @@ private int getNthInt(A...)(uint index, A args) @safe unittest { - assertThrown!FormatException(format("%*.d", 5.1, 2)); // float width - assertThrown!FormatException(format("%.*d", 5.1, 2)); // float precision - assertThrown!FormatException(format("%*.d", 5)); // missing arg - assertThrown!FormatException(format("%*.*d", 5, 4)); // missing arg + assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) + == "width/precision must be an integer, not double"); + assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) + == "width/precision must be an integer, not char"); + assert(collectExceptionMsg!FormatException(format("%.*d", 5)) + == "Orphan format specifier: %d"); + assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) + == "missing integer width/precision argument"); } /* ======================== Unit Tests ====================================== */ From d1e6cf03406f61c08f8a5c462b92bc9dac686238 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 31 Mar 2017 10:16:54 +0100 Subject: [PATCH 035/262] Add tests for incompatible format spec Note: Remaining formatValue overloads may defer to these value types, e.g. pointer -> int, bool -> int, char -> int. --- std/format.d | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/std/format.d b/std/format.d index eb8cd01aa0d..3b2a1a3d8eb 100644 --- a/std/format.d +++ b/std/format.d @@ -1696,6 +1696,8 @@ if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) @safe pure unittest { + assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p'); + assertCTFEable!( { formatTest( null, "null" ); @@ -1882,6 +1884,8 @@ private void formatUnsigned(Writer, T, Char)(Writer w, T arg, const ref FormatSp @safe pure unittest { + assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c'); + assertCTFEable!( { formatTest(9, "9"); @@ -2079,6 +2083,9 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) @safe /*pure*/ unittest // formatting floating point values is now impure { import std.conv : to; + + assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd'); + foreach (T; AliasSeq!(float, double, real)) { formatTest( to!( T)(5.5), "5.5" ); @@ -2788,6 +2795,11 @@ if (isInputRange!T) throw new Exception(text("Incorrect format specifier for range: %", f.spec)); } +@safe pure unittest +{ + assert(collectExceptionMsg(format("%d", "hi")).back == 'd'); +} + // character formatting with ecaping private void formatChar(Writer)(Writer w, in dchar c, in char quote) { @@ -3015,6 +3027,8 @@ if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) @safe unittest { + assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd'); + int[string] aa0; formatTest( aa0, `[]` ); From a7651e327c6ee4fed4a24cce2e3e544c4d418437 Mon Sep 17 00:00:00 2001 From: byebye Date: Mon, 13 Mar 2017 02:48:10 +0100 Subject: [PATCH 036/262] std.container.Array - do not realloc if enough capacity --- std/container/array.d | 66 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/std/container/array.d b/std/container/array.d index 47a00e24bd2..a3b570f074f 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -305,16 +305,24 @@ if (!is(Unqual!T == bool)) _payload = _payload.ptr[0 .. newLength]; return; } - // enlarge immutable startEmplace = length; - import core.checkedint : mulu; - bool overflow; - const nbytes = mulu(newLength, T.sizeof, overflow); - if (overflow) assert(0); - _payload = (cast(T*) realloc(_payload.ptr, - nbytes))[0 .. newLength]; + if (_capacity < newLength) + { + // enlarge + import core.checkedint : mulu; + + bool overflow; + const nbytes = mulu(newLength, T.sizeof, overflow); + if (overflow) + assert(0); + _payload = (cast(T*) realloc(_payload.ptr, nbytes))[0 .. newLength]; + _capacity = newLength; + } + else + { + _payload = _payload.ptr[0 .. newLength]; + } initializeAll(_payload.ptr[startEmplace .. newLength]); - _capacity = newLength; } @property size_t capacity() const @@ -328,7 +336,8 @@ if (!is(Unqual!T == bool)) import core.checkedint : mulu; bool overflow; const sz = mulu(elements, T.sizeof, overflow); - if (overflow) assert(0); + if (overflow) + assert(0); static if (hasIndirections!T) { /* Because of the transactional nature of this @@ -1052,13 +1061,46 @@ if (!is(Unqual!T == bool)) @system unittest { - Array!int a; + struct Dumb { int x = 5; } + Array!Dumb a; a.length = 10; assert(a.length == 10); assert(a.capacity >= a.length); + immutable cap = a.capacity; + foreach (ref e; a) + e.x = 10; a.length = 5; assert(a.length == 5); - assert(a.capacity >= a.length); + // do not realloc if length decreases + assert(a.capacity == cap); + foreach (ref e; a) + assert(e.x == 10); + + a.length = 8; + assert(a.length == 8); + // do not realloc if capacity sufficient + assert(a.capacity == cap); + assert(Dumb.init.x == 5); + foreach (i; 0 .. 5) + assert(a[i].x == 10); + foreach (i; 5 .. a.length) + assert(a[i].x == Dumb.init.x); + + // realloc required, check if values properly copied + a[] = Dumb(1); + a.length = 20; + assert(a.capacity >= 20); + foreach (i; 0 .. 8) + assert(a[i].x == 1); + foreach (i; 8 .. a.length) + assert(a[i].x == Dumb.init.x); + + // check if overlapping elements properly initialized + a.length = 1; + a.length = 20; + assert(a[0].x == 1); + foreach (e; a[1 .. $]) + assert(e.x == Dumb.init.x); } @system unittest @@ -1742,6 +1784,8 @@ if (is(Unqual!T == bool)) assert(a.capacity == 0); a.reserve(15657); assert(a.capacity >= 15657); + a.reserve(100); + assert(a.capacity >= 15657); } /** From 9b4ae1e779a0d086b5e07c76df99f3c097884f5f Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Fri, 31 Mar 2017 20:04:02 -0400 Subject: [PATCH 037/262] fix issue 15763 - Document behavior of relative difference. Also fix issue where symmetry was assumed when lhs was a value and rhs was not. --- std/math.d | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/std/math.d b/std/math.d index 32b8eb66976..aad451c26cc 100644 --- a/std/math.d +++ b/std/math.d @@ -7098,13 +7098,23 @@ private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc /** - Computes whether $(D lhs) is approximately equal to $(D rhs) - admitting a maximum relative difference $(D maxRelDiff) and a - maximum absolute difference $(D maxAbsDiff). - - If the two inputs are ranges, $(D approxEqual) returns true if and - only if the ranges have the same number of elements and if $(D - approxEqual) evaluates to $(D true) for each pair of elements. + Computes whether two values are approximately equal, admitting a maximum + relative difference, and a maximum absolute difference. + + Params: + lhs = First item to compare. + rhs = Second item to compare. + maxRelDiff = Maximum allowable difference relative to `rhs`. + maxAbsDiff = Maximum absolute difference. + + Returns: + `true` if the two items are approximately equal under either criterium. + If one item is a range, and the other is a single value, then the result + is the logical and-ing of calling `approxEqual` on each element of the + ranged item against the single item. If both items are ranges, then + `approxEqual` returns `true` if and only if the ranges have the same + number of elements and if `approxEqual` evaluates to `true` for each + pair of elements. */ bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5) { @@ -7142,8 +7152,13 @@ bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-5) { static if (isInputRange!U) { - // lhs is number, rhs is array - return approxEqual(rhs, lhs, maxRelDiff, maxAbsDiff); + // lhs is number, rhs is range + for (; !rhs.empty; rhs.popFront()) + { + if (!approxEqual(lhs, rhs.front, maxRelDiff, maxAbsDiff)) + return false; + } + return true; } else static if (isIntegral!T || isIntegral!U) { From 344fd38809f70eff8f1952a8fe6d6bb2edce6c50 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Sun, 2 Apr 2017 16:28:43 -0400 Subject: [PATCH 038/262] clear changelog --- changelog/std-algorithm-searching-extremum.dd | 22 ------------------ changelog/std-experimental-checkedint.dd | 21 ----------------- .../std-expertimental-ndslice-removed.dd | 5 ---- changelog/std-format-formattedRead.dd | 18 --------------- changelog/std-format-formattedWrite.dd | 18 --------------- changelog/std-random-MersenneTwisterEngine.dd | 23 ------------------- changelog/std-range-bitwise.dd | 11 --------- changelog/std-stdio-readf-only-pointers.dd | 14 ----------- changelog/std-traits-hasStaticMember.dd | 14 ----------- changelog/std-utf-decodeBack.dd | 12 ---------- changelog/std-variant-genericfunc.dd | 23 ------------------- 11 files changed, 181 deletions(-) delete mode 100644 changelog/std-algorithm-searching-extremum.dd delete mode 100644 changelog/std-experimental-checkedint.dd delete mode 100644 changelog/std-expertimental-ndslice-removed.dd delete mode 100644 changelog/std-format-formattedRead.dd delete mode 100644 changelog/std-format-formattedWrite.dd delete mode 100644 changelog/std-random-MersenneTwisterEngine.dd delete mode 100644 changelog/std-range-bitwise.dd delete mode 100644 changelog/std-stdio-readf-only-pointers.dd delete mode 100644 changelog/std-traits-hasStaticMember.dd delete mode 100644 changelog/std-utf-decodeBack.dd delete mode 100644 changelog/std-variant-genericfunc.dd diff --git a/changelog/std-algorithm-searching-extremum.dd b/changelog/std-algorithm-searching-extremum.dd deleted file mode 100644 index 21027820cf7..00000000000 --- a/changelog/std-algorithm-searching-extremum.dd +++ /dev/null @@ -1,22 +0,0 @@ -Performance improvements for `std.algorithm.searching.{min,max}Element` - -$(REF minElement, std, algorithm, searching) and $(REF maxElement, std, algorithm, searching) -are now considerably faster (almost 2x in microbenchmarks) -as a special path for the identity case is provided. This allows the compiler -to make use of SSE instructions. - -The exact improvements are: - -$(CONSOLE -% dmd -release -O test.d && ./test -extremum.before = 54 secs, 12 ms, 347 μs, and 9 hnsecs -extremum.after = 29 secs, 521 ms, 896 μs, and 5 hnsecs -) - -$(CONSOLE -% ldc -release -O3 test.d && ./test -extremum.before = 13 secs, 186 ms, 176 μs, and 4 hnsecs -extremum.after = 2 secs, 241 ms, 454 μs, and 9 hnsecs -) - -$(LINK2 https://gist.github.com/wilzbach/9f757fa76200956aadb97059d614df34, See the benchmark code). diff --git a/changelog/std-experimental-checkedint.dd b/changelog/std-experimental-checkedint.dd deleted file mode 100644 index 5c8216e3273..00000000000 --- a/changelog/std-experimental-checkedint.dd +++ /dev/null @@ -1,21 +0,0 @@ -New: Checked, a lightweight and highly configurable checked integral - -$(REF Checked, std, experimental, checkedint) is a wrapper around any integral -type that inserts checks against common sources of bugs: overflow in operators, -mixed-sign comparisons, and casts that lose information. - -The example below illustrates the basic use of the facility: - -------- -void main() -{ - import std.experimental.checkedint, std.stdio; - writeln((checked(5) + 7).get); // 12 - writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow -} -------- - -By default, all checks are enabled and the program is aborted if any check -fails. An implementation based on hooks discoverable by using Design by -Introspection allows unbounded customizations of both the checks to do, and the -enforcement policy. diff --git a/changelog/std-expertimental-ndslice-removed.dd b/changelog/std-expertimental-ndslice-removed.dd deleted file mode 100644 index 7567d6ffdfe..00000000000 --- a/changelog/std-expertimental-ndslice-removed.dd +++ /dev/null @@ -1,5 +0,0 @@ -`std.experimental.ndslice` has been removed - -The synchronization between Phobos and Mir turned out to be a high amount of work with litte gain. -Users of `std.experimental.ndslice` are advised to switch to the upstream -$(LINK2 https://github.com/libmir/mir-algorithm, mir package). diff --git a/changelog/std-format-formattedRead.dd b/changelog/std-format-formattedRead.dd deleted file mode 100644 index ee5ee1b7491..00000000000 --- a/changelog/std-format-formattedRead.dd +++ /dev/null @@ -1,18 +0,0 @@ -`std.format.formattedRead` now accepts `ref` parameters as input arguments. - -When $(REF formattedRead, std, format) is used with `ref` parameters it is `@safe`. -For compatibility it's still possible to use $(REF formattedRead, std, format) -with pointers: - -------- -import std.format; -void main() { - string text = "1 2 3"; - int a, b, c; - formattedRead(text, "%d %d %d", a, b, c); - // pointers can still be used - formattedRead(text, "%d %d %d", &a, &b, &c); - // and even combined: - formattedRead(text, "%d %d %d", a, &b, c); -} -------- diff --git a/changelog/std-format-formattedWrite.dd b/changelog/std-format-formattedWrite.dd deleted file mode 100644 index 4e8cac1f6fd..00000000000 --- a/changelog/std-format-formattedWrite.dd +++ /dev/null @@ -1,18 +0,0 @@ -`std.format.formattedWrite` now accepts a compile-time checked format string - -$(REF formattedWrite, std, format) and related functions now have overloads to -take the format string as a compile-time argument. This allows the format -specifiers to be matched against the argument types passed. Any mismatch or -orphaned specifiers/arguments will cause a compile-time error: - -------- -import std.format, std.stdio; - -auto s = format!"%s is %s"("Pi", 3.14); -assert(s == "Pi is 3.14"); -writefln!"%c is %s"('e', 1.61); - -static assert(!__traits(compiles, {s = format!"%l"();})); // missing arg -static assert(!__traits(compiles, {s = format!""(404);})); // surplus arg -static assert(!__traits(compiles, {s = format!"%d"(4.03);})); // incompatible arg -------- diff --git a/changelog/std-random-MersenneTwisterEngine.dd b/changelog/std-random-MersenneTwisterEngine.dd deleted file mode 100644 index bca5f71c6a2..00000000000 --- a/changelog/std-random-MersenneTwisterEngine.dd +++ /dev/null @@ -1,23 +0,0 @@ -`MersenneTwisterEngine` has been updated so that its template signature matches the C++11 standard. - -`MersenneTwisterEngine` has been updated so that its template signature -matches the C++11 standard (adding two new template parameters, an extra -tempering parameter `d` and the initialization multiplier `f`). - -Handling of the word size `w` has been fixed so that the generator will -now properly handle cases where it is less than the number of bits in -the chosen `UIntType`. This has been validated against the behaviour -of a widely-used implementation of the C++11 standard. - -For anyone using the standard template instantiation `Mt19937` this will -have no noticeable effect. However, these will be breaking changes for -anyone using the `MersenneTwisterEngine` template directly. - -The internal implementation has been reworked to use Ilya Yaroshenko's -highly optimized algorithm from `mir.random`. This should have a very -noticeable positive effect for anyone who cares about generating a lot -of random numbers quickly. - -A new `Mt19937_64` template instantiation has been added, corresponding -to the standard 64-bit implementation of the algorithm (MT19937-64). -This fixes $(LINK https://issues.dlang.org/show_bug.cgi?id=10900). diff --git a/changelog/std-range-bitwise.dd b/changelog/std-range-bitwise.dd deleted file mode 100644 index 66e06f2ba0b..00000000000 --- a/changelog/std-range-bitwise.dd +++ /dev/null @@ -1,11 +0,0 @@ -Added `std.range.bitwise` to create a bitwise adapter over an integral type range, consuming the range elements bit by bit. - -`std.range.bitwise` creates a bit by bit adapter over an integral type range: -------- -import std.range : bitwise; -ubyte[] arr = [3, 9]; -auto r = arr.bitwise; - -r[2] = 1; -assert(arr[0] == 7); -------- diff --git a/changelog/std-stdio-readf-only-pointers.dd b/changelog/std-stdio-readf-only-pointers.dd deleted file mode 100644 index 2c28ad3c40c..00000000000 --- a/changelog/std-stdio-readf-only-pointers.dd +++ /dev/null @@ -1,14 +0,0 @@ -`std.stdio.readf` now accepts `ref` parameters as input arguments. - -------- -import std.stdio : readf; -void main() { - // assume every line from stdin is similar to "1 2 3"; - int a, b, c; - readf(" %d %d %d", a, b, c); - // pointers can still be used - readf(" %d %d %d, &a, &b, &c); - // and even combined: - readf(" %d %d %d, a, &b, c); -} -------- diff --git a/changelog/std-traits-hasStaticMember.dd b/changelog/std-traits-hasStaticMember.dd deleted file mode 100644 index 83e33810b6a..00000000000 --- a/changelog/std-traits-hasStaticMember.dd +++ /dev/null @@ -1,14 +0,0 @@ -Added `std.traits.hasStaticMember` to check whether a symbol is a static member of a type. - -------- -import std.traits : hasStaticMember; - -struct S -{ - static int staticVar; - int nonstaticVar; -} - -assert( hasStaticMember!(S, "staticVar")); -assert(!hasStaticMember!(S, "nonstaticVar")); -------- diff --git a/changelog/std-utf-decodeBack.dd b/changelog/std-utf-decodeBack.dd deleted file mode 100644 index 46872805a3a..00000000000 --- a/changelog/std-utf-decodeBack.dd +++ /dev/null @@ -1,12 +0,0 @@ -Added `std.utf.decodeBack` which decodes the last UTF code point of given character range. - -------- - import std.utf : decodeBack; - - string text = "サイト"; - - assert(decodeBack(text) == 'ト'); - assert(decodeBack(text) == 'イ'); - assert(decodeBack(text) == 'サ'); - assert(text.empty); -------- diff --git a/changelog/std-variant-genericfunc.dd b/changelog/std-variant-genericfunc.dd deleted file mode 100644 index 60ad1fdf78e..00000000000 --- a/changelog/std-variant-genericfunc.dd +++ /dev/null @@ -1,23 +0,0 @@ -Allow a generic function for std.variant.visit. - -If a lambda with a single generic parameter is provided as a handler -to std.variant.visit, it is invoked for any type contained in the -Algebraic which does not have a handler for that type. - -This allows something like: - -------- -// Assume Circle, Square, and Triangle all define center() -Algebraic!(Circle, Square, Triangle) someShape; -auto center = someShape.visit!(x => x.center); -------- - -This may be combined with explicitly typed handlers and a single -fallback handler for an empty variant: - -------- -Algebraic!(int, float, string) something; -something.visit!((string s) => s.length, // called for string - x => x, // called for int/float - () => 0); // called if empty -------- From 2ff0c3113f75b3b9b949ea297d683aa2c3c06c2b Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Sun, 2 Apr 2017 16:41:31 -0400 Subject: [PATCH 039/262] Added changelog entry for secureCompare --- changelog/std-digest-digest-secureCompare.dd | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 changelog/std-digest-digest-secureCompare.dd diff --git a/changelog/std-digest-digest-secureCompare.dd b/changelog/std-digest-digest-secureCompare.dd new file mode 100644 index 00000000000..03d4026f3c6 --- /dev/null +++ b/changelog/std-digest-digest-secureCompare.dd @@ -0,0 +1,29 @@ +Added a constant time comparison function for cryptographic hashes + +Added a new function to $(REF secureEqual, std, digest, digest) that compares +two ranges that represent hashes in a secure manner. The comparison is done in +constant time regardless of the equality of the two ranges in order to protect +against timing attacks. For more information on the attack, please refer to +the docs on $(REF secureEqual, std, digest, digest). + +``` +import std.digest.digest : secureEqual, toHexString; +import std.digest.hmac : hmac; +import std.digest.sha : SHA1; +import std.string : representation; + +void main() +{ + // a typical HMAC data integrity verification + auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation; + auto data = "data".representation; + + string hex1 = data.hmac!SHA1(secret).toHexString; + string hex2 = data.hmac!SHA1(secret).toHexString; + + string hex3 = "data1".representation.hmac!SHA1(secret).toHexString; + + assert( secureEqual(hex1, hex2)); + assert(!secureEqual(hex1, hex3)); +} +``` \ No newline at end of file From df400d9dff930a804998ba5ea8536ea312b8ca76 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Mon, 3 Apr 2017 12:55:49 +0300 Subject: [PATCH 040/262] Fix Issue 17283 - std.experimental.typecons uses private module members --- std/typecons.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/typecons.d b/std/typecons.d index a0c05ef8949..d43da0331b7 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -4813,7 +4813,7 @@ if (!isMutable!Target) } // Make a tuple of non-static function symbols -private template GetOverloadedMethods(T) +package template GetOverloadedMethods(T) { import std.meta : Filter; @@ -4955,7 +4955,7 @@ version(unittest) static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max); } -private template DerivedFunctionType(T...) +package template DerivedFunctionType(T...) { static if (!T.length) { @@ -5080,7 +5080,7 @@ package template staticIota(int beg, int end) } } -private template mixinAll(mixins...) +package template mixinAll(mixins...) { static if (mixins.length == 1) { @@ -5101,7 +5101,7 @@ private template mixinAll(mixins...) } } -private template Bind(alias Template, args1...) +package template Bind(alias Template, args1...) { alias Bind(args2...) = Template!(args1, args2); } From 3f0daf00b6e47c33b58a45375561d7db523e62a2 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Fri, 31 Mar 2017 19:34:04 -0400 Subject: [PATCH 041/262] Fix issue 15534 - Rework constructor docs, removing reference to string parameter that doesn't exist, and using more appropriate ddoc style. --- std/experimental/logger/core.d | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 62b3b184ca7..ab63a7e793a 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -744,12 +744,14 @@ abstract class Logger Logger logger; } - /** This constructor takes a name of type $(D string), and a $(D LogLevel). + /** + Every subclass of `Logger` has to call this constructor from their + constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal + handler will throw an `Error` if a log call is made with level + `LogLevel.fatal`. - Every subclass of $(D Logger) has to call this constructor from their - constructor. It sets the $(D LogLevel), the name of the $(D Logger), and - creates a fatal handler. The fatal handler will throw an $(D Error) if a - log call is made with a $(D LogLevel) $(D LogLevel.fatal). + Params: + lv = `LogLevel` to use for this `Logger` instance. */ this(LogLevel lv) @safe { From 69b3298090011e12e7a87a16d549f50d8223f413 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Mon, 3 Apr 2017 08:53:50 -0400 Subject: [PATCH 042/262] Add unit test --- std/math.d | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/std/math.d b/std/math.d index aad451c26cc..d26638e7c61 100644 --- a/std/math.d +++ b/std/math.d @@ -7300,6 +7300,18 @@ deprecated("Phobos1 math functions are deprecated, use isInfinity ") alias isinf auto y = ceil(1.2); } +@safe pure nothrow unittest +{ + // relative comparison depends on rhs, make sure proper side is used when + // comparing range to single value. Based on bugzilla issue 15763 + auto a = [2e-3 - 1e-5]; + auto b = 2e-3 + 1e-5; + assert(a[0].approxEqual(b)); + assert(!b.approxEqual(a[0])); + assert(a.approxEqual(b)); + assert(!b.approxEqual(a)); +} + /*********************************** * Defines a total order on all floating-point numbers. * From 7c0ebad377fd96687866aa6a69530d2fe9affcb1 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 3 Apr 2017 20:32:38 +0300 Subject: [PATCH 043/262] Make allocators' resolveInternalPointer take a const pointer as source --- .../allocator/building_blocks/affix_allocator.d | 2 +- .../allocator/building_blocks/bitmapped_block.d | 2 +- std/experimental/allocator/building_blocks/bucketizer.d | 2 +- .../allocator/building_blocks/fallback_allocator.d | 2 +- std/experimental/allocator/building_blocks/null_allocator.d | 2 +- std/experimental/allocator/building_blocks/segregator.d | 2 +- std/experimental/allocator/gc_allocator.d | 5 +++-- std/experimental/allocator/package.d | 6 +++--- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index ca03b682712..1d27796bd0b 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -175,7 +175,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) } static if (hasMember!(Allocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { void[] p1; Ternary r = parent.resolveInternalPointer(p, p1); diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index b05b1fe1d87..b0aa3b2b925 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -996,7 +996,7 @@ struct BitmappedBlockWithInternalPointers( } /// Ditto - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { if (p < _heap._payload.ptr || p >= _heap._payload.ptr + _heap._payload.length) diff --git a/std/experimental/allocator/building_blocks/bucketizer.d b/std/experimental/allocator/building_blocks/bucketizer.d index 5aa1e717aa6..e63c9274c59 100644 --- a/std/experimental/allocator/building_blocks/bucketizer.d +++ b/std/experimental/allocator/building_blocks/bucketizer.d @@ -206,7 +206,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) resolveInternalPointer), and tries it for each bucket in turn. */ static if (hasMember!(Allocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { foreach (ref a; buckets) { diff --git a/std/experimental/allocator/building_blocks/fallback_allocator.d b/std/experimental/allocator/building_blocks/fallback_allocator.d index 59e9b83e198..f3f099dfaf0 100644 --- a/std/experimental/allocator/building_blocks/fallback_allocator.d +++ b/std/experimental/allocator/building_blocks/fallback_allocator.d @@ -207,7 +207,7 @@ struct FallbackAllocator(Primary, Fallback) */ static if (hasMember!(Primary, "resolveInternalPointer") && hasMember!(Fallback, "resolveInternalPointer")) - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { Ternary r = primary.resolveInternalPointer(p, result); return r == Ternary.no ? fallback.resolveInternalPointer(p, result) : r; diff --git a/std/experimental/allocator/building_blocks/null_allocator.d b/std/experimental/allocator/building_blocks/null_allocator.d index cea4e1998d6..68bab708a87 100644 --- a/std/experimental/allocator/building_blocks/null_allocator.d +++ b/std/experimental/allocator/building_blocks/null_allocator.d @@ -43,7 +43,7 @@ struct NullAllocator /** Returns $(D Ternary.no). */ - Ternary resolveInternalPointer(void*, ref void[]) shared const + Ternary resolveInternalPointer(const void*, ref void[]) shared const { return Ternary.no; } /** No-op. diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d index 4e9f563ffad..2d8ecffb2c8 100644 --- a/std/experimental/allocator/building_blocks/segregator.d +++ b/std/experimental/allocator/building_blocks/segregator.d @@ -247,7 +247,7 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator) static if (hasMember!(SmallAllocator, "resolveInternalPointer") && hasMember!(LargeAllocator, "resolveInternalPointer")) - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { Ternary r = _small.resolveInternalPointer(p, result); return r == Ternary.no ? _large.resolveInternalPointer(p, result) : r; diff --git a/std/experimental/allocator/gc_allocator.d b/std/experimental/allocator/gc_allocator.d index 842d6a86943..f3673e5549f 100644 --- a/std/experimental/allocator/gc_allocator.d +++ b/std/experimental/allocator/gc_allocator.d @@ -70,9 +70,10 @@ struct GCAllocator } /// Ditto - pure nothrow Ternary resolveInternalPointer(void* p, ref void[] result) shared + pure nothrow + Ternary resolveInternalPointer(const void* p, ref void[] result) shared { - auto r = GC.addrOf(p); + auto r = GC.addrOf(cast(void*)p); if (!r) return Ternary.no; result = r[0 .. GC.sizeOf(r)]; return Ternary.yes; diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 4cb0eed234d..fbeaf76b4fa 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -339,7 +339,7 @@ interface IAllocator Resolves an internal pointer to the full block allocated. Implementations that don't support this primitive should always return `Ternary.unknown`. */ - Ternary resolveInternalPointer(void* p, ref void[] result); + Ternary resolveInternalPointer(const void* p, ref void[] result); /** Deallocates a memory block. Implementations that don't support this @@ -1953,7 +1953,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } // Undocumented for now - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { static if (hasMember!(Allocator, "resolveInternalPointer")) { @@ -2412,7 +2412,7 @@ private struct InternalPointersTree(Allocator) /** Returns the block inside which $(D p) resides, or $(D null) if the pointer does not belong. */ - Ternary resolveInternalPointer(void* p, ref void[] result) + Ternary resolveInternalPointer(const void* p, ref void[] result) { // Must define a custom find Tree.Node* find() From 04e844da77ca41c0ee9e2f0130cd31dbdc767ca2 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 4 Apr 2017 16:46:38 +0300 Subject: [PATCH 044/262] Allocators should take a ubyte[] memory block instead of a void[] one Allocators that rely on taking a block of unused memory to manage and allocate from it should take a ubyte[] instead of a void[] in order to prevent users from accidentally passing valid data and cause memory corruption/leaks. --- .../building_blocks/affix_allocator.d | 2 +- .../building_blocks/allocator_list.d | 10 ++++----- .../building_blocks/bitmapped_block.d | 21 ++++++++++--------- .../allocator/building_blocks/free_list.d | 20 +++++++++--------- .../building_blocks/kernighan_ritchie.d | 10 +++++---- .../allocator/building_blocks/region.d | 10 ++++----- std/experimental/allocator/package.d | 5 +++-- 7 files changed, 41 insertions(+), 37 deletions(-) diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index 1d27796bd0b..761840dc54a 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -397,7 +397,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) import std.experimental.allocator.common : testAllocator; testAllocator!({ auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) - (BitmappedBlock!128(new void[128 * 4096])); + (BitmappedBlock!128(new ubyte[128 * 4096])); return a; }); } diff --git a/std/experimental/allocator/building_blocks/allocator_list.d b/std/experimental/allocator/building_blocks/allocator_list.d index 5dc39fd0e44..bd212fbb4f2 100644 --- a/std/experimental/allocator/building_blocks/allocator_list.d +++ b/std/experimental/allocator/building_blocks/allocator_list.d @@ -555,7 +555,7 @@ version(Posix) @system unittest // Ouroboros allocator list based upon 4MB regions, fetched from the garbage // collector. Memory is left to the collector. alias A3 = AllocatorList!( - (n) => Region!NullAllocator(new void[max(n, 1024 * 4096)]), + (n) => Region!NullAllocator(new ubyte[max(n, 1024 * 4096)]), NullAllocator); // Allocator list that creates one freelist for all objects @@ -563,7 +563,7 @@ version(Posix) @system unittest Segregator!( 64, AllocatorList!( (n) => ContiguousFreeList!(NullAllocator, 0, 64)( - GCAllocator.instance.allocate(4096))), + cast(ubyte[])(GCAllocator.instance.allocate(4096)))), GCAllocator); A4 a; @@ -581,7 +581,7 @@ version(Posix) @system unittest // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.region : Region; - AllocatorList!((n) => Region!GCAllocator(new void[max(n, 1024 * 4096)]), + AllocatorList!((n) => Region!GCAllocator(new ubyte[max(n, 1024 * 4096)]), NullAllocator) a; const b1 = a.allocate(1024 * 8192); assert(b1 !is null); // still works due to overdimensioning @@ -595,7 +595,7 @@ version(Posix) @system unittest // Create an allocator based upon 4MB regions, fetched from the GC heap. import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.region : Region; - AllocatorList!((n) => Region!()(new void[max(n, 1024 * 4096)])) a; + AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a; auto b1 = a.allocate(1024 * 8192); assert(b1 !is null); // still works due to overdimensioning b1 = a.allocate(1024 * 10); @@ -608,7 +608,7 @@ version(Posix) @system unittest import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.region : Region; import std.typecons : Ternary; - AllocatorList!((n) => Region!()(new void[max(n, 1024 * 4096)])) a; + AllocatorList!((n) => Region!()(new ubyte[max(n, 1024 * 4096)])) a; auto b1 = a.allocate(1024 * 8192); assert(b1 !is null); b1 = a.allocate(1024 * 10); diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index b0aa3b2b925..a0ded14ab68 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -54,8 +54,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { import std.experimental.allocator.mallocator : AlignedMallocator; import std.algorithm.comparison : max; - auto m = AlignedMallocator.instance.alignedAllocate(1024 * 64, - max(theAlignment, cast(uint) size_t.sizeof)); + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + max(theAlignment, cast(uint) size_t.sizeof))); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlock(m)); } @@ -153,7 +153,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment ParentAllocator.deallocate).) ) */ - this(void[] data) + this(ubyte[] data) { immutable a = data.ptr.effectiveAlignment; assert(a >= size_t.alignof || !data.ptr, @@ -189,7 +189,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment this(size_t capacity) { size_t toAllocate = totalAllocation(capacity); - auto data = parent.allocate(toAllocate); + auto data = cast(ubyte[])(parent.allocate(toAllocate)); this(data); assert(_blocks * blockSize >= capacity); } @@ -696,7 +696,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment import std.experimental.allocator.building_blocks.region : InSituRegion; import std.traits : hasMember; InSituRegion!(10_240, 64) r; - auto a = BitmappedBlock!(64, 64)(r.allocateAll()); + auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll())); static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); const b = a.allocate(100); assert(b.length == 100); @@ -716,7 +716,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment assert(bs); import std.experimental.allocator.gc_allocator : GCAllocator; auto a = BitmappedBlock!(bs, min(bs, platformAlignment))( - GCAllocator.instance.allocate((blocks * bs * 8 + blocks) / 8) + cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + + blocks) / 8)) ); import std.conv : text; assert(blocks >= a._blocks, text(blocks, " < ", a._blocks)); @@ -863,8 +864,8 @@ struct BitmappedBlockWithInternalPointers( @system unittest { import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = AlignedMallocator.instance.alignedAllocate(1024 * 64, - theAlignment); + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + theAlignment)); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlockWithInternalPointers(m)); } @@ -878,7 +879,7 @@ struct BitmappedBlockWithInternalPointers( Constructors accepting desired capacity or a preallocated buffer, similar in semantics to those of $(D BitmappedBlock). */ - this(void[] data) + this(ubyte[] data) { _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); } @@ -1064,7 +1065,7 @@ struct BitmappedBlockWithInternalPointers( { import std.typecons : Ternary; - auto h = BitmappedBlockWithInternalPointers!(4096)(new void[4096 * 1024]); + auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); auto b = h.allocate(123); assert(b.length == 123); diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index a11d9b808d1..cf2354e97d5 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -453,7 +453,7 @@ struct ContiguousFreeList(ParentAllocator, /// Alignment offered. enum uint alignment = (void*).alignof; - private void initialize(void[] buffer, size_t itemSize = fl.max) + private void initialize(ubyte[] buffer, size_t itemSize = fl.max) { assert(itemSize != unbounded && itemSize != chooseAtRuntime); assert(buffer.ptr.alignedAt(alignment)); @@ -497,14 +497,14 @@ struct ContiguousFreeList(ParentAllocator, initialized with $(D max). */ static if (!stateSize!ParentAllocator) - this(void[] buffer) + this(ubyte[] buffer) { initialize(buffer); } /// ditto static if (stateSize!ParentAllocator) - this(ParentAllocator parent, void[] buffer) + this(ParentAllocator parent, ubyte[] buffer) { initialize(buffer); this.parent = SParent(parent); @@ -514,14 +514,14 @@ struct ContiguousFreeList(ParentAllocator, static if (!stateSize!ParentAllocator) this(size_t bytes) { - initialize(ParentAllocator.instance.allocate(bytes)); + initialize(cast(ubyte[])(ParentAllocator.instance.allocate(bytes))); } /// ditto static if (stateSize!ParentAllocator) this(ParentAllocator parent, size_t bytes) { - initialize(parent.allocate(bytes)); + initialize(cast(ubyte[])(parent.allocate(bytes))); this.parent = SParent(parent); } @@ -532,7 +532,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - initialize(parent.allocate(bytes), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); } /// ditto @@ -542,7 +542,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - initialize(parent.allocate(bytes), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); this.parent = SParent(parent); } @@ -554,7 +554,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - initialize(parent.allocate(bytes), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } @@ -567,7 +567,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - initialize(parent.allocate(bytes), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } @@ -690,7 +690,7 @@ struct ContiguousFreeList(ParentAllocator, : NullAllocator; import std.typecons : Ternary; alias A = ContiguousFreeList!(NullAllocator, 0, 64); - auto a = A(new void[1024]); + auto a = A(new ubyte[1024]); assert(a.empty == Ternary.yes); diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 35dd0f77382..849892e8265 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -311,7 +311,7 @@ struct KRRegion(ParentAllocator = NullAllocator) n = Capacity desired. This constructor is defined only if $(D ParentAllocator) is not $(D NullAllocator). */ - this(void[] b) + this(ubyte[] b) { if (b.length < Node.sizeof) { @@ -338,7 +338,7 @@ struct KRRegion(ParentAllocator = NullAllocator) this(size_t n) { assert(n > Node.sizeof); - this(parent.allocate(n)); + this(cast(ubyte[])(parent.allocate(n))); } /// Ditto @@ -751,7 +751,8 @@ it actually returns memory to the operating system when possible. { import std.experimental.allocator.gc_allocator : GCAllocator; import std.typecons : Ternary; - auto alloc = KRRegion!()(GCAllocator.instance.allocate(1024 * 1024)); + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); const store = alloc.allocate(KRRegion!().sizeof); auto p = cast(KRRegion!()* ) store.ptr; import std.conv : emplace; @@ -783,7 +784,8 @@ it actually returns memory to the operating system when possible. @system unittest { import std.experimental.allocator.gc_allocator : GCAllocator; - auto alloc = KRRegion!()(GCAllocator.instance.allocate(1024 * 1024)); + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); auto p = alloc.allocateAll(); assert(p.length == 1024 * 1024); alloc.deallocateAll(); diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index c60c49bd5a8..3809b9dd7e4 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -67,9 +67,9 @@ struct Region(ParentAllocator = NullAllocator, $(D parent.allocate(n)) returns $(D null), the region will be initialized as empty (correctly initialized but unable to allocate). */ - this(void[] store) + this(ubyte[] store) { - store = store.roundUpToAlignment(alignment); + store = cast(ubyte[])(store.roundUpToAlignment(alignment)); store = store[0 .. $.roundDownToAlignment(alignment)]; assert(store.ptr.alignedAt(minAlign)); assert(store.length % minAlign == 0); @@ -85,7 +85,7 @@ struct Region(ParentAllocator = NullAllocator, static if (!is(ParentAllocator == NullAllocator)) this(size_t n) { - this(parent.allocate(n.roundUpToAlignment(alignment))); + this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment)))); } /* @@ -550,14 +550,14 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) // Reap with GC fallback. InSituRegion!(128 * 1024, 8) tmp3; FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3; - r3.primary = BitmappedBlock!(64, 8)(tmp3.allocateAll()); + r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[])(tmp3.allocateAll())); const a3 = r3.allocate(103); assert(a3.length == 103); // Reap/GC with a freelist for small objects up to 16 bytes. InSituRegion!(128 * 1024, 64) tmp4; FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4; - r4.parent.primary = BitmappedBlock!(64, 64)(tmp4.allocateAll()); + r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[])(tmp4.allocateAll())); const a4 = r4.allocate(104); assert(a4.length == 104); } diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index fbeaf76b4fa..8dd697d3a08 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -249,8 +249,9 @@ public import std.experimental.allocator.common, 2048, Bucketizer!(FList, 1025, 2048, 256), 3584, Bucketizer!(FList, 2049, 3584, 512), 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)(GCAllocator.instance.allocate( - max(n, 4072 * 1024)))), + (n) => BitmappedBlock!(4096)( + cast(ubyte[])(GCAllocator.instance.allocate( + max(n, 4072 * 1024))))), GCAllocator ); A tuMalloc; From b19f46a7d1b7e4d0ba4c97f9b0d09e7b6adc0c70 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 5 Apr 2017 10:55:22 -0400 Subject: [PATCH 045/262] Fix style problems in std.experimental.allocator.gc_allocator --- std/experimental/allocator/gc_allocator.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/experimental/allocator/gc_allocator.d b/std/experimental/allocator/gc_allocator.d index f3673e5549f..41894568f03 100644 --- a/std/experimental/allocator/gc_allocator.d +++ b/std/experimental/allocator/gc_allocator.d @@ -73,7 +73,7 @@ struct GCAllocator pure nothrow Ternary resolveInternalPointer(const void* p, ref void[] result) shared { - auto r = GC.addrOf(cast(void*)p); + auto r = GC.addrOf(cast(void*) p); if (!r) return Ternary.no; result = r[0 .. GC.sizeOf(r)]; return Ternary.yes; From 9e4aa3249d5ba35879432a78258bb9ee09115c82 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Thu, 6 Apr 2017 10:36:41 -0700 Subject: [PATCH 046/262] Remove now-redundant import of onRangeError. --- std/format.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/format.d b/std/format.d index f9aeeb76502..4c57b084808 100644 --- a/std/format.d +++ b/std/format.d @@ -6534,7 +6534,7 @@ immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar */ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { - import core.exception : onRangeError, RangeError; + import core.exception : RangeError; import std.utf : encode; import std.format : formattedWrite, FormatException; From 7cdfeee0a925f4b2985f9257e4d5141d89e92b64 Mon Sep 17 00:00:00 2001 From: "Q. F. Schroll" Date: Fri, 7 Apr 2017 19:59:10 +0200 Subject: [PATCH 047/262] Changed unit test --- std/experimental/typecons.d | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 2795cbf649c..76a091b3624 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -1044,10 +1044,9 @@ pure nothrow @nogc @system unittest pure nothrow @system unittest { Final!(int[]) arr; - static assert(!__traits(compiles, - arr.length++ - )); - arr.length = 10; + // static assert(!__traits(compiles, + // arr.length = 10; // bug! + // )); static assert(!__traits(compiles, arr.ptr = null )); From 2c969e0478c504f442b25f7ba4c842d2e948bc24 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Sun, 9 Apr 2017 10:37:34 +0200 Subject: [PATCH 048/262] update pipeline ci repo url - finally moved repo under dlang --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index b6ae0190e50..e0f3e3288ff 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ def clone (repo_url, git_ref = "master") { def pipeline node { dir('dlang/ci') { - clone 'https://github.com/Dicebot/dlangci.git', 'master' + clone 'https://github.com/dlang/ci.git', 'master' } pipeline = load 'dlang/ci/pipeline.groovy' } From 0912b243026e733db3b18c2c650d1809e5cb52dc Mon Sep 17 00:00:00 2001 From: Michael Coulombe Date: Sun, 9 Apr 2017 09:22:37 -0400 Subject: [PATCH 049/262] fix issue 17314 --- std/container/binaryheap.d | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 3b4ff097647..a5839f7acc5 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -282,7 +282,7 @@ and $(D length == capacity), throws an exception. import std.traits : isDynamicArray; static if (isDynamicArray!Store) { - if (_store.length == 0) + if (_store.length < 5) _store.length = 8; else if (length == _store.length) _store.length = length * 3 / 2; @@ -586,3 +586,12 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, assert(equal(heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1])); assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10])); } + +@system unittest // Issue 17314 +{ + import std.algorithm.comparison : equal; + int[] a = [5]; + auto heap = heapify(a); + heap.insert(6); + assert(equal(heap, [6, 5])); +} From ef9f4b9fee9277dfb0ad049a1806579eeaaa2042 Mon Sep 17 00:00:00 2001 From: Michael Coulombe Date: Sun, 9 Apr 2017 11:55:26 -0400 Subject: [PATCH 050/262] fix issue 17314 --- std/container/binaryheap.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index a5839f7acc5..2ff6a416132 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -282,7 +282,7 @@ and $(D length == capacity), throws an exception. import std.traits : isDynamicArray; static if (isDynamicArray!Store) { - if (_store.length < 5) + if (_store.length < 6) _store.length = 8; else if (length == _store.length) _store.length = length * 3 / 2; From 2245025f6b7692dd6db166011b75618054e59b53 Mon Sep 17 00:00:00 2001 From: kirsybuu Date: Tue, 11 Apr 2017 11:31:55 -0400 Subject: [PATCH 051/262] Only grow if full --- std/container/binaryheap.d | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 2ff6a416132..f295bae8b06 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -282,10 +282,8 @@ and $(D length == capacity), throws an exception. import std.traits : isDynamicArray; static if (isDynamicArray!Store) { - if (_store.length < 6) - _store.length = 8; - else if (length == _store.length) - _store.length = length * 3 / 2; + if(length == _store.length) + _store.length = (length < 6 ? 8 : length * 3 / 2); _store[_length] = value; } else From cded892ebadcd6547cd36c755fe870c1e5edcaa0 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Tue, 11 Apr 2017 15:39:52 -0400 Subject: [PATCH 052/262] Fix style --- std/container/binaryheap.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index f295bae8b06..4ce3953ab0d 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -282,7 +282,7 @@ and $(D length == capacity), throws an exception. import std.traits : isDynamicArray; static if (isDynamicArray!Store) { - if(length == _store.length) + if (length == _store.length) _store.length = (length < 6 ? 8 : length * 3 / 2); _store[_length] = value; } From 91f0544227b6cde4e0beb7eaa7d915975416912b Mon Sep 17 00:00:00 2001 From: Ilya Yaroshenko Date: Fri, 14 Apr 2017 09:18:21 +0700 Subject: [PATCH 053/262] fix link The link is broken because the module name. Hope this will fix it. --- std/random.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/random.d b/std/random.d index c8fcb3801f5..335d00f7b66 100644 --- a/std/random.d +++ b/std/random.d @@ -52,7 +52,7 @@ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) Masahiro Nakagawa (Xorshift random generator) $(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling) - Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-random, mir.random)) + Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-random, Mir Random)) Credits: The entire random number library architecture is derived from the excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) random number facility proposed by Jens Maurer and contributed to by From 67e3438c4789a124dd2942a1661b0b08ab3152c6 Mon Sep 17 00:00:00 2001 From: Ilya Yaroshenko Date: Fri, 14 Apr 2017 10:16:05 +0700 Subject: [PATCH 054/262] Update random.d --- std/random.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/random.d b/std/random.d index 335d00f7b66..5148ac86f5f 100644 --- a/std/random.d +++ b/std/random.d @@ -52,7 +52,7 @@ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) Masahiro Nakagawa (Xorshift random generator) $(HTTP braingam.es, Joseph Rushton Wakeling) (Algorithm D for random sampling) - Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-random, Mir Random)) + Ilya Yaroshenko (Mersenne Twister implementation, adapted from $(HTTPS github.com/libmir/mir-_random, mir-_random)) Credits: The entire random number library architecture is derived from the excellent $(HTTP open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf, C++0X) random number facility proposed by Jens Maurer and contributed to by From c1d49fc4948977239fa4ed8c15b3d318b3e60ca9 Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Sun, 16 Apr 2017 00:21:49 -0700 Subject: [PATCH 055/262] Fix issue 17327 - std.getopt: Repeated boolean command option fails. --- std/getopt.d | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index 7b40e39db05..886b098379f 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -859,12 +859,12 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, if (val.length) { *receiver = to!(typeof(*receiver))(val); - break; + continue; } // no argument means set it to true *receiver = true; - break; + continue; } else { @@ -1720,3 +1720,56 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) assertThrown!AssertError(getopt(args, "abc|a", &abc, "def|a", &def)); assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def)); } + +@system unittest // Issue 17327 repeated option use +{ + long num = 0; + + string[] args = ["program", "--num", "3"]; + getopt(args, "n|num", &num); + assert(num == 3); + + args = ["program", "--num", "3", "--num", "5"]; + getopt(args, "n|num", &num); + assert(num == 5); + + args = ["program", "--n", "3", "--num", "5", "-n", "-7"]; + getopt(args, "n|num", &num); + assert(num == -7); + + void add1() { num++; } + void add2(string option) { num += 2; } + void addN(string option, string value) + { + import std.conv : to; + num += value.to!long; + } + + num = 0; + args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"]; + getopt(args, + "add1", "Add 1 to num", &add1, + "add2", "Add 2 to num", &add2, + "add", "Add N to num", &addN,); + assert(num == 21); + + bool flag = false; + args = ["program", "--flag"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); + + flag = false; + args = ["program", "-f", "-f"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); + + flag = false; + args = ["program", "--flag=true", "--flag=false"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(!flag); + + flag = false; + args = ["program", "--flag=true", "--flag=false", "-f"]; + getopt(args, "f|flag", "Boolean", &flag); + assert(flag); +} From 684f41b64ee96a35bd9fba3aca95e5cdbf99d09c Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Sun, 16 Apr 2017 08:01:49 -0700 Subject: [PATCH 056/262] Fix issue 17327. Review comments: drop continue statement. --- std/getopt.d | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index 886b098379f..7d690b10e76 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -855,16 +855,16 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, static if (is(typeof(*receiver) == bool)) { - // parse '--b=true/false' if (val.length) { + // parse '--b=true/false' *receiver = to!(typeof(*receiver))(val); - continue; } - - // no argument means set it to true - *receiver = true; - continue; + else + { + // no argument means set it to true + *receiver = true; + } } else { From 7a9008a725662c6047d52e85bf3ea2136bd5702f Mon Sep 17 00:00:00 2001 From: Nikolay Tolstokulakov Date: Mon, 17 Apr 2017 08:52:11 +0000 Subject: [PATCH 057/262] netbsd small changes: fix datetime.d --- std/datetime.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/datetime.d b/std/datetime.d index 140b8bb4ccc..4401cabeee4 100644 --- a/std/datetime.d +++ b/std/datetime.d @@ -462,7 +462,7 @@ public: return unixTimeToStdTime(core.stdc.time.time(null)); else { - import core.sys.posix.sys.time : timeval; + import core.sys.posix.sys.time : gettimeofday, timeval; timeval tv; if (gettimeofday(&tv, null) != 0) throw new TimeException("Call to gettimeofday() failed"); @@ -28336,10 +28336,10 @@ public: { setTZEnvVar("America/Los_Angeles"); assert(LocalTime().dstName == "PDT"); - } - setTZEnvVar("America/New_York"); - assert(LocalTime().dstName == "EDT"); + setTZEnvVar("America/New_York"); + assert(LocalTime().dstName == "EDT"); + } } } From 7ddc85db37535449f826815d6083e1df9d4c3cd7 Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Wed, 5 Apr 2017 15:02:14 +0200 Subject: [PATCH 058/262] format with comma formatspec to add separator into numbers --- std/format.d | 347 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 334 insertions(+), 13 deletions(-) diff --git a/std/format.d b/std/format.d index 4c1101ac44c..273d5c57f78 100644 --- a/std/format.d +++ b/std/format.d @@ -140,7 +140,7 @@ $(I FormatString): $(I FormatStringItem)* $(I FormatStringItem): $(B '%%') - $(B '%') $(I Position) $(I Flags) $(I Width) $(I Precision) $(I FormatChar) + $(B '%') $(I Position) $(I Flags) $(I Width) $(I Separator) $(I Precision) $(I FormatChar) $(B '%$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)') $(I OtherCharacterExceptPercent) $(I Position): @@ -157,6 +157,14 @@ $(I Width): $(I empty) $(I Integer) $(B '*') +$(I Separator): + $(I empty) + $(B ',') + $(B ',') $(B '?') + $(B ',') $(B '*') $(B '?') + $(B ',') $(I Integer) $(B '?') + $(B ',') $(B '*') + $(B ',') $(I Integer) $(I Precision): $(I empty) $(B '.') @@ -215,6 +223,18 @@ $(I FormatChar): preceding the actual argument, is taken as the precision. If it is negative, it is as if there was no $(I Precision) specifier.) + $(DT $(I Separator)) + $(DD Inserts the separator symbols ',' every $(I X) digits, from right + to left, into numeric values to increase readability. + The fractional part of floating point values inserts the separator + from left to right. + Entering an integer after the ',' allows to specify $(I X). + If a '*' is placed after the ',' then $(I X) is specified by an + additional parameter to the format function. + Adding a '?' after the ',' or $(I X) specifier allows to specify + the separator character as an additional parameter. + ) + $(DT $(I FormatChar)) $(DD $(DL @@ -471,6 +491,14 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) uint currentArg = 0; while (spec.writeUpToNextSpec(w)) { + if (currentArg == funs.length && !spec.indexStart) + { + // leftover spec? + enforceFmt(fmt.length == 0, + text("Orphan format specifier: %", spec.spec)); + break; + } + if (spec.width == spec.DYNAMIC) { auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); @@ -496,6 +524,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) } spec.width = width; } + if (spec.precision == spec.DYNAMIC) { auto precision = to!(typeof(spec.precision))( @@ -517,6 +546,21 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) // else negative precision is same as no precision else spec.precision = spec.UNSPECIFIED; } + + if (spec.separators == spec.DYNAMIC) + { + auto separators = to!(typeof(spec.width))(getNthInt(currentArg, args)); + spec.separators = separators; + ++currentArg; + } + + if (spec.separatorCharPos == spec.DYNAMIC) + { + auto separatorChar = getNth!(isSomeChar,dchar)(currentArg, args); + spec.separatorChar = separatorChar; + ++currentArg; + } + if (currentArg == A.length && !spec.indexStart) { // leftover spec? @@ -524,6 +568,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) text("Orphan format specifier: %", spec.spec)); break; } + // Format! if (spec.indexStart > 0) { @@ -555,6 +600,20 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) return currentArg; } +/// +@safe unittest +{ + assert(format("%,d", 1000) == "1,000"); + assert(format("%,f", 1234567.891011) == "1,234,567.891,011"); + assert(format("%,?d", '?', 1000) == "1?000"); + assert(format("%,1d", 1000) == "1,0,0,0", format("%,1d", 1000)); + assert(format("%,*d", 4, -12345) == "-1,2345"); + assert(format("%,*?d", 4, '_', -12345) == "-1_2345"); + assert(format("%,6?d", '_', -12345678) == "-12_345678"); + assert(format("%12,3.3f", 1234.5678) == " 1,234.568", "'" ~ + format("%12,3.3f", 1234.5678) ~ "'"); +} + @safe pure unittest { import std.array; @@ -941,7 +1000,7 @@ if (!is(Unqual!Char == Char)) struct FormatSpec(Char) if (is(Unqual!Char == Char)) { - import std.ascii : isDigit; + import std.ascii : isDigit, isPunctuation, isAlpha; import std.algorithm.searching : startsWith; import std.conv : parse, text, to; @@ -957,6 +1016,23 @@ if (is(Unqual!Char == Char)) */ int precision = UNSPECIFIED; + /** + Separator. Its value defines how many digits are printed between + $(D SeparatorChar). + */ + int separators = UNSPECIFIED; + + /** + Separator. Its value defines how many digits are printed between + $(D SeparatorChar). + */ + int separatorCharPos = UNSPECIFIED; + + /** + SeparatorChar. The character that is inserted every $(D separators) character. + */ + dchar separatorChar = ','; + /** Special value for width and precision. $(D DYNAMIC) width or precision means that they were specified with $(D '*') in the @@ -1019,6 +1095,11 @@ if (is(Unqual!Char == Char)) */ bool flHash; + /** + The format specifier contained a $(D ',') + */ + bool flSeparator; + // Fake field to allow compilation ubyte allFlags; } @@ -1033,7 +1114,8 @@ if (is(Unqual!Char == Char)) bool, "flSpace", 1, bool, "flPlus", 1, bool, "flHash", 1, - ubyte, "", 3)); + bool, "flSeparator", 1, + ubyte, "", 2)); ubyte allFlags; } } @@ -1162,6 +1244,7 @@ if (is(Unqual!Char == Char)) flSpace = false; flPlus = false; flHash = false; + flSeparator = false; } else { @@ -1293,6 +1376,36 @@ if (is(Unqual!Char == Char)) // width width = to!int(widthOrArgIndex); } + break; + case ',': + // Precision + ++i; + flSeparator = true; + + if (trailing[i] == '*') + { + ++i; + // read result + separators = DYNAMIC; + } + else if (isDigit(trailing[i])) + { + auto tmp = trailing[i .. $]; + separators = parse!int(tmp); + i = arrayPtrDiff(tmp, trailing); + } + else + { + // "," was specified, but nothing after it + separators = 3; + } + + if (trailing[i] == '?') + { + separatorCharPos = DYNAMIC; + ++i; + } + break; case '.': // Precision @@ -1358,6 +1471,7 @@ if (is(Unqual!Char == Char)) flSpace = false; flPlus = false; flHash = false; + flSeparator = false; } else { @@ -1430,6 +1544,7 @@ if (is(Unqual!Char == Char)) if (flSpace) put(w, ' '); if (flPlus) put(w, '+'); if (flHash) put(w, '#'); + if (flSeparator) put(w, ','); if (width != 0) formatValue(w, width, f); if (precision != FormatSpec!Char.UNSPECIFIED) @@ -1493,6 +1608,7 @@ if (is(Unqual!Char == Char)) "\nflSpace = ", flSpace, "\nflPlus = ", flPlus, "\nflHash = ", flHash, + "\nflSeparator = ", flSeparator, "\nnested = ", nested, "\ntrailing = ", trailing, "\n"); } @@ -1534,6 +1650,32 @@ if (is(Unqual!Char == Char)) assertThrown(f.writeUpToNextSpec(a)); } +@safe unittest +{ + import std.array : appender; + auto a = appender!(string)(); + + auto f = FormatSpec!char("%,d"); + f.writeUpToNextSpec(a); + + assert(f.spec == 'd', format("%s", f.spec)); + assert(f.precision == FormatSpec!char.UNSPECIFIED); + assert(f.separators == 3); + + f = FormatSpec!char("%5,10f"); + f.writeUpToNextSpec(a); + assert(f.spec == 'f', format("%s", f.spec)); + assert(f.separators == 10); + assert(f.width == 5); + + f = FormatSpec!char("%5,10.4f"); + f.writeUpToNextSpec(a); + assert(f.spec == 'f', format("%s", f.spec)); + assert(f.separators == 10); + assert(f.width == 5); + assert(f.precision == 4); +} + /** Helper function that returns a $(D FormatSpec) for a single specifier given in $(D fmt). @@ -1855,7 +1997,14 @@ private void formatUnsigned(Writer, T, Char)(Writer w, T arg, const ref FormatSp size_t leftpad = 0; size_t rightpad = 0; - immutable ptrdiff_t spacesToPrint = fs.width - ((prefix1 != 0) + (prefix2 != 0) + zerofill + digits.length); + immutable ptrdiff_t spacesToPrint = + fs.width - ( + (prefix1 != 0) + + (prefix2 != 0) + + zerofill + + digits.length + + ((fs.flSeparator != 0) * (digits.length / fs.separators)) + ); if (spacesToPrint > 0) // need to do some padding { if (padChar == '0') @@ -1876,7 +2025,21 @@ private void formatUnsigned(Writer, T, Char)(Writer w, T arg, const ref FormatSp foreach (i ; 0 .. zerofill) put(w, '0'); - put(w, digits); + if (fs.flSeparator) + { + for (size_t j = 0; j < digits.length; ++j) + { + if (j != 0 && (digits.length - j) % fs.separators == 0) + { + put(w, fs.separatorChar); + } + put(w, digits[j]); + } + } + else + { + put(w, digits); + } foreach (i ; 0 .. rightpad) put(w, ' '); @@ -1972,6 +2135,8 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { import std.algorithm.searching : find; import std.algorithm.comparison : min; + import std.string : indexOf, indexOfAny, indexOfNeither; + FormatSpec!Char fs = f; // fs is copy for change its values. FloatingPointTypeOf!T val = obj; @@ -2066,7 +2231,75 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) enforceFmt(n >= 0, "floating point formatting failure"); - put(w, buf[0 .. min(n, buf.length-1)]); + + auto len = min(n, buf.length-1); + ptrdiff_t dot = buf[0 .. len].indexOf('.'); + if (fs.flSeparator && dot != -1) + { + ptrdiff_t firstDigit = buf[0 .. len].indexOfAny("0123456789"); + ptrdiff_t ePos = buf[0 .. len].indexOf('e'); + size_t j; + + ptrdiff_t firstLen = dot - firstDigit; + + size_t separatorScoreCnt = firstLen / fs.separators; + + size_t afterDotIdx; + if (ePos != -1) + { + afterDotIdx = ePos; + } + else + { + afterDotIdx = len; + } + + if (dot != -1) + { + ptrdiff_t mantissaLen = afterDotIdx - (dot + 1); + separatorScoreCnt += (mantissaLen > 0) ? (mantissaLen - 1) / fs.separators : 0; + } + + // plus, minus prefix + ptrdiff_t digitsBegin = buf[0 .. separatorScoreCnt].indexOfNeither(" "); + if (digitsBegin == -1) + { + digitsBegin = separatorScoreCnt; + } + put(w, buf[digitsBegin .. firstDigit]); + + // digits until dot with separator + for (j = 0; j < firstLen; ++j) + { + if (j > 0 && (firstLen - j) % fs.separators == 0) + { + put(w, fs.separatorChar); + } + put(w, buf[j + firstDigit]); + } + put(w, '.'); + + // digits after dot + for (j = dot + 1; j < afterDotIdx; ++j) + { + auto realJ = (j - (dot + 1)); + if (realJ != 0 && realJ % fs.separators == 0) + { + put(w, fs.separatorChar); + } + put(w, buf[j]); + } + + // rest + if (ePos != -1) + { + put(w, buf[afterDotIdx .. len]); + } + } + else + { + put(w, buf[0 .. len]); + } } /// @@ -3834,6 +4067,11 @@ private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f //------------------------------------------------------------------------------ // Fix for issue 1591 private int getNthInt(A...)(uint index, A args) +{ + return getNth!(isIntegral,int)(index, args); +} + +private T getNth(alias Condition, T,A...)(uint index, A args) { import std.conv : to; @@ -3841,16 +4079,16 @@ private int getNthInt(A...)(uint index, A args) { if (index) { - return getNthInt(index - 1, args[1 .. $]); + return getNth!(Condition,T)(index - 1, args[1 .. $]); } - static if (isIntegral!(typeof(args[0]))) + static if (Condition!(typeof(args[0]))) { - return to!int(args[0]); + return to!T(args[0]); } else { - throw new FormatException("width/precision must be an integer, not " - ~ typeof(args[0]).stringof); + throw new FormatException(T.stringof ~ " expected, not " ~ + typeof(args[0]).stringof); } } else @@ -3862,9 +4100,9 @@ private int getNthInt(A...)(uint index, A args) @safe unittest { assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) - == "width/precision must be an integer, not double"); + == "int expected, not double"); assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) - == "width/precision must be an integer, not char"); + == "int expected, not char"); assert(collectExceptionMsg!FormatException(format("%.*d", 5)) == "Orphan format specifier: %d"); assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) @@ -5721,3 +5959,86 @@ char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { return array1.ptr - array2.ptr; } + +@safe unittest +{ + assertCTFEable!({ + auto tmp = format("%,d", 1000); + assert(tmp == "1,000", "'" ~ tmp ~ "'"); + + tmp = format("%,?d", 'z', 1234567); + assert(tmp == "1z234z567", "'" ~ tmp ~ "'"); + + tmp = format("%10,?d", 'z', 1234567); + assert(tmp == " 1z234z567", "'" ~ tmp ~ "'"); + + tmp = format("%11,2?d", 'z', 1234567); + assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); + + tmp = format("%11,*?d", 2, 'z', 1234567); + assert(tmp == " 1z23z45z67", "'" ~ tmp ~ "'"); + + tmp = format("%11,*d", 2, 1234567); + assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); + + tmp = format("%11,2d", 1234567); + assert(tmp == " 1,23,45,67", "'" ~ tmp ~ "'"); + }); +} + +@safe unittest +{ + auto tmp = format("%,f", 1000.0); + assert(tmp == "1,000.000,000", "'" ~ tmp ~ "'"); + + tmp = format("%,f", 1234567.891011); + assert(tmp == "1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%,f", -1234567.891011); + assert(tmp == "-1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%,2f", 1234567.891011); + assert(tmp == "1,23,45,67.89,10,11", "'" ~ tmp ~ "'"); + + tmp = format("%18,f", 1234567.891011); + assert(tmp == " 1,234,567.891,011", "'" ~ tmp ~ "'"); + + tmp = format("%18,?f", '.', 1234567.891011); + assert(tmp == " 1.234.567.891.011", "'" ~ tmp ~ "'"); + + tmp = format("%,?.3f", 'ä', 1234567.891011); + assert(tmp == "1ä234ä567.891", "'" ~ tmp ~ "'"); + + tmp = format("%,*?.3f", 1, 'ä', 1234567.891011); + assert(tmp == "1ä2ä3ä4ä5ä6ä7.8ä9ä1", "'" ~ tmp ~ "'"); + + tmp = format("%,4?.3f", '_', 1234567.891011); + assert(tmp == "123_4567.891", "'" ~ tmp ~ "'"); + + tmp = format("%12,3.3f", 1234.5678); + assert(tmp == " 1,234.568", "'" ~ tmp ~ "'"); + + tmp = format("%,e", 3.141592653589793238462); + assert(tmp == "3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%15,e", 3.141592653589793238462); + assert(tmp == " 3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%15,e", -3.141592653589793238462); + assert(tmp == " -3.141,593e+00", "'" ~ tmp ~ "'"); + + tmp = format("%.4,*e", 2, 3.141592653589793238462); + assert(tmp == "3.14,16e+00", "'" ~ tmp ~ "'"); + + tmp = format("%13.4,*e", 2, 3.141592653589793238462); + assert(tmp == " 3.14,16e+00", "'" ~ tmp ~ "'"); + + tmp = format("%,.0f", 3.14); + assert(tmp == "3", "'" ~ tmp ~ "'"); + + tmp = format("%3,g", 1_000_000.123456); + assert(tmp == "1e+06", "'" ~ tmp ~ "'"); + + tmp = format("%19,?f", '.', -1234567.891011); + assert(tmp == " -1.234.567.891.011", "'" ~ tmp ~ "'"); +} From 629c21d45b195331938a28ac626def2a0aac4d27 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Thu, 20 Apr 2017 13:29:11 -0400 Subject: [PATCH 059/262] Link to ConvException docs in std.bigint ctor doc --- std/bigint.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/bigint.d b/std/bigint.d index ed39974cae6..72a7978dc40 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -59,7 +59,7 @@ public: * s = a finite bidirectional range of any character type * * Throws: - * $(D ConvException) if the string doesn't represent a valid number + * $(REF ConvException, std,conv) if the string doesn't represent a valid number */ this(Range)(Range s) if ( isBidirectionalRange!Range && From bbd6421c3d845ea567fef9b742c51362d95b356c Mon Sep 17 00:00:00 2001 From: Nikolay Tolstokulakov Date: Mon, 17 Apr 2017 08:52:11 +0000 Subject: [PATCH 060/262] apply changes for netbsd: stdio - set name to not null value for reopen --- std/stdio.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/stdio.d b/std/stdio.d index 8fc8f15eebd..9da7656a4f9 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -507,7 +507,7 @@ Throws: $(D ErrnoException) in case of error. enforce(isOpen, "Attempting to reopen() an unopened file"); - auto namez = name.tempCString!FSChar(); + auto namez = (name == null ? _name : name).tempCString!FSChar(); auto modez = stdioOpenmode.tempCString!FSChar(); FILE* fd = _p.handle; From e63c620433f51da97a45e5400ca0a71202d7b129 Mon Sep 17 00:00:00 2001 From: tsbockman Date: Fri, 21 Apr 2017 12:26:16 -0700 Subject: [PATCH 061/262] issue 15645 - Prevent unsafe usage of Tuple.slice --- std/typecons.d | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/std/typecons.d b/std/typecons.d index d43da0331b7..6671f5e9d46 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -970,7 +970,7 @@ template Tuple(Specs...) } /** - * Takes a slice of this `Tuple`. + * Takes a slice by-reference of this `Tuple`. * * Params: * from = A `size_t` designating the starting position of the slice. @@ -982,9 +982,14 @@ template Tuple(Specs...) * the original. */ @property - ref Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() @trusted + ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted if (from <= to && to <= Types.length) { + static assert( + (typeof(this).alignof % typeof(return).alignof == 0) && + (expand[from].offsetof % typeof(return).alignof == 0), + "Slicing by reference is impossible because of an alignment mistmatch. (See Phobos issue #15645.)"); + return *cast(typeof(return)*) &(field[from]); } @@ -997,6 +1002,10 @@ template Tuple(Specs...) auto s = a.slice!(1, 3); static assert(is(typeof(s) == Tuple!(string, float))); assert(s[0] == "abc" && s[1] == 4.5); + + // Phobos issue #15645 + Tuple!(int, short, bool, double) b; + static assert(!__traits(compiles, b.slice!(2, 4))); } /** From 9d59d8cd73e4f1a8312f1c3f6e9182a1cc1a8be0 Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Mon, 24 Apr 2017 00:32:47 -0700 Subject: [PATCH 062/262] std.getopt: Add unit tests for delegates as callbacks. --- std/getopt.d | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/std/getopt.d b/std/getopt.d index 7d690b10e76..c8251e469fb 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1773,3 +1773,37 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) getopt(args, "f|flag", "Boolean", &flag); assert(flag); } + +@system unittest // Delegates as callbacks +{ + alias TwoArgOptionHandler = void delegate(string option, string value); + + TwoArgOptionHandler makeAddNHandler(ref long dest) + { + void addN(ref long dest, string n) + { + import std.conv : to; + dest += n.to!long; + } + + return (option, value) => addN(dest, value); + } + + long x = 0; + long y = 0; + + string[] args = + ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10", + "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"]; + + getopt(args, + "x-plus-1", "Add one to x", delegate void() { x += 1; }, + "x-plus-5", "Add five to x", delegate void(string option) { x += 5; }, + "x-plus-n", "Add NUM to x", makeAddNHandler(x), + "y-plus-7", "Add seven to y", delegate void() { y += 7; }, + "y-plus-3", "Add three to y", delegate void(string option) { y += 3; }, + "y-plus-n", "Add NUM to x", makeAddNHandler(y),); + + assert(x == 17); + assert(y == 50); +} From b0b63869a3ae2bf2cce75962098917984eddef16 Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Mon, 24 Apr 2017 00:48:32 -0700 Subject: [PATCH 063/262] std.getopt unit tests: No trailing whitespace. --- std/getopt.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index c8251e469fb..ea9b09a69a5 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1777,7 +1777,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) @system unittest // Delegates as callbacks { alias TwoArgOptionHandler = void delegate(string option, string value); - + TwoArgOptionHandler makeAddNHandler(ref long dest) { void addN(ref long dest, string n) @@ -1788,14 +1788,14 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) return (option, value) => addN(dest, value); } - + long x = 0; long y = 0; - + string[] args = ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10", "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"]; - + getopt(args, "x-plus-1", "Add one to x", delegate void() { x += 1; }, "x-plus-5", "Add five to x", delegate void(string option) { x += 5; }, @@ -1803,7 +1803,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) "y-plus-7", "Add seven to y", delegate void() { y += 7; }, "y-plus-3", "Add three to y", delegate void(string option) { y += 3; }, "y-plus-n", "Add NUM to x", makeAddNHandler(y),); - + assert(x == 17); assert(y == 50); } From cacf586e93ef0330739cfdf35b86a1d53d5c3aa1 Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Mon, 24 Apr 2017 00:54:21 -0700 Subject: [PATCH 064/262] std.getopt unit tests: No trailing whitespace. --- std/getopt.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/getopt.d b/std/getopt.d index ea9b09a69a5..9caa7759b7f 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1785,7 +1785,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) import std.conv : to; dest += n.to!long; } - + return (option, value) => addN(dest, value); } From a1f4df7e9b64d2e699202e31dd6852f80b09446a Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 19 Apr 2017 11:28:33 +0100 Subject: [PATCH 065/262] Refactor formatNth without mixin --- std/format.d | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/std/format.d b/std/format.d index 273d5c57f78..476e5377470 100644 --- a/std/format.d +++ b/std/format.d @@ -4027,25 +4027,14 @@ private void formatGeneric(Writer, D, Char)(Writer w, const(void)* arg, const re private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f, size_t index, A args) { - import std.conv : to; - static string gencode(size_t count)() + switch (index) { - string result; - foreach (n; 0 .. count) + foreach (n, _; A) { - auto num = to!string(n); - result ~= - "case "~num~":"~ - " formatValue(w, args["~num~"], f);"~ - " break;"; + case n: + formatValue(w, args[n], f); + return; } - return result; - } - - switch (index) - { - mixin(gencode!(A.length)()); - default: assert(0, "n = "~cast(char)(index + '0')); } From 9ef964bf53be52d748ce6d0d1d603ed1fa9ae59c Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 20 Apr 2017 12:57:28 +0100 Subject: [PATCH 066/262] Refactor getNth with switch Switch is faster & reduces template bloat vs. recursion. Also fixes over-specific missing argument message (not just for int). --- std/format.d | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/std/format.d b/std/format.d index 476e5377470..28191e253e8 100644 --- a/std/format.d +++ b/std/format.d @@ -4060,29 +4060,27 @@ private int getNthInt(A...)(uint index, A args) return getNth!(isIntegral,int)(index, args); } -private T getNth(alias Condition, T,A...)(uint index, A args) +private T getNth(alias Condition, T, A...)(uint index, A args) { import std.conv : to; - static if (A.length) + switch (index) { - if (index) - { - return getNth!(Condition,T)(index - 1, args[1 .. $]); - } - static if (Condition!(typeof(args[0]))) - { - return to!T(args[0]); - } - else + foreach (n, _; A) { - throw new FormatException(T.stringof ~ " expected, not " ~ - typeof(args[0]).stringof); + case n: + static if (Condition!(typeof(args[n]))) + { + return to!T(args[n]); + } + else + { + throw new FormatException(T.stringof ~ " expected, not " ~ + typeof(args[n]).stringof); + } } - } - else - { - throw new FormatException("missing integer width/precision argument"); + default: + throw new FormatException("missing argument #" ~ to!string(index + 1)); } } @@ -4095,7 +4093,7 @@ private T getNth(alias Condition, T,A...)(uint index, A args) assert(collectExceptionMsg!FormatException(format("%.*d", 5)) == "Orphan format specifier: %d"); assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) - == "missing integer width/precision argument"); + == "missing argument #2"); } /* ======================== Unit Tests ====================================== */ From 339fe619e7c6c107cdc4ec76dbba0f49b7c9f892 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 15:00:07 -0700 Subject: [PATCH 067/262] Add missing sig constraints to std.numeric.gcd. First of all, it makes no sense to take arbitrary types (e.g., gcd("a","abc") should not even match the template). Second of all, the current implementation only works with built-in types, so the function should restrict itself to only accepting built-in types so that it is overloadable for other types (e.g., BigInt). --- std/numeric.d | 1 + 1 file changed, 1 insertion(+) diff --git a/std/numeric.d b/std/numeric.d index 1274f74a848..151e2b503c6 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2603,6 +2603,7 @@ an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm. */ T gcd(T)(T a, T b) + if (isIntegral!T) { static if (is(T == const) || is(T == immutable)) { From 9fe2ede16db44a8ac544aaca1910174b6116ba7c Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 16:58:33 -0700 Subject: [PATCH 068/262] Implement generic gcd for non-builtin types. --- std/numeric.d | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/std/numeric.d b/std/numeric.d index 151e2b503c6..4a99b29655a 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2656,6 +2656,89 @@ T gcd(T)(T a, T b) assert(gcd(a, b) == 13); } +// This overload is for non-builtin numerical types like BigInt or +// user-defined types. +/// ditto +T gcd(T)(T a, T b) + if (!isIntegral!T && + is(typeof(T.init % T.init)) && + is(typeof(T.init == 0 || T.init > 0))) +{ + import std.algorithm.mutation : swap; + + enum canUseBinaryGcd = is(typeof(() { + T t, u; + t <<= 1; + t >>= 1; + t -= u; + bool b = (t & 1) == 0; + swap(t, u); + })); + + assert(a >= 0 && b >= 0); + + static if (canUseBinaryGcd) + { + uint shift = 0; + while ((a & 1) == 0 && (b & 1) == 0) + { + a >>= 1; + b >>= 1; + shift++; + } + + do + { + assert((a & 1) != 0); + while ((b & 1) == 0) + b >>= 1; + if (a > b) + swap(a, b); + b -= a; + } while (b); + + return a << shift; + } + else + { + // The only thing we have is %; fallback to Euclidean algorithm. + while (b != 0) + { + auto t = b; + b = a % b; + a = t; + } + return a; + } +} + +// Issue 7102 +unittest +{ + import std.bigint : BigInt; + assert(gcd(BigInt("71_000_000_000_000_000_000"), + BigInt("31_000_000_000_000_000_000")) == + BigInt("1_000_000_000_000_000_000")); +} + +unittest +{ + // A numerical type that only supports % and - (to force gcd implementation + // to use Euclidean algorithm). + struct CrippledInt + { + int impl; + CrippledInt opBinary(string op : "%")(CrippledInt i) + { + return CrippledInt(impl % i.impl); + } + int opEquals(CrippledInt i) { return impl == i.impl; } + int opEquals(int i) { return impl == i; } + int opCmp(int i) { return (impl < i) ? -1 : (impl > i) ? 1 : 0; } + } + assert(gcd(CrippledInt(2310), CrippledInt(1309)) == CrippledInt(77)); +} + // This is to make tweaking the speed/size vs. accuracy tradeoff easy, // though floats seem accurate enough for all practical purposes, since // they pass the "approxEqual(inverseFft(fft(arr)), arr)" test even for From e0f94770479e81d9fed0f84cd74897f7f7655ac3 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Tue, 25 Apr 2017 17:06:57 -0700 Subject: [PATCH 069/262] Documentation. --- std/numeric.d | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/numeric.d b/std/numeric.d index 4a99b29655a..b24fa280604 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2601,6 +2601,13 @@ GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F) Computes the greatest common divisor of $(D a) and $(D b) by using an efficient algorithm such as $(HTTPS en.wikipedia.org/wiki/Euclidean_algorithm, Euclid's) or $(HTTPS en.wikipedia.org/wiki/Binary_GCD_algorithm, Stein's) algorithm. + +Params: + T = Any numerical type that supports the modulo operator `%`. If + bit-shifting `<<` and `>>` are also supported, Stein's algorithm will + be used; otherwise, Euclid's algorithm is used as _a fallback. +Returns: + The greatest common divisor of the given arguments. */ T gcd(T)(T a, T b) if (isIntegral!T) From a976764d5435205939c72f8f57465afdb3cc6c8f Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Tue, 25 Apr 2017 23:10:40 -0700 Subject: [PATCH 070/262] std.getopt: Make private functions safe. --- std/getopt.d | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index 9caa7759b7f..492868cb179 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1080,7 +1080,7 @@ private struct configuration } private bool optMatch(string arg, string optPattern, ref string value, - configuration cfg) + configuration cfg) @safe { import std.uni : toUpper; import std.string : indexOf; @@ -1140,7 +1140,7 @@ private bool optMatch(string arg, string optPattern, ref string value, return false; } -private void setConfig(ref configuration cfg, config option) +private void setConfig(ref configuration cfg, config option) @safe { switch (option) { @@ -1341,6 +1341,16 @@ private void setConfig(ref configuration cfg, config option) catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); } } +@safe unittest // @safe std.getopt.config option use +{ + long x = 0; + string[] args = ["program", "--inc-x", "--inc-x"]; + getopt(args, + std.getopt.config.caseSensitive, + "inc-x", "Add one to x", delegate void() { x++; }); + assert(x == 2); +} + @system unittest { // From bugzilla 2142 @@ -1774,9 +1784,9 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) assert(flag); } -@system unittest // Delegates as callbacks +@safe unittest // Delegates as callbacks { - alias TwoArgOptionHandler = void delegate(string option, string value); + alias TwoArgOptionHandler = void delegate(string option, string value) @safe; TwoArgOptionHandler makeAddNHandler(ref long dest) { From cc8eff102f4ddcf15c4def1a5b87786c3040689d Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 26 Apr 2017 09:46:26 +0100 Subject: [PATCH 071/262] Add tests --- std/format.d | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/std/format.d b/std/format.d index 28191e253e8..b4bc329700c 100644 --- a/std/format.d +++ b/std/format.d @@ -4040,6 +4040,11 @@ private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f } } +@safe pure unittest +{ + assert(format("%2$s, %1$s", "2nd", "1st") == "1st, 2nd"); +} + @safe pure unittest { int[] a = [ 1, 3, 2 ]; @@ -4086,6 +4091,8 @@ private T getNth(alias Condition, T, A...)(uint index, A args) @safe unittest { + assert(format("%*.d, %.*f", 3, 7, 2, 3.1415) == " 7, 3.14"); + assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) == "int expected, not double"); assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) From abf4e93fa0de5c7d001e39dc04d5fdbe07d22135 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Wed, 26 Apr 2017 09:38:57 -0700 Subject: [PATCH 072/262] Annotate unittests per Phobos coding style. --- std/numeric.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/numeric.d b/std/numeric.d index b24fa280604..c6cea6fc608 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2720,7 +2720,7 @@ T gcd(T)(T a, T b) } // Issue 7102 -unittest +@system pure unittest { import std.bigint : BigInt; assert(gcd(BigInt("71_000_000_000_000_000_000"), @@ -2728,7 +2728,7 @@ unittest BigInt("1_000_000_000_000_000_000")); } -unittest +@safe pure nothrow unittest { // A numerical type that only supports % and - (to force gcd implementation // to use Euclidean algorithm). From 69cfa938ee37708add2a6973899fea7b7d017545 Mon Sep 17 00:00:00 2001 From: Jon Degenhardt Date: Wed, 26 Apr 2017 10:56:48 -0700 Subject: [PATCH 073/262] std.getopt: make setConfig safe, pure, nothrow, nogc --- std/getopt.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index 492868cb179..badf2829b2e 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1140,9 +1140,9 @@ private bool optMatch(string arg, string optPattern, ref string value, return false; } -private void setConfig(ref configuration cfg, config option) @safe +private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc { - switch (option) + final switch (option) { case config.caseSensitive: cfg.caseSensitive = true; break; case config.caseInsensitive: cfg.caseSensitive = false; break; @@ -1155,7 +1155,6 @@ private void setConfig(ref configuration cfg, config option) @safe cfg.stopOnFirstNonOption = true; break; case config.keepEndOfOptions: cfg.keepEndOfOptions = true; break; - default: assert(false); } } From ee6e55621ee22d48224a76682f47f8d9827f1288 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Sun, 30 Apr 2017 19:23:44 +0300 Subject: [PATCH 074/262] SharedFreeList.reallocate must be shared and take a ref void[] --- std/experimental/allocator/building_blocks/free_list.d | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index cf2354e97d5..136e9b44db4 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -966,7 +966,7 @@ struct SharedFreeList(ParentAllocator, /// Ditto static if (hasMember!(ParentAllocator, "reallocate")) - bool reallocate(void[] b, size_t s) + bool reallocate(ref void[] b, size_t s) shared { return parent.reallocate(b, s); } @@ -1095,7 +1095,10 @@ struct SharedFreeList(ParentAllocator, { import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; - a.allocate(64); + auto c = a.allocate(64); + assert(a.reallocate(c, 96)); + assert(c.length == 96); + a.deallocate(c); } @system unittest From 5bc80e6dd653ca095a4d7d2b9bcfe33607d4d36d Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Thu, 27 Apr 2017 19:36:38 +0300 Subject: [PATCH 075/262] Add ISharedAllocator and have the _processAllocator use it --- std/experimental/allocator/common.d | 366 ++++++++++++++------ std/experimental/allocator/package.d | 500 +++++++++++++++++++++++++-- 2 files changed, 727 insertions(+), 139 deletions(-) diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index 126882df7a4..c1d2540b0c8 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -418,126 +418,266 @@ Forwards each of the methods in `funs` (if defined) to `member`. } version(unittest) -package void testAllocator(alias make)() { - import std.conv : text; - import std.stdio : writeln, stderr; - import std.math : isPowerOf2; - import std.typecons : Ternary; - alias A = typeof(make()); - scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); - - auto a = make(); - - // Test alignment - static assert(A.alignment.isPowerOf2); - - // Test goodAllocSize - assert(a.goodAllocSize(1) >= A.alignment, - text(a.goodAllocSize(1), " < ", A.alignment)); - assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); - assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); - - // Test allocate - assert(a.allocate(0) is null); - - auto b1 = a.allocate(1); - assert(b1.length == 1); - auto b2 = a.allocate(2); - assert(b2.length == 2); - assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); - - // Test alignedAllocate - static if (hasMember!(A, "alignedAllocate")) - {{ - auto b3 = a.alignedAllocate(1, 256); - assert(b3.length <= 1); - assert(b3.ptr.alignedAt(256)); - assert(a.alignedReallocate(b3, 2, 512)); - assert(b3.ptr.alignedAt(512)); - static if (hasMember!(A, "alignedDeallocate")) + import std.experimental.allocator : IAllocator, ISharedAllocator; + + package void testAllocator(alias make)() + { + import std.conv : text; + import std.stdio : writeln, stderr; + import std.math : isPowerOf2; + import std.typecons : Ternary; + alias A = typeof(make()); + scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); + + auto a = make(); + + // Test alignment + static assert(A.alignment.isPowerOf2); + + // Test goodAllocSize + assert(a.goodAllocSize(1) >= A.alignment, + text(a.goodAllocSize(1), " < ", A.alignment)); + assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); + assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); + + // Test allocate + assert(a.allocate(0) is null); + + auto b1 = a.allocate(1); + assert(b1.length == 1); + auto b2 = a.allocate(2); + assert(b2.length == 2); + assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); + + // Test alignedAllocate + static if (hasMember!(A, "alignedAllocate")) + {{ + auto b3 = a.alignedAllocate(1, 256); + assert(b3.length <= 1); + assert(b3.ptr.alignedAt(256)); + assert(a.alignedReallocate(b3, 2, 512)); + assert(b3.ptr.alignedAt(512)); + static if (hasMember!(A, "alignedDeallocate")) + { + a.alignedDeallocate(b3); + } + }} + else { - a.alignedDeallocate(b3); + static assert(!hasMember!(A, "alignedDeallocate")); + // This seems to be a bug in the compiler: + //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); } - }} - else - { - static assert(!hasMember!(A, "alignedDeallocate")); - // This seems to be a bug in the compiler: - //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); + + static if (hasMember!(A, "allocateAll")) + {{ + auto aa = make(); + if (aa.allocateAll().ptr) + { + // Can't get any more memory + assert(!aa.allocate(1).ptr); + } + auto ab = make(); + const b4 = ab.allocateAll(); + assert(b4.length); + // Can't get any more memory + assert(!ab.allocate(1).ptr); + }} + + static if (hasMember!(A, "expand")) + {{ + assert(a.expand(b1, 0)); + auto len = b1.length; + if (a.expand(b1, 102)) + { + assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); + } + auto aa = make(); + void[] b5 = null; + assert(aa.expand(b5, 0)); + assert(b5 is null); + assert(!aa.expand(b5, 1)); + assert(b5.length == 0); + }} + + void[] b6 = null; + assert(a.reallocate(b6, 0)); + assert(b6.length == 0); + assert(a.reallocate(b6, 1)); + assert(b6.length == 1, text(b6.length)); + assert(a.reallocate(b6, 2)); + assert(b6.length == 2); + + // Test owns + static if (hasMember!(A, "owns")) + {{ + assert(a.owns(null) == Ternary.no); + assert(a.owns(b1) == Ternary.yes); + assert(a.owns(b2) == Ternary.yes); + assert(a.owns(b6) == Ternary.yes); + }} + + static if (hasMember!(A, "resolveInternalPointer")) + {{ + void[] p; + assert(a.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = a.resolveInternalPointer(b1.ptr, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b2.ptr, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b6.ptr, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + static int[10] b7 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); + int[3] b8 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); + }} } - static if (hasMember!(A, "allocateAll")) - {{ - auto aa = make(); - if (aa.allocateAll().ptr) + package void testAllocatorObject(AllocInterface)(AllocInterface a) + if (is(AllocInterface : IAllocator) + || is (AllocInterface : shared ISharedAllocator)) + { + import std.conv : text; + import std.stdio : writeln, stderr; + import std.math : isPowerOf2; + import std.typecons : Ternary; + scope(failure) stderr.writeln("testAllocatorObject failed for ", + AllocInterface.stringof); + + assert(a); + + // Test alignment + assert(a.alignment.isPowerOf2); + + // Test goodAllocSize + assert(a.goodAllocSize(1) >= a.alignment, + text(a.goodAllocSize(1), " < ", a.alignment)); + assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(a.alignment)); + assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(a.alignment)); + + // Test empty + assert(a.empty != Ternary.no); + + // Test allocate + assert(a.allocate(0) is null); + + auto b1 = a.allocate(1); + assert(b1.length == 1); + auto b2 = a.allocate(2); + assert(b2.length == 2); + assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); + + // Test alignedAllocate { - // Can't get any more memory - assert(!aa.allocate(1).ptr); + // If not implemented it will return null, so those should pass + auto b3 = a.alignedAllocate(1, 256); + assert(b3.length <= 1); + assert(b3.ptr.alignedAt(256)); + if (a.alignedReallocate(b3, 1, 256)) + { + // If it is false, then the wrapped allocator did not implement + // this + assert(a.alignedReallocate(b3, 2, 512)); + assert(b3.ptr.alignedAt(512)); + } } - auto ab = make(); - const b4 = ab.allocateAll(); - assert(b4.length); - // Can't get any more memory - assert(!ab.allocate(1).ptr); - }} - - static if (hasMember!(A, "expand")) - {{ - assert(a.expand(b1, 0)); - auto len = b1.length; - if (a.expand(b1, 102)) + + // Test allocateAll { - assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); + auto aa = a.allocateAll(); + if (aa.ptr) + { + // Can't get any more memory + assert(!a.allocate(1).ptr); + a.deallocate(aa); + } + const b4 = a.allocateAll(); + if (b4.ptr) + { + // Can't get any more memory + assert(!a.allocate(1).ptr); + } } - auto aa = make(); - void[] b5 = null; - assert(aa.expand(b5, 0)); - assert(b5 is null); - assert(!aa.expand(b5, 1)); - assert(b5.length == 0); - }} - - void[] b6 = null; - assert(a.reallocate(b6, 0)); - assert(b6.length == 0); - assert(a.reallocate(b6, 1)); - assert(b6.length == 1, text(b6.length)); - assert(a.reallocate(b6, 2)); - assert(b6.length == 2); - - // Test owns - static if (hasMember!(A, "owns")) - {{ - assert(a.owns(null) == Ternary.no); - assert(a.owns(b1) == Ternary.yes); - assert(a.owns(b2) == Ternary.yes); - assert(a.owns(b6) == Ternary.yes); - }} - - static if (hasMember!(A, "resolveInternalPointer")) - {{ - void[] p; - assert(a.resolveInternalPointer(null, p) == Ternary.no); - Ternary r = a.resolveInternalPointer(b1.ptr, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); - assert(p.ptr is b1.ptr && p.length >= b1.length); - r = a.resolveInternalPointer(b2.ptr, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); - assert(p.ptr is b2.ptr && p.length >= b2.length); - r = a.resolveInternalPointer(b6.ptr, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); - assert(p.ptr is b6.ptr && p.length >= b6.length); - static int[10] b7 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); - int[3] b8 = [ 1, 2, 3 ]; - assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); - assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); - }} + + // Test expand + { + assert(a.expand(b1, 0)); + auto len = b1.length; + if (a.expand(b1, 102)) + { + assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); + } + } + + void[] b6 = null; + assert(a.reallocate(b6, 0)); + assert(b6.length == 0); + assert(a.reallocate(b6, 1)); + assert(b6.length == 1, text(b6.length)); + assert(a.reallocate(b6, 2)); + assert(b6.length == 2); + + // Test owns + { + if (a.owns(null) != Ternary.unknown) + { + assert(a.owns(null) == Ternary.no); + assert(a.owns(b1) == Ternary.yes); + assert(a.owns(b2) == Ternary.yes); + assert(a.owns(b6) == Ternary.yes); + } + } + + // Test resolveInternalPointer + { + void[] p; + if (a.resolveInternalPointer(null, p) != Ternary.unknown) + { + assert(a.resolveInternalPointer(null, p) == Ternary.no); + Ternary r = a.resolveInternalPointer(b1.ptr, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); + assert(p.ptr is b1.ptr && p.length >= b1.length); + r = a.resolveInternalPointer(b2.ptr, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); + assert(p.ptr is b2.ptr && p.length >= b2.length); + r = a.resolveInternalPointer(b6.ptr, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); + assert(p.ptr is b6.ptr && p.length >= b6.length); + static int[10] b7 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); + int[3] b8 = [ 1, 2, 3 ]; + assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); + assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); + } + } + + // Test deallocateAll + { + if (a.deallocateAll()) + { + if (a.empty != Ternary.unknown) + { + assert(a.empty == Ternary.yes); + } + } + } + } } diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 8dd697d3a08..d4724830490 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -363,20 +363,189 @@ interface IAllocator Ternary empty(); } -__gshared IAllocator _processAllocator; +/** +Dynamic shared allocator interface. Code that defines allocators shareable +across threads ultimately implements this interface. This should be used +wherever a uniform type is required for encapsulating various allocator +implementations. + +Composition of allocators is not recommended at this level due to +inflexibility of dynamic interfaces and inefficiencies caused by cascaded +multiple calls. Instead, compose allocators using the static interface defined +in $(A std_experimental_allocator_building_blocks.html, +`std.experimental.allocator.building_blocks`), then adapt the composed +allocator to `ISharedAllocator` (possibly by using $(LREF CSharedAllocatorImpl) below). + +Methods returning $(D Ternary) return $(D Ternary.yes) upon success, +$(D Ternary.no) upon failure, and $(D Ternary.unknown) if the primitive is not +implemented by the allocator instance. +*/ +interface ISharedAllocator +{ + /** + Returns the alignment offered. + */ + @property uint alignment() shared; + + /** + Returns the good allocation size that guarantees zero internal + fragmentation. + */ + size_t goodAllocSize(size_t s) shared; + + /** + Allocates `n` bytes of memory. + */ + void[] allocate(size_t, TypeInfo ti = null) shared; + + /** + Allocates `n` bytes of memory with specified alignment `a`. Implementations + that do not support this primitive should always return `null`. + */ + void[] alignedAllocate(size_t n, uint a) shared; + + /** + Allocates and returns all memory available to this allocator. + Implementations that do not support this primitive should always return + `null`. + */ + void[] allocateAll() shared; + + /** + Expands a memory block in place and returns `true` if successful. + Implementations that don't support this primitive should always return + `false`. + */ + bool expand(ref void[], size_t) shared; + + /// Reallocates a memory block. + bool reallocate(ref void[], size_t) shared; + + /// Reallocates a memory block with specified alignment. + bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared; + + /** + Returns $(D Ternary.yes) if the allocator owns $(D b), $(D Ternary.no) if + the allocator doesn't own $(D b), and $(D Ternary.unknown) if ownership + cannot be determined. Implementations that don't support this primitive + should always return `Ternary.unknown`. + */ + Ternary owns(void[] b) shared; + + /** + Resolves an internal pointer to the full block allocated. Implementations + that don't support this primitive should always return `Ternary.unknown`. + */ + Ternary resolveInternalPointer(const void* p, ref void[] result) shared; + + /** + Deallocates a memory block. Implementations that don't support this + primitive should always return `false`. A simple way to check that an + allocator supports deallocation is to call $(D deallocate(null)). + */ + bool deallocate(void[] b) shared; + + /** + Deallocates all memory. Implementations that don't support this primitive + should always return `false`. + */ + bool deallocateAll() shared; + + /** + Returns $(D Ternary.yes) if no memory is currently allocated from this + allocator, $(D Ternary.no) if some allocations are currently active, or + $(D Ternary.unknown) if not supported. + */ + Ternary empty() shared; +} + +shared ISharedAllocator _processAllocator; IAllocator _threadAllocator; shared static this() { assert(!_processAllocator); import std.experimental.allocator.gc_allocator : GCAllocator; - _processAllocator = allocatorObject(GCAllocator.instance); + _processAllocator = sharedAllocatorObject(GCAllocator.instance); } static this() { + /* + Forwards the `_threadAllocator` calls to the `_processAllocator` + */ + static class ThreadAllocator : IAllocator + { + override @property uint alignment() + { + return _processAllocator.alignment(); + } + + override size_t goodAllocSize(size_t s) + { + return _processAllocator.goodAllocSize(s); + } + + override void[] allocate(size_t n, TypeInfo ti = null) + { + return _processAllocator.allocate(n, ti); + } + + override void[] alignedAllocate(size_t n, uint a) + { + return _processAllocator.alignedAllocate(n, a); + } + + override void[] allocateAll() + { + return _processAllocator.allocateAll(); + } + + override bool expand(ref void[] b, size_t size) + { + return _processAllocator.expand(b, size); + } + + override bool reallocate(ref void[] b, size_t size) + { + return _processAllocator.reallocate(b, size); + } + + override bool alignedReallocate(ref void[] b, size_t size, uint alignment) + { + return _processAllocator.alignedReallocate(b, size, alignment); + } + + override Ternary owns(void[] b) + { + return _processAllocator.owns(b); + } + + override Ternary resolveInternalPointer(const void* p, ref void[] result) + { + return _processAllocator.resolveInternalPointer(p, result); + } + + override bool deallocate(void[] b) + { + return _processAllocator.deallocate(b); + } + + override bool deallocateAll() + { + return _processAllocator.deallocateAll(); + } + + override Ternary empty() + { + return _processAllocator.empty(); + } + } + assert(!_threadAllocator); - _threadAllocator = _processAllocator; + import std.conv : emplace; + static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState; + _threadAllocator = emplace!(ThreadAllocator)(_threadAllocatorState[]); } /** @@ -418,13 +587,13 @@ Gets/sets the allocator for the current process. This allocator must be used for allocating memory shared across threads. Objects created using this allocator can be cast to $(D shared). */ -@property IAllocator processAllocator() +@property shared(ISharedAllocator) processAllocator() { return _processAllocator; } /// Ditto -@property void processAllocator(IAllocator a) +@property void processAllocator(shared ISharedAllocator a) { assert(a); _processAllocator = a; @@ -432,8 +601,40 @@ allocator can be cast to $(D shared). @system unittest { + import std.experimental.allocator.mallocator : Mallocator; + import std.experimental.allocator.building_blocks.free_list : SharedFreeList; + import std.exception : assertThrown; + import core.exception : AssertError; + assert(processAllocator); - assert(processAllocator is theAllocator); + assert(theAllocator); + + testAllocatorObject(processAllocator); + testAllocatorObject(theAllocator); + + shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL; + shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL); + assert(sharedFLObj); + testAllocatorObject(sharedFLObj); + + // Test processAllocator setter + shared ISharedAllocator oldProcessAllocator = processAllocator; + processAllocator = sharedFLObj; + assert(processAllocator is sharedFLObj); + + testAllocatorObject(processAllocator); + testAllocatorObject(theAllocator); + assertThrown!AssertError(processAllocator = null); + + // Restore initial processAllocator state + processAllocator = oldProcessAllocator; + assert(processAllocator is oldProcessAllocator); + + shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL); + testAllocatorObject(indirectShFLObj); + + IAllocator indirectMallocator = allocatorObject(&Mallocator.instance); + testAllocatorObject(indirectMallocator); } /** @@ -1848,12 +2049,79 @@ CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa) /** -Implementation of $(D IAllocator) using $(D Allocator). This adapts a -statically-built allocator type to $(D IAllocator) that is directly usable by +Returns a dynamically-typed $(D CSharedAllocator) built around a given statically- +typed allocator $(D a) of type $(D A). Passing a pointer to the allocator +creates a dynamic allocator around the allocator pointed to by the pointer, +without attempting to copy or move it. Passing the allocator by value or +reference behaves as follows. + +$(UL +$(LI If $(D A) has no state, the resulting object is allocated in static +shared storage.) +$(LI If $(D A) has state and is copyable, the result will store a copy of it +within. The result itself is allocated in its own statically-typed allocator.) +$(LI If $(D A) has state and is not copyable, the result will move the +passed-in argument into the result. The result itself is allocated in its own +statically-typed allocator.) +) + +*/ +shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a) +if (!isPointer!A) +{ + import std.conv : emplace; + static if (stateSize!A == 0) + { + enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof); + static __gshared ulong[s] state; + static shared CSharedAllocatorImpl!A result; + if (!result) + { + // Don't care about a few races + result = cast(shared + CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[])); + } + assert(result); + return result; + } + else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable + { + auto state = a.allocate(stateSize!(CSharedAllocatorImpl!A)); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) a.deallocate(state); + } + return emplace!(shared CSharedAllocatorImpl!A)(state); + } + else // the allocator object is not copyable + { + assert(0, "Not yet implemented"); + } +} + +/// Ditto +shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa) +{ + assert(pa); + import std.conv : emplace; + auto state = pa.allocate(stateSize!(CSharedAllocatorImpl!(A, Yes.indirect))); + import std.traits : hasMember; + static if (hasMember!(A, "deallocate")) + { + scope(failure) pa.deallocate(state); + } + return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa); +} + + +/** + +Implementation of `IAllocator` using `Allocator`. This adapts a +statically-built allocator type to `IAllocator` that is directly usable by non-templated code. -Usually $(D CAllocatorImpl) is used indirectly by calling -$(LREF theAllocator). +Usually `CAllocatorImpl` is used indirectly by calling $(LREF theAllocator). */ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) : IAllocator @@ -1881,14 +2149,14 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) else alias impl = Allocator.instance; } - /// Returns $(D impl.alignment). + /// Returns `impl.alignment`. override @property uint alignment() { return impl.alignment; } /** - Returns $(D impl.goodAllocSize(s)). + Returns `impl.goodAllocSize(s)`. */ override size_t goodAllocSize(size_t s) { @@ -1896,7 +2164,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - Returns $(D impl.allocate(s)). + Returns `impl.allocate(s)`. */ override void[] allocate(size_t s, TypeInfo ti = null) { @@ -1904,7 +2172,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - If $(D impl.alignedAllocate) exists, calls it and returns the result. + If `impl.alignedAllocate` exists, calls it and returns the result. Otherwise, always returns `null`. */ override void[] alignedAllocate(size_t s, uint a) @@ -1925,7 +2193,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) else return Ternary.unknown; } - /// Returns $(D impl.expand(b, s)) if defined, $(D false) otherwise. + /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. override bool expand(ref void[] b, size_t s) { static if (hasMember!(Allocator, "expand")) @@ -1940,7 +2208,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) return impl.reallocate(b, s); } - /// Forwards to $(D impl.alignedReallocate). + /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. bool alignedReallocate(ref void[] b, size_t s, uint a) { static if (!hasMember!(Allocator, "alignedAllocate")) @@ -1967,11 +2235,8 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - If $(D impl.deallocate) is not defined, returns $(D Ternary.unknown). If - $(D impl.deallocate) returns $(D void) (the common case), calls it and - returns $(D Ternary.yes). If $(D impl.deallocate) returns $(D bool), calls - it and returns $(D Ternary.yes) for $(D true), $(D Ternary.no) for $(D - false). + If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards + the call. */ override bool deallocate(void[] b) { @@ -1986,8 +2251,8 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - Calls $(D impl.deallocateAll()) and returns $(D Ternary.yes) if defined, - otherwise returns $(D Ternary.unknown). + Calls `impl.deallocateAll()` and returns the result if defined, + otherwise returns `false`. */ override bool deallocateAll() { @@ -2002,8 +2267,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - Forwards to $(D impl.empty()) if defined, otherwise returns - $(D Ternary.unknown). + Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. */ override Ternary empty() { @@ -2018,7 +2282,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } /** - Returns $(D impl.allocateAll()) if present, $(D null) otherwise. + Returns `impl.allocateAll()` if present, `null` otherwise. */ override void[] allocateAll() { @@ -2033,6 +2297,190 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) } } +/** + +Implementation of `ISharedAllocator` using `Allocator`. This adapts a +statically-built, shareable across threads, allocator type to `ISharedAllocator` +that is directly usable by non-templated code. + +Usually `CSharedAllocatorImpl` is used indirectly by calling +$(LREF processAllocator). +*/ +class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) + : ISharedAllocator +{ + import std.traits : hasMember; + + /** + The implementation is available as a public member. + */ + static if (indirect) + { + private shared Allocator* pimpl; + ref Allocator impl() shared + { + return *pimpl; + } + this(Allocator* pa) shared + { + pimpl = pa; + } + } + else + { + static if (stateSize!Allocator) shared Allocator impl; + else alias impl = Allocator.instance; + } + + /// Returns `impl.alignment`. + override @property uint alignment() shared + { + return impl.alignment; + } + + /** + Returns `impl.goodAllocSize(s)`. + */ + override size_t goodAllocSize(size_t s) shared + { + return impl.goodAllocSize(s); + } + + /** + Returns `impl.allocate(s)`. + */ + override void[] allocate(size_t s, TypeInfo ti = null) shared + { + return impl.allocate(s); + } + + /** + If `impl.alignedAllocate` exists, calls it and returns the result. + Otherwise, always returns `null`. + */ + override void[] alignedAllocate(size_t s, uint a) shared + { + static if (hasMember!(Allocator, "alignedAllocate")) + return impl.alignedAllocate(s, a); + else + return null; + } + + /** + If `Allocator` implements `owns`, forwards to it. Otherwise, returns + `Ternary.unknown`. + */ + override Ternary owns(void[] b) shared + { + static if (hasMember!(Allocator, "owns")) return impl.owns(b); + else return Ternary.unknown; + } + + /// Returns $(D impl.expand(b, s)) if defined, `false` otherwise. + override bool expand(ref void[] b, size_t s) shared + { + static if (hasMember!(Allocator, "expand")) + return impl.expand(b, s); + else + return s == 0; + } + + /// Returns $(D impl.reallocate(b, s)). + override bool reallocate(ref void[] b, size_t s) shared + { + return impl.reallocate(b, s); + } + + /// Forwards to `impl.alignedReallocate` if defined, `false` otherwise. + bool alignedReallocate(ref void[] b, size_t s, uint a) shared + { + static if (!hasMember!(Allocator, "alignedAllocate")) + { + return false; + } + else + { + return impl.alignedReallocate(b, s, a); + } + } + + // Undocumented for now + Ternary resolveInternalPointer(const void* p, ref void[] result) shared + { + static if (hasMember!(Allocator, "resolveInternalPointer")) + { + return impl.resolveInternalPointer(p, result); + } + else + { + return Ternary.unknown; + } + } + + /** + If `impl.deallocate` is not defined, returns `false`. Otherwise it forwards + the call. + */ + override bool deallocate(void[] b) shared + { + static if (hasMember!(Allocator, "deallocate")) + { + return impl.deallocate(b); + } + else + { + return false; + } + } + + /** + Calls `impl.deallocateAll()` and returns the result if defined, + otherwise returns `false`. + */ + override bool deallocateAll() shared + { + static if (hasMember!(Allocator, "deallocateAll")) + { + return impl.deallocateAll(); + } + else + { + return false; + } + } + + /** + Forwards to `impl.empty()` if defined, otherwise returns `Ternary.unknown`. + */ + override Ternary empty() shared + { + static if (hasMember!(Allocator, "empty")) + { + return Ternary(impl.empty); + } + else + { + return Ternary.unknown; + } + } + + /** + Returns `impl.allocateAll()` if present, `null` otherwise. + */ + override void[] allocateAll() shared + { + static if (hasMember!(Allocator, "allocateAll")) + { + return impl.allocateAll(); + } + else + { + return null; + } + } +} + + // Example in intro above @system unittest { From 6dd51ec8f181719041c07dc0ee381e8b0144b043 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 26 Apr 2017 13:21:30 +0100 Subject: [PATCH 076/262] Throw exception when positional format spec exceeds args.length Before this threw "Orphan format arguments: args[3..2]" (and that is due to be removed). --- std/format.d | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/std/format.d b/std/format.d index b4bc329700c..c3d29bdcd33 100644 --- a/std/format.d +++ b/std/format.d @@ -579,7 +579,11 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) { foreach (i; spec.indexStart - 1 .. spec.indexEnd) { - if (A.length <= i) break; + if (A.length <= i) + throw new FormatException( + text("Positional specifier %", i + 1, '$', spec.spec, + " index exceeds ", A.length)); + if (__ctfe) formatNth(w, spec, i, args); else @@ -4040,11 +4044,6 @@ private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f } } -@safe pure unittest -{ - assert(format("%2$s, %1$s", "2nd", "1st") == "1st, 2nd"); -} - @safe pure unittest { int[] a = [ 1, 3, 2 ]; @@ -4225,6 +4224,9 @@ void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, s 42, 0); assert(w.data == "Numbers 0 and 42 are reversed and 420 repeated", w.data); + assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2)) + == "Positional specifier %3$s index exceeds 2"); + w.clear(); formattedWrite(w, "asd%s", 23); assert(w.data == "asd23", w.data); From 8803c5e58254b27b217359bbb87dba5d4490c823 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 2 May 2017 13:15:57 +0200 Subject: [PATCH 077/262] allow precise scanning of DATA/TLS segment: buffers for preallocated class instances should be typed void[], not ubyte[] and must be properly aligned --- std/experimental/logger/core.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index ab63a7e793a..56741bbbc1f 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -1631,7 +1631,7 @@ private @property Logger defaultSharedLoggerImpl() @trusted import std.conv : emplace; import std.stdio : stderr; - static __gshared ubyte[__traits(classInstanceSize, FileLogger)] _buffer; + static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer; synchronized (stdSharedLoggerMutex) { @@ -1763,7 +1763,7 @@ private @property Logger stdThreadLocalLogImpl() @trusted { import std.conv : emplace; - static ubyte[__traits(classInstanceSize, StdForwardLogger)] _buffer; + static align(StdForwardLogger.alignof) void[__traits(classInstanceSize, StdForwardLogger)] _buffer; auto buffer = cast(ubyte[]) _buffer; From a3e7298686f97e58d6fe44d8c0ee3a15444ac632 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 2 May 2017 15:03:24 +0200 Subject: [PATCH 078/262] workaround ld rejecting explicit alignment --- std/experimental/logger/core.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 56741bbbc1f..22ba7d25283 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -1763,7 +1763,7 @@ private @property Logger stdThreadLocalLogImpl() @trusted { import std.conv : emplace; - static align(StdForwardLogger.alignof) void[__traits(classInstanceSize, StdForwardLogger)] _buffer; + static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer; auto buffer = cast(ubyte[]) _buffer; From 8ceb2542e1ca51aef22afd28680c96fb4b1624f7 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 18 Mar 2017 01:24:22 -0700 Subject: [PATCH 079/262] Move std/datetime.d into std/datetime/package.d. --- posix.mak | 7 ++++--- std/{datetime.d => datetime/package.d} | 0 win32.mak | 16 ++++++++-------- win64.mak | 11 +++++++---- 4 files changed, 19 insertions(+), 15 deletions(-) rename std/{datetime.d => datetime/package.d} (100%) diff --git a/posix.mak b/posix.mak index 33eae909424..4f1b0768530 100644 --- a/posix.mak +++ b/posix.mak @@ -164,7 +164,7 @@ P2MODULES=$(foreach P,$1,$(addprefix $P/,$(PACKAGE_$(subst /,_,$P)))) # xy/zz is in variable PACKAGE_xy_zz. This allows automation in iterating # packages and their modules. STD_PACKAGES = std $(addprefix std/,\ - algorithm container digest experimental/allocator \ + algorithm container datetime digest experimental/allocator \ experimental/allocator/building_blocks experimental/logger \ net \ experimental range regex) @@ -172,7 +172,7 @@ STD_PACKAGES = std $(addprefix std/,\ # Modules broken down per package PACKAGE_std = array ascii base64 bigint bitmanip compiler complex concurrency \ - conv csv datetime demangle encoding exception file format \ + conv csv demangle encoding exception file format \ functional getopt json math mathspecial meta mmfile numeric \ outbuffer parallelism path process random signals socket stdint \ stdio string system traits typecons typetuple uni \ @@ -181,6 +181,7 @@ PACKAGE_std_experimental = checkedint typecons PACKAGE_std_algorithm = comparison iteration mutation package searching setops \ sorting PACKAGE_std_container = array binaryheap dlist package rbtree slist util +PACKAGE_std_datetime = package PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha PACKAGE_std_experimental_logger = core filelogger \ nulllogger multilogger package @@ -557,7 +558,7 @@ publictests: $(LIB) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/package.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" .PHONY : auto-tester-build auto-tester-build: all checkwhitespace diff --git a/std/datetime.d b/std/datetime/package.d similarity index 100% rename from std/datetime.d rename to std/datetime/package.d diff --git a/win32.mak b/win32.mak index fe0b826f4a8..ab049bf98ce 100644 --- a/win32.mak +++ b/win32.mak @@ -150,9 +150,6 @@ SRC_STD_3a= \ std\concurrency.d \ std\concurrencybase.d -SRC_STD_3b= \ - std\datetime.d - SRC_STD_4= \ std\uuid.d @@ -175,7 +172,6 @@ SRC_STD= \ $(SRC_STD_2a) \ $(SRC_STD_3) \ $(SRC_STD_3a) \ - $(SRC_STD_3b) \ $(SRC_STD_4) \ $(SRC_STD_6) \ $(SRC_STD_7) @@ -199,6 +195,9 @@ SRC_STD_CONTAINER= \ std\container\util.d \ std\container\package.d +SRC_STD_DATETIME= \ + std\datetime\package.d + SRC_STD_DIGEST= \ std\digest\crc.d \ std\digest\sha.d \ @@ -342,6 +341,7 @@ SRC_TO_COMPILE= \ $(SRC_STD) \ $(SRC_STD_ALGO) \ $(SRC_STD_CONTAINER) \ + $(SRC_STD_DATETIME) \ $(SRC_STD_DIGEST) \ $(SRC_STD_NET) \ $(SRC_STD_RANGE) \ @@ -567,7 +567,7 @@ unittest : $(LIB) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest2a.obj $(SRC_STD_2a) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3.obj $(SRC_STD_3) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3a.obj $(SRC_STD_3a) - $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3b.obj $(SRC_STD_3b) + $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest3b.obj $(SRC_STD_DATETIME) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest4.obj $(SRC_STD_4) $(SRC_STD_DIGEST) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest5.obj $(SRC_STD_ALGO) $(DMD) $(UDFLAGS) -L/co -c -unittest -ofunittest6.obj $(SRC_STD_6) $(SRC_STD_CONTAINER) $(SRC_STD_EXP_ALLOC) $(SRC_STD_EXP_LOGGER) @@ -628,7 +628,7 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=92 -unittest -main -run std\exception.d $(DMD) -conf= -cov=73 -unittest -main -run std\concurrency.d $(DMD) -conf= -cov=100 -unittest -main -run std\concurrencybase.d - $(DMD) -conf= -cov=95 -unittest -main -run std\datetime.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d $(DMD) -conf= -cov=96 -unittest -main -run std\uuid.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\crc.d $(DMD) -conf= -cov=55 -unittest -main -run std\digest\sha.d @@ -823,8 +823,8 @@ $(DOC)\std_range_interfaces.html : $(STDDOC) std\range\interfaces.d $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DMD) -c -o- $(DFLAGS) -Df$(DOC)\std_csv.html $(STDDOC) std\csv.d -$(DOC)\std_datetime.html : $(STDDOC) std\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime.d +$(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d diff --git a/win64.mak b/win64.mak index b8c3ccb5380..c7e512c4ef4 100644 --- a/win64.mak +++ b/win64.mak @@ -157,7 +157,6 @@ SRC_STD_3c= \ std\concurrencybase.d SRC_STD_3d= \ - std\datetime.d \ std\bitmanip.d \ std\typecons.d @@ -221,6 +220,9 @@ SRC_STD_CONTAINER= \ std\container\util.d \ std\container\package.d +SRC_STD_DATETIME= \ + std\datetime\package.d + SRC_STD_DIGEST= \ std\digest\crc.d \ std\digest\sha.d \ @@ -364,6 +366,7 @@ SRC_TO_COMPILE= \ $(SRC_STD) \ $(SRC_STD_ALGO) \ $(SRC_STD_CONTAINER) \ + $(SRC_STD_DATETIME) \ $(SRC_STD_DIGEST) \ $(SRC_STD_NET) \ $(SRC_STD_RANGE) \ @@ -601,7 +604,7 @@ unittest : $(LIB) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3a.obj $(SRC_STD_3a) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3b.obj $(SRC_STD_3b) $(DMD) $(UDFLAGS) -c -unittest -ofunittest3c.obj $(SRC_STD_3c) - $(DMD) $(UDFLAGS) -c -unittest -ofunittest3d.obj $(SRC_STD_3d) + $(DMD) $(UDFLAGS) -c -unittest -ofunittest3d.obj $(SRC_STD_3d) $(SRC_STD_DATETIME) $(DMD) $(UDFLAGS) -c -unittest -ofunittest4.obj $(SRC_STD_4) $(SRC_STD_DIGEST) $(DMD) $(UDFLAGS) -c -unittest -ofunittest5a.obj $(SRC_STD_ALGO_1) $(DMD) $(UDFLAGS) -c -unittest -ofunittest5b.obj $(SRC_STD_ALGO_2) @@ -800,8 +803,8 @@ $(DOC)\std_range_interfaces.html : $(STDDOC) std\range\interfaces.d $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DMD) -c -o- $(DFLAGS) -Df$(DOC)\std_csv.html $(STDDOC) std\csv.d -$(DOC)\std_datetime.html : $(STDDOC) std\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime.d +$(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d From e532c4f1553c158fea4a03d10b514956b4f517f1 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 18 Mar 2017 01:50:59 -0700 Subject: [PATCH 080/262] Create the new (empty) files in std/datetime for the split. --- posix.mak | 4 ++-- std/datetime/common.d | 10 +++++++++ std/datetime/date.d | 10 +++++++++ std/datetime/datetime.d | 10 +++++++++ std/datetime/interval.d | 10 +++++++++ std/datetime/package.d | 8 ++++++++ std/datetime/systime.d | 10 +++++++++ std/datetime/timeofday.d | 10 +++++++++ std/datetime/timezone.d | 10 +++++++++ win32.mak | 44 +++++++++++++++++++++++++++++++++++++++- win64.mak | 37 ++++++++++++++++++++++++++++++++- 11 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 std/datetime/common.d create mode 100644 std/datetime/date.d create mode 100644 std/datetime/datetime.d create mode 100644 std/datetime/interval.d create mode 100644 std/datetime/systime.d create mode 100644 std/datetime/timeofday.d create mode 100644 std/datetime/timezone.d diff --git a/posix.mak b/posix.mak index 4f1b0768530..b294784d582 100644 --- a/posix.mak +++ b/posix.mak @@ -181,7 +181,7 @@ PACKAGE_std_experimental = checkedint typecons PACKAGE_std_algorithm = comparison iteration mutation package searching setops \ sorting PACKAGE_std_container = array binaryheap dlist package rbtree slist util -PACKAGE_std_datetime = package +PACKAGE_std_datetime = common date datetime interval package systime timeofday timezone PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha PACKAGE_std_experimental_logger = core filelogger \ nulllogger multilogger package @@ -558,7 +558,7 @@ publictests: $(LIB) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/package.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/common.d,datetime/date.d,datetime/datetime.d,datetime/interval.d,datetime/package.d,datetime/systime.d,datetime/timeofday.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" .PHONY : auto-tester-build auto-tester-build: all checkwhitespace diff --git a/std/datetime/common.d b/std/datetime/common.d new file mode 100644 index 00000000000..9c5b5683d83 --- /dev/null +++ b/std/datetime/common.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.common; diff --git a/std/datetime/date.d b/std/datetime/date.d new file mode 100644 index 00000000000..39d54c4e457 --- /dev/null +++ b/std/datetime/date.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.date; diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d new file mode 100644 index 00000000000..322b8b65175 --- /dev/null +++ b/std/datetime/datetime.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.datetime; diff --git a/std/datetime/interval.d b/std/datetime/interval.d new file mode 100644 index 00000000000..d1236fe17a8 --- /dev/null +++ b/std/datetime/interval.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.interval; diff --git a/std/datetime/package.d b/std/datetime/package.d index 4401cabeee4..dc6d958eeae 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -106,6 +106,14 @@ auto restoredTime = SysTime.fromISOExtString(timeString); module std.datetime; public import core.time; +public import std.datetime.common; +public import std.datetime.date; +public import std.datetime.datetime; +public import std.datetime.interval; +public import std.datetime.systime; +public import std.datetime.timeofday; +public import std.datetime.timezone; + import core.exception; // AssertError diff --git a/std/datetime/systime.d b/std/datetime/systime.d new file mode 100644 index 00000000000..8b3cdb293b9 --- /dev/null +++ b/std/datetime/systime.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.systime; diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d new file mode 100644 index 00000000000..8c91bef774a --- /dev/null +++ b/std/datetime/timeofday.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.timeofday; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d new file mode 100644 index 00000000000..761a68471c9 --- /dev/null +++ b/std/datetime/timezone.d @@ -0,0 +1,10 @@ +// Written in the D programming language + +/++ + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis + Source: $(PHOBOSSRC std/_datetime.d) + Macros: + LREF2=$(D $2) ++/ +module std.datetime.timezone; diff --git a/win32.mak b/win32.mak index ab049bf98ce..9e7a42456cc 100644 --- a/win32.mak +++ b/win32.mak @@ -196,7 +196,14 @@ SRC_STD_CONTAINER= \ std\container\package.d SRC_STD_DATETIME= \ - std\datetime\package.d + std\datetime\common.d \ + std\datetime\date.d \ + std\datetime\datetime.d \ + std\datetime\interval.d \ + std\datetime\package.d \ + std\datetime\systime.d \ + std\datetime\timeofday.d \ + std\datetime\timezone.d SRC_STD_DIGEST= \ std\digest\crc.d \ @@ -448,6 +455,13 @@ DOCS= \ $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ + $(DOC)\std_datetime_common.html \ + $(DOC)\std_datetime_date.html \ + $(DOC)\std_datetime_datetime.html \ + $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_systime.html \ + $(DOC)\std_datetime_timeofday.html \ + $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ $(DOC)\std_exception.html \ @@ -628,7 +642,14 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=92 -unittest -main -run std\exception.d $(DMD) -conf= -cov=73 -unittest -main -run std\concurrency.d $(DMD) -conf= -cov=100 -unittest -main -run std\concurrencybase.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\common.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\date.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\datetime.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\interval.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\systime.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timeofday.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timezone.d $(DMD) -conf= -cov=96 -unittest -main -run std\uuid.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\crc.d $(DMD) -conf= -cov=55 -unittest -main -run std\digest\sha.d @@ -826,6 +847,27 @@ $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d +$(DOC)\std_datetime_common.html : $(STDDOC) std\datetime\common.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_common.html $(STDDOC) std\datetime\common.d + +$(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d + +$(DOC)\std_datetime_datetime.html : $(STDDOC) std\datetime\datetime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_datetime.html $(STDDOC) std\datetime\datetime.d + +$(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d + +$(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d + +$(DOC)\std_datetime_timeofday.html : $(STDDOC) std\datetime\timeofday.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timeofday.html $(STDDOC) std\datetime\timeofday.d + +$(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d + $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d diff --git a/win64.mak b/win64.mak index c7e512c4ef4..cdb5ea9cf87 100644 --- a/win64.mak +++ b/win64.mak @@ -221,7 +221,14 @@ SRC_STD_CONTAINER= \ std\container\package.d SRC_STD_DATETIME= \ - std\datetime\package.d + std\datetime\common.d \ + std\datetime\date.d \ + std\datetime\datetime.d \ + std\datetime\interval.d \ + std\datetime\package.d \ + std\datetime\systime.d \ + std\datetime\timeofday.d \ + std\datetime\timezone.d SRC_STD_DIGEST= \ std\digest\crc.d \ @@ -473,6 +480,13 @@ DOCS= \ $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ + $(DOC)\std_datetime_common.html \ + $(DOC)\std_datetime_date.html \ + $(DOC)\std_datetime_datetime.html \ + $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_systime.html \ + $(DOC)\std_datetime_timeofday.html \ + $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ $(DOC)\std_exception.html \ @@ -806,6 +820,27 @@ $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d +$(DOC)\std_datetime_common.html : $(STDDOC) std\datetime\common.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_common.html $(STDDOC) std\datetime\common.d + +$(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d + +$(DOC)\std_datetime_datetime.html : $(STDDOC) std\datetime\datetime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_datetime.html $(STDDOC) std\datetime\datetime.d + +$(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d + +$(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d + +$(DOC)\std_datetime_timeofday.html : $(STDDOC) std\datetime\timeofday.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timeofday.html $(STDDOC) std\datetime\timeofday.d + +$(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d + $(DOC)\std_demangle.html : $(STDDOC) std\demangle.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_demangle.html $(STDDOC) std\demangle.d From 88fc139610b5e84c673a9f95f76db2652cb231dc Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 3 May 2017 15:46:55 +0100 Subject: [PATCH 081/262] Show type for missing width/precision/separatorChar Also add tests for incorrect arguments with separatorChar. --- std/format.d | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/std/format.d b/std/format.d index c3d29bdcd33..5b8736ec170 100644 --- a/std/format.d +++ b/std/format.d @@ -4066,7 +4066,7 @@ private int getNthInt(A...)(uint index, A args) private T getNth(alias Condition, T, A...)(uint index, A args) { - import std.conv : to; + import std.conv : text, to; switch (index) { @@ -4084,14 +4084,14 @@ private T getNth(alias Condition, T, A...)(uint index, A args) } } default: - throw new FormatException("missing argument #" ~ to!string(index + 1)); + throw new FormatException( + text("Missing ", T.stringof, " for argument #", index + 1)); } } @safe unittest { - assert(format("%*.d, %.*f", 3, 7, 2, 3.1415) == " 7, 3.14"); - + // width/precision assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) == "int expected, not double"); assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) @@ -4099,7 +4099,15 @@ private T getNth(alias Condition, T, A...)(uint index, A args) assert(collectExceptionMsg!FormatException(format("%.*d", 5)) == "Orphan format specifier: %d"); assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) - == "missing argument #2"); + == "Missing int for argument #2"); + + // separatorCharPos + assert(collectExceptionMsg!FormatException(format("%,?d", 5)) + == "dchar expected, not int"); + assert(collectExceptionMsg!FormatException(format("%,?d", '?')) + == "Orphan format specifier: %d"); + assert(collectExceptionMsg!FormatException(format("%.*,?d", 5)) + == "Missing dchar for argument #2"); } /* ======================== Unit Tests ====================================== */ From 88e26cbda6bddd0fd2ff67705d62d251a0647c96 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 3 May 2017 16:08:34 +0100 Subject: [PATCH 082/262] Improve format separator docs --- std/format.d | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/std/format.d b/std/format.d index 5b8736ec170..7943f6b344e 100644 --- a/std/format.d +++ b/std/format.d @@ -1021,19 +1021,17 @@ if (is(Unqual!Char == Char)) int precision = UNSPECIFIED; /** - Separator. Its value defines how many digits are printed between - $(D SeparatorChar). + Number of digits printed between _separators. */ int separators = UNSPECIFIED; /** - Separator. Its value defines how many digits are printed between - $(D SeparatorChar). + Set to `DYNAMIC` when the separator character is supplied at runtime. */ int separatorCharPos = UNSPECIFIED; /** - SeparatorChar. The character that is inserted every $(D separators) character. + Character to insert between digits. */ dchar separatorChar = ','; From f9da3c1c134c2d13429e04bccc12d476cd5718bb Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 18 Mar 2017 03:55:22 -0700 Subject: [PATCH 083/262] Move several items from std/datetime/package.d to std/datetime/common.d. --- std/datetime/common.d | 575 +++++++++++++++++++++++++++++++++++++++++ std/datetime/package.d | 552 --------------------------------------- 2 files changed, 575 insertions(+), 552 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index 9c5b5683d83..7f864c3c542 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -8,3 +8,578 @@ LREF2=$(D $2) +/ module std.datetime.common; + +import core.time : TimeException; +import std.typecons : Flag; + + +/++ + Exception type used by std.datetime. It's an alias to $(REF TimeException, core,time). + Either can be caught without concern about which + module it came from. + +/ +alias DateTimeException = TimeException; + + +/++ + Represents the 12 months of the Gregorian year (January is 1). + +/ +enum Month : ubyte { jan = 1, /// + feb, /// + mar, /// + apr, /// + may, /// + jun, /// + jul, /// + aug, /// + sep, /// + oct, /// + nov, /// + dec /// + } + + +/++ + Represents the 7 days of the Gregorian week (Sunday is 0). + +/ +enum DayOfWeek : ubyte { sun = 0, /// + mon, /// + tue, /// + wed, /// + thu, /// + fri, /// + sat /// + } + + +/++ + In some date calculations, adding months or years can cause the date to fall + on a day of the month which is not valid (e.g. February 29th 2001 or + June 31st 2000). If overflow is allowed (as is the default), then the month + will be incremented accordingly (so, February 29th 2001 would become + March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow + is not allowed, then the day will be adjusted to the last valid day in that + month (so, February 29th 2001 would become February 28th 2001 and + June 31st 2000 would become June 30th 2000). + + AllowDayOverflow only applies to calculations involving months or years. + + If set to $(D AllowDayOverflow.no), then day overflow is not allowed. + + Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is + allowed. + +/ +alias AllowDayOverflow = Flag!"allowDayOverflow"; + + +/++ + Array of the strings representing time units, starting with the smallest + unit and going to the largest. It does not include $(D "nsecs"). + + Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), + $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), + $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and + $(D "years") + +/ +immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", + "hours", "days", "weeks", "months", "years"]; + + +/++ + Returns whether the given value is valid for the given unit type when in a + time point. Naturally, a duration is not held to a particular range, but + the values in a time point are (e.g. a month must be in the range of + 1 - 12 inclusive). + + Params: + units = The units of time to validate. + value = The number to validate. + +/ +bool valid(string units)(int value) @safe pure nothrow +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + static if (units == "months") + return value >= Month.jan && value <= Month.dec; + else static if (units == "hours") + return value >= 0 && value <= 23; + else static if (units == "minutes") + return value >= 0 && value <= 59; + else static if (units == "seconds") + return value >= 0 && value <= 59; +} + +/// +@safe unittest +{ + assert(valid!"hours"(12)); + assert(!valid!"hours"(32)); + assert(valid!"months"(12)); + assert(!valid!"months"(13)); +} + +/++ + Returns whether the given day is valid for the given year and month. + + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + +/ +bool valid(string units)(int year, int month, int day) @safe pure nothrow +if (units == "days") +{ + return day > 0 && day <= maxDay(year, month); +} + + +/++ + Params: + units = The units of time to validate. + value = The number to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!units(value)) is false. + +/ +void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + import std.format : format; + + static if (units == "months") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); + } + else static if (units == "hours") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); + } + else static if (units == "minutes") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); + } + else static if (units == "seconds") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); + } +} + + +/++ + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. + +/ +void enforceValid(string units) + (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "days") +{ + import std.format : format; + if (!valid!"days"(year, month, day)) + throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); +} + + +/++ + Returns the number of days from the current day of the week to the given + day of the week. If they are the same, then the result is 0. + + Params: + currDoW = The current day of the week. + dow = The day of the week to get the number of days to. + +/ +int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow +{ + if (currDoW == dow) + return 0; + if (currDoW < dow) + return dow - currDoW; + return (DayOfWeek.sat - currDoW) + dow + 1; +} + +@safe unittest +{ + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); + + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); + + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); + + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); + + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); + + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); + + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); +} + + +/++ + Returns the number of months from the current months of the year to the + given month of the year. If they are the same, then the result is 0. + + Params: + currMonth = The current month of the year. + month = The month of the year to get the number of months to. + +/ +int monthsToMonth(int currMonth, int month) @safe pure +{ + enforceValid!"months"(currMonth); + enforceValid!"months"(month); + + if (currMonth == month) + return 0; + if (currMonth < month) + return month - currMonth; + return (Month.dec - currMonth) + month; +} + +@safe unittest +{ + assert(monthsToMonth(Month.jan, Month.jan) == 0); + assert(monthsToMonth(Month.jan, Month.feb) == 1); + assert(monthsToMonth(Month.jan, Month.mar) == 2); + assert(monthsToMonth(Month.jan, Month.apr) == 3); + assert(monthsToMonth(Month.jan, Month.may) == 4); + assert(monthsToMonth(Month.jan, Month.jun) == 5); + assert(monthsToMonth(Month.jan, Month.jul) == 6); + assert(monthsToMonth(Month.jan, Month.aug) == 7); + assert(monthsToMonth(Month.jan, Month.sep) == 8); + assert(monthsToMonth(Month.jan, Month.oct) == 9); + assert(monthsToMonth(Month.jan, Month.nov) == 10); + assert(monthsToMonth(Month.jan, Month.dec) == 11); + + assert(monthsToMonth(Month.may, Month.jan) == 8); + assert(monthsToMonth(Month.may, Month.feb) == 9); + assert(monthsToMonth(Month.may, Month.mar) == 10); + assert(monthsToMonth(Month.may, Month.apr) == 11); + assert(monthsToMonth(Month.may, Month.may) == 0); + assert(monthsToMonth(Month.may, Month.jun) == 1); + assert(monthsToMonth(Month.may, Month.jul) == 2); + assert(monthsToMonth(Month.may, Month.aug) == 3); + assert(monthsToMonth(Month.may, Month.sep) == 4); + assert(monthsToMonth(Month.may, Month.oct) == 5); + assert(monthsToMonth(Month.may, Month.nov) == 6); + assert(monthsToMonth(Month.may, Month.dec) == 7); + + assert(monthsToMonth(Month.oct, Month.jan) == 3); + assert(monthsToMonth(Month.oct, Month.feb) == 4); + assert(monthsToMonth(Month.oct, Month.mar) == 5); + assert(monthsToMonth(Month.oct, Month.apr) == 6); + assert(monthsToMonth(Month.oct, Month.may) == 7); + assert(monthsToMonth(Month.oct, Month.jun) == 8); + assert(monthsToMonth(Month.oct, Month.jul) == 9); + assert(monthsToMonth(Month.oct, Month.aug) == 10); + assert(monthsToMonth(Month.oct, Month.sep) == 11); + assert(monthsToMonth(Month.oct, Month.oct) == 0); + assert(monthsToMonth(Month.oct, Month.nov) == 1); + assert(monthsToMonth(Month.oct, Month.dec) == 2); + + assert(monthsToMonth(Month.dec, Month.jan) == 1); + assert(monthsToMonth(Month.dec, Month.feb) == 2); + assert(monthsToMonth(Month.dec, Month.mar) == 3); + assert(monthsToMonth(Month.dec, Month.apr) == 4); + assert(monthsToMonth(Month.dec, Month.may) == 5); + assert(monthsToMonth(Month.dec, Month.jun) == 6); + assert(monthsToMonth(Month.dec, Month.jul) == 7); + assert(monthsToMonth(Month.dec, Month.aug) == 8); + assert(monthsToMonth(Month.dec, Month.sep) == 9); + assert(monthsToMonth(Month.dec, Month.oct) == 10); + assert(monthsToMonth(Month.dec, Month.nov) == 11); + assert(monthsToMonth(Month.dec, Month.dec) == 0); +} + + +/++ + Whether the given Gregorian Year is a leap year. + + Params: + year = The year to to be tested. + +/ +bool yearIsLeapYear(int year) @safe pure nothrow +{ + if (year % 400 == 0) + return true; + if (year % 100 == 0) + return false; + return year % 4 == 0; +} + +@safe unittest +{ + import std.format : format; + foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, + 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) + { + assert(!yearIsLeapYear(year), format("year: %s.", year)); + assert(!yearIsLeapYear(-year), format("year: %s.", year)); + } + + foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) + { + assert(yearIsLeapYear(year), format("year: %s.", year)); + assert(yearIsLeapYear(-year), format("year: %s.", year)); + } +} + + +/++ + Whether the given type defines all of the necessary functions for it to + function as a time point. + + 1. $(D T) must define a static property named $(D min) which is the smallest + value of $(D T) as $(Unqual!T). + + 2. $(D T) must define a static property named $(D max) which is the largest + value of $(D T) as $(Unqual!T). + + 3. $(D T) must define an $(D opBinary) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D Unqual!T). + + 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). + + 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T) + and returns returns $(REF Duration, core,time). + +/ +template isTimePoint(T) +{ + import core.time : Duration; + import std.traits : FunctionAttribute, functionAttributes, Unqual; + + enum isTimePoint = hasMin && + hasMax && + hasOverloadedOpBinaryWithDuration && + hasOverloadedOpAssignWithDuration && + hasOverloadedOpBinaryWithSelf && + !is(U == Duration); + +private: + + alias U = Unqual!T; + + enum hasMin = __traits(hasMember, T, "min") && + is(typeof(T.min) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.min));})); + + enum hasMax = __traits(hasMember, T, "max") && + is(typeof(T.max) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.max));})); + + enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && + is(typeof(T.init - Duration.init) == U); + + enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && + is(typeof(U.init -= Duration.init) == U) && + is(typeof( + { + // Until the overload with TickDuration is removed, this is ambiguous. + //alias add = U.opOpAssign!"+"; + //alias sub = U.opOpAssign!"-"; + U u; + auto ref add() { return u += Duration.init; } + auto ref sub() { return u -= Duration.init; } + alias FA = FunctionAttribute; + static assert((functionAttributes!add & FA.ref_) != 0); + static assert((functionAttributes!sub & FA.ref_) != 0); + })); + + enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); +} + +/// +@safe unittest +{ + import core.time : Duration; + import std.datetime : Date, DateTime, Interval, SysTime, TimeOfDay; // temporary + /+ + import std.datetime.date : Date; + import std.datetime.datetime : DateTime; + import std.datetime.interval : Interval; + import std.datetime.systime : SysTime; + import std.datetime.timeofday : TimeOfDay; + +/ + + static assert(isTimePoint!Date); + static assert(isTimePoint!DateTime); + static assert(isTimePoint!SysTime); + static assert(isTimePoint!TimeOfDay); + + static assert(!isTimePoint!int); + static assert(!isTimePoint!Duration); + static assert(!isTimePoint!(Interval!SysTime)); +} + +@safe unittest +{ + import core.time; + import std.datetime : Date, DateTime, Interval, NegInfInterval, PosInfInterval, SysTime, TimeOfDay; // temporary + /+ + import std.datetime.date; + import std.datetime.datetime; + import std.datetime.interval; + import std.datetime.systime; + import std.datetime.timeofday; + +/ + import std.meta : AliasSeq; + + foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) + { + static assert(isTimePoint!(const TP), TP.stringof); + static assert(isTimePoint!(immutable TP), TP.stringof); + } + + foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) + static assert(!isTimePoint!T, T.stringof); +} + + +package: + +/+ + The maximum valid Day in the given month in the given year. + + Params: + year = The year to get the day for. + month = The month of the Gregorian Calendar to get the day for. + +/ +ubyte maxDay(int year, int month) @safe pure nothrow +in +{ + assert(valid!"months"(month)); +} +body +{ + switch (month) + { + case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: + return 31; + case Month.feb: + return yearIsLeapYear(year) ? 29 : 28; + case Month.apr, Month.jun, Month.sep, Month.nov: + return 30; + default: + assert(0, "Invalid month."); + } +} + +@safe unittest +{ + // Test A.D. + assert(maxDay(1999, 1) == 31); + assert(maxDay(1999, 2) == 28); + assert(maxDay(1999, 3) == 31); + assert(maxDay(1999, 4) == 30); + assert(maxDay(1999, 5) == 31); + assert(maxDay(1999, 6) == 30); + assert(maxDay(1999, 7) == 31); + assert(maxDay(1999, 8) == 31); + assert(maxDay(1999, 9) == 30); + assert(maxDay(1999, 10) == 31); + assert(maxDay(1999, 11) == 30); + assert(maxDay(1999, 12) == 31); + + assert(maxDay(2000, 1) == 31); + assert(maxDay(2000, 2) == 29); + assert(maxDay(2000, 3) == 31); + assert(maxDay(2000, 4) == 30); + assert(maxDay(2000, 5) == 31); + assert(maxDay(2000, 6) == 30); + assert(maxDay(2000, 7) == 31); + assert(maxDay(2000, 8) == 31); + assert(maxDay(2000, 9) == 30); + assert(maxDay(2000, 10) == 31); + assert(maxDay(2000, 11) == 30); + assert(maxDay(2000, 12) == 31); + + // Test B.C. + assert(maxDay(-1999, 1) == 31); + assert(maxDay(-1999, 2) == 28); + assert(maxDay(-1999, 3) == 31); + assert(maxDay(-1999, 4) == 30); + assert(maxDay(-1999, 5) == 31); + assert(maxDay(-1999, 6) == 30); + assert(maxDay(-1999, 7) == 31); + assert(maxDay(-1999, 8) == 31); + assert(maxDay(-1999, 9) == 30); + assert(maxDay(-1999, 10) == 31); + assert(maxDay(-1999, 11) == 30); + assert(maxDay(-1999, 12) == 31); + + assert(maxDay(-2000, 1) == 31); + assert(maxDay(-2000, 2) == 29); + assert(maxDay(-2000, 3) == 31); + assert(maxDay(-2000, 4) == 30); + assert(maxDay(-2000, 5) == 31); + assert(maxDay(-2000, 6) == 30); + assert(maxDay(-2000, 7) == 31); + assert(maxDay(-2000, 8) == 31); + assert(maxDay(-2000, 9) == 30); + assert(maxDay(-2000, 10) == 31); + assert(maxDay(-2000, 11) == 30); + assert(maxDay(-2000, 12) == 31); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index dc6d958eeae..371bc12edda 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -172,54 +172,6 @@ else version(Posix) // Section with public enums and constants. //============================================================================== -/++ - Represents the 12 months of the Gregorian year (January is 1). - +/ -enum Month : ubyte { jan = 1, /// - feb, /// - mar, /// - apr, /// - may, /// - jun, /// - jul, /// - aug, /// - sep, /// - oct, /// - nov, /// - dec /// - } - -/++ - Represents the 7 days of the Gregorian week (Sunday is 0). - +/ -enum DayOfWeek : ubyte { sun = 0, /// - mon, /// - tue, /// - wed, /// - thu, /// - fri, /// - sat /// - } - -/++ - In some date calculations, adding months or years can cause the date to fall - on a day of the month which is not valid (e.g. February 29th 2001 or - June 31st 2000). If overflow is allowed (as is the default), then the month - will be incremented accordingly (so, February 29th 2001 would become - March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow - is not allowed, then the day will be adjusted to the last valid day in that - month (so, February 29th 2001 would become February 28th 2001 and - June 31st 2000 would become June 30th 2000). - - AllowDayOverflow only applies to calculations involving months or years. - - If set to $(D AllowDayOverflow.no), then day overflow is not allowed. - - Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is - allowed. - +/ -alias AllowDayOverflow = Flag!"allowDayOverflow"; - /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s $(LREF expand, expand) function which uses it to indicate whether the interval should @@ -282,30 +234,11 @@ alias PopFirst = Flag!"popFirst"; +/ alias AutoStart = Flag!"autoStart"; -/++ - Array of the strings representing time units, starting with the smallest - unit and going to the largest. It does not include $(D "nsecs"). - - Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), - $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), - $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and - $(D "years") - +/ -immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", - "hours", "days", "weeks", "months", "years"]; - //============================================================================== // Section with other types. //============================================================================== -/++ - Exception type used by std.datetime. It's an alias to $(REF TimeException, core,time). - Either can be caught without concern about which - module it came from. - +/ -alias DateTimeException = TimeException; - /++ Effectively a namespace to make it clear that the methods it contains are getting the time from the system clock. It cannot be instantiated. @@ -32121,131 +32054,6 @@ ComparingBenchmarkResult comparingBenchmark(alias baseFunc, // Section with public helper functions and templates. //============================================================================== - -/++ - Whether the given type defines all of the necessary functions for it to - function as a time point. - - 1. $(D T) must define a static property named $(D min) which is the smallest - value of $(D T) as $(Unqual!T). - - 2. $(D T) must define a static property named $(D max) which is the largest - value of $(D T) as $(Unqual!T). - - 3. $(D T) must define an $(D opBinary) for addition and subtraction that - accepts $(REF Duration, core,time) and returns $(D Unqual!T). - - 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that - accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). - - 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T) - and returns returns $(REF Duration, core,time). - +/ -template isTimePoint(T) -{ - import std.traits : FunctionAttribute, functionAttributes; - enum isTimePoint = hasMin && - hasMax && - hasOverloadedOpBinaryWithDuration && - hasOverloadedOpAssignWithDuration && - hasOverloadedOpBinaryWithSelf && - !is(U == Duration); - -private: - - alias U = Unqual!T; - - enum hasMin = __traits(hasMember, T, "min") && - is(typeof(T.min) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.min));})); - - enum hasMax = __traits(hasMember, T, "max") && - is(typeof(T.max) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.max));})); - - enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && - is(typeof(T.init - Duration.init) == U); - - enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && - is(typeof(U.init -= Duration.init) == U) && - is(typeof( - { - // Until the overload with TickDuration is removed, this is ambiguous. - //alias add = U.opOpAssign!"+"; - //alias sub = U.opOpAssign!"-"; - U u; - auto ref add() { return u += Duration.init; } - auto ref sub() { return u -= Duration.init; } - alias FA = FunctionAttribute; - static assert((functionAttributes!add & FA.ref_) != 0); - static assert((functionAttributes!sub & FA.ref_) != 0); - })); - - enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); -} - -/// -@safe unittest -{ - static assert(isTimePoint!Date); - static assert(isTimePoint!DateTime); - static assert(isTimePoint!SysTime); - static assert(isTimePoint!TimeOfDay); - - static assert(!isTimePoint!int); - static assert(!isTimePoint!Duration); - static assert(!isTimePoint!(Interval!SysTime)); -} - -@safe unittest -{ - import std.meta : AliasSeq; - - foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) - { - static assert(isTimePoint!(const TP), TP.stringof); - static assert(isTimePoint!(immutable TP), TP.stringof); - } - - foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) - static assert(!isTimePoint!T, T.stringof); -} - - -/++ - Whether the given Gregorian Year is a leap year. - - Params: - year = The year to to be tested. - +/ -static bool yearIsLeapYear(int year) @safe pure nothrow -{ - if (year % 400 == 0) - return true; - - if (year % 100 == 0) - return false; - - return year % 4 == 0; -} - -@safe unittest -{ - import std.format : format; - foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, - 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) - { - assert(!yearIsLeapYear(year), format("year: %s.", year)); - assert(!yearIsLeapYear(-year), format("year: %s.", year)); - } - - foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) - { - assert(yearIsLeapYear(year), format("year: %s.", year)); - assert(yearIsLeapYear(-year), format("year: %s.", year)); - } -} - /++ Converts from unix time (which uses midnight, January 1st, 1970 UTC as its epoch and seconds as its units) to "std time" (which uses midnight, @@ -33710,280 +33518,6 @@ private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow } -/++ - Returns whether the given value is valid for the given unit type when in a - time point. Naturally, a duration is not held to a particular range, but - the values in a time point are (e.g. a month must be in the range of - 1 - 12 inclusive). - - Params: - units = The units of time to validate. - value = The number to validate. - +/ -bool valid(string units)(int value) @safe pure nothrow -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - static if (units == "months") - return value >= Month.jan && value <= Month.dec; - else static if (units == "hours") - return value >= 0 && value <= TimeOfDay.maxHour; - else static if (units == "minutes") - return value >= 0 && value <= TimeOfDay.maxMinute; - else static if (units == "seconds") - return value >= 0 && value <= TimeOfDay.maxSecond; -} - -/// -@safe unittest -{ - assert(valid!"hours"(12)); - assert(!valid!"hours"(32)); - assert(valid!"months"(12)); - assert(!valid!"months"(13)); -} - - -/++ - Returns whether the given day is valid for the given year and month. - - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - +/ -bool valid(string units)(int year, int month, int day) @safe pure nothrow -if (units == "days") -{ - return day > 0 && day <= maxDay(year, month); -} - - -/++ - Params: - units = The units of time to validate. - value = The number to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!units(value)) is false. - +/ -void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - import std.format : format; - - static if (units == "months") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); - } - else static if (units == "hours") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); - } - else static if (units == "minutes") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); - } - else static if (units == "seconds") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); - } -} - - -/++ - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. - +/ -void enforceValid(string units) - (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "days") -{ - import std.format : format; - if (!valid!"days"(year, month, day)) - throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); -} - - -/++ - Returns the number of months from the current months of the year to the - given month of the year. If they are the same, then the result is 0. - - Params: - currMonth = The current month of the year. - month = The month of the year to get the number of months to. - +/ -static int monthsToMonth(int currMonth, int month) @safe pure -{ - enforceValid!"months"(currMonth); - enforceValid!"months"(month); - - if (currMonth == month) - return 0; - - if (currMonth < month) - return month - currMonth; - - return (Month.dec - currMonth) + month; -} - -@safe unittest -{ - assert(monthsToMonth(Month.jan, Month.jan) == 0); - assert(monthsToMonth(Month.jan, Month.feb) == 1); - assert(monthsToMonth(Month.jan, Month.mar) == 2); - assert(monthsToMonth(Month.jan, Month.apr) == 3); - assert(monthsToMonth(Month.jan, Month.may) == 4); - assert(monthsToMonth(Month.jan, Month.jun) == 5); - assert(monthsToMonth(Month.jan, Month.jul) == 6); - assert(monthsToMonth(Month.jan, Month.aug) == 7); - assert(monthsToMonth(Month.jan, Month.sep) == 8); - assert(monthsToMonth(Month.jan, Month.oct) == 9); - assert(monthsToMonth(Month.jan, Month.nov) == 10); - assert(monthsToMonth(Month.jan, Month.dec) == 11); - - assert(monthsToMonth(Month.may, Month.jan) == 8); - assert(monthsToMonth(Month.may, Month.feb) == 9); - assert(monthsToMonth(Month.may, Month.mar) == 10); - assert(monthsToMonth(Month.may, Month.apr) == 11); - assert(monthsToMonth(Month.may, Month.may) == 0); - assert(monthsToMonth(Month.may, Month.jun) == 1); - assert(monthsToMonth(Month.may, Month.jul) == 2); - assert(monthsToMonth(Month.may, Month.aug) == 3); - assert(monthsToMonth(Month.may, Month.sep) == 4); - assert(monthsToMonth(Month.may, Month.oct) == 5); - assert(monthsToMonth(Month.may, Month.nov) == 6); - assert(monthsToMonth(Month.may, Month.dec) == 7); - - assert(monthsToMonth(Month.oct, Month.jan) == 3); - assert(monthsToMonth(Month.oct, Month.feb) == 4); - assert(monthsToMonth(Month.oct, Month.mar) == 5); - assert(monthsToMonth(Month.oct, Month.apr) == 6); - assert(monthsToMonth(Month.oct, Month.may) == 7); - assert(monthsToMonth(Month.oct, Month.jun) == 8); - assert(monthsToMonth(Month.oct, Month.jul) == 9); - assert(monthsToMonth(Month.oct, Month.aug) == 10); - assert(monthsToMonth(Month.oct, Month.sep) == 11); - assert(monthsToMonth(Month.oct, Month.oct) == 0); - assert(monthsToMonth(Month.oct, Month.nov) == 1); - assert(monthsToMonth(Month.oct, Month.dec) == 2); - - assert(monthsToMonth(Month.dec, Month.jan) == 1); - assert(monthsToMonth(Month.dec, Month.feb) == 2); - assert(monthsToMonth(Month.dec, Month.mar) == 3); - assert(monthsToMonth(Month.dec, Month.apr) == 4); - assert(monthsToMonth(Month.dec, Month.may) == 5); - assert(monthsToMonth(Month.dec, Month.jun) == 6); - assert(monthsToMonth(Month.dec, Month.jul) == 7); - assert(monthsToMonth(Month.dec, Month.aug) == 8); - assert(monthsToMonth(Month.dec, Month.sep) == 9); - assert(monthsToMonth(Month.dec, Month.oct) == 10); - assert(monthsToMonth(Month.dec, Month.nov) == 11); - assert(monthsToMonth(Month.dec, Month.dec) == 0); -} - - -/++ - Returns the number of days from the current day of the week to the given - day of the week. If they are the same, then the result is 0. - - Params: - currDoW = The current day of the week. - dow = The day of the week to get the number of days to. - +/ -static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow -{ - if (currDoW == dow) - return 0; - - if (currDoW < dow) - return dow - currDoW; - - return (DayOfWeek.sat - currDoW) + dow + 1; -} - -@safe unittest -{ - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); - - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); - - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); - - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); - - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); - - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); - - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); -} - - /++ Function for starting to a stop watch time when the function is called and stopping it when its return value goes out of scope and is destroyed. @@ -34293,92 +33827,6 @@ if (validTimeUnits(units) && assert(hnsecs == 2595000000007L); } - -/+ - The maximum valid Day in the given month in the given year. - - Params: - year = The year to get the day for. - month = The month of the Gregorian Calendar to get the day for. - +/ -static ubyte maxDay(int year, int month) @safe pure nothrow -in -{ - assert(valid!"months"(month)); -} -body -{ - switch (month) - { - case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: - return 31; - case Month.feb: - return yearIsLeapYear(year) ? 29 : 28; - case Month.apr, Month.jun, Month.sep, Month.nov: - return 30; - default: - assert(0, "Invalid month."); - } -} - -@safe unittest -{ - //Test A.D. - assert(maxDay(1999, 1) == 31); - assert(maxDay(1999, 2) == 28); - assert(maxDay(1999, 3) == 31); - assert(maxDay(1999, 4) == 30); - assert(maxDay(1999, 5) == 31); - assert(maxDay(1999, 6) == 30); - assert(maxDay(1999, 7) == 31); - assert(maxDay(1999, 8) == 31); - assert(maxDay(1999, 9) == 30); - assert(maxDay(1999, 10) == 31); - assert(maxDay(1999, 11) == 30); - assert(maxDay(1999, 12) == 31); - - assert(maxDay(2000, 1) == 31); - assert(maxDay(2000, 2) == 29); - assert(maxDay(2000, 3) == 31); - assert(maxDay(2000, 4) == 30); - assert(maxDay(2000, 5) == 31); - assert(maxDay(2000, 6) == 30); - assert(maxDay(2000, 7) == 31); - assert(maxDay(2000, 8) == 31); - assert(maxDay(2000, 9) == 30); - assert(maxDay(2000, 10) == 31); - assert(maxDay(2000, 11) == 30); - assert(maxDay(2000, 12) == 31); - - //Test B.C. - assert(maxDay(-1999, 1) == 31); - assert(maxDay(-1999, 2) == 28); - assert(maxDay(-1999, 3) == 31); - assert(maxDay(-1999, 4) == 30); - assert(maxDay(-1999, 5) == 31); - assert(maxDay(-1999, 6) == 30); - assert(maxDay(-1999, 7) == 31); - assert(maxDay(-1999, 8) == 31); - assert(maxDay(-1999, 9) == 30); - assert(maxDay(-1999, 10) == 31); - assert(maxDay(-1999, 11) == 30); - assert(maxDay(-1999, 12) == 31); - - assert(maxDay(-2000, 1) == 31); - assert(maxDay(-2000, 2) == 29); - assert(maxDay(-2000, 3) == 31); - assert(maxDay(-2000, 4) == 30); - assert(maxDay(-2000, 5) == 31); - assert(maxDay(-2000, 6) == 30); - assert(maxDay(-2000, 7) == 31); - assert(maxDay(-2000, 8) == 31); - assert(maxDay(-2000, 9) == 30); - assert(maxDay(-2000, 10) == 31); - assert(maxDay(-2000, 11) == 30); - assert(maxDay(-2000, 12) == 31); -} - - /+ Returns the day of the week for the given day of the Gregorian Calendar. From 2b03373d8fd64328b366895f1beada5db8826f46 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 09:43:33 +0200 Subject: [PATCH 084/262] Move Interval to std.datetime.interval. --- std/datetime/interval.d | 3059 +++++++++++++++++++++++++++++++++++++ std/datetime/package.d | 3228 +-------------------------------------- 2 files changed, 3062 insertions(+), 3225 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index d1236fe17a8..2b10ecba7f4 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -8,3 +8,3062 @@ LREF2=$(D $2) +/ module std.datetime.interval; + +import core.time : Duration; +import std.datetime.common; +import std.exception : enforce; +import std.traits : isIntegral, Unqual; +import std.typecons : Flag; + +version(unittest) import std.exception : assertThrown; + +import core.time : dur; // temporary +import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary +import std.datetime : PosInfInterval, NegInfInterval, IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary +import std.datetime : everyDayOfWeek; // temporary + +/++ + Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s + $(LREF expand, expand) function which uses it to indicate whether the interval should + be expanded backwards (into the past), forwards (into the future), or both. + +/ +enum Direction +{ + /// Backward. + bwd, + + /// Forward. + fwd, + + /// Both backward and forward. + both +} + + +/++ + Used to indicate whether $(D popFront) should be called immediately upon + creating a range. The idea is that for some functions used to generate a + range for an interval, $(D front) is not necessarily a time point which + would ever be generated by the range (e.g. if the range were every Sunday + within an interval, but the interval started on a Monday), so there needs + to be a way to deal with that. To get the first time point in the range to + match what the function generates, then use $(D PopFirst.yes) to indicate + that the range should have $(D popFront) called on it before the range is + returned so that $(D front) is a time point which the function would + generate. To let the first time point not match the generator function, + use $(D PopFront.no). + + For instance, if the function used to generate a range of time points + generated successive Easters (i.e. you're iterating over all of the Easters + within the interval), the initial date probably isn't an Easter. Using + $(D PopFirst.yes) would tell the function which returned the range that + $(D popFront) was to be called so that front would then be an Easter - the + next one generated by the function (which when iterating forward would be + the Easter following the original $(D front), while when iterating backward, + it would be the Easter prior to the original $(D front)). If + $(D PopFirst.no) were used, then $(D front) would remain the original time + point and it would not necessarily be a time point which would be generated + by the range-generating function (which in many cases is exactly what is + desired - e.g. if iterating over every day starting at the beginning of the + interval). + + If set to $(D PopFirst.no), then popFront is not called before returning + the range. + + Otherwise, if set to $(D PopFirst.yes), then popFront is called before + returning the range. + +/ +alias PopFirst = Flag!"popFirst"; + + +/++ + Represents an interval of time. + + An $(D Interval) has a starting point and an end point. The interval of time + is therefore the time starting at the starting point up to, but not + including, the end point. e.g. + + $(BOOKTABLE, + $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) + $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) + $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) + ) + + A range can be obtained from an $(D Interval), allowing iteration over + that interval, with the exact time points which are iterated over depending + on the function which generates the range. + +/ +struct Interval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + end = The time point which ends (but is not included in) the + interval. + + Throws: + $(LREF DateTimeException) if $(D_PARAM end) is before $(D_PARAM begin). + + Example: + -------------------- + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + -------------------- + +/ + this(U)(in TP begin, in U end) pure + if (is(Unqual!TP == Unqual!U)) + { + if (!_valid(begin, end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _begin = cast(TP) begin; + _end = cast(TP) end; + } + + + /++ + Params: + begin = The time point which begins the interval. + duration = The duration from the starting point to the end point. + + Throws: + $(LREF DateTimeException) if the resulting $(D end) is before + $(D begin). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == + Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + -------------------- + +/ + this(D)(in TP begin, in D duration) pure + if (__traits(compiles, begin + duration)) + { + _begin = cast(TP) begin; + _end = begin + duration; + if (!_valid(_begin, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + } + + + /++ + Params: + rhs = The $(LREF2 .Interval, Interval) to assign to this one. + +/ + ref Interval opAssign(const ref Interval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + _end = cast(TP) rhs._end; + return this; + } + + + /++ + Params: + rhs = The $(LREF2 .Interval, Interval) to assign to this one. + +/ + ref Interval opAssign(Interval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + _end = cast(TP) rhs._end; + return this; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == + Date(1996, 1, 2)); + -------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP) _begin; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set $(D begin) to. + + Throws: + $(LREF DateTimeException) if the resulting interval would be invalid. + +/ + @property void begin(TP timePoint) pure + { + if (!_valid(timePoint, _end)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _begin = timePoint; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == + Date(2012, 3, 1)); + -------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP) _end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + + Throws: + $(LREF DateTimeException) if the resulting interval would be invalid. + +/ + @property void end(TP timePoint) pure + { + if (!_valid(_begin, timePoint)) + throw new DateTimeException("Arguments would result in an invalid Interval."); + _end = timePoint; + } + + + /++ + Returns the duration between $(D begin) and $(D end). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == + dur!"days"(5903)); + -------------------- + +/ + @property auto length() const pure nothrow + { + return _end - _begin; + } + + + /++ + Whether the interval's length is 0, that is, whether $(D begin == end). + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); + -------------------- + +/ + @property bool empty() const pure nothrow + { + return _begin == _end; + } + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(1994, 12, 24))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Date(2012, 3, 1))); + -------------------- + +/ + bool contains(in TP timePoint) const pure + { + _enforceNotEmpty(); + return timePoint >= _begin && timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + -------------------- + +/ + bool contains(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return interval._begin >= _begin && + interval._begin < _end && + interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an + interval going to positive infinity can never be contained in a finite + interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); + -------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false (unless this interval is empty), because an + interval beginning at negative infinity can never be contained in a + finite interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); + -------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is before + it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(1994, 12, 24))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(2000, 1, 5))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Date(2012, 3, 1))); + -------------------- + +/ + bool isBefore(in TP timePoint) const pure + { + _enforceNotEmpty(); + return _end <= timePoint; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + -------------------- + +/ + bool isBefore(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(2013, 3, 7)))); + -------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect with it. + + Always returns false (unless this interval is empty) because a finite + interval can never be before an interval beginning at negative infinity. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); + -------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(1994, 12, 24))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(2000, 1, 5))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Date(2012, 3, 1))); + -------------------- + +/ + bool isAfter(in TP timePoint) const pure + { + _enforceNotEmpty(); + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + -------------------- + +/ + bool isAfter(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false (unless this interval is empty) because a finite + interval can never be after an interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); + -------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(1996, 1, 2)))); + -------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + -------------------- + +/ + bool intersects(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return interval._begin < _end && interval._end > _begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(2012, 3, 1)))); + -------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end > interval._begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(2000, 1, 2)))); + -------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect or if + either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + -------------------- + +/ + Interval intersection(in Interval interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + auto end = _end < interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect or if + this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + -------------------- + +/ + Interval intersection(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin > interval._begin ? _begin : interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect or if + this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + -------------------- + +/ + Interval intersection(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval(_begin, _end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + -------------------- + +/ + bool isAdjacent(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + return _begin == interval._end || _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(2012, 3, 1)))); + -------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _end == interval._begin; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(2000, 1, 2)))); + -------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect and are + not adjacent or if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + -------------------- + +/ + Interval merge(in Interval interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect and are + not adjacent or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + PosInfInterval!Date(Date(2012, 3, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + -------------------- + +/ + PosInfInterval!TP merge(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect and are not + adjacent or if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(1996, 1, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); + -------------------- + +/ + NegInfInterval!TP merge(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(LREF DateTimeException) if either interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + -------------------- + +/ + Interval span(in Interval interval) const pure + { + _enforceNotEmpty(); + interval._enforceNotEmpty(); + + auto begin = _begin < interval._begin ? _begin : interval._begin; + auto end = _end > interval._end ? _end : interval._end; + + return Interval(begin, end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + PosInfInterval!Date(Date(2050, 1, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + -------------------- + +/ + PosInfInterval!TP span(in PosInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this interval. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Example: + -------------------- + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(1602, 5, 21))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); + -------------------- + +/ + NegInfInterval!TP span(in NegInfInterval!TP interval) const pure + { + _enforceNotEmpty(); + return NegInfInterval!TP(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the interval forward or backwards in time by the given duration + (a positive duration shifts the interval forward; a negative duration + shifts it backward). Effectively, it does $(D begin += duration) and + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Throws: + $(LREF DateTimeException) this interval is empty or if the resulting + interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); + -------------------- + +/ + void shift(D)(D duration) pure + if (__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + auto begin = _begin + duration; + auto end = _end + duration; + + if (!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the interval forward or backwards in time by the given number + of years and/or months (a positive number of years and months shifts + the interval forward; a negative number shifts it backward). + It adds the years the given years and months to both begin and end. + It effectively calls $(D add!"years"()) and then $(D add!"months"()) + on begin and end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin) and $(D end), causing their month + to increment. + + Throws: + $(LREF DateTimeException) if this interval is empty or if the + resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + _enforceNotEmpty(); + + auto begin = _begin; + auto end = _end; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + + _begin = begin; + _end = end; + } + } + + + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it does $(D begin -= duration) and/or $(D end += duration). Whether + it expands forwards and/or backwards in time is determined by + $(D_PARAM dir). + + Params: + duration = The duration to expand the interval by. + dir = The direction in time to expand the interval. + + Throws: + $(LREF DateTimeException) this interval is empty or if the resulting + interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void expand(D)(D duration, Direction dir = Direction.both) pure + if (__traits(compiles, begin + duration)) + { + _enforceNotEmpty(); + + switch (dir) + { + case Direction.both: + { + auto begin = _begin - duration; + auto end = _end + duration; + + if (!_valid(begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end + duration; + + if (!_valid(_begin, end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin - duration; + + if (!_valid(begin, _end)) + throw new DateTimeException("Argument would result in an invalid Interval."); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from $(D begin) and + adds them to $(D end). Whether it expands forwards and/or backwards + in time is determined by $(D_PARAM dir). + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin) and $(D end), causing their month + to increment. + dir = The direction in time to expand the interval. + + Throws: + $(LREF DateTimeException) if this interval is empty or if the + resulting interval would be invalid. + + Example: + -------------------- + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); + -------------------- + +/ + void expand(T)(T years, + T months = 0, + AllowDayOverflow allowOverflow = AllowDayOverflow.yes, + Direction dir = Direction.both) + if (isIntegral!T) + { + _enforceNotEmpty(); + + switch (dir) + { + case Direction.both: + { + auto begin = _begin; + auto end = _end; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + _end = end; + + return; + } + case Direction.fwd: + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + enforce(_valid(_begin, end), + new DateTimeException("Argument would result in an invalid Interval.")); + _end = end; + + return; + } + case Direction.bwd: + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + enforce(_valid(begin, _end), + new DateTimeException("Argument would result in an invalid Interval.")); + _begin = begin; + + return; + } + default: + assert(0, "Invalid Direction."); + } + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at $(D begin), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point less than or equal to the + current $(D front) of the range, then a $(LREF DateTimeException) will be + thrown. The range will be empty and iteration complete when + $(D_PARAM func) generates a time point equal to or beyond the $(D end) + of the interval. + + There are helper functions in this module which generate common + delegates to pass to $(D fwdRange). Their documentation starts with + "Range-generating function," making them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant if when creating a custom delegate. + + Example: + -------------------- + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) // For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + assert(range.front == Date(2010, 9, 1)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.empty); + -------------------- + +/ + IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.fwd)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at $(D end), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D end). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point greater than or equal to + the current $(D front) of the range, then a $(LREF DateTimeException) will + be thrown. The range will be empty and iteration complete when + $(D_PARAM func) generates a time point equal to or less than the + $(D begin) of the interval. + + There are helper functions in this module which generate common + delegates to pass to $(D bwdRange). Their documentation starts with + "Range-generating function," making them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: + -------------------- + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) // For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.empty); + -------------------- + +/ + IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + _enforceNotEmpty(); + + auto range = IntervalRange!(TP, Direction.bwd)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + // have versions of toString() with extra modifiers, so we define one version + // with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + // Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + // have versions of toString() with extra modifiers, so we define one version + // with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + + +private: +package: // temporary + + /+ + Since we have two versions of toString, we have _toStringImpl + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[%s - %s)", _begin, _end); + catch (Exception e) + assert(0, "format() threw."); + } + + + /+ + Throws: + $(LREF DateTimeException) if this interval is empty. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if (empty) + throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); + } + + + /+ + Whether the given values form a valid time interval. + + Params: + begin = The starting point of the interval. + end = The end point of the interval. + +/ + static bool _valid(in TP begin, in TP end) pure nothrow + { + return begin <= end; + } + + + pure invariant() + { + assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); + } + + + TP _begin; + TP _end; +} + +// Test Interval's constructors. +@safe unittest +{ + assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); + + Interval!Date(Date.init, Date.init); + Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); + Interval!DateTime(DateTime.init, DateTime.init); + Interval!SysTime(SysTime(0), SysTime(0)); + + Interval!DateTime(DateTime.init, dur!"days"(7)); + + // Verify Examples. + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == + Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == + Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == + Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); +} + +// Test Interval's begin. +@safe unittest +{ + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.begin == Date(2010, 7, 4)); + assert(iInterval.begin == Date(2010, 7, 4)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); +} + +// Test Interval's end. +@safe unittest +{ + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.end == Date(2012, 1, 7)); + assert(iInterval.end == Date(2012, 1, 7)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +// Test Interval's length. +@safe unittest +{ + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); + assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); + assert(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == + dur!"seconds"(129_127)); + assert(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)),SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length == + dur!"seconds"(129_127)); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.length != Duration.zero); + assert(iInterval.length != Duration.zero); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); +} + +// Test Interval's empty. +@safe unittest +{ + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); + assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); + assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); + assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); + assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.empty); + assert(!iInterval.empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); +} + +// Test Interval's contains(time point). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); + + assert(!interval.contains(Date(2009, 7, 4))); + assert(!interval.contains(Date(2010, 7, 3))); + assert(interval.contains(Date(2010, 7, 4))); + assert(interval.contains(Date(2010, 7, 5))); + assert(interval.contains(Date(2011, 7, 1))); + assert(interval.contains(Date(2012, 1, 6))); + assert(!interval.contains(Date(2012, 1, 7))); + assert(!interval.contains(Date(2012, 1, 8))); + assert(!interval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(interval.contains(cdate)); + assert(cInterval.contains(cdate)); + assert(iInterval.contains(cdate)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +// Test Interval's contains(Interval). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4),dur!"days"(0)).contains( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.contains(interval)); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); + + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(interval.contains(interval)); + assert(interval.contains(cInterval)); + assert(interval.contains(iInterval)); + assert(!interval.contains(posInfInterval)); + assert(!interval.contains(cPosInfInterval)); + assert(!interval.contains(iPosInfInterval)); + assert(!interval.contains(negInfInterval)); + assert(!interval.contains(cNegInfInterval)); + assert(!interval.contains(iNegInfInterval)); + assert(cInterval.contains(interval)); + assert(cInterval.contains(cInterval)); + assert(cInterval.contains(iInterval)); + assert(!cInterval.contains(posInfInterval)); + assert(!cInterval.contains(cPosInfInterval)); + assert(!cInterval.contains(iPosInfInterval)); + assert(!cInterval.contains(negInfInterval)); + assert(!cInterval.contains(cNegInfInterval)); + assert(!cInterval.contains(iNegInfInterval)); + assert(iInterval.contains(interval)); + assert(iInterval.contains(cInterval)); + assert(iInterval.contains(iInterval)); + assert(!iInterval.contains(posInfInterval)); + assert(!iInterval.contains(cPosInfInterval)); + assert(!iInterval.contains(iPosInfInterval)); + assert(!iInterval.contains(negInfInterval)); + assert(!iInterval.contains(cNegInfInterval)); + assert(!iInterval.contains(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +// Test Interval's isBefore(time point). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); + + assert(!interval.isBefore(Date(2009, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 4))); + assert(!interval.isBefore(Date(2010, 7, 5))); + assert(!interval.isBefore(Date(2011, 7, 1))); + assert(!interval.isBefore(Date(2012, 1, 6))); + assert(interval.isBefore(Date(2012, 1, 7))); + assert(interval.isBefore(Date(2012, 1, 8))); + assert(interval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!interval.isBefore(cdate)); + assert(!cInterval.isBefore(cdate)); + assert(!iInterval.isBefore(cdate)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +// Test Interval's isBefore(Interval). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); + + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(cInterval)); + assert(!interval.isBefore(iInterval)); + assert(!interval.isBefore(posInfInterval)); + assert(!interval.isBefore(cPosInfInterval)); + assert(!interval.isBefore(iPosInfInterval)); + assert(!interval.isBefore(negInfInterval)); + assert(!interval.isBefore(cNegInfInterval)); + assert(!interval.isBefore(iNegInfInterval)); + assert(!cInterval.isBefore(interval)); + assert(!cInterval.isBefore(cInterval)); + assert(!cInterval.isBefore(iInterval)); + assert(!cInterval.isBefore(posInfInterval)); + assert(!cInterval.isBefore(cPosInfInterval)); + assert(!cInterval.isBefore(iPosInfInterval)); + assert(!cInterval.isBefore(negInfInterval)); + assert(!cInterval.isBefore(cNegInfInterval)); + assert(!cInterval.isBefore(iNegInfInterval)); + assert(!iInterval.isBefore(interval)); + assert(!iInterval.isBefore(cInterval)); + assert(!iInterval.isBefore(iInterval)); + assert(!iInterval.isBefore(posInfInterval)); + assert(!iInterval.isBefore(cPosInfInterval)); + assert(!iInterval.isBefore(iPosInfInterval)); + assert(!iInterval.isBefore(negInfInterval)); + assert(!iInterval.isBefore(cNegInfInterval)); + assert(!iInterval.isBefore(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +// Test Interval's isAfter(time point). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); + + assert(interval.isAfter(Date(2009, 7, 4))); + assert(interval.isAfter(Date(2010, 7, 3))); + assert(!interval.isAfter(Date(2010, 7, 4))); + assert(!interval.isAfter(Date(2010, 7, 5))); + assert(!interval.isAfter(Date(2011, 7, 1))); + assert(!interval.isAfter(Date(2012, 1, 6))); + assert(!interval.isAfter(Date(2012, 1, 7))); + assert(!interval.isAfter(Date(2012, 1, 8))); + assert(!interval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!interval.isAfter(cdate)); + assert(!cInterval.isAfter(cdate)); + assert(!iInterval.isAfter(cdate)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +} + +// Test Interval's isAfter(Interval). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAfter(interval)); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); + + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isAfter(interval)); + assert(!interval.isAfter(cInterval)); + assert(!interval.isAfter(iInterval)); + assert(!interval.isAfter(posInfInterval)); + assert(!interval.isAfter(cPosInfInterval)); + assert(!interval.isAfter(iPosInfInterval)); + assert(!interval.isAfter(negInfInterval)); + assert(!interval.isAfter(cNegInfInterval)); + assert(!interval.isAfter(iNegInfInterval)); + assert(!cInterval.isAfter(interval)); + assert(!cInterval.isAfter(cInterval)); + assert(!cInterval.isAfter(iInterval)); + assert(!cInterval.isAfter(posInfInterval)); + assert(!cInterval.isAfter(cPosInfInterval)); + assert(!cInterval.isAfter(iPosInfInterval)); + assert(!cInterval.isAfter(negInfInterval)); + assert(!cInterval.isAfter(cNegInfInterval)); + assert(!cInterval.isAfter(iNegInfInterval)); + assert(!iInterval.isAfter(interval)); + assert(!iInterval.isAfter(cInterval)); + assert(!iInterval.isAfter(iInterval)); + assert(!iInterval.isAfter(posInfInterval)); + assert(!iInterval.isAfter(cPosInfInterval)); + assert(!iInterval.isAfter(iPosInfInterval)); + assert(!iInterval.isAfter(negInfInterval)); + assert(!iInterval.isAfter(cNegInfInterval)); + assert(!iInterval.isAfter(iNegInfInterval)); + + // Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); +} + +// Test Interval's intersects(). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.intersects(interval)); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); + + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(interval.intersects(interval)); + assert(interval.intersects(cInterval)); + assert(interval.intersects(iInterval)); + assert(interval.intersects(posInfInterval)); + assert(interval.intersects(cPosInfInterval)); + assert(interval.intersects(iPosInfInterval)); + assert(interval.intersects(negInfInterval)); + assert(interval.intersects(cNegInfInterval)); + assert(interval.intersects(iNegInfInterval)); + assert(cInterval.intersects(interval)); + assert(cInterval.intersects(cInterval)); + assert(cInterval.intersects(iInterval)); + assert(cInterval.intersects(posInfInterval)); + assert(cInterval.intersects(cPosInfInterval)); + assert(cInterval.intersects(iPosInfInterval)); + assert(cInterval.intersects(negInfInterval)); + assert(cInterval.intersects(cNegInfInterval)); + assert(cInterval.intersects(iNegInfInterval)); + assert(iInterval.intersects(interval)); + assert(iInterval.intersects(cInterval)); + assert(iInterval.intersects(iInterval)); + assert(iInterval.intersects(posInfInterval)); + assert(iInterval.intersects(cPosInfInterval)); + assert(iInterval.intersects(iPosInfInterval)); + assert(iInterval.intersects(negInfInterval)); + assert(iInterval.intersects(cNegInfInterval)); + assert(iInterval.intersects(iNegInfInterval)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +// Test Interval's intersection(). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection( + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); + + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(interval.intersection(interval) == interval); + assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.intersection(interval).empty); + assert(!interval.intersection(cInterval).empty); + assert(!interval.intersection(iInterval).empty); + assert(!interval.intersection(posInfInterval).empty); + assert(!interval.intersection(cPosInfInterval).empty); + assert(!interval.intersection(iPosInfInterval).empty); + assert(!interval.intersection(negInfInterval).empty); + assert(!interval.intersection(cNegInfInterval).empty); + assert(!interval.intersection(iNegInfInterval).empty); + assert(!cInterval.intersection(interval).empty); + assert(!cInterval.intersection(cInterval).empty); + assert(!cInterval.intersection(iInterval).empty); + assert(!cInterval.intersection(posInfInterval).empty); + assert(!cInterval.intersection(cPosInfInterval).empty); + assert(!cInterval.intersection(iPosInfInterval).empty); + assert(!cInterval.intersection(negInfInterval).empty); + assert(!cInterval.intersection(cNegInfInterval).empty); + assert(!cInterval.intersection(iNegInfInterval).empty); + assert(!iInterval.intersection(interval).empty); + assert(!iInterval.intersection(cInterval).empty); + assert(!iInterval.intersection(iInterval).empty); + assert(!iInterval.intersection(posInfInterval).empty); + assert(!iInterval.intersection(cPosInfInterval).empty); + assert(!iInterval.intersection(iPosInfInterval).empty); + assert(!iInterval.intersection(negInfInterval).empty); + assert(!iInterval.intersection(cNegInfInterval).empty); + assert(!iInterval.intersection(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12),Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); +} + +// Test Interval's isAdjacent(). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.isAdjacent(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); + + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(cInterval)); + assert(!interval.isAdjacent(iInterval)); + assert(!interval.isAdjacent(posInfInterval)); + assert(!interval.isAdjacent(cPosInfInterval)); + assert(!interval.isAdjacent(iPosInfInterval)); + assert(!interval.isAdjacent(negInfInterval)); + assert(!interval.isAdjacent(cNegInfInterval)); + assert(!interval.isAdjacent(iNegInfInterval)); + assert(!cInterval.isAdjacent(interval)); + assert(!cInterval.isAdjacent(cInterval)); + assert(!cInterval.isAdjacent(iInterval)); + assert(!cInterval.isAdjacent(posInfInterval)); + assert(!cInterval.isAdjacent(cPosInfInterval)); + assert(!cInterval.isAdjacent(iPosInfInterval)); + assert(!cInterval.isAdjacent(negInfInterval)); + assert(!cInterval.isAdjacent(cNegInfInterval)); + assert(!cInterval.isAdjacent(iNegInfInterval)); + assert(!iInterval.isAdjacent(interval)); + assert(!iInterval.isAdjacent(cInterval)); + assert(!iInterval.isAdjacent(iInterval)); + assert(!iInterval.isAdjacent(posInfInterval)); + assert(!iInterval.isAdjacent(cPosInfInterval)); + assert(!iInterval.isAdjacent(iPosInfInterval)); + assert(!iInterval.isAdjacent(negInfInterval)); + assert(!iInterval.isAdjacent(cNegInfInterval)); + assert(!iInterval.isAdjacent(iNegInfInterval)); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)) .isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); +} + +// Test Interval's merge(). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(I)(in Interval!Date interval1, in I interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); + + assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); + + assert(interval.merge(interval) == interval); + assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.merge(interval).empty); + assert(!interval.merge(cInterval).empty); + assert(!interval.merge(iInterval).empty); + assert(!interval.merge(posInfInterval).empty); + assert(!interval.merge(cPosInfInterval).empty); + assert(!interval.merge(iPosInfInterval).empty); + assert(!interval.merge(negInfInterval).empty); + assert(!interval.merge(cNegInfInterval).empty); + assert(!interval.merge(iNegInfInterval).empty); + assert(!cInterval.merge(interval).empty); + assert(!cInterval.merge(cInterval).empty); + assert(!cInterval.merge(iInterval).empty); + assert(!cInterval.merge(posInfInterval).empty); + assert(!cInterval.merge(cPosInfInterval).empty); + assert(!cInterval.merge(iPosInfInterval).empty); + assert(!cInterval.merge(negInfInterval).empty); + assert(!cInterval.merge(cNegInfInterval).empty); + assert(!cInterval.merge(iNegInfInterval).empty); + assert(!iInterval.merge(interval).empty); + assert(!iInterval.merge(cInterval).empty); + assert(!iInterval.merge(iInterval).empty); + assert(!iInterval.merge(posInfInterval).empty); + assert(!iInterval.merge(cPosInfInterval).empty); + assert(!iInterval.merge(iPosInfInterval).empty); + assert(!iInterval.merge(negInfInterval).empty); + assert(!iInterval.merge(cNegInfInterval).empty); + assert(!iInterval.merge(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +} + +// Test Interval's span(). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)),interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), + Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.span(interval) == interval); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!interval.span(interval).empty); + assert(!interval.span(cInterval).empty); + assert(!interval.span(iInterval).empty); + assert(!interval.span(posInfInterval).empty); + assert(!interval.span(cPosInfInterval).empty); + assert(!interval.span(iPosInfInterval).empty); + assert(!interval.span(negInfInterval).empty); + assert(!interval.span(cNegInfInterval).empty); + assert(!interval.span(iNegInfInterval).empty); + assert(!cInterval.span(interval).empty); + assert(!cInterval.span(cInterval).empty); + assert(!cInterval.span(iInterval).empty); + assert(!cInterval.span(posInfInterval).empty); + assert(!cInterval.span(cPosInfInterval).empty); + assert(!cInterval.span(iPosInfInterval).empty); + assert(!cInterval.span(negInfInterval).empty); + assert(!cInterval.span(cNegInfInterval).empty); + assert(!cInterval.span(iNegInfInterval).empty); + assert(!iInterval.span(interval).empty); + assert(!iInterval.span(cInterval).empty); + assert(!iInterval.span(iInterval).empty); + assert(!iInterval.span(posInfInterval).empty); + assert(!iInterval.span(cPosInfInterval).empty); + assert(!iInterval.span(iPosInfInterval).empty); + assert(!iInterval.span(negInfInterval).empty); + assert(!iInterval.span(cNegInfInterval).empty); + assert(!iInterval.span(iNegInfInterval).empty); + + // Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +} + +// Test Interval's shift(duration). +@safe unittest +{ + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, in Duration duration) + { + interval.shift(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); +} + +// Test Interval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.shift(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(5))); + static assert(!__traits(compiles, iInterval.shift(5))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); +} + +// Test Interval's expand(Duration). +@safe unittest +{ + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, in Duration duration) + { + interval.expand(duration); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); +} + +// Test Interval's expand(int, int, AllowDayOverflow, Direction) +@safe unittest +{ + { + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.expand(years, months); + } + + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow, dir); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); + + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); + + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, + Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, + Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, + Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, + Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, + Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(5))); + static assert(!__traits(compiles, iInterval.expand(5))); + + // Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); +} + +// Test Interval's fwdRange. +@system unittest +{ + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); + + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); + + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 17)); + } + + // Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + assert(range.front == Date(2010, 9, 1)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +// Test Interval's bwdRange. +@system unittest +{ + { + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + + static void testInterval1(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + } + + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + static void testInterval2(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval2(interval)); + + assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); + + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == Date(2010, 10, 1)); + + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); + } + + // Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + // An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.empty); + } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +// Test Interval's toString(). +@safe unittest +{ + assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(cInterval.toString()); + assert(iInterval.toString()); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index 371bc12edda..c1d3213fe8e 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -172,56 +172,6 @@ else version(Posix) // Section with public enums and constants. //============================================================================== -/++ - Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s - $(LREF expand, expand) function which uses it to indicate whether the interval should - be expanded backwards (into the past), forwards (into the future), or both. - +/ -enum Direction -{ - /// Backward. - bwd, - - /// Forward. - fwd, - - /// Both backward and forward. - both -} - -/++ - Used to indicate whether $(D popFront) should be called immediately upon - creating a range. The idea is that for some functions used to generate a - range for an interval, $(D front) is not necessarily a time point which - would ever be generated by the range. To get the first time point - in the range to match what the function generates, then use - $(D PopFirst.yes) to indicate that the range should have $(D popFront) - called on it before the range is returned so that $(D front) is a time point - which the function would generate. - - For instance, if the function used to generate a range of time points - generated successive Easters (i.e. you're iterating over all of the Easters - within the interval), the initial date probably isn't an Easter. Using - $(D PopFirst.yes) would tell the function which returned the - range that $(D popFront) was to be called so that front would then be - an Easter - the next one generated by the function (which when - iterating forward would be the Easter following the original $(D front), - while when iterating backward, it would be the Easter prior to the - original $(D front)). If $(D PopFirst.no) were used, then $(D front) would - remain the original time point and it would not necessarily be a time point - which would be generated by the range-generating function (which in many - cases is exactly what is desired - - e.g. if iterating over every day starting at the beginning - of the interval). - - If set to $(D PopFirst.no), then popFront is not called before returning - the range. - - Otherwise, if set to $(D PopFirst.yes), then popFront is called before - returning the range. - +/ -alias PopFirst = Flag!"popFirst"; - /++ Used by StopWatch to indicate whether it should start immediately upon construction. @@ -18269,3181 +18219,6 @@ private: // Section with intervals. //============================================================================== -/++ - Represents an interval of time. - - An $(D Interval) has a starting point and an end point. The interval of time - is therefore the time starting at the starting point up to, but not - including, the end point. e.g. - - $(BOOKTABLE, - $(TR $(TD [January 5th, 2010 - March 10th, 2010$(RPAREN))) - $(TR $(TD [05:00:30 - 12:00:00$(RPAREN))) - $(TR $(TD [1982-01-04T08:59:00 - 2010-07-04T12:00:00$(RPAREN))) - ) - - A range can be obtained from an $(D Interval), allowing iteration over - that interval, with the exact time points which are iterated over depending - on the function which generates the range. - +/ -struct Interval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - end = The time point which ends (but is not included in) the - interval. - - Throws: - $(LREF DateTimeException) if $(D_PARAM end) is before $(D_PARAM begin). - - Example: --------------------- -Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); --------------------- - +/ - this(U)(in TP begin, in U end) pure - if (is(Unqual!TP == Unqual!U)) - { - if (!_valid(begin, end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _begin = cast(TP) begin; - _end = cast(TP) end; - } - - - /++ - Params: - begin = The time point which begins the interval. - duration = The duration from the starting point to the end point. - - Throws: - $(LREF DateTimeException) if the resulting $(D end) is before - $(D begin). - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == - Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); --------------------- - +/ - this(D)(in TP begin, in D duration) pure - if (__traits(compiles, begin + duration)) - { - _begin = cast(TP) begin; - _end = begin + duration; - - if (!_valid(_begin, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - } - - - /++ - Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. - +/ - ref Interval opAssign(const ref Interval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - _end = cast(TP) rhs._end; - return this; - } - - - /++ - Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. - +/ - ref Interval opAssign(Interval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - _end = cast(TP) rhs._end; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == - Date(1996, 1, 2)); --------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP)_begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set $(D begin) to. - - Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. - +/ - @property void begin(TP timePoint) pure - { - if (!_valid(timePoint, _end)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _begin = timePoint; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == - Date(2012, 3, 1)); --------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP)_end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - - Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. - +/ - @property void end(TP timePoint) pure - { - if (!_valid(_begin, timePoint)) - throw new DateTimeException("Arguments would result in an invalid Interval."); - - _end = timePoint; - } - - - /++ - Returns the duration between $(D begin) and $(D end). - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == - dur!"days"(5903)); --------------------- - +/ - @property auto length() const pure nothrow - { - return _end - _begin; - } - - - /++ - Whether the interval's length is 0, that is, whether $(D begin == end). - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); --------------------- - +/ - @property bool empty() const pure nothrow - { - return _begin == _end; - } - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(1994, 12, 24))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2000, 1, 5))); -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Date(2012, 3, 1))); --------------------- - +/ - bool contains(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return timePoint >= _begin && timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return interval._begin >= _begin && - interval._begin < _end && - interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval going to positive infinity can never be contained in a finite - interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool contains(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false (unless this interval is empty), because an - interval beginning at negative infinity can never be contained in a - finite interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool contains(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(1994, 12, 24))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2000, 1, 5))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Date(2012, 3, 1))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return _end <= timePoint; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); --------------------- - +/ - bool isBefore(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); --------------------- - +/ - bool isBefore(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect with it. - - Always returns false (unless this interval is empty) because a finite - interval can never be before an interval beginning at negative infinity. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool isBefore(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(1994, 12, 24))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2000, 1, 5))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Date(2012, 3, 1))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure - { - _enforceNotEmpty(); - - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false (unless this interval is empty) because a finite - interval can never be after an interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool isAfter(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool intersects(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return interval._begin < _end && interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool intersects(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end > interval._begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2000, 1, 2)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - either interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); --------------------- - +/ - Interval intersection(in Interval interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - auto end = _end < interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval intersection(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - return Interval(_begin > interval._begin ? _begin : interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); --------------------- - +/ - Interval intersection(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - return Interval(_begin, _end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - return _begin == interval._end || _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _end == interval._begin; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2000, 1, 2)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if either interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); --------------------- - +/ - Interval merge(in Interval interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - PosInfInterval!Date(Date(2012, 3, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval!TP merge(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are not - adjacent or if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1996, 1, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval!TP merge(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if either interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); --------------------- - +/ - Interval span(in Interval interval) const pure - { - _enforceNotEmpty(); - interval._enforceNotEmpty(); - - auto begin = _begin < interval._begin ? _begin : interval._begin; - auto end = _end > interval._end ? _end : interval._end; - - return Interval(begin, end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - PosInfInterval!Date(Date(2050, 1, 1))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval!TP span(in PosInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return PosInfInterval!TP(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this interval. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Example: --------------------- -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1602, 5, 21))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval!TP span(in NegInfInterval!TP interval) const pure - { - _enforceNotEmpty(); - - return NegInfInterval!TP(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the interval forward or backwards in time by the given duration - (a positive duration shifts the interval forward; a negative duration - shifts it backward). Effectively, it does $(D begin += duration) and - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. - - Example: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); --------------------- - +/ - void shift(D)(D duration) pure - if (__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - auto begin = _begin + duration; - auto end = _end + duration; - - if (!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the interval forward or backwards in time by the given number - of years and/or months (a positive number of years and months shifts - the interval forward; a negative number shifts it backward). - It adds the years the given years and months to both begin and end. - It effectively calls $(D add!"years"()) and then $(D add!"months"()) - on begin and end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin) and $(D end), causing their month - to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Example: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.shift(2); -assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - -interval2.shift(-2); -assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) - if (isIntegral!T) - { - _enforceNotEmpty(); - - auto begin = _begin; - auto end = _end; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - - _begin = begin; - _end = end; - } - } - - - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it does $(D begin -= duration) and/or $(D end += duration). Whether - it expands forwards and/or backwards in time is determined by - $(D_PARAM dir). - - Params: - duration = The duration to expand the interval by. - dir = The direction in time to expand the interval. - - Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. - - Example: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void expand(D)(D duration, Direction dir = Direction.both) pure - if (__traits(compiles, begin + duration)) - { - _enforceNotEmpty(); - - switch (dir) - { - case Direction.both: - { - auto begin = _begin - duration; - auto end = _end + duration; - - if (!_valid(begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end + duration; - - if (!_valid(_begin, end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin - duration; - - if (!_valid(begin, _end)) - throw new DateTimeException("Argument would result in an invalid Interval."); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from $(D begin) and - adds them to $(D end). Whether it expands forwards and/or backwards - in time is determined by $(D_PARAM dir). - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin) and $(D end), causing their month - to increment. - dir = The direction in time to expand the interval. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Example: --------------------- -auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); -auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); --------------------- - +/ - void expand(T)( - T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow, - Direction dir = Direction.both) if (isIntegral!T) - { - _enforceNotEmpty(); - - switch (dir) - { - case Direction.both: - { - auto begin = _begin; - auto end = _end; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce(_valid(begin, end), new DateTimeException("Argument would result in an invalid Interval.")); - _begin = begin; - _end = end; - - return; - } - case Direction.fwd: - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - enforce( - _valid(_begin, end), - new DateTimeException("Argument would result in an invalid Interval.") - ); - _end = end; - - return; - } - case Direction.bwd: - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - enforce( - _valid(begin, _end), - new DateTimeException("Argument would result in an invalid Interval.") - ); - _begin = begin; - - return; - } - default: - assert(0, "Invalid Direction."); - } - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at $(D begin), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D Yes.popFirst), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or beyond the $(D end) - of the interval. - - There are helper functions in this module which generate common - delegates to pass to $(D fwdRange). Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant if when creating a custom delegate. - - Example: --------------------- -auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); -auto func = delegate (in Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; -auto range = interval.fwdRange(func); - - //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 2). -assert(range.front == Date(2010, 9, 1)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.empty); --------------------- - +/ - IntervalRange!(TP, Direction.fwd) fwdRange(TP delegate(in TP) func, PopFirst popFirst = No.popFirst) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.fwd)(this, func); - - if (popFirst == Yes.popFirst) - range.popFront(); - - return range; - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at $(D end), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D end). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D Yes.popFirst), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or less than the - $(D begin) of the interval. - - There are helper functions in this module which generate common - delegates to pass to $(D bwdRange). Their documentation starts with - "Range-generating function," making them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: --------------------- -auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); -auto func = delegate (in Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; -auto range = interval.bwdRange(func); - -//An odd day. Using Yes.popFirst would have made this Date(2010, 9, 8). -assert(range.front == Date(2010, 9, 9)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.empty); --------------------- - +/ - IntervalRange!(TP, Direction.bwd) bwdRange(TP delegate(in TP) func, PopFirst popFirst = No.popFirst) const - { - _enforceNotEmpty(); - - auto range = IntervalRange!(TP, Direction.bwd)(this, func); - - if (popFirst == Yes.popFirst) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - - -private: - - /+ - Since we have two versions of toString, we have _toStringImpl - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[%s - %s)", _begin, _end); - catch (Exception e) - assert(0, "format() threw."); - } - - - /+ - Throws: - $(LREF DateTimeException) if this interval is empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if (empty) - throw new DateTimeException("Invalid operation for an empty Interval.", __FILE__, line); - } - - - /+ - Whether the given values form a valid time interval. - - Params: - begin = The starting point of the interval. - end = The end point of the interval. - +/ - static bool _valid(in TP begin, in TP end) pure nothrow - { - return begin <= end; - } - - - pure invariant() - { - assert(_valid(_begin, _end), "Invariant Failure: begin is not before or equal to end."); - } - - - TP _begin; - TP _end; -} - -//Test Interval's constructors. -@safe unittest -{ - assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); - - Interval!Date(Date.init, Date.init); - Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); - Interval!DateTime(DateTime.init, DateTime.init); - Interval!SysTime(SysTime(0), SysTime(0)); - - Interval!DateTime(DateTime.init, dur!"days"(7)); - - //Verify Examples. - Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - assert( - Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date( - Date(1996, 1, 2), Date(1996, 1, 23)) - ); - assert( - Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date( - Date(1996, 1, 2), Date(1996, 1, 5)) - ); - assert( - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime( - DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0)) - ); - assert( - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == Interval!DateTime( - DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0)) - ); - assert( - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == Interval!DateTime( - DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3)) - ); - assert( - Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == Interval!DateTime( - DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3)) - ); -} - -//Test Interval's begin. -@safe unittest -{ - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.begin == Date(2010, 7, 4)); - assert(iInterval.begin == Date(2010, 7, 4)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); -} - -//Test Interval's end. -@safe unittest -{ - assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.end == Date(2012, 1, 7)); - assert(iInterval.end == Date(2012, 1, 7)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -//Test Interval's length. -@safe unittest -{ - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); - assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); - assert(Interval!DateTime( - DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == dur!"seconds"(129_127)); - assert(Interval!SysTime( - SysTime(DateTime(2010, 1, 1, 0, 30, 0)), - SysTime(DateTime(2010, 1, 2, 12, 22, 7)) - ).length == dur!"seconds"(129_127)); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.length != Duration.zero); - assert(iInterval.length != Duration.zero); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); -} - -//Test Interval's empty. -@safe unittest -{ - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); - assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); - assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); - assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); - assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.empty); - assert(!iInterval.empty); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); -} - -//Test Interval's contains(time point). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); - - assert(!interval.contains(Date(2009, 7, 4))); - assert(!interval.contains(Date(2010, 7, 3))); - assert(interval.contains(Date(2010, 7, 4))); - assert(interval.contains(Date(2010, 7, 5))); - assert(interval.contains(Date(2011, 7, 1))); - assert(interval.contains(Date(2012, 1, 6))); - assert(!interval.contains(Date(2012, 1, 7))); - assert(!interval.contains(Date(2012, 1, 8))); - assert(!interval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(interval.contains(cdate)); - assert(cInterval.contains(cdate)); - assert(iInterval.contains(cdate)); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -//Test Interval's contains(Interval). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); - assertThrown!DateTimeException(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ).contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.contains(interval)); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); - - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(interval.contains(interval)); - assert(interval.contains(cInterval)); - assert(interval.contains(iInterval)); - assert(!interval.contains(posInfInterval)); - assert(!interval.contains(cPosInfInterval)); - assert(!interval.contains(iPosInfInterval)); - assert(!interval.contains(negInfInterval)); - assert(!interval.contains(cNegInfInterval)); - assert(!interval.contains(iNegInfInterval)); - assert(cInterval.contains(interval)); - assert(cInterval.contains(cInterval)); - assert(cInterval.contains(iInterval)); - assert(!cInterval.contains(posInfInterval)); - assert(!cInterval.contains(cPosInfInterval)); - assert(!cInterval.contains(iPosInfInterval)); - assert(!cInterval.contains(negInfInterval)); - assert(!cInterval.contains(cNegInfInterval)); - assert(!cInterval.contains(iNegInfInterval)); - assert(iInterval.contains(interval)); - assert(iInterval.contains(cInterval)); - assert(iInterval.contains(iInterval)); - assert(!iInterval.contains(posInfInterval)); - assert(!iInterval.contains(cPosInfInterval)); - assert(!iInterval.contains(iPosInfInterval)); - assert(!iInterval.contains(negInfInterval)); - assert(!iInterval.contains(cNegInfInterval)); - assert(!iInterval.contains(iNegInfInterval)); - - //Verify Examples. - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test Interval's isBefore(time point). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); - - assert(!interval.isBefore(Date(2009, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 4))); - assert(!interval.isBefore(Date(2010, 7, 5))); - assert(!interval.isBefore(Date(2011, 7, 1))); - assert(!interval.isBefore(Date(2012, 1, 6))); - assert(interval.isBefore(Date(2012, 1, 7))); - assert(interval.isBefore(Date(2012, 1, 8))); - assert(interval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!interval.isBefore(cdate)); - assert(!cInterval.isBefore(cdate)); - assert(!iInterval.isBefore(cdate)); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -//Test Interval's isBefore(Interval). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); - assertThrown!DateTimeException(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ).isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); - - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(cInterval)); - assert(!interval.isBefore(iInterval)); - assert(!interval.isBefore(posInfInterval)); - assert(!interval.isBefore(cPosInfInterval)); - assert(!interval.isBefore(iPosInfInterval)); - assert(!interval.isBefore(negInfInterval)); - assert(!interval.isBefore(cNegInfInterval)); - assert(!interval.isBefore(iNegInfInterval)); - assert(!cInterval.isBefore(interval)); - assert(!cInterval.isBefore(cInterval)); - assert(!cInterval.isBefore(iInterval)); - assert(!cInterval.isBefore(posInfInterval)); - assert(!cInterval.isBefore(cPosInfInterval)); - assert(!cInterval.isBefore(iPosInfInterval)); - assert(!cInterval.isBefore(negInfInterval)); - assert(!cInterval.isBefore(cNegInfInterval)); - assert(!cInterval.isBefore(iNegInfInterval)); - assert(!iInterval.isBefore(interval)); - assert(!iInterval.isBefore(cInterval)); - assert(!iInterval.isBefore(iInterval)); - assert(!iInterval.isBefore(posInfInterval)); - assert(!iInterval.isBefore(cPosInfInterval)); - assert(!iInterval.isBefore(iPosInfInterval)); - assert(!iInterval.isBefore(negInfInterval)); - assert(!iInterval.isBefore(cNegInfInterval)); - assert(!iInterval.isBefore(iNegInfInterval)); - - //Verify Examples. - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test Interval's isAfter(time point). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); - - assert(interval.isAfter(Date(2009, 7, 4))); - assert(interval.isAfter(Date(2010, 7, 3))); - assert(!interval.isAfter(Date(2010, 7, 4))); - assert(!interval.isAfter(Date(2010, 7, 5))); - assert(!interval.isAfter(Date(2011, 7, 1))); - assert(!interval.isAfter(Date(2012, 1, 6))); - assert(!interval.isAfter(Date(2012, 1, 7))); - assert(!interval.isAfter(Date(2012, 1, 8))); - assert(!interval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!interval.isAfter(cdate)); - assert(!cInterval.isAfter(cdate)); - assert(!iInterval.isAfter(cdate)); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); -} - -//Test Interval's isAfter(Interval). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); - assertThrown!DateTimeException(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ).isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAfter(interval)); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); - - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isAfter(interval)); - assert(!interval.isAfter(cInterval)); - assert(!interval.isAfter(iInterval)); - assert(!interval.isAfter(posInfInterval)); - assert(!interval.isAfter(cPosInfInterval)); - assert(!interval.isAfter(iPosInfInterval)); - assert(!interval.isAfter(negInfInterval)); - assert(!interval.isAfter(cNegInfInterval)); - assert(!interval.isAfter(iNegInfInterval)); - assert(!cInterval.isAfter(interval)); - assert(!cInterval.isAfter(cInterval)); - assert(!cInterval.isAfter(iInterval)); - assert(!cInterval.isAfter(posInfInterval)); - assert(!cInterval.isAfter(cPosInfInterval)); - assert(!cInterval.isAfter(iPosInfInterval)); - assert(!cInterval.isAfter(negInfInterval)); - assert(!cInterval.isAfter(cNegInfInterval)); - assert(!cInterval.isAfter(iNegInfInterval)); - assert(!iInterval.isAfter(interval)); - assert(!iInterval.isAfter(cInterval)); - assert(!iInterval.isAfter(iInterval)); - assert(!iInterval.isAfter(posInfInterval)); - assert(!iInterval.isAfter(cPosInfInterval)); - assert(!iInterval.isAfter(iPosInfInterval)); - assert(!iInterval.isAfter(negInfInterval)); - assert(!iInterval.isAfter(cNegInfInterval)); - assert(!iInterval.isAfter(iNegInfInterval)); - - //Verify Examples. - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); -} - -//Test Interval's intersects(). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); - assertThrown!DateTimeException(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ).intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.intersects(interval)); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); - - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(interval.intersects(interval)); - assert(interval.intersects(cInterval)); - assert(interval.intersects(iInterval)); - assert(interval.intersects(posInfInterval)); - assert(interval.intersects(cPosInfInterval)); - assert(interval.intersects(iPosInfInterval)); - assert(interval.intersects(negInfInterval)); - assert(interval.intersects(cNegInfInterval)); - assert(interval.intersects(iNegInfInterval)); - assert(cInterval.intersects(interval)); - assert(cInterval.intersects(cInterval)); - assert(cInterval.intersects(iInterval)); - assert(cInterval.intersects(posInfInterval)); - assert(cInterval.intersects(cPosInfInterval)); - assert(cInterval.intersects(iPosInfInterval)); - assert(cInterval.intersects(negInfInterval)); - assert(cInterval.intersects(cNegInfInterval)); - assert(cInterval.intersects(iNegInfInterval)); - assert(iInterval.intersects(interval)); - assert(iInterval.intersects(cInterval)); - assert(iInterval.intersects(iInterval)); - assert(iInterval.intersects(posInfInterval)); - assert(iInterval.intersects(cPosInfInterval)); - assert(iInterval.intersects(iPosInfInterval)); - assert(iInterval.intersects(negInfInterval)); - assert(iInterval.intersects(cNegInfInterval)); - assert(iInterval.intersects(iNegInfInterval)); - - //Verify Examples. - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -//Test Interval's intersection(). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ).intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); - - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(interval.intersection(interval) == interval); - assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.intersection(interval).empty); - assert(!interval.intersection(cInterval).empty); - assert(!interval.intersection(iInterval).empty); - assert(!interval.intersection(posInfInterval).empty); - assert(!interval.intersection(cPosInfInterval).empty); - assert(!interval.intersection(iPosInfInterval).empty); - assert(!interval.intersection(negInfInterval).empty); - assert(!interval.intersection(cNegInfInterval).empty); - assert(!interval.intersection(iNegInfInterval).empty); - assert(!cInterval.intersection(interval).empty); - assert(!cInterval.intersection(cInterval).empty); - assert(!cInterval.intersection(iInterval).empty); - assert(!cInterval.intersection(posInfInterval).empty); - assert(!cInterval.intersection(cPosInfInterval).empty); - assert(!cInterval.intersection(iPosInfInterval).empty); - assert(!cInterval.intersection(negInfInterval).empty); - assert(!cInterval.intersection(cNegInfInterval).empty); - assert(!cInterval.intersection(iNegInfInterval).empty); - assert(!iInterval.intersection(interval).empty); - assert(!iInterval.intersection(cInterval).empty); - assert(!iInterval.intersection(iInterval).empty); - assert(!iInterval.intersection(posInfInterval).empty); - assert(!iInterval.intersection(cPosInfInterval).empty); - assert(!iInterval.intersection(iPosInfInterval).empty); - assert(!iInterval.intersection(negInfInterval).empty); - assert(!iInterval.intersection(cNegInfInterval).empty); - assert(!iInterval.intersection(iNegInfInterval).empty); - - //Verify Examples. - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(Interval!Date( - Date(1990, 7, 6), - Date(2000, 8, 2)) - ) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(Interval!Date( - Date(1999, 1, 12), - Date(2011, 9, 17)) - ) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); -} - -//Test Interval's isAdjacent(). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.isAdjacent(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); - - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(cInterval)); - assert(!interval.isAdjacent(iInterval)); - assert(!interval.isAdjacent(posInfInterval)); - assert(!interval.isAdjacent(cPosInfInterval)); - assert(!interval.isAdjacent(iPosInfInterval)); - assert(!interval.isAdjacent(negInfInterval)); - assert(!interval.isAdjacent(cNegInfInterval)); - assert(!interval.isAdjacent(iNegInfInterval)); - assert(!cInterval.isAdjacent(interval)); - assert(!cInterval.isAdjacent(cInterval)); - assert(!cInterval.isAdjacent(iInterval)); - assert(!cInterval.isAdjacent(posInfInterval)); - assert(!cInterval.isAdjacent(cPosInfInterval)); - assert(!cInterval.isAdjacent(iPosInfInterval)); - assert(!cInterval.isAdjacent(negInfInterval)); - assert(!cInterval.isAdjacent(cNegInfInterval)); - assert(!cInterval.isAdjacent(iNegInfInterval)); - assert(!iInterval.isAdjacent(interval)); - assert(!iInterval.isAdjacent(cInterval)); - assert(!iInterval.isAdjacent(iInterval)); - assert(!iInterval.isAdjacent(posInfInterval)); - assert(!iInterval.isAdjacent(cPosInfInterval)); - assert(!iInterval.isAdjacent(iPosInfInterval)); - assert(!iInterval.isAdjacent(negInfInterval)); - assert(!iInterval.isAdjacent(cNegInfInterval)); - assert(!iInterval.isAdjacent(iNegInfInterval)); - - //Verify Examples. - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); - - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); -} - -//Test Interval's merge(). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(I)(in Interval!Date interval1, in I interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); - - assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); - - assert(interval.merge(interval) == interval); - assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.merge(interval).empty); - assert(!interval.merge(cInterval).empty); - assert(!interval.merge(iInterval).empty); - assert(!interval.merge(posInfInterval).empty); - assert(!interval.merge(cPosInfInterval).empty); - assert(!interval.merge(iPosInfInterval).empty); - assert(!interval.merge(negInfInterval).empty); - assert(!interval.merge(cNegInfInterval).empty); - assert(!interval.merge(iNegInfInterval).empty); - assert(!cInterval.merge(interval).empty); - assert(!cInterval.merge(cInterval).empty); - assert(!cInterval.merge(iInterval).empty); - assert(!cInterval.merge(posInfInterval).empty); - assert(!cInterval.merge(cPosInfInterval).empty); - assert(!cInterval.merge(iPosInfInterval).empty); - assert(!cInterval.merge(negInfInterval).empty); - assert(!cInterval.merge(cNegInfInterval).empty); - assert(!cInterval.merge(iNegInfInterval).empty); - assert(!iInterval.merge(interval).empty); - assert(!iInterval.merge(cInterval).empty); - assert(!iInterval.merge(iInterval).empty); - assert(!iInterval.merge(posInfInterval).empty); - assert(!iInterval.merge(cPosInfInterval).empty); - assert(!iInterval.merge(iPosInfInterval).empty); - assert(!iInterval.merge(negInfInterval).empty); - assert(!iInterval.merge(cNegInfInterval).empty); - assert(!iInterval.merge(iNegInfInterval).empty); - - //Verify Examples. - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(Interval!Date( - Date(1990, 7, 6), - Date(2000, 8, 2)) - ) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(Interval!Date( - Date(2012, 3, 1), - Date(2013, 5, 7)) - ) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(PosInfInterval!Date( - Date(1990, 7, 6)) - ) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(PosInfInterval!Date( - Date(2012, 3, 1)) - ) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(NegInfInterval!Date( - Date(1996, 1, 2)) - ) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).merge(NegInfInterval!Date( - Date(2013, 1, 12)) - ) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test Interval's span(). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date( - Date(2010, 7, 4), - dur!"days"(0)) - )); - assertThrown!DateTimeException(testInterval(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ),interval)); - assertThrown!DateTimeException(testInterval(Interval!Date( - Date(2010, 7, 4), - dur!"days"(0) - ), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.span(interval) == interval); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!interval.span(interval).empty); - assert(!interval.span(cInterval).empty); - assert(!interval.span(iInterval).empty); - assert(!interval.span(posInfInterval).empty); - assert(!interval.span(cPosInfInterval).empty); - assert(!interval.span(iPosInfInterval).empty); - assert(!interval.span(negInfInterval).empty); - assert(!interval.span(cNegInfInterval).empty); - assert(!interval.span(iNegInfInterval).empty); - assert(!cInterval.span(interval).empty); - assert(!cInterval.span(cInterval).empty); - assert(!cInterval.span(iInterval).empty); - assert(!cInterval.span(posInfInterval).empty); - assert(!cInterval.span(cPosInfInterval).empty); - assert(!cInterval.span(iPosInfInterval).empty); - assert(!cInterval.span(negInfInterval).empty); - assert(!cInterval.span(cNegInfInterval).empty); - assert(!cInterval.span(iNegInfInterval).empty); - assert(!iInterval.span(interval).empty); - assert(!iInterval.span(cInterval).empty); - assert(!iInterval.span(iInterval).empty); - assert(!iInterval.span(posInfInterval).empty); - assert(!iInterval.span(cPosInfInterval).empty); - assert(!iInterval.span(iPosInfInterval).empty); - assert(!iInterval.span(negInfInterval).empty); - assert(!iInterval.span(cNegInfInterval).empty); - assert(!iInterval.span(iNegInfInterval).empty); - - //Verify Examples. - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(Interval!Date( - Date(1990, 7, 6), - Date(1991, 1, 8)) - ) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(Interval!Date( - Date(2012, 3, 1), - Date(2013, 5, 7)) - ) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(PosInfInterval!Date( - Date(1990, 7, 6)) - ) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(PosInfInterval!Date( - Date(2050, 1, 1)) - ) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(NegInfInterval!Date( - Date(1602, 5, 21)) - ) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date( - Date(1996, 1, 2), - Date(2012, 3, 1) - ).span(NegInfInterval!Date( - Date(2013, 1, 12)) - ) == NegInfInterval!Date(Date(2013, 1 , 12))); -} - -//Test Interval's shift(duration). -@safe unittest -{ - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, in Duration duration) - { - interval.shift(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); -} - -//Test Interval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.shift(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, No.allowDayOverflow, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, No.allowDayOverflow, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, No.allowDayOverflow, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(5))); - static assert(!__traits(compiles, iInterval.shift(5))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); -} - -//Test Interval's expand(Duration). -@safe unittest -{ - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, in Duration duration) - { - interval.expand(duration); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - assertThrown!DateTimeException(testIntervalFail(Interval!Date( - Date(2010, 7, 4), - Date(2010, 7, 5) - ), dur!"days"(-5))); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); -} - -//Test Interval's expand(int, int, AllowDayOverflow, Direction) -@safe unittest -{ - { - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.expand(years, months); - } - - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - Direction dir, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow, dir); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); - - testInterval(interval, 5, 0, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); - - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, Direction.both, - Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, Direction.both, - Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, No.allowDayOverflow, Direction.both, - Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, No.allowDayOverflow, Direction.both, - Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, No.allowDayOverflow, Direction.both, - Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, No.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, No.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, No.allowDayOverflow, Direction.fwd, - Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, Direction.bwd, - Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, Direction.bwd, - Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, No.allowDayOverflow, Direction.bwd, - Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, No.allowDayOverflow, Direction.bwd, - Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, No.allowDayOverflow, Direction.bwd, - Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(5))); - static assert(!__traits(compiles, iInterval.expand(5))); - - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); -} - -//Test Interval's fwdRange. -@system unittest -{ - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), Yes.popFirst).empty); - - assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert( - Interval!Date( - Date(2010, 9, 12), - Date(2010, 10, 1) - ).fwdRange( - everyDayOfWeek!Date(DayOfWeek.fri), Yes.popFirst).front == Date(2010, 9, 17)); - } - - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - assert(range.front == Date(2010, 9, 1)); //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 2). - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -//Test Interval's bwdRange. -@system unittest -{ - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - static void testInterval2(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval2(interval)); - - assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), Yes.popFirst).empty); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)) - .bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == Date(2010, - 10, 1)); - - assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)) - .bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), Yes.popFirst).front == Date(2010, - 9, 24)); - } - - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - assert(range.front == Date(2010, 9, 9)); //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 8). - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.empty); - } - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -//Test Interval's toString(). -@safe unittest -{ - assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(cInterval.toString()); - assert(iInterval.toString()); -} - - /++ Represents an interval of time which has positive infinity as its end point. @@ -22454,6 +19229,7 @@ assert(!range.empty); } private: +package: // temporary /+ Since we have two versions of toString(), we have _toStringImpl() @@ -24683,6 +21459,7 @@ assert(!range.empty); } private: +package: // temporary /+ Since we have two versions of toString(), we have _toStringImpl() @@ -26531,6 +23308,7 @@ public: private: +package: // temporary /+ Params: From 9d6388a589cc5fa6c614901af24e2fb31ecfb423 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 11:27:47 +0200 Subject: [PATCH 085/262] Move PosInfInterval to std.datetime.interval. --- std/datetime/interval.d | 2161 +++++++++++++++++++++++++++++++++++++- std/datetime/package.d | 2216 +-------------------------------------- 2 files changed, 2161 insertions(+), 2216 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 2b10ecba7f4..0e41144af39 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -19,7 +19,7 @@ version(unittest) import std.exception : assertThrown; import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary -import std.datetime : PosInfInterval, NegInfInterval, IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary +import std.datetime : NegInfInterval, IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary import std.datetime : everyDayOfWeek; // temporary /++ @@ -3067,3 +3067,2162 @@ package: // temporary assert(cInterval.toString()); assert(iInterval.toString()); } + + +/++ + Represents an interval of time which has positive infinity as its end point. + + Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the + main purpose of using $(D PosInfInterval) is to create an infinite range + which starts at a fixed point in time and goes to positive infinity. + +/ +struct PosInfInterval(TP) +{ +public: + + /++ + Params: + begin = The time point which begins the interval. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP begin) pure nothrow + { + _begin = cast(TP) begin; + } + + + /++ + Params: + rhs = The $(D PosInfInterval) to assign to this one. + +/ + ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + return this; + } + + + /++ + Params: + rhs = The $(D PosInfInterval) to assign to this one. + +/ + ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow + { + _begin = cast(TP) rhs._begin; + return this; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +-------------------- + +/ + @property TP begin() const pure nothrow + { + return cast(TP)_begin; + } + + + /++ + The starting point of the interval. It is included in the interval. + + Params: + timePoint = The time point to set $(D begin) to. + +/ + @property void begin(TP timePoint) pure nothrow + { + _begin = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + enum bool empty = false; + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + PosInfInterval!Date(Date(1995, 7, 2)))); +-------------------- + +/ + bool contains(in PosInfInterval interval) const pure nothrow + { + return interval._begin >= _begin; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval going to positive infinity + can never contain an interval beginning at negative infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool contains(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given time point. + + Always returns false because an interval going to positive infinity + can never be before any time point. + + Params: + timePoint = The time point to check whether this interval is before + it. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false (unless the given interval is empty) because an + interval going to positive infinity can never be before any other + interval. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + PosInfInterval!Date(Date(1992, 5, 4)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + PosInfInterval!Date(Date(2013, 3, 7)))); +-------------------- + +/ + bool isBefore(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval going to positive infinity can + never be before any other interval. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); +-------------------- + +/ + bool isBefore(in NegInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return timePoint < _begin; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _begin >= interval._end; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval going to positive infinity can + never be after another interval going to positive infinity. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool isAfter(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAfter(in NegInfInterval!TP interval) const pure nothrow + { + return _begin >= interval._end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._end > _begin; + } + + + /++ + Whether the given interval overlaps this interval. + + Always returns true because two intervals going to positive infinity + always overlap. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool intersects(in PosInfInterval interval) const pure nothrow + { + return true; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return _begin < interval._end; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect or if + the given interval is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto begin = _begin > interval._begin ? _begin : interval._begin; + + return Interval!TP(begin, interval._end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1996, 1 , 2))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1999, 1 , 12))); +-------------------- + +/ + PosInfInterval intersection(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); +-------------------- + +/ + Interval!TP intersection(in NegInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(_begin, interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _begin == interval._end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals going to positive infinity + can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + PosInfInterval!Date(Date(1990, 1, 7)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + PosInfInterval!Date(Date(1996, 1, 2)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + NegInfInterval!Date(Date(1996, 1, 2)))); + +assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( + NegInfInterval!Date(Date(2000, 7, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow + { + return _begin == interval._end; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect and are + not adjacent or if the given interval is empty. + + Note: + There is no overload for $(D merge) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval merge(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Note: + There is no overload for $(D span) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == + PosInfInterval!Date(Date(500, 8, 9))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Note: + There is no overload for $(D span) which takes a + $(D NegInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7 , 6))); + +assert(PosInfInterval!Date(Date(1996, 1, 2)).span( + PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1 , 2))); +-------------------- + +/ + PosInfInterval span(in PosInfInterval interval) const pure nothrow + { + return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); + } + + + /++ + Shifts the $(D begin) of this interval forward or backwards in time by + the given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D begin += duration). + + Params: + duration = The duration to shift the interval by. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if (__traits(compiles, begin + duration)) + { + _begin += duration; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Shifts the $(D begin) of this interval forward or backwards in time + by the given number of years and/or months (a positive number of years + and months shifts the interval forward; a negative number shifts it + backward). It adds the years the given years and months to + $(D begin). It effectively calls $(D add!"years"()) and then + $(D add!"months"()) on $(D begin) with the given number of years and + months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin), causing its month to increment. + + Throws: + $(LREF DateTimeException) if this interval is empty or if the + resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(years, allowOverflow); + begin.add!"months"(months, allowOverflow); + + _begin = begin; + } + } + + + /++ + Expands the interval backwards in time. Effectively, it does + $(D begin -= duration). + + Params: + duration = The duration to expand the interval by. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if (__traits(compiles, begin + duration)) + { + _begin -= duration; + } + + + static if (__traits(compiles, begin.add!"months"(1)) && + __traits(compiles, begin.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it subtracts the given number of months/years from $(D begin). + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D begin), causing its month to increment. + + Throws: + $(LREF DateTimeException) if this interval is empty or if the + resulting interval would be invalid. + + Example: +-------------------- +auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); +auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + +interval1.expand(2); +assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + +interval2.expand(-2); +assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto begin = _begin; + + begin.add!"years"(-years, allowOverflow); + begin.add!"months"(-months, allowOverflow); + + _begin = begin; + } + } + + + /++ + Returns a range which iterates forward over the interval, starting + at $(D begin), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point less than or equal to the + current $(D front) of the range, then a $(LREF DateTimeException) will be + thrown. + + There are helper functions in this module which generate common + delegates to pass to $(D fwdRange). Their documentation starts with + "Range-generating function," to make them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(2010, 9, 1)); +auto func = delegate (in Date date) //For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + + return date + dur!"days"(1); + }; +auto range = interval.fwdRange(func); + +//An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). +assert(range.front == Date(2010, 9, 1)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = PosInfIntervalRange!(TP)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: +package: // temporary + + /+ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[%s - ∞)", _begin); + catch (Exception e) + assert(0, "format() threw."); + } + + + TP _begin; +} + +//Test PosInfInterval's constructor. +@safe unittest +{ + PosInfInterval!Date(Date.init); + PosInfInterval!TimeOfDay(TimeOfDay.init); + PosInfInterval!DateTime(DateTime.init); + PosInfInterval!SysTime(SysTime(0)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +} + +//Test PosInfInterval's begin. +@safe unittest +{ + assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); + assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(cPosInfInterval.begin != Date.init); + assert(iPosInfInterval.begin != Date.init); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); +} + +//Test PosInfInterval's empty. +@safe unittest +{ + assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + assert(!cPosInfInterval.empty); + assert(!iPosInfInterval.empty); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test PosInfInterval's contains(time point). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.contains(Date(2009, 7, 4))); + assert(!posInfInterval.contains(Date(2010, 7, 3))); + assert(posInfInterval.contains(Date(2010, 7, 4))); + assert(posInfInterval.contains(Date(2010, 7, 5))); + assert(posInfInterval.contains(Date(2011, 7, 1))); + assert(posInfInterval.contains(Date(2012, 1, 6))); + assert(posInfInterval.contains(Date(2012, 1, 7))); + assert(posInfInterval.contains(Date(2012, 1, 8))); + assert(posInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(posInfInterval.contains(cdate)); + assert(cPosInfInterval.contains(cdate)); + assert(iPosInfInterval.contains(cdate)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); +} + +//Test PosInfInterval's contains(Interval). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.contains(posInfInterval)); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); + + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(posInfInterval.contains(interval)); + assert(posInfInterval.contains(cInterval)); + assert(posInfInterval.contains(iInterval)); + assert(posInfInterval.contains(posInfInterval)); + assert(posInfInterval.contains(cPosInfInterval)); + assert(posInfInterval.contains(iPosInfInterval)); + assert(!posInfInterval.contains(negInfInterval)); + assert(!posInfInterval.contains(cNegInfInterval)); + assert(!posInfInterval.contains(iNegInfInterval)); + assert(cPosInfInterval.contains(interval)); + assert(cPosInfInterval.contains(cInterval)); + assert(cPosInfInterval.contains(iInterval)); + assert(cPosInfInterval.contains(posInfInterval)); + assert(cPosInfInterval.contains(cPosInfInterval)); + assert(cPosInfInterval.contains(iPosInfInterval)); + assert(!cPosInfInterval.contains(negInfInterval)); + assert(!cPosInfInterval.contains(cNegInfInterval)); + assert(!cPosInfInterval.contains(iNegInfInterval)); + assert(iPosInfInterval.contains(interval)); + assert(iPosInfInterval.contains(cInterval)); + assert(iPosInfInterval.contains(iInterval)); + assert(iPosInfInterval.contains(posInfInterval)); + assert(iPosInfInterval.contains(cPosInfInterval)); + assert(iPosInfInterval.contains(iPosInfInterval)); + assert(!iPosInfInterval.contains(negInfInterval)); + assert(!iPosInfInterval.contains(cNegInfInterval)); + assert(!iPosInfInterval.contains(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isBefore(time point). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.isBefore(Date(2009, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 4))); + assert(!posInfInterval.isBefore(Date(2010, 7, 5))); + assert(!posInfInterval.isBefore(Date(2011, 7, 1))); + assert(!posInfInterval.isBefore(Date(2012, 1, 6))); + assert(!posInfInterval.isBefore(Date(2012, 1, 7))); + assert(!posInfInterval.isBefore(Date(2012, 1, 8))); + assert(!posInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!posInfInterval.isBefore(cdate)); + assert(!cPosInfInterval.isBefore(cdate)); + assert(!iPosInfInterval.isBefore(cdate)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isBefore(Interval). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); + + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isBefore(interval)); + assert(!posInfInterval.isBefore(cInterval)); + assert(!posInfInterval.isBefore(iInterval)); + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(cPosInfInterval)); + assert(!posInfInterval.isBefore(iPosInfInterval)); + assert(!posInfInterval.isBefore(negInfInterval)); + assert(!posInfInterval.isBefore(cNegInfInterval)); + assert(!posInfInterval.isBefore(iNegInfInterval)); + assert(!cPosInfInterval.isBefore(interval)); + assert(!cPosInfInterval.isBefore(cInterval)); + assert(!cPosInfInterval.isBefore(iInterval)); + assert(!cPosInfInterval.isBefore(posInfInterval)); + assert(!cPosInfInterval.isBefore(cPosInfInterval)); + assert(!cPosInfInterval.isBefore(iPosInfInterval)); + assert(!cPosInfInterval.isBefore(negInfInterval)); + assert(!cPosInfInterval.isBefore(cNegInfInterval)); + assert(!cPosInfInterval.isBefore(iNegInfInterval)); + assert(!iPosInfInterval.isBefore(interval)); + assert(!iPosInfInterval.isBefore(cInterval)); + assert(!iPosInfInterval.isBefore(iInterval)); + assert(!iPosInfInterval.isBefore(posInfInterval)); + assert(!iPosInfInterval.isBefore(cPosInfInterval)); + assert(!iPosInfInterval.isBefore(iPosInfInterval)); + assert(!iPosInfInterval.isBefore(negInfInterval)); + assert(!iPosInfInterval.isBefore(cNegInfInterval)); + assert(!iPosInfInterval.isBefore(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); +} + +//Test PosInfInterval's isAfter(time point). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(posInfInterval.isAfter(Date(2009, 7, 3))); + assert(posInfInterval.isAfter(Date(2010, 7, 3))); + assert(!posInfInterval.isAfter(Date(2010, 7, 4))); + assert(!posInfInterval.isAfter(Date(2010, 7, 5))); + assert(!posInfInterval.isAfter(Date(2011, 7, 1))); + assert(!posInfInterval.isAfter(Date(2012, 1, 6))); + assert(!posInfInterval.isAfter(Date(2012, 1, 7))); + assert(!posInfInterval.isAfter(Date(2012, 1, 8))); + assert(!posInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!posInfInterval.isAfter(cdate)); + assert(!cPosInfInterval.isAfter(cdate)); + assert(!iPosInfInterval.isAfter(cdate)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); +} + +//Test PosInfInterval's isAfter(Interval). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAfter(posInfInterval)); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); + + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isAfter(interval)); + assert(!posInfInterval.isAfter(cInterval)); + assert(!posInfInterval.isAfter(iInterval)); + assert(!posInfInterval.isAfter(posInfInterval)); + assert(!posInfInterval.isAfter(cPosInfInterval)); + assert(!posInfInterval.isAfter(iPosInfInterval)); + assert(!posInfInterval.isAfter(negInfInterval)); + assert(!posInfInterval.isAfter(cNegInfInterval)); + assert(!posInfInterval.isAfter(iNegInfInterval)); + assert(!cPosInfInterval.isAfter(interval)); + assert(!cPosInfInterval.isAfter(cInterval)); + assert(!cPosInfInterval.isAfter(iInterval)); + assert(!cPosInfInterval.isAfter(posInfInterval)); + assert(!cPosInfInterval.isAfter(cPosInfInterval)); + assert(!cPosInfInterval.isAfter(iPosInfInterval)); + assert(!cPosInfInterval.isAfter(negInfInterval)); + assert(!cPosInfInterval.isAfter(cNegInfInterval)); + assert(!cPosInfInterval.isAfter(iNegInfInterval)); + assert(!iPosInfInterval.isAfter(interval)); + assert(!iPosInfInterval.isAfter(cInterval)); + assert(!iPosInfInterval.isAfter(iInterval)); + assert(!iPosInfInterval.isAfter(posInfInterval)); + assert(!iPosInfInterval.isAfter(cPosInfInterval)); + assert(!iPosInfInterval.isAfter(iPosInfInterval)); + assert(!iPosInfInterval.isAfter(negInfInterval)); + assert(!iPosInfInterval.isAfter(cNegInfInterval)); + assert(!iPosInfInterval.isAfter(iNegInfInterval)); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersects(). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.intersects(posInfInterval)); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); + + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(posInfInterval.intersects(interval)); + assert(posInfInterval.intersects(cInterval)); + assert(posInfInterval.intersects(iInterval)); + assert(posInfInterval.intersects(posInfInterval)); + assert(posInfInterval.intersects(cPosInfInterval)); + assert(posInfInterval.intersects(iPosInfInterval)); + assert(posInfInterval.intersects(negInfInterval)); + assert(posInfInterval.intersects(cNegInfInterval)); + assert(posInfInterval.intersects(iNegInfInterval)); + assert(cPosInfInterval.intersects(interval)); + assert(cPosInfInterval.intersects(cInterval)); + assert(cPosInfInterval.intersects(iInterval)); + assert(cPosInfInterval.intersects(posInfInterval)); + assert(cPosInfInterval.intersects(cPosInfInterval)); + assert(cPosInfInterval.intersects(iPosInfInterval)); + assert(cPosInfInterval.intersects(negInfInterval)); + assert(cPosInfInterval.intersects(cNegInfInterval)); + assert(cPosInfInterval.intersects(iNegInfInterval)); + assert(iPosInfInterval.intersects(interval)); + assert(iPosInfInterval.intersects(cInterval)); + assert(iPosInfInterval.intersects(iInterval)); + assert(iPosInfInterval.intersects(posInfInterval)); + assert(iPosInfInterval.intersects(cPosInfInterval)); + assert(iPosInfInterval.intersects(iPosInfInterval)); + assert(iPosInfInterval.intersects(negInfInterval)); + assert(iPosInfInterval.intersects(cNegInfInterval)); + assert(iPosInfInterval.intersects(iNegInfInterval)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's intersection(). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(posInfInterval.intersection(posInfInterval) == posInfInterval); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); + + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 5))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2012, 1, 6))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2012, 1, 7))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2012, 1, 8))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 5))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 6))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 7))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == PosInfInterval!Date(Date(2012, 1, 8))); + + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.intersection(interval).empty); + assert(!posInfInterval.intersection(cInterval).empty); + assert(!posInfInterval.intersection(iInterval).empty); + assert(!posInfInterval.intersection(posInfInterval).empty); + assert(!posInfInterval.intersection(cPosInfInterval).empty); + assert(!posInfInterval.intersection(iPosInfInterval).empty); + assert(!posInfInterval.intersection(negInfInterval).empty); + assert(!posInfInterval.intersection(cNegInfInterval).empty); + assert(!posInfInterval.intersection(iNegInfInterval).empty); + assert(!cPosInfInterval.intersection(interval).empty); + assert(!cPosInfInterval.intersection(cInterval).empty); + assert(!cPosInfInterval.intersection(iInterval).empty); + assert(!cPosInfInterval.intersection(posInfInterval).empty); + assert(!cPosInfInterval.intersection(cPosInfInterval).empty); + assert(!cPosInfInterval.intersection(iPosInfInterval).empty); + assert(!cPosInfInterval.intersection(negInfInterval).empty); + assert(!cPosInfInterval.intersection(cNegInfInterval).empty); + assert(!cPosInfInterval.intersection(iNegInfInterval).empty); + assert(!iPosInfInterval.intersection(interval).empty); + assert(!iPosInfInterval.intersection(cInterval).empty); + assert(!iPosInfInterval.intersection(iInterval).empty); + assert(!iPosInfInterval.intersection(posInfInterval).empty); + assert(!iPosInfInterval.intersection(cPosInfInterval).empty); + assert(!iPosInfInterval.intersection(iPosInfInterval).empty); + assert(!iPosInfInterval.intersection(negInfInterval).empty); + assert(!iPosInfInterval.intersection(cNegInfInterval).empty); + assert(!iPosInfInterval.intersection(iNegInfInterval).empty); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1996, 1, 2), Date(2000, 8, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1996, 1, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1999, 1, 12))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == + Interval!Date(Date(1996, 1, 2), Date(1999, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == + Interval!Date(Date(1996, 1, 2), Date(2013, 1, 12))); +} + +//Test PosInfInterval's isAdjacent(). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); + + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.isAdjacent(interval)); + assert(!posInfInterval.isAdjacent(cInterval)); + assert(!posInfInterval.isAdjacent(iInterval)); + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(cPosInfInterval)); + assert(!posInfInterval.isAdjacent(iPosInfInterval)); + assert(!posInfInterval.isAdjacent(negInfInterval)); + assert(!posInfInterval.isAdjacent(cNegInfInterval)); + assert(!posInfInterval.isAdjacent(iNegInfInterval)); + assert(!cPosInfInterval.isAdjacent(interval)); + assert(!cPosInfInterval.isAdjacent(cInterval)); + assert(!cPosInfInterval.isAdjacent(iInterval)); + assert(!cPosInfInterval.isAdjacent(posInfInterval)); + assert(!cPosInfInterval.isAdjacent(cPosInfInterval)); + assert(!cPosInfInterval.isAdjacent(iPosInfInterval)); + assert(!cPosInfInterval.isAdjacent(negInfInterval)); + assert(!cPosInfInterval.isAdjacent(cNegInfInterval)); + assert(!cPosInfInterval.isAdjacent(iNegInfInterval)); + assert(!iPosInfInterval.isAdjacent(interval)); + assert(!iPosInfInterval.isAdjacent(cInterval)); + assert(!iPosInfInterval.isAdjacent(iInterval)); + assert(!iPosInfInterval.isAdjacent(posInfInterval)); + assert(!iPosInfInterval.isAdjacent(cPosInfInterval)); + assert(!iPosInfInterval.isAdjacent(iPosInfInterval)); + assert(!iPosInfInterval.isAdjacent(negInfInterval)); + assert(!iPosInfInterval.isAdjacent(cNegInfInterval)); + assert(!iPosInfInterval.isAdjacent(iNegInfInterval)); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); +} + +//Test PosInfInterval's merge(). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.merge(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + + assert(posInfInterval.merge(posInfInterval) == posInfInterval); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.merge(interval).empty); + assert(!posInfInterval.merge(cInterval).empty); + assert(!posInfInterval.merge(iInterval).empty); + assert(!posInfInterval.merge(posInfInterval).empty); + assert(!posInfInterval.merge(cPosInfInterval).empty); + assert(!posInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); + assert(!cPosInfInterval.merge(interval).empty); + assert(!cPosInfInterval.merge(cInterval).empty); + assert(!cPosInfInterval.merge(iInterval).empty); + assert(!cPosInfInterval.merge(posInfInterval).empty); + assert(!cPosInfInterval.merge(cPosInfInterval).empty); + assert(!cPosInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); + assert(!iPosInfInterval.merge(interval).empty); + assert(!iPosInfInterval.merge(cInterval).empty); + assert(!iPosInfInterval.merge(iInterval).empty); + assert(!iPosInfInterval.merge(posInfInterval).empty); + assert(!iPosInfInterval.merge(cPosInfInterval).empty); + assert(!iPosInfInterval.merge(iPosInfInterval).empty); + static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1, 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1, 2))); +} + +//Test PosInfInterval's span(). +@safe unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.span(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.span(posInfInterval) == posInfInterval); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!posInfInterval.span(interval).empty); + assert(!posInfInterval.span(cInterval).empty); + assert(!posInfInterval.span(iInterval).empty); + assert(!posInfInterval.span(posInfInterval).empty); + assert(!posInfInterval.span(cPosInfInterval).empty); + assert(!posInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); + assert(!cPosInfInterval.span(interval).empty); + assert(!cPosInfInterval.span(cInterval).empty); + assert(!cPosInfInterval.span(iInterval).empty); + assert(!cPosInfInterval.span(posInfInterval).empty); + assert(!cPosInfInterval.span(cPosInfInterval).empty); + assert(!cPosInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); + assert(!iPosInfInterval.span(interval).empty); + assert(!iPosInfInterval.span(cInterval).empty); + assert(!iPosInfInterval.span(iInterval).empty); + assert(!iPosInfInterval.span(posInfInterval).empty); + assert(!iPosInfInterval.span(cPosInfInterval).empty); + assert(!iPosInfInterval.span(iPosInfInterval).empty); + static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == + PosInfInterval!Date(Date(500, 8, 9))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == + PosInfInterval!Date(Date(1996, 1, 2))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == + PosInfInterval!Date(Date(1990, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == + PosInfInterval!Date(Date(1996, 1, 2))); +} + +//Test PosInfInterval's shift(). +@safe unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); +} + +//Test PosInfInterval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + { + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.shift(1))); + static assert(!__traits(compiles, iPosInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.shift(2); + assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); + + interval2.shift(-2); + assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); +} + +//Test PosInfInterval's expand(). +@safe unittest +{ + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); + + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); +} + +//Test PosInfInterval's expand(int, int, AllowDayOverflow). +@safe unittest +{ + { + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + } + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.expand(1))); + static assert(!__traits(compiles, iPosInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + + interval1.expand(2); + assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + + interval2.expand(-2); + assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); +} + +//Test PosInfInterval's fwdRange(). +@system unittest +{ + auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); + + static void testInterval(PosInfInterval!Date posInfInterval) + { + posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(posInfInterval)); + + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); + + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 17)); + + //Verify Examples. + auto interval = PosInfInterval!Date(Date(2010, 9, 1)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date + dur!"days"(2); + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); + + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(!range.empty); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(!cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(!iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); +} + +//Test PosInfInterval's toString(). +@safe unittest +{ + assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + assert(cPosInfInterval.toString()); + assert(iPosInfInterval.toString()); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index c1d3213fe8e..c869aedacf6 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,2221 +18219,6 @@ private: // Section with intervals. //============================================================================== -/++ - Represents an interval of time which has positive infinity as its end point. - - Any ranges which iterate over a $(D PosInfInterval) are infinite. So, the - main purpose of using $(D PosInfInterval) is to create an infinite range - which starts at a fixed point in time and goes to positive infinity. - +/ -struct PosInfInterval(TP) -{ -public: - - /++ - Params: - begin = The time point which begins the interval. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(in TP begin) pure nothrow - { - _begin = cast(TP) begin; - } - - - /++ - Params: - rhs = The $(D PosInfInterval) to assign to this one. - +/ - ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - return this; - } - - - /++ - Params: - rhs = The $(D PosInfInterval) to assign to this one. - +/ - ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow - { - _begin = cast(TP) rhs._begin; - return this; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); --------------------- - +/ - @property TP begin() const pure nothrow - { - return cast(TP)_begin; - } - - - /++ - The starting point of the interval. It is included in the interval. - - Params: - timePoint = The time point to set $(D begin) to. - +/ - @property void begin(TP timePoint) pure nothrow - { - _begin = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - enum bool empty = false; - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - PosInfInterval!Date(Date(1995, 7, 2)))); --------------------- - +/ - bool contains(in PosInfInterval interval) const pure nothrow - { - return interval._begin >= _begin; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval going to positive infinity - can never contain an interval beginning at negative infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool contains(in NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given time point. - - Always returns false because an interval going to positive infinity - can never be before any time point. - - Params: - timePoint = The time point to check whether this interval is before - it. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval going to positive infinity can never be before any other - interval. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isBefore(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(1992, 5, 4)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - PosInfInterval!Date(Date(2013, 3, 7)))); --------------------- - +/ - bool isBefore(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval going to positive infinity can - never be before any other interval. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); --------------------- - +/ - bool isBefore(in NegInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure nothrow - { - return timePoint < _begin; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool isAfter(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _begin >= interval._end; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval going to positive infinity can - never be after another interval going to positive infinity. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool isAfter(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAfter(in NegInfInterval!TP interval) const pure nothrow - { - return _begin >= interval._end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); --------------------- - +/ - bool intersects(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._end > _begin; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals going to positive infinity - always overlap. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool intersects(in PosInfInterval interval) const pure nothrow - { - return true; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow - { - return _begin < interval._end; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); --------------------- - +/ - Interval!TP intersection(in Interval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - auto begin = _begin > interval._begin ? _begin : interval._begin; - - return Interval!TP(begin, interval._end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1996, 1 , 2))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1999, 1 , 12))); --------------------- - +/ - PosInfInterval intersection(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? interval._begin : _begin); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); --------------------- - +/ - Interval!TP intersection(in NegInfInterval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - return Interval!TP(_begin, interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); --------------------- - +/ - bool isAdjacent(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _begin == interval._end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals going to positive infinity - can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1990, 1, 7)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - PosInfInterval!Date(Date(1996, 1, 2)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(1996, 1, 2)))); - -assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( - NegInfInterval!Date(Date(2000, 7, 1)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval!TP interval) const pure nothrow - { - return _begin == interval._end; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. - - Note: - There is no overload for $(D merge) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for $(D merge) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval merge(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Note: - There is no overload for $(D span) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == - PosInfInterval!Date(Date(500, 8, 9))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for $(D span) which takes a - $(D NegInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1990, 7, 6))) == - PosInfInterval!Date(Date(1990, 7 , 6))); - -assert(PosInfInterval!Date(Date(1996, 1, 2)).span( - PosInfInterval!Date(Date(1999, 1, 12))) == - PosInfInterval!Date(Date(1996, 1 , 2))); --------------------- - +/ - PosInfInterval span(in PosInfInterval interval) const pure nothrow - { - return PosInfInterval(_begin < interval._begin ? _begin : interval._begin); - } - - - /++ - Shifts the $(D begin) of this interval forward or backwards in time by - the given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D begin += duration). - - Params: - duration = The duration to shift the interval by. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) - { - _begin += duration; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Shifts the $(D begin) of this interval forward or backwards in time - by the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to - $(D begin). It effectively calls $(D add!"years"()) and then - $(D add!"months"()) on $(D begin) with the given number of years and - months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin), causing its month to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) - if (isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(years, allowOverflow); - begin.add!"months"(months, allowOverflow); - - _begin = begin; - } - } - - - /++ - Expands the interval backwards in time. Effectively, it does - $(D begin -= duration). - - Params: - duration = The duration to expand the interval by. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if (__traits(compiles, begin + duration)) - { - _begin -= duration; - } - - - static if (__traits(compiles, begin.add!"months"(1)) && - __traits(compiles, begin.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it subtracts the given number of months/years from $(D begin). - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D begin), causing its month to increment. - - Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. - - Example: --------------------- -auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); -auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - -interval1.expand(2); -assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - -interval2.expand(-2); -assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) - if (isIntegral!T) - { - auto begin = _begin; - - begin.add!"years"(-years, allowOverflow); - begin.add!"months"(-months, allowOverflow); - - _begin = begin; - - return; - } - } - - - /++ - Returns a range which iterates forward over the interval, starting - at $(D begin), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D begin). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D Yes.popFirst), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. - - There are helper functions in this module which generate common - delegates to pass to $(D fwdRange). Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(2010, 9, 1)); -auto func = delegate (in Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; -auto range = interval.fwdRange(func); - -//An odd day. Using Yes.popFirst would have made this Date(2010, 9, 2). -assert(range.front == Date(2010, 9, 1)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - PosInfIntervalRange!(TP) fwdRange(TP delegate(in TP) func, PopFirst popFirst = No.popFirst) const - { - auto range = PosInfIntervalRange!(TP)(this, func); - - if (popFirst == Yes.popFirst) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: -package: // temporary - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[%s - ∞)", _begin); - catch (Exception e) - assert(0, "format() threw."); - } - - - TP _begin; -} - -//Test PosInfInterval's constructor. -@safe unittest -{ - PosInfInterval!Date(Date.init); - PosInfInterval!TimeOfDay(TimeOfDay.init); - PosInfInterval!DateTime(DateTime.init); - PosInfInterval!SysTime(SysTime(0)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(1996, 1, 2)); -} - -//Test PosInfInterval's begin. -@safe unittest -{ - assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); - assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); - assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(cPosInfInterval.begin != Date.init); - assert(iPosInfInterval.begin != Date.init); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); -} - -//Test PosInfInterval's empty. -@safe unittest -{ - assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - assert(!cPosInfInterval.empty); - assert(!iPosInfInterval.empty); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test PosInfInterval's contains(time point). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.contains(Date(2009, 7, 4))); - assert(!posInfInterval.contains(Date(2010, 7, 3))); - assert(posInfInterval.contains(Date(2010, 7, 4))); - assert(posInfInterval.contains(Date(2010, 7, 5))); - assert(posInfInterval.contains(Date(2011, 7, 1))); - assert(posInfInterval.contains(Date(2012, 1, 6))); - assert(posInfInterval.contains(Date(2012, 1, 7))); - assert(posInfInterval.contains(Date(2012, 1, 8))); - assert(posInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(posInfInterval.contains(cdate)); - assert(cPosInfInterval.contains(cdate)); - assert(iPosInfInterval.contains(cdate)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); -} - -//Test PosInfInterval's contains(Interval). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.contains(posInfInterval)); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); - - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(posInfInterval.contains(interval)); - assert(posInfInterval.contains(cInterval)); - assert(posInfInterval.contains(iInterval)); - assert(posInfInterval.contains(posInfInterval)); - assert(posInfInterval.contains(cPosInfInterval)); - assert(posInfInterval.contains(iPosInfInterval)); - assert(!posInfInterval.contains(negInfInterval)); - assert(!posInfInterval.contains(cNegInfInterval)); - assert(!posInfInterval.contains(iNegInfInterval)); - assert(cPosInfInterval.contains(interval)); - assert(cPosInfInterval.contains(cInterval)); - assert(cPosInfInterval.contains(iInterval)); - assert(cPosInfInterval.contains(posInfInterval)); - assert(cPosInfInterval.contains(cPosInfInterval)); - assert(cPosInfInterval.contains(iPosInfInterval)); - assert(!cPosInfInterval.contains(negInfInterval)); - assert(!cPosInfInterval.contains(cNegInfInterval)); - assert(!cPosInfInterval.contains(iNegInfInterval)); - assert(iPosInfInterval.contains(interval)); - assert(iPosInfInterval.contains(cInterval)); - assert(iPosInfInterval.contains(iInterval)); - assert(iPosInfInterval.contains(posInfInterval)); - assert(iPosInfInterval.contains(cPosInfInterval)); - assert(iPosInfInterval.contains(iPosInfInterval)); - assert(!iPosInfInterval.contains(negInfInterval)); - assert(!iPosInfInterval.contains(cNegInfInterval)); - assert(!iPosInfInterval.contains(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isBefore(time point). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(!posInfInterval.isBefore(Date(2009, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 4))); - assert(!posInfInterval.isBefore(Date(2010, 7, 5))); - assert(!posInfInterval.isBefore(Date(2011, 7, 1))); - assert(!posInfInterval.isBefore(Date(2012, 1, 6))); - assert(!posInfInterval.isBefore(Date(2012, 1, 7))); - assert(!posInfInterval.isBefore(Date(2012, 1, 8))); - assert(!posInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!posInfInterval.isBefore(cdate)); - assert(!cPosInfInterval.isBefore(cdate)); - assert(!iPosInfInterval.isBefore(cdate)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isBefore(Interval). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); - - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isBefore(interval)); - assert(!posInfInterval.isBefore(cInterval)); - assert(!posInfInterval.isBefore(iInterval)); - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(cPosInfInterval)); - assert(!posInfInterval.isBefore(iPosInfInterval)); - assert(!posInfInterval.isBefore(negInfInterval)); - assert(!posInfInterval.isBefore(cNegInfInterval)); - assert(!posInfInterval.isBefore(iNegInfInterval)); - assert(!cPosInfInterval.isBefore(interval)); - assert(!cPosInfInterval.isBefore(cInterval)); - assert(!cPosInfInterval.isBefore(iInterval)); - assert(!cPosInfInterval.isBefore(posInfInterval)); - assert(!cPosInfInterval.isBefore(cPosInfInterval)); - assert(!cPosInfInterval.isBefore(iPosInfInterval)); - assert(!cPosInfInterval.isBefore(negInfInterval)); - assert(!cPosInfInterval.isBefore(cNegInfInterval)); - assert(!cPosInfInterval.isBefore(iNegInfInterval)); - assert(!iPosInfInterval.isBefore(interval)); - assert(!iPosInfInterval.isBefore(cInterval)); - assert(!iPosInfInterval.isBefore(iInterval)); - assert(!iPosInfInterval.isBefore(posInfInterval)); - assert(!iPosInfInterval.isBefore(cPosInfInterval)); - assert(!iPosInfInterval.isBefore(iPosInfInterval)); - assert(!iPosInfInterval.isBefore(negInfInterval)); - assert(!iPosInfInterval.isBefore(cNegInfInterval)); - assert(!iPosInfInterval.isBefore(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); -} - -//Test PosInfInterval's isAfter(time point). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - assert(posInfInterval.isAfter(Date(2009, 7, 3))); - assert(posInfInterval.isAfter(Date(2010, 7, 3))); - assert(!posInfInterval.isAfter(Date(2010, 7, 4))); - assert(!posInfInterval.isAfter(Date(2010, 7, 5))); - assert(!posInfInterval.isAfter(Date(2011, 7, 1))); - assert(!posInfInterval.isAfter(Date(2012, 1, 6))); - assert(!posInfInterval.isAfter(Date(2012, 1, 7))); - assert(!posInfInterval.isAfter(Date(2012, 1, 8))); - assert(!posInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!posInfInterval.isAfter(cdate)); - assert(!cPosInfInterval.isAfter(cdate)); - assert(!iPosInfInterval.isAfter(cdate)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); -} - -//Test PosInfInterval's isAfter(Interval). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAfter(posInfInterval)); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); - - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isAfter(interval)); - assert(!posInfInterval.isAfter(cInterval)); - assert(!posInfInterval.isAfter(iInterval)); - assert(!posInfInterval.isAfter(posInfInterval)); - assert(!posInfInterval.isAfter(cPosInfInterval)); - assert(!posInfInterval.isAfter(iPosInfInterval)); - assert(!posInfInterval.isAfter(negInfInterval)); - assert(!posInfInterval.isAfter(cNegInfInterval)); - assert(!posInfInterval.isAfter(iNegInfInterval)); - assert(!cPosInfInterval.isAfter(interval)); - assert(!cPosInfInterval.isAfter(cInterval)); - assert(!cPosInfInterval.isAfter(iInterval)); - assert(!cPosInfInterval.isAfter(posInfInterval)); - assert(!cPosInfInterval.isAfter(cPosInfInterval)); - assert(!cPosInfInterval.isAfter(iPosInfInterval)); - assert(!cPosInfInterval.isAfter(negInfInterval)); - assert(!cPosInfInterval.isAfter(cNegInfInterval)); - assert(!cPosInfInterval.isAfter(iNegInfInterval)); - assert(!iPosInfInterval.isAfter(interval)); - assert(!iPosInfInterval.isAfter(cInterval)); - assert(!iPosInfInterval.isAfter(iInterval)); - assert(!iPosInfInterval.isAfter(posInfInterval)); - assert(!iPosInfInterval.isAfter(cPosInfInterval)); - assert(!iPosInfInterval.isAfter(iPosInfInterval)); - assert(!iPosInfInterval.isAfter(negInfInterval)); - assert(!iPosInfInterval.isAfter(cNegInfInterval)); - assert(!iPosInfInterval.isAfter(iNegInfInterval)); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersects(). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.intersects(posInfInterval)); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); - - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(posInfInterval.intersects(interval)); - assert(posInfInterval.intersects(cInterval)); - assert(posInfInterval.intersects(iInterval)); - assert(posInfInterval.intersects(posInfInterval)); - assert(posInfInterval.intersects(cPosInfInterval)); - assert(posInfInterval.intersects(iPosInfInterval)); - assert(posInfInterval.intersects(negInfInterval)); - assert(posInfInterval.intersects(cNegInfInterval)); - assert(posInfInterval.intersects(iNegInfInterval)); - assert(cPosInfInterval.intersects(interval)); - assert(cPosInfInterval.intersects(cInterval)); - assert(cPosInfInterval.intersects(iInterval)); - assert(cPosInfInterval.intersects(posInfInterval)); - assert(cPosInfInterval.intersects(cPosInfInterval)); - assert(cPosInfInterval.intersects(iPosInfInterval)); - assert(cPosInfInterval.intersects(negInfInterval)); - assert(cPosInfInterval.intersects(cNegInfInterval)); - assert(cPosInfInterval.intersects(iNegInfInterval)); - assert(iPosInfInterval.intersects(interval)); - assert(iPosInfInterval.intersects(cInterval)); - assert(iPosInfInterval.intersects(iInterval)); - assert(iPosInfInterval.intersects(posInfInterval)); - assert(iPosInfInterval.intersects(cPosInfInterval)); - assert(iPosInfInterval.intersects(iPosInfInterval)); - assert(iPosInfInterval.intersects(negInfInterval)); - assert(iPosInfInterval.intersects(cNegInfInterval)); - assert(iPosInfInterval.intersects(iNegInfInterval)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's intersection(). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); - - assert(posInfInterval.intersection(posInfInterval) == - posInfInterval); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); - assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); - - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 5))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2012, 1, 6))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2012, 1, 7))); - assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2012, 1, 8))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 5))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 6))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 7))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == - PosInfInterval!Date(Date(2012, 1, 8))); - - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.intersection(interval).empty); - assert(!posInfInterval.intersection(cInterval).empty); - assert(!posInfInterval.intersection(iInterval).empty); - assert(!posInfInterval.intersection(posInfInterval).empty); - assert(!posInfInterval.intersection(cPosInfInterval).empty); - assert(!posInfInterval.intersection(iPosInfInterval).empty); - assert(!posInfInterval.intersection(negInfInterval).empty); - assert(!posInfInterval.intersection(cNegInfInterval).empty); - assert(!posInfInterval.intersection(iNegInfInterval).empty); - assert(!cPosInfInterval.intersection(interval).empty); - assert(!cPosInfInterval.intersection(cInterval).empty); - assert(!cPosInfInterval.intersection(iInterval).empty); - assert(!cPosInfInterval.intersection(posInfInterval).empty); - assert(!cPosInfInterval.intersection(cPosInfInterval).empty); - assert(!cPosInfInterval.intersection(iPosInfInterval).empty); - assert(!cPosInfInterval.intersection(negInfInterval).empty); - assert(!cPosInfInterval.intersection(cNegInfInterval).empty); - assert(!cPosInfInterval.intersection(iNegInfInterval).empty); - assert(!iPosInfInterval.intersection(interval).empty); - assert(!iPosInfInterval.intersection(cInterval).empty); - assert(!iPosInfInterval.intersection(iInterval).empty); - assert(!iPosInfInterval.intersection(posInfInterval).empty); - assert(!iPosInfInterval.intersection(cPosInfInterval).empty); - assert(!iPosInfInterval.intersection(iPosInfInterval).empty); - assert(!iPosInfInterval.intersection(negInfInterval).empty); - assert(!iPosInfInterval.intersection(cNegInfInterval).empty); - assert(!iPosInfInterval.intersection(iNegInfInterval).empty); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, - 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1, 2), Date(2000, 8, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, - 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)) - .intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, - 1, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)) - .intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, - 1, 12))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)) - .intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, - 1, 2), Date(1999, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)) - .intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, - 1, 2), Date(2013, 1, 12))); -} - -//Test PosInfInterval's isAdjacent(). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); - - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.isAdjacent(interval)); - assert(!posInfInterval.isAdjacent(cInterval)); - assert(!posInfInterval.isAdjacent(iInterval)); - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(cPosInfInterval)); - assert(!posInfInterval.isAdjacent(iPosInfInterval)); - assert(!posInfInterval.isAdjacent(negInfInterval)); - assert(!posInfInterval.isAdjacent(cNegInfInterval)); - assert(!posInfInterval.isAdjacent(iNegInfInterval)); - assert(!cPosInfInterval.isAdjacent(interval)); - assert(!cPosInfInterval.isAdjacent(cInterval)); - assert(!cPosInfInterval.isAdjacent(iInterval)); - assert(!cPosInfInterval.isAdjacent(posInfInterval)); - assert(!cPosInfInterval.isAdjacent(cPosInfInterval)); - assert(!cPosInfInterval.isAdjacent(iPosInfInterval)); - assert(!cPosInfInterval.isAdjacent(negInfInterval)); - assert(!cPosInfInterval.isAdjacent(cNegInfInterval)); - assert(!cPosInfInterval.isAdjacent(iNegInfInterval)); - assert(!iPosInfInterval.isAdjacent(interval)); - assert(!iPosInfInterval.isAdjacent(cInterval)); - assert(!iPosInfInterval.isAdjacent(iInterval)); - assert(!iPosInfInterval.isAdjacent(posInfInterval)); - assert(!iPosInfInterval.isAdjacent(cPosInfInterval)); - assert(!iPosInfInterval.isAdjacent(iPosInfInterval)); - assert(!iPosInfInterval.isAdjacent(negInfInterval)); - assert(!iPosInfInterval.isAdjacent(cNegInfInterval)); - assert(!iPosInfInterval.isAdjacent(iNegInfInterval)); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); -} - -//Test PosInfInterval's merge(). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.merge(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - - assert(posInfInterval.merge(posInfInterval) == - posInfInterval); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.merge(interval).empty); - assert(!posInfInterval.merge(cInterval).empty); - assert(!posInfInterval.merge(iInterval).empty); - assert(!posInfInterval.merge(posInfInterval).empty); - assert(!posInfInterval.merge(cPosInfInterval).empty); - assert(!posInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); - assert(!cPosInfInterval.merge(interval).empty); - assert(!cPosInfInterval.merge(cInterval).empty); - assert(!cPosInfInterval.merge(iInterval).empty); - assert(!cPosInfInterval.merge(posInfInterval).empty); - assert(!cPosInfInterval.merge(cPosInfInterval).empty); - assert(!cPosInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); - assert(!iPosInfInterval.merge(interval).empty); - assert(!iPosInfInterval.merge(cInterval).empty); - assert(!iPosInfInterval.merge(iInterval).empty); - assert(!iPosInfInterval.merge(posInfInterval).empty); - assert(!iPosInfInterval.merge(cPosInfInterval).empty); - assert(!iPosInfInterval.merge(iPosInfInterval).empty); - static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, - 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, - 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1, 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, - 7, 6))) == PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, - 1, 12))) == PosInfInterval!Date(Date(1996, 1, 2))); -} - -//Test PosInfInterval's span(). -@safe unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.span(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.span(posInfInterval) == - posInfInterval); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 1))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!posInfInterval.span(interval).empty); - assert(!posInfInterval.span(cInterval).empty); - assert(!posInfInterval.span(iInterval).empty); - assert(!posInfInterval.span(posInfInterval).empty); - assert(!posInfInterval.span(cPosInfInterval).empty); - assert(!posInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); - assert(!cPosInfInterval.span(interval).empty); - assert(!cPosInfInterval.span(cInterval).empty); - assert(!cPosInfInterval.span(iInterval).empty); - assert(!cPosInfInterval.span(posInfInterval).empty); - assert(!cPosInfInterval.span(cPosInfInterval).empty); - assert(!cPosInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); - assert(!iPosInfInterval.span(interval).empty); - assert(!iPosInfInterval.span(cInterval).empty); - assert(!iPosInfInterval.span(iInterval).empty); - assert(!iPosInfInterval.span(posInfInterval).empty); - assert(!iPosInfInterval.span(cPosInfInterval).empty); - assert(!iPosInfInterval.span(iPosInfInterval).empty); - static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, - 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, - 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, - 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1, 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, - 7, 6))) == PosInfInterval!Date(Date(1990, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, - 1, 12))) == PosInfInterval!Date(Date(1996, 1, 2))); -} - -//Test PosInfInterval's shift(). -@safe unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); -} - -//Test PosInfInterval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, PosInfInterval!Date(Date(2015, 7, 4))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, PosInfInterval!Date(Date(1999, 3, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, 1, -1, No.allowDayOverflow, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, No.allowDayOverflow, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, No.allowDayOverflow, PosInfInterval!Date(Date(1999, 2, 28))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.shift(1))); - static assert(!__traits(compiles, iPosInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.shift(2); - assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); - - interval2.shift(-2); - assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); -} - -//Test PosInfInterval's expand(). -@safe unittest -{ - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); - - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); -} - -//Test PosInfInterval's expand(int, int, AllowDayOverflow). -@safe unittest -{ - { - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, PosInfInterval!Date(Date(1995, 7, 4))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, PosInfInterval!Date(Date(2005, 7, 4))); - - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, PosInfInterval!Date(Date(1999, 3, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, PosInfInterval!Date(Date(2000, 12, 29))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, No.allowDayOverflow, PosInfInterval!Date(Date(1999, 2, 28))); - testInterval(interval2, -1, -1, No.allowDayOverflow, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, -1, 1, No.allowDayOverflow, PosInfInterval!Date(Date(2000, 12, 29))); - } - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.expand(1))); - static assert(!__traits(compiles, iPosInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - - interval1.expand(2); - assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - - interval2.expand(-2); - assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); -} - -//Test PosInfInterval's fwdRange(). -@system unittest -{ - auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); - - static void testInterval(PosInfInterval!Date posInfInterval) - { - posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(posInfInterval)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == - Date(2010, 9, 12)); - - assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), Yes.popFirst).front == - Date(2010, 9, 17)); - - //Verify Examples. - auto interval = PosInfInterval!Date(Date(2010, 9, 1)); - auto func = delegate (in Date date) - { - if ((date.day & 1) == 0) - return date + dur!"days"(2); - - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); - - assert(range.front == Date(2010, 9, 1)); //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 2). - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(!range.empty); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(!cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(!iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); -} - -//Test PosInfInterval's toString(). -@safe unittest -{ - assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); - - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - assert(cPosInfInterval.toString()); - assert(iPosInfInterval.toString()); -} - - /++ Represents an interval of time which has negative infinity as its starting point. @@ -23806,6 +21591,7 @@ public: private: +package: // temporary /+ Params: From bad1fbf1b93bdb9ab8194cfc0ec08997bd33ea92 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 21:15:20 +0200 Subject: [PATCH 086/262] Move NegInfInterval to std.datetime.interval. --- std/datetime/interval.d | 2229 +++++++++++++++++++++++++++++++++++++- std/datetime/package.d | 2240 +-------------------------------------- 2 files changed, 2200 insertions(+), 2269 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 0e41144af39..1acee7f82dd 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -19,7 +19,7 @@ version(unittest) import std.exception : assertThrown; import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary -import std.datetime : NegInfInterval, IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary +import std.datetime : IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary import std.datetime : everyDayOfWeek; // temporary /++ @@ -1578,10 +1578,8 @@ package: // temporary // Verify Examples. Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == - Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); - assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == - Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == @@ -2610,31 +2608,19 @@ package: // temporary assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == - PosInfInterval!Date(Date(2010, 7, 3))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == - PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == - PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == PosInfInterval!Date(Date(2010, 7, 4))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -5052,7 +5038,7 @@ package: // temporary auto interval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) + in I expected, size_t line = __LINE__) { interval.shift(years, months, allow); assert(interval == expected); @@ -5226,3 +5212,2186 @@ package: // temporary assert(cPosInfInterval.toString()); assert(iPosInfInterval.toString()); } + + +/++ + Represents an interval of time which has negative infinity as its starting + point. + + Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the + main purpose of using $(D NegInfInterval) is to create an infinite range + which starts at negative infinity and goes to a fixed end point. + Iterate over it in reverse. + +/ +struct NegInfInterval(TP) +{ +public: + + /++ + Params: + end = The time point which ends the interval. + + Example: +-------------------- +auto interval = PosInfInterval!Date(Date(1996, 1, 2)); +-------------------- + +/ + this(in TP end) pure nothrow + { + _end = cast(TP) end; + } + + + /++ + Params: + rhs = The $(D NegInfInterval) to assign to this one. + +/ + ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow + { + _end = cast(TP) rhs._end; + return this; + } + + + /++ + Params: + rhs = The $(D NegInfInterval) to assign to this one. + +/ + ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow + { + _end = cast(TP) rhs._end; + return this; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +-------------------- + +/ + @property TP end() const pure nothrow + { + return cast(TP)_end; + } + + + /++ + The end point of the interval. It is excluded from the interval. + + Params: + timePoint = The time point to set end to. + +/ + @property void end(TP timePoint) pure nothrow + { + _end = timePoint; + } + + + /++ + Whether the interval's length is 0. Always returns false. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +-------------------- + +/ + enum bool empty = false; + + + /++ + Whether the given time point is within this interval. + + Params: + timePoint = The time point to check for inclusion in this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +-------------------- + +/ + bool contains(TP timePoint) const pure nothrow + { + return timePoint < _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); +-------------------- + +/ + bool contains(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._end <= _end; + } + + + /++ + Whether the given interval is completely within this interval. + + Always returns false because an interval beginning at negative + infinity can never contain an interval going to positive infinity. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + PosInfInterval!Date(Date(1999, 5, 4)))); +-------------------- + +/ + bool contains(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval is completely within this interval. + + Params: + interval = The interval to check for inclusion in this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool contains(in NegInfInterval interval) const pure nothrow + { + return interval._end <= _end; + } + + + /++ + Whether this interval is before the given time point. + + Params: + timePoint = The time point to check whether this interval is + before it. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +-------------------- + +/ + bool isBefore(in TP timePoint) const pure nothrow + { + return timePoint >= _end; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Params: + interval = The interval to check for against this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isBefore(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isBefore(in PosInfInterval!TP interval) const pure nothrow + { + return _end <= interval._begin; + } + + + /++ + Whether this interval is before the given interval and does not + intersect it. + + Always returns false because an interval beginning at negative + infinity can never be before another interval beginning at negative + infinity. + + Params: + interval = The interval to check for against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isBefore(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given time point. + + Always returns false because an interval beginning at negative infinity + can never be after any time point. + + Params: + timePoint = The time point to check whether this interval is after + it. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); +-------------------- + +/ + bool isAfter(in TP timePoint) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not + intersect it. + + Always returns false (unless the given interval is empty) because an + interval beginning at negative infinity can never be after any other + interval. + + Params: + interval = The interval to check against this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAfter(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAfter(in PosInfInterval!TP interval) const pure nothrow + { + return false; + } + + + /++ + Whether this interval is after the given interval and does not intersect + it. + + Always returns false because an interval beginning at negative infinity + can never be after any other interval. + + Params: + interval = The interval to check against this interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool isAfter(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool intersects(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps this interval. + + Params: + interval = The interval to check for intersection with this + interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool intersects(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin < _end; + } + + + /++ + Whether the given interval overlaps this interval. + + Always returns true because two intervals beginning at negative infinity + always overlap. + + Params: + interval = The interval to check for intersection with this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( + NegInfInterval!Date(Date(2013, 7, 9)))); +-------------------- + +/ + bool intersects(in NegInfInterval!TP interval) const pure nothrow + { + return true; + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect or if + the given interval is empty. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + auto end = _end < interval._end ? _end : interval._end; + + return Interval!TP(interval._begin, end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); +-------------------- + +/ + Interval!TP intersection(in PosInfInterval!TP interval) const + { + import std.format : format; + + enforce(this.intersects(interval), + new DateTimeException(format("%s and %s do not intersect.", this, interval))); + + return Interval!TP(interval._begin, _end); + } + + + /++ + Returns the intersection of two intervals + + Params: + interval = The interval to intersect with this interval. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(1999, 7 , 6))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2012, 3 , 1))); +-------------------- + +/ + NegInfInterval intersection(in NegInfInterval interval) const nothrow + { + return NegInfInterval(_end < interval._end ? _end : interval._end); + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); +-------------------- + +/ + bool isAdjacent(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(1999, 5, 4)))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + PosInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow + { + return interval._begin == _end; + } + + + /++ + Whether the given interval is adjacent to this interval. + + Always returns false because two intervals beginning at negative + infinity can never be adjacent to one another. + + Params: + interval = The interval to check whether its adjecent to this + interval. + + Example: +-------------------- +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(1996, 5, 4)))); + +assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( + NegInfInterval!Date(Date(2012, 3, 1)))); +-------------------- + +/ + bool isAdjacent(in NegInfInterval interval) const pure nothrow + { + return false; + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Throws: + $(LREF DateTimeException) if the two intervals do not intersect and are + not adjacent or if the given interval is empty. + + Note: + There is no overload for $(D merge) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9 , 2))); +-------------------- + +/ + NegInfInterval merge(in Interval!TP interval) const + { + import std.format : format; + + enforce(this.isAdjacent(interval) || this.intersects(interval), + new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); + + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns the union of two intervals + + Params: + interval = The interval to merge with this interval. + + Note: + There is no overload for $(D merge) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval merge(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Throws: + $(LREF DateTimeException) if the given interval is empty. + + Note: + There is no overload for $(D span) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9 , 2))); + +assert(NegInfInterval!Date(Date(1600, 1, 7)).span( + Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == + NegInfInterval!Date(Date(2017, 7 , 1))); +-------------------- + +/ + NegInfInterval span(in Interval!TP interval) const pure + { + interval._enforceNotEmpty(); + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Returns an interval that covers from the earliest time point of two + intervals up to (but not including) the latest time point of two + intervals. + + Params: + interval = The interval to create a span together with this + interval. + + Note: + There is no overload for $(D span) which takes a + $(D PosInfInterval), because an interval + going from negative infinity to positive infinity + is not possible. + + Example: +-------------------- +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3 , 1))); + +assert(NegInfInterval!Date(Date(2012, 3, 1)).span( + NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1 , 12))); +-------------------- + +/ + NegInfInterval span(in NegInfInterval interval) const pure nothrow + { + return NegInfInterval(_end > interval._end ? _end : interval._end); + } + + + /++ + Shifts the $(D end) of this interval forward or backwards in time by the + given duration (a positive duration shifts the interval forward; a + negative duration shifts it backward). Effectively, it does + $(D end += duration). + + Params: + duration = The duration to shift the interval by. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); +auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + +interval1.shift(dur!"days"(50)); +assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + +interval2.shift(dur!"days"(-50)); +assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +-------------------- + +/ + void shift(D)(D duration) pure nothrow + if (__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if (__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Shifts the $(D end) of this interval forward or backwards in time by + the given number of years and/or months (a positive number of years + and months shifts the interval forward; a negative number shifts it + backward). It adds the years the given years and months to end. It + effectively calls $(D add!"years"()) and then $(D add!"months"()) + on end with the given number of years and months. + + Params: + years = The number of years to shift the interval by. + months = The number of months to shift the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D end), causing its month to increment. + + Throws: + $(LREF DateTimeException) if empty is true or if the resulting + interval would be invalid. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.shift(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.shift(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + } + } + + + /++ + Expands the interval forwards in time. Effectively, it does + $(D end += duration). + + Params: + duration = The duration to expand the interval by. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(dur!"days"(2)); +assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + +interval2.expand(dur!"days"(-2)); +assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +-------------------- + +/ + void expand(D)(D duration) pure nothrow + if (__traits(compiles, end + duration)) + { + _end += duration; + } + + + static if (__traits(compiles, end.add!"months"(1)) && + __traits(compiles, end.add!"years"(1))) + { + /++ + Expands the interval forwards and/or backwards in time. Effectively, + it adds the given number of months/years to end. + + Params: + years = The number of years to expand the interval by. + months = The number of months to expand the interval by. + allowOverflow = Whether the days should be allowed to overflow + on $(D end), causing their month to increment. + + Throws: + $(LREF DateTimeException) if empty is true or if the resulting + interval would be invalid. + + Example: +-------------------- +auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); +auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + +interval1.expand(2); +assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + +interval2.expand(-2); +assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +-------------------- + +/ + void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) + if (isIntegral!T) + { + auto end = _end; + + end.add!"years"(years, allowOverflow); + end.add!"months"(months, allowOverflow); + + _end = end; + } + } + + + /++ + Returns a range which iterates backwards over the interval, starting + at $(D end), using $(D_PARAM func) to generate each successive time + point. + + The range's $(D front) is the interval's $(D end). $(D_PARAM func) is + used to generate the next $(D front) when $(D popFront) is called. If + $(D_PARAM popFirst) is $(D PopFirst.yes), then $(D popFront) is called + before the range is returned (so that $(D front) is a time point which + $(D_PARAM func) would generate). + + If $(D_PARAM func) ever generates a time point greater than or equal to + the current $(D front) of the range, then a $(LREF DateTimeException) will + be thrown. + + There are helper functions in this module which generate common + delegates to pass to $(D bwdRange). Their documentation starts with + "Range-generating function," to make them easily searchable. + + Params: + func = The function used to generate the time points of the + range over the interval. + popFirst = Whether $(D popFront) should be called on the range + before returning it. + + Throws: + $(LREF DateTimeException) if this interval is empty. + + Warning: + $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) + would be a function pointer to a pure function, but forcing + $(D_PARAM func) to be pure is far too restrictive to be useful, and + in order to have the ease of use of having functions which generate + functions to pass to $(D fwdRange), $(D_PARAM func) must be a + delegate. + + If $(D_PARAM func) retains state which changes as it is called, then + some algorithms will not work correctly, because the range's + $(D save) will have failed to have really saved the range's state. + To avoid such bugs, don't pass a delegate which is + not logically pure to $(D fwdRange). If $(D_PARAM func) is given the + same time point with two different calls, it must return the same + result both times. + + Of course, none of the functions in this module have this problem, + so it's only relevant for custom delegates. + + Example: +-------------------- +auto interval = NegInfInterval!Date(Date(2010, 9, 9)); +auto func = delegate (in Date date) //For iterating over even-numbered days. + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + + return date - dur!"days"(1); + }; +auto range = interval.bwdRange(func); + +assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + +range.popFront(); +assert(range.front == Date(2010, 9, 8)); + +range.popFront(); +assert(range.front == Date(2010, 9, 6)); + +range.popFront(); +assert(range.front == Date(2010, 9, 4)); + +range.popFront(); +assert(range.front == Date(2010, 9, 2)); + +range.popFront(); +assert(!range.empty); +-------------------- + +/ + NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = PopFirst.no) const + { + auto range = NegInfIntervalRange!(TP)(this, func); + + if (popFirst == PopFirst.yes) + range.popFront(); + + return range; + } + + + /+ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() + { + return _toStringImpl(); + } + + + /++ + Converts this interval to a string. + +/ + //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't + //have versions of toString() with extra modifiers, so we define one version + //with modifiers and one without. + string toString() const nothrow + { + return _toStringImpl(); + } + +private: +package: // temporary + + /+ + Since we have two versions of toString(), we have _toStringImpl() + so that they can share implementations. + +/ + string _toStringImpl() const nothrow + { + import std.format : format; + try + return format("[-∞ - %s)", _end); + catch (Exception e) + assert(0, "format() threw."); + } + + + TP _end; +} + +//Test NegInfInterval's constructor. +@safe unittest +{ + NegInfInterval!Date(Date.init); + NegInfInterval!TimeOfDay(TimeOfDay.init); + NegInfInterval!DateTime(DateTime.init); + NegInfInterval!SysTime(SysTime(0)); +} + +//Test NegInfInterval's end. +@safe unittest +{ + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(cNegInfInterval.end != Date.init); + assert(iNegInfInterval.end != Date.init); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); +} + +//Test NegInfInterval's empty. +@safe unittest +{ + assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!cNegInfInterval.empty); + assert(!iNegInfInterval.empty); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); +} + +//Test NegInfInterval's contains(time point). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(negInfInterval.contains(Date(2009, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 3))); + assert(negInfInterval.contains(Date(2010, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 5))); + assert(negInfInterval.contains(Date(2011, 7, 1))); + assert(negInfInterval.contains(Date(2012, 1, 6))); + assert(!negInfInterval.contains(Date(2012, 1, 7))); + assert(!negInfInterval.contains(Date(2012, 1, 8))); + assert(!negInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.contains(cdate)); + assert(cNegInfInterval.contains(cdate)); + assert(iNegInfInterval.contains(cdate)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); +} + +//Test NegInfInterval's contains(Interval). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); + + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.contains(interval)); + assert(negInfInterval.contains(cInterval)); + assert(negInfInterval.contains(iInterval)); + assert(!negInfInterval.contains(posInfInterval)); + assert(!negInfInterval.contains(cPosInfInterval)); + assert(!negInfInterval.contains(iPosInfInterval)); + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(cNegInfInterval)); + assert(negInfInterval.contains(iNegInfInterval)); + assert(cNegInfInterval.contains(interval)); + assert(cNegInfInterval.contains(cInterval)); + assert(cNegInfInterval.contains(iInterval)); + assert(!cNegInfInterval.contains(posInfInterval)); + assert(!cNegInfInterval.contains(cPosInfInterval)); + assert(!cNegInfInterval.contains(iPosInfInterval)); + assert(cNegInfInterval.contains(negInfInterval)); + assert(cNegInfInterval.contains(cNegInfInterval)); + assert(cNegInfInterval.contains(iNegInfInterval)); + assert(iNegInfInterval.contains(interval)); + assert(iNegInfInterval.contains(cInterval)); + assert(iNegInfInterval.contains(iInterval)); + assert(!iNegInfInterval.contains(posInfInterval)); + assert(!iNegInfInterval.contains(cPosInfInterval)); + assert(!iNegInfInterval.contains(iPosInfInterval)); + assert(iNegInfInterval.contains(negInfInterval)); + assert(iNegInfInterval.contains(cNegInfInterval)); + assert(iNegInfInterval.contains(iNegInfInterval)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isBefore(time point). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isBefore(Date(2009, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 3))); + assert(!negInfInterval.isBefore(Date(2010, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 5))); + assert(!negInfInterval.isBefore(Date(2011, 7, 1))); + assert(!negInfInterval.isBefore(Date(2012, 1, 6))); + assert(negInfInterval.isBefore(Date(2012, 1, 7))); + assert(negInfInterval.isBefore(Date(2012, 1, 8))); + assert(negInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isBefore(cdate)); + assert(!cNegInfInterval.isBefore(cdate)); + assert(!iNegInfInterval.isBefore(cdate)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); +} + +//Test NegInfInterval's isBefore(Interval). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); + + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isBefore(interval)); + assert(!negInfInterval.isBefore(cInterval)); + assert(!negInfInterval.isBefore(iInterval)); + assert(!negInfInterval.isBefore(posInfInterval)); + assert(!negInfInterval.isBefore(cPosInfInterval)); + assert(!negInfInterval.isBefore(iPosInfInterval)); + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(cNegInfInterval)); + assert(!negInfInterval.isBefore(iNegInfInterval)); + assert(!cNegInfInterval.isBefore(interval)); + assert(!cNegInfInterval.isBefore(cInterval)); + assert(!cNegInfInterval.isBefore(iInterval)); + assert(!cNegInfInterval.isBefore(posInfInterval)); + assert(!cNegInfInterval.isBefore(cPosInfInterval)); + assert(!cNegInfInterval.isBefore(iPosInfInterval)); + assert(!cNegInfInterval.isBefore(negInfInterval)); + assert(!cNegInfInterval.isBefore(cNegInfInterval)); + assert(!cNegInfInterval.isBefore(iNegInfInterval)); + assert(!iNegInfInterval.isBefore(interval)); + assert(!iNegInfInterval.isBefore(cInterval)); + assert(!iNegInfInterval.isBefore(iInterval)); + assert(!iNegInfInterval.isBefore(posInfInterval)); + assert(!iNegInfInterval.isBefore(cPosInfInterval)); + assert(!iNegInfInterval.isBefore(iPosInfInterval)); + assert(!iNegInfInterval.isBefore(negInfInterval)); + assert(!iNegInfInterval.isBefore(cNegInfInterval)); + assert(!iNegInfInterval.isBefore(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's isAfter(time point). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isAfter(Date(2009, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 3))); + assert(!negInfInterval.isAfter(Date(2010, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 5))); + assert(!negInfInterval.isAfter(Date(2011, 7, 1))); + assert(!negInfInterval.isAfter(Date(2012, 1, 6))); + assert(!negInfInterval.isAfter(Date(2012, 1, 7))); + assert(!negInfInterval.isAfter(Date(2012, 1, 8))); + assert(!negInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAfter(cdate)); + assert(!cNegInfInterval.isAfter(cdate)); + assert(!iNegInfInterval.isAfter(cdate)); +} + +//Test NegInfInterval's isAfter(Interval). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); + + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAfter(interval)); + assert(!negInfInterval.isAfter(cInterval)); + assert(!negInfInterval.isAfter(iInterval)); + assert(!negInfInterval.isAfter(posInfInterval)); + assert(!negInfInterval.isAfter(cPosInfInterval)); + assert(!negInfInterval.isAfter(iPosInfInterval)); + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(cNegInfInterval)); + assert(!negInfInterval.isAfter(iNegInfInterval)); + assert(!cNegInfInterval.isAfter(interval)); + assert(!cNegInfInterval.isAfter(cInterval)); + assert(!cNegInfInterval.isAfter(iInterval)); + assert(!cNegInfInterval.isAfter(posInfInterval)); + assert(!cNegInfInterval.isAfter(cPosInfInterval)); + assert(!cNegInfInterval.isAfter(iPosInfInterval)); + assert(!cNegInfInterval.isAfter(negInfInterval)); + assert(!cNegInfInterval.isAfter(cNegInfInterval)); + assert(!cNegInfInterval.isAfter(iNegInfInterval)); + assert(!iNegInfInterval.isAfter(interval)); + assert(!iNegInfInterval.isAfter(cInterval)); + assert(!iNegInfInterval.isAfter(iInterval)); + assert(!iNegInfInterval.isAfter(posInfInterval)); + assert(!iNegInfInterval.isAfter(cPosInfInterval)); + assert(!iNegInfInterval.isAfter(iPosInfInterval)); + assert(!iNegInfInterval.isAfter(negInfInterval)); + assert(!iNegInfInterval.isAfter(cNegInfInterval)); + assert(!iNegInfInterval.isAfter(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersects(). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); + + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(negInfInterval.intersects(interval)); + assert(negInfInterval.intersects(cInterval)); + assert(negInfInterval.intersects(iInterval)); + assert(negInfInterval.intersects(posInfInterval)); + assert(negInfInterval.intersects(cPosInfInterval)); + assert(negInfInterval.intersects(iPosInfInterval)); + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(cNegInfInterval)); + assert(negInfInterval.intersects(iNegInfInterval)); + assert(cNegInfInterval.intersects(interval)); + assert(cNegInfInterval.intersects(cInterval)); + assert(cNegInfInterval.intersects(iInterval)); + assert(cNegInfInterval.intersects(posInfInterval)); + assert(cNegInfInterval.intersects(cPosInfInterval)); + assert(cNegInfInterval.intersects(iPosInfInterval)); + assert(cNegInfInterval.intersects(negInfInterval)); + assert(cNegInfInterval.intersects(cNegInfInterval)); + assert(cNegInfInterval.intersects(iNegInfInterval)); + assert(iNegInfInterval.intersects(interval)); + assert(iNegInfInterval.intersects(cInterval)); + assert(iNegInfInterval.intersects(iInterval)); + assert(iNegInfInterval.intersects(posInfInterval)); + assert(iNegInfInterval.intersects(cPosInfInterval)); + assert(iNegInfInterval.intersects(iPosInfInterval)); + assert(iNegInfInterval.intersects(negInfInterval)); + assert(iNegInfInterval.intersects(cNegInfInterval)); + assert(iNegInfInterval.intersects(iNegInfInterval)); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); +} + +//Test NegInfInterval's intersection(). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(negInfInterval.intersection(negInfInterval) == negInfInterval); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2010, 7, 3))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2010, 7, 4))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2010, 7, 5))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 6))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 7))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 3))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 4))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == NegInfInterval!Date(Date(2010, 7, 5))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 6))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.intersection(interval).empty); + assert(!negInfInterval.intersection(cInterval).empty); + assert(!negInfInterval.intersection(iInterval).empty); + assert(!negInfInterval.intersection(posInfInterval).empty); + assert(!negInfInterval.intersection(cPosInfInterval).empty); + assert(!negInfInterval.intersection(iPosInfInterval).empty); + assert(!negInfInterval.intersection(negInfInterval).empty); + assert(!negInfInterval.intersection(cNegInfInterval).empty); + assert(!negInfInterval.intersection(iNegInfInterval).empty); + assert(!cNegInfInterval.intersection(interval).empty); + assert(!cNegInfInterval.intersection(cInterval).empty); + assert(!cNegInfInterval.intersection(iInterval).empty); + assert(!cNegInfInterval.intersection(posInfInterval).empty); + assert(!cNegInfInterval.intersection(cPosInfInterval).empty); + assert(!cNegInfInterval.intersection(iPosInfInterval).empty); + assert(!cNegInfInterval.intersection(negInfInterval).empty); + assert(!cNegInfInterval.intersection(cNegInfInterval).empty); + assert(!cNegInfInterval.intersection(iNegInfInterval).empty); + assert(!iNegInfInterval.intersection(interval).empty); + assert(!iNegInfInterval.intersection(cInterval).empty); + assert(!iNegInfInterval.intersection(iInterval).empty); + assert(!iNegInfInterval.intersection(posInfInterval).empty); + assert(!iNegInfInterval.intersection(cPosInfInterval).empty); + assert(!iNegInfInterval.intersection(iPosInfInterval).empty); + assert(!iNegInfInterval.intersection(negInfInterval).empty); + assert(!iNegInfInterval.intersection(cNegInfInterval).empty); + assert(!iNegInfInterval.intersection(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == + Interval!Date(Date(1990, 7, 6), Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == + Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(1999, 7, 6))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2012, 3, 1))); +} + +//Test NegInfInterval's isAdjacent(). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); + + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.isAdjacent(interval)); + assert(!negInfInterval.isAdjacent(cInterval)); + assert(!negInfInterval.isAdjacent(iInterval)); + assert(!negInfInterval.isAdjacent(posInfInterval)); + assert(!negInfInterval.isAdjacent(cPosInfInterval)); + assert(!negInfInterval.isAdjacent(iPosInfInterval)); + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(cNegInfInterval)); + assert(!negInfInterval.isAdjacent(iNegInfInterval)); + assert(!cNegInfInterval.isAdjacent(interval)); + assert(!cNegInfInterval.isAdjacent(cInterval)); + assert(!cNegInfInterval.isAdjacent(iInterval)); + assert(!cNegInfInterval.isAdjacent(posInfInterval)); + assert(!cNegInfInterval.isAdjacent(cPosInfInterval)); + assert(!cNegInfInterval.isAdjacent(iPosInfInterval)); + assert(!cNegInfInterval.isAdjacent(negInfInterval)); + assert(!cNegInfInterval.isAdjacent(cNegInfInterval)); + assert(!cNegInfInterval.isAdjacent(iNegInfInterval)); + assert(!iNegInfInterval.isAdjacent(interval)); + assert(!iNegInfInterval.isAdjacent(cInterval)); + assert(!iNegInfInterval.isAdjacent(iInterval)); + assert(!iNegInfInterval.isAdjacent(posInfInterval)); + assert(!iNegInfInterval.isAdjacent(cPosInfInterval)); + assert(!iNegInfInterval.isAdjacent(iPosInfInterval)); + assert(!iNegInfInterval.isAdjacent(negInfInterval)); + assert(!iNegInfInterval.isAdjacent(cNegInfInterval)); + assert(!iNegInfInterval.isAdjacent(iNegInfInterval)); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); +} + +//Test NegInfInterval's merge(). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.merge(negInfInterval) == negInfInterval); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.merge(interval).empty); + assert(!negInfInterval.merge(cInterval).empty); + assert(!negInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); + assert(!negInfInterval.merge(negInfInterval).empty); + assert(!negInfInterval.merge(cNegInfInterval).empty); + assert(!negInfInterval.merge(iNegInfInterval).empty); + assert(!cNegInfInterval.merge(interval).empty); + assert(!cNegInfInterval.merge(cInterval).empty); + assert(!cNegInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); + assert(!cNegInfInterval.merge(negInfInterval).empty); + assert(!cNegInfInterval.merge(cNegInfInterval).empty); + assert(!cNegInfInterval.merge(iNegInfInterval).empty); + assert(!iNegInfInterval.merge(interval).empty); + assert(!iNegInfInterval.merge(cInterval).empty); + assert(!iNegInfInterval.merge(iInterval).empty); + static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); + assert(!iNegInfInterval.merge(negInfInterval).empty); + assert(!iNegInfInterval.merge(cNegInfInterval).empty); + assert(!iNegInfInterval.merge(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9, 2))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1, 12))); +} + +//Test NegInfInterval's span(). +@safe unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.span(negInfInterval) == negInfInterval); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + NegInfInterval!Date(Date(2012, 1, 9))); + + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!negInfInterval.span(interval).empty); + assert(!negInfInterval.span(cInterval).empty); + assert(!negInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); + assert(!negInfInterval.span(negInfInterval).empty); + assert(!negInfInterval.span(cNegInfInterval).empty); + assert(!negInfInterval.span(iNegInfInterval).empty); + assert(!cNegInfInterval.span(interval).empty); + assert(!cNegInfInterval.span(cInterval).empty); + assert(!cNegInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); + assert(!cNegInfInterval.span(negInfInterval).empty); + assert(!cNegInfInterval.span(cNegInfInterval).empty); + assert(!cNegInfInterval.span(iNegInfInterval).empty); + assert(!iNegInfInterval.span(interval).empty); + assert(!iNegInfInterval.span(cInterval).empty); + assert(!iNegInfInterval.span(iInterval).empty); + static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); + assert(!iNegInfInterval.span(negInfInterval).empty); + assert(!iNegInfInterval.span(cNegInfInterval).empty); + assert(!iNegInfInterval.span(iNegInfInterval).empty); + + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == + NegInfInterval!Date(Date(2015, 9, 2))); + assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == + NegInfInterval!Date(Date(2017, 7, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == + NegInfInterval!Date(Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == + NegInfInterval!Date(Date(2013, 1, 12))); +} + +//Test NegInfInterval's shift(). +@safe unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); + auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + + interval1.shift(dur!"days"(50)); + assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + + interval2.shift(dur!"days"(-50)); + assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); +} + +//Test NegInfInterval's shift(int, int, AllowDayOverflow). +@safe unittest +{ + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testIntervalFail(I)(I interval, int years, int months) + { + interval.shift(years, months); + } + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.shift(1))); + static assert(!__traits(compiles, iNegInfInterval.shift(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.shift(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.shift(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's expand(). +@safe unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } + + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(dur!"days"(2)); + assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + + interval2.expand(dur!"days"(-2)); + assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); +} + +//Test NegInfInterval's expand(int, int, AllowDayOverflow). +@safe unittest +{ + { + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, + in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } + + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); + } + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.expand(1))); + static assert(!__traits(compiles, iNegInfInterval.expand(1))); + + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + + interval1.expand(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + + interval2.expand(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); +} + +//Test NegInfInterval's bwdRange(). +@system unittest +{ + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(NegInfInterval!Date negInfInterval) + { + negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } + + assertThrown!DateTimeException(testInterval(negInfInterval)); + + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == + Date(2010, 10, 1)); + + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == Date(2010, 9, 24)); + + //Verify Examples. + auto interval = NegInfInterval!Date(Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if ((date.day & 1) == 0) + return date - dur!"days"(2); + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); + + //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); + + range.popFront(); + assert(range.front == Date(2010, 9, 8)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 4)); + + range.popFront(); + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(!range.empty); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(!cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(!iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); +} + +//Test NegInfInterval's toString(). +@safe unittest +{ + assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); + + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + assert(cNegInfInterval.toString()); + assert(iNegInfInterval.toString()); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index c869aedacf6..4818126f5df 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,2245 +18219,6 @@ private: // Section with intervals. //============================================================================== -/++ - Represents an interval of time which has negative infinity as its starting - point. - - Any ranges which iterate over a $(D NegInfInterval) are infinite. So, the - main purpose of using $(D NegInfInterval) is to create an infinite range - which starts at negative infinity and goes to a fixed end point. - Iterate over it in reverse. - +/ -struct NegInfInterval(TP) -{ -public: - - /++ - Params: - end = The time point which ends the interval. - - Example: --------------------- -auto interval = PosInfInterval!Date(Date(1996, 1, 2)); --------------------- - +/ - this(in TP end) pure nothrow - { - _end = cast(TP) end; - } - - - /++ - Params: - rhs = The $(D NegInfInterval) to assign to this one. - +/ - ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow - { - _end = cast(TP) rhs._end; - return this; - } - - - /++ - Params: - rhs = The $(D NegInfInterval) to assign to this one. - +/ - ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow - { - _end = cast(TP) rhs._end; - return this; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); --------------------- - +/ - @property TP end() const pure nothrow - { - return cast(TP)_end; - } - - - /++ - The end point of the interval. It is excluded from the interval. - - Params: - timePoint = The time point to set end to. - +/ - @property void end(TP timePoint) pure nothrow - { - _end = timePoint; - } - - - /++ - Whether the interval's length is 0. Always returns false. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); --------------------- - +/ - enum bool empty = false; - - - /++ - Whether the given time point is within this interval. - - Params: - timePoint = The time point to check for inclusion in this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); --------------------- - +/ - bool contains(TP timePoint) const pure nothrow - { - return timePoint < _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); --------------------- - +/ - bool contains(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._end <= _end; - } - - - /++ - Whether the given interval is completely within this interval. - - Always returns false because an interval beginning at negative - infinity can never contain an interval going to positive infinity. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - PosInfInterval!Date(Date(1999, 5, 4)))); --------------------- - +/ - bool contains(in PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval is completely within this interval. - - Params: - interval = The interval to check for inclusion in this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool contains(in NegInfInterval interval) const pure nothrow - { - return interval._end <= _end; - } - - - /++ - Whether this interval is before the given time point. - - Params: - timePoint = The time point to check whether this interval is - before it. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); --------------------- - +/ - bool isBefore(in TP timePoint) const pure nothrow - { - return timePoint >= _end; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isBefore(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isBefore(in PosInfInterval!TP interval) const pure nothrow - { - return _end <= interval._begin; - } - - - /++ - Whether this interval is before the given interval and does not - intersect it. - - Always returns false because an interval beginning at negative - infinity can never be before another interval beginning at negative - infinity. - - Params: - interval = The interval to check for against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isBefore(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given time point. - - Always returns false because an interval beginning at negative infinity - can never be after any time point. - - Params: - timePoint = The time point to check whether this interval is after - it. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); --------------------- - +/ - bool isAfter(in TP timePoint) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not - intersect it. - - Always returns false (unless the given interval is empty) because an - interval beginning at negative infinity can never be after any other - interval. - - Params: - interval = The interval to check against this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAfter(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAfter(in PosInfInterval!TP interval) const pure nothrow - { - return false; - } - - - /++ - Whether this interval is after the given interval and does not intersect - it. - - Always returns false because an interval beginning at negative infinity - can never be after any other interval. - - Params: - interval = The interval to check against this interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool isAfter(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool intersects(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Params: - interval = The interval to check for intersection with this - interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool intersects(in PosInfInterval!TP interval) const pure nothrow - { - return interval._begin < _end; - } - - - /++ - Whether the given interval overlaps this interval. - - Always returns true because two intervals beginning at negative infinity - always overlap. - - Params: - interval = The interval to check for intersection with this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( - NegInfInterval!Date(Date(2013, 7, 9)))); --------------------- - +/ - bool intersects(in NegInfInterval!TP interval) const pure nothrow - { - return true; - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(in Interval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - auto end = _end < interval._end ? _end : interval._end; - - return Interval!TP(interval._begin, end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1990, 7, 6))) == - Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - PosInfInterval!Date(Date(1999, 1, 12))) == - Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); --------------------- - +/ - Interval!TP intersection(in PosInfInterval!TP interval) const - { - import std.format : format; - - enforce( - this.intersects(interval), - new DateTimeException(format("%s and %s do not intersect.", this, interval)) - ); - - return Interval!TP(interval._begin, _end); - } - - - /++ - Returns the intersection of two intervals - - Params: - interval = The interval to intersect with this interval. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(1999, 7 , 6))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2012, 3 , 1))); --------------------- - +/ - NegInfInterval intersection(in NegInfInterval interval) const nothrow - { - return NegInfInterval(_end < interval._end ? _end : interval._end); - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); --------------------- - +/ - bool isAdjacent(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(1999, 5, 4)))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - PosInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in PosInfInterval!TP interval) const pure nothrow - { - return interval._begin == _end; - } - - - /++ - Whether the given interval is adjacent to this interval. - - Always returns false because two intervals beginning at negative - infinity can never be adjacent to one another. - - Params: - interval = The interval to check whether its adjecent to this - interval. - - Example: --------------------- -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(1996, 5, 4)))); - -assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( - NegInfInterval!Date(Date(2012, 3, 1)))); --------------------- - +/ - bool isAdjacent(in NegInfInterval interval) const pure nothrow - { - return false; - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. - - Note: - There is no overload for $(D merge) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); --------------------- - +/ - NegInfInterval merge(in Interval!TP interval) const - { - import std.format : format; - - enforce(this.isAdjacent(interval) || this.intersects(interval), - new DateTimeException(format("%s and %s are not adjacent and do not intersect.", this, interval))); - - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns the union of two intervals - - Params: - interval = The interval to merge with this interval. - - Note: - There is no overload for $(D merge) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval merge(in NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Throws: - $(LREF DateTimeException) if the given interval is empty. - - Note: - There is no overload for $(D span) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == - NegInfInterval!Date(Date(2015, 9 , 2))); - -assert(NegInfInterval!Date(Date(1600, 1, 7)).span( - Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == - NegInfInterval!Date(Date(2017, 7 , 1))); --------------------- - +/ - NegInfInterval span(in Interval!TP interval) const pure - { - interval._enforceNotEmpty(); - - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Returns an interval that covers from the earliest time point of two - intervals up to (but not including) the latest time point of two - intervals. - - Params: - interval = The interval to create a span together with this - interval. - - Note: - There is no overload for $(D span) which takes a - $(D PosInfInterval), because an interval - going from negative infinity to positive infinity - is not possible. - - Example: --------------------- -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(1999, 7, 6))) == - NegInfInterval!Date(Date(2012, 3 , 1))); - -assert(NegInfInterval!Date(Date(2012, 3, 1)).span( - NegInfInterval!Date(Date(2013, 1, 12))) == - NegInfInterval!Date(Date(2013, 1 , 12))); --------------------- - +/ - NegInfInterval span(in NegInfInterval interval) const pure nothrow - { - return NegInfInterval(_end > interval._end ? _end : interval._end); - } - - - /++ - Shifts the $(D end) of this interval forward or backwards in time by the - given duration (a positive duration shifts the interval forward; a - negative duration shifts it backward). Effectively, it does - $(D end += duration). - - Params: - duration = The duration to shift the interval by. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); -auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - -interval1.shift(dur!"days"(50)); -assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - -interval2.shift(dur!"days"(-50)); -assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); --------------------- - +/ - void shift(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if (__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Shifts the $(D end) of this interval forward or backwards in time by - the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to end. It - effectively calls $(D add!"years"()) and then $(D add!"months"()) - on end with the given number of years and months. - - Params: - years = The number of years to shift the interval by. - months = The number of months to shift the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D end), causing its month to increment. - - Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.shift(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.shift(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void shift(T)(T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) - if (isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - } - } - - - /++ - Expands the interval forwards in time. Effectively, it does - $(D end += duration). - - Params: - duration = The duration to expand the interval by. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(dur!"days"(2)); -assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - -interval2.expand(dur!"days"(-2)); -assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); --------------------- - +/ - void expand(D)(D duration) pure nothrow - if (__traits(compiles, end + duration)) - { - _end += duration; - } - - - static if (__traits(compiles, end.add!"months"(1)) && - __traits(compiles, end.add!"years"(1))) - { - /++ - Expands the interval forwards and/or backwards in time. Effectively, - it adds the given number of months/years to end. - - Params: - years = The number of years to expand the interval by. - months = The number of months to expand the interval by. - allowOverflow = Whether the days should be allowed to overflow - on $(D end), causing their month to increment. - - Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. - - Example: --------------------- -auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); -auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - -interval1.expand(2); -assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - -interval2.expand(-2); -assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); --------------------- - +/ - void expand(T)(T years, T months = 0, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) - if (isIntegral!T) - { - auto end = _end; - - end.add!"years"(years, allowOverflow); - end.add!"months"(months, allowOverflow); - - _end = end; - - return; - } - } - - - /++ - Returns a range which iterates backwards over the interval, starting - at $(D end), using $(D_PARAM func) to generate each successive time - point. - - The range's $(D front) is the interval's $(D end). $(D_PARAM func) is - used to generate the next $(D front) when $(D popFront) is called. If - $(D_PARAM popFirst) is $(D Yes.popFirst), then $(D popFront) is called - before the range is returned (so that $(D front) is a time point which - $(D_PARAM func) would generate). - - If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. - - There are helper functions in this module which generate common - delegates to pass to $(D bwdRange). Their documentation starts with - "Range-generating function," to make them easily searchable. - - Params: - func = The function used to generate the time points of the - range over the interval. - popFirst = Whether $(D popFront) should be called on the range - before returning it. - - Throws: - $(LREF DateTimeException) if this interval is empty. - - Warning: - $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) - would be a function pointer to a pure function, but forcing - $(D_PARAM func) to be pure is far too restrictive to be useful, and - in order to have the ease of use of having functions which generate - functions to pass to $(D fwdRange), $(D_PARAM func) must be a - delegate. - - If $(D_PARAM func) retains state which changes as it is called, then - some algorithms will not work correctly, because the range's - $(D save) will have failed to have really saved the range's state. - To avoid such bugs, don't pass a delegate which is - not logically pure to $(D fwdRange). If $(D_PARAM func) is given the - same time point with two different calls, it must return the same - result both times. - - Of course, none of the functions in this module have this problem, - so it's only relevant for custom delegates. - - Example: --------------------- -auto interval = NegInfInterval!Date(Date(2010, 9, 9)); -auto func = delegate (in Date date) //For iterating over even-numbered days. - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; -auto range = interval.bwdRange(func); - -assert(range.front == Date(2010, 9, 9)); //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 8). - -range.popFront(); -assert(range.front == Date(2010, 9, 8)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 4)); - -range.popFront(); -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(!range.empty); --------------------- - +/ - NegInfIntervalRange!(TP) bwdRange(TP delegate(in TP) func, PopFirst popFirst = No.popFirst) const - { - auto range = NegInfIntervalRange!(TP)(this, func); - - if (popFirst == Yes.popFirst) - range.popFront(); - - return range; - } - - - /+ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return _toStringImpl(); - } - - - /++ - Converts this interval to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow - { - return _toStringImpl(); - } - -private: -package: // temporary - - /+ - Since we have two versions of toString(), we have _toStringImpl() - so that they can share implementations. - +/ - string _toStringImpl() const nothrow - { - import std.format : format; - try - return format("[-∞ - %s)", _end); - catch (Exception e) - assert(0, "format() threw."); - } - - - TP _end; -} - -//Test NegInfInterval's constructor. -@safe unittest -{ - NegInfInterval!Date(Date.init); - NegInfInterval!TimeOfDay(TimeOfDay.init); - NegInfInterval!DateTime(DateTime.init); - NegInfInterval!SysTime(SysTime(0)); -} - -//Test NegInfInterval's end. -@safe unittest -{ - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); - assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(cNegInfInterval.end != Date.init); - assert(iNegInfInterval.end != Date.init); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); -} - -//Test NegInfInterval's empty. -@safe unittest -{ - assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!cNegInfInterval.empty); - assert(!iNegInfInterval.empty); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); -} - -//Test NegInfInterval's contains(time point). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(negInfInterval.contains(Date(2009, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 3))); - assert(negInfInterval.contains(Date(2010, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 5))); - assert(negInfInterval.contains(Date(2011, 7, 1))); - assert(negInfInterval.contains(Date(2012, 1, 6))); - assert(!negInfInterval.contains(Date(2012, 1, 7))); - assert(!negInfInterval.contains(Date(2012, 1, 8))); - assert(!negInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.contains(cdate)); - assert(cNegInfInterval.contains(cdate)); - assert(iNegInfInterval.contains(cdate)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); -} - -//Test NegInfInterval's contains(Interval). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); - - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.contains(interval)); - assert(negInfInterval.contains(cInterval)); - assert(negInfInterval.contains(iInterval)); - assert(!negInfInterval.contains(posInfInterval)); - assert(!negInfInterval.contains(cPosInfInterval)); - assert(!negInfInterval.contains(iPosInfInterval)); - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(cNegInfInterval)); - assert(negInfInterval.contains(iNegInfInterval)); - assert(cNegInfInterval.contains(interval)); - assert(cNegInfInterval.contains(cInterval)); - assert(cNegInfInterval.contains(iInterval)); - assert(!cNegInfInterval.contains(posInfInterval)); - assert(!cNegInfInterval.contains(cPosInfInterval)); - assert(!cNegInfInterval.contains(iPosInfInterval)); - assert(cNegInfInterval.contains(negInfInterval)); - assert(cNegInfInterval.contains(cNegInfInterval)); - assert(cNegInfInterval.contains(iNegInfInterval)); - assert(iNegInfInterval.contains(interval)); - assert(iNegInfInterval.contains(cInterval)); - assert(iNegInfInterval.contains(iInterval)); - assert(!iNegInfInterval.contains(posInfInterval)); - assert(!iNegInfInterval.contains(cPosInfInterval)); - assert(!iNegInfInterval.contains(iPosInfInterval)); - assert(iNegInfInterval.contains(negInfInterval)); - assert(iNegInfInterval.contains(cNegInfInterval)); - assert(iNegInfInterval.contains(iNegInfInterval)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isBefore(time point). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isBefore(Date(2009, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 3))); - assert(!negInfInterval.isBefore(Date(2010, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 5))); - assert(!negInfInterval.isBefore(Date(2011, 7, 1))); - assert(!negInfInterval.isBefore(Date(2012, 1, 6))); - assert(negInfInterval.isBefore(Date(2012, 1, 7))); - assert(negInfInterval.isBefore(Date(2012, 1, 8))); - assert(negInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isBefore(cdate)); - assert(!cNegInfInterval.isBefore(cdate)); - assert(!iNegInfInterval.isBefore(cdate)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); -} - -//Test NegInfInterval's isBefore(Interval). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); - - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isBefore(interval)); - assert(!negInfInterval.isBefore(cInterval)); - assert(!negInfInterval.isBefore(iInterval)); - assert(!negInfInterval.isBefore(posInfInterval)); - assert(!negInfInterval.isBefore(cPosInfInterval)); - assert(!negInfInterval.isBefore(iPosInfInterval)); - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(cNegInfInterval)); - assert(!negInfInterval.isBefore(iNegInfInterval)); - assert(!cNegInfInterval.isBefore(interval)); - assert(!cNegInfInterval.isBefore(cInterval)); - assert(!cNegInfInterval.isBefore(iInterval)); - assert(!cNegInfInterval.isBefore(posInfInterval)); - assert(!cNegInfInterval.isBefore(cPosInfInterval)); - assert(!cNegInfInterval.isBefore(iPosInfInterval)); - assert(!cNegInfInterval.isBefore(negInfInterval)); - assert(!cNegInfInterval.isBefore(cNegInfInterval)); - assert(!cNegInfInterval.isBefore(iNegInfInterval)); - assert(!iNegInfInterval.isBefore(interval)); - assert(!iNegInfInterval.isBefore(cInterval)); - assert(!iNegInfInterval.isBefore(iInterval)); - assert(!iNegInfInterval.isBefore(posInfInterval)); - assert(!iNegInfInterval.isBefore(cPosInfInterval)); - assert(!iNegInfInterval.isBefore(iPosInfInterval)); - assert(!iNegInfInterval.isBefore(negInfInterval)); - assert(!iNegInfInterval.isBefore(cNegInfInterval)); - assert(!iNegInfInterval.isBefore(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's isAfter(time point). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isAfter(Date(2009, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 3))); - assert(!negInfInterval.isAfter(Date(2010, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 5))); - assert(!negInfInterval.isAfter(Date(2011, 7, 1))); - assert(!negInfInterval.isAfter(Date(2012, 1, 6))); - assert(!negInfInterval.isAfter(Date(2012, 1, 7))); - assert(!negInfInterval.isAfter(Date(2012, 1, 8))); - assert(!negInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAfter(cdate)); - assert(!cNegInfInterval.isAfter(cdate)); - assert(!iNegInfInterval.isAfter(cdate)); -} - -//Test NegInfInterval's isAfter(Interval). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); - - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAfter(interval)); - assert(!negInfInterval.isAfter(cInterval)); - assert(!negInfInterval.isAfter(iInterval)); - assert(!negInfInterval.isAfter(posInfInterval)); - assert(!negInfInterval.isAfter(cPosInfInterval)); - assert(!negInfInterval.isAfter(iPosInfInterval)); - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(cNegInfInterval)); - assert(!negInfInterval.isAfter(iNegInfInterval)); - assert(!cNegInfInterval.isAfter(interval)); - assert(!cNegInfInterval.isAfter(cInterval)); - assert(!cNegInfInterval.isAfter(iInterval)); - assert(!cNegInfInterval.isAfter(posInfInterval)); - assert(!cNegInfInterval.isAfter(cPosInfInterval)); - assert(!cNegInfInterval.isAfter(iPosInfInterval)); - assert(!cNegInfInterval.isAfter(negInfInterval)); - assert(!cNegInfInterval.isAfter(cNegInfInterval)); - assert(!cNegInfInterval.isAfter(iNegInfInterval)); - assert(!iNegInfInterval.isAfter(interval)); - assert(!iNegInfInterval.isAfter(cInterval)); - assert(!iNegInfInterval.isAfter(iInterval)); - assert(!iNegInfInterval.isAfter(posInfInterval)); - assert(!iNegInfInterval.isAfter(cPosInfInterval)); - assert(!iNegInfInterval.isAfter(iPosInfInterval)); - assert(!iNegInfInterval.isAfter(negInfInterval)); - assert(!iNegInfInterval.isAfter(cNegInfInterval)); - assert(!iNegInfInterval.isAfter(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersects(). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); - - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(negInfInterval.intersects(interval)); - assert(negInfInterval.intersects(cInterval)); - assert(negInfInterval.intersects(iInterval)); - assert(negInfInterval.intersects(posInfInterval)); - assert(negInfInterval.intersects(cPosInfInterval)); - assert(negInfInterval.intersects(iPosInfInterval)); - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(cNegInfInterval)); - assert(negInfInterval.intersects(iNegInfInterval)); - assert(cNegInfInterval.intersects(interval)); - assert(cNegInfInterval.intersects(cInterval)); - assert(cNegInfInterval.intersects(iInterval)); - assert(cNegInfInterval.intersects(posInfInterval)); - assert(cNegInfInterval.intersects(cPosInfInterval)); - assert(cNegInfInterval.intersects(iPosInfInterval)); - assert(cNegInfInterval.intersects(negInfInterval)); - assert(cNegInfInterval.intersects(cNegInfInterval)); - assert(cNegInfInterval.intersects(iNegInfInterval)); - assert(iNegInfInterval.intersects(interval)); - assert(iNegInfInterval.intersects(cInterval)); - assert(iNegInfInterval.intersects(iInterval)); - assert(iNegInfInterval.intersects(posInfInterval)); - assert(iNegInfInterval.intersects(cPosInfInterval)); - assert(iNegInfInterval.intersects(iPosInfInterval)); - assert(iNegInfInterval.intersects(negInfInterval)); - assert(iNegInfInterval.intersects(cNegInfInterval)); - assert(iNegInfInterval.intersects(iNegInfInterval)); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); -} - -//Test NegInfInterval's intersection(). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(negInfInterval.intersection(negInfInterval) == - negInfInterval); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2010, 7, 3))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2010, 7, 4))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2010, 7, 5))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 6))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 7))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 3))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 4))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2010, 7, 5))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 6))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == - Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == - Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == - Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); - assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == - Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.intersection(interval).empty); - assert(!negInfInterval.intersection(cInterval).empty); - assert(!negInfInterval.intersection(iInterval).empty); - assert(!negInfInterval.intersection(posInfInterval).empty); - assert(!negInfInterval.intersection(cPosInfInterval).empty); - assert(!negInfInterval.intersection(iPosInfInterval).empty); - assert(!negInfInterval.intersection(negInfInterval).empty); - assert(!negInfInterval.intersection(cNegInfInterval).empty); - assert(!negInfInterval.intersection(iNegInfInterval).empty); - assert(!cNegInfInterval.intersection(interval).empty); - assert(!cNegInfInterval.intersection(cInterval).empty); - assert(!cNegInfInterval.intersection(iInterval).empty); - assert(!cNegInfInterval.intersection(posInfInterval).empty); - assert(!cNegInfInterval.intersection(cPosInfInterval).empty); - assert(!cNegInfInterval.intersection(iPosInfInterval).empty); - assert(!cNegInfInterval.intersection(negInfInterval).empty); - assert(!cNegInfInterval.intersection(cNegInfInterval).empty); - assert(!cNegInfInterval.intersection(iNegInfInterval).empty); - assert(!iNegInfInterval.intersection(interval).empty); - assert(!iNegInfInterval.intersection(cInterval).empty); - assert(!iNegInfInterval.intersection(iInterval).empty); - assert(!iNegInfInterval.intersection(posInfInterval).empty); - assert(!iNegInfInterval.intersection(cPosInfInterval).empty); - assert(!iNegInfInterval.intersection(iPosInfInterval).empty); - assert(!iNegInfInterval.intersection(negInfInterval).empty); - assert(!iNegInfInterval.intersection(cNegInfInterval).empty); - assert(!iNegInfInterval.intersection(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, - 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, - 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)) - .intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, - 7, 6), Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)) - .intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, - 1, 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)) - .intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, - 7, 6))); - assert(NegInfInterval!Date(Date(2012, 3, 1)) - .intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, - 3, 1))); -} - -//Test NegInfInterval's isAdjacent(). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); - - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.isAdjacent(interval)); - assert(!negInfInterval.isAdjacent(cInterval)); - assert(!negInfInterval.isAdjacent(iInterval)); - assert(!negInfInterval.isAdjacent(posInfInterval)); - assert(!negInfInterval.isAdjacent(cPosInfInterval)); - assert(!negInfInterval.isAdjacent(iPosInfInterval)); - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(cNegInfInterval)); - assert(!negInfInterval.isAdjacent(iNegInfInterval)); - assert(!cNegInfInterval.isAdjacent(interval)); - assert(!cNegInfInterval.isAdjacent(cInterval)); - assert(!cNegInfInterval.isAdjacent(iInterval)); - assert(!cNegInfInterval.isAdjacent(posInfInterval)); - assert(!cNegInfInterval.isAdjacent(cPosInfInterval)); - assert(!cNegInfInterval.isAdjacent(iPosInfInterval)); - assert(!cNegInfInterval.isAdjacent(negInfInterval)); - assert(!cNegInfInterval.isAdjacent(cNegInfInterval)); - assert(!cNegInfInterval.isAdjacent(iNegInfInterval)); - assert(!iNegInfInterval.isAdjacent(interval)); - assert(!iNegInfInterval.isAdjacent(cInterval)); - assert(!iNegInfInterval.isAdjacent(iInterval)); - assert(!iNegInfInterval.isAdjacent(posInfInterval)); - assert(!iNegInfInterval.isAdjacent(cPosInfInterval)); - assert(!iNegInfInterval.isAdjacent(iPosInfInterval)); - assert(!iNegInfInterval.isAdjacent(negInfInterval)); - assert(!iNegInfInterval.isAdjacent(cNegInfInterval)); - assert(!iNegInfInterval.isAdjacent(iNegInfInterval)); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); -} - -//Test NegInfInterval's merge(). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.merge(negInfInterval) == - negInfInterval); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.merge(interval).empty); - assert(!negInfInterval.merge(cInterval).empty); - assert(!negInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); - assert(!negInfInterval.merge(negInfInterval).empty); - assert(!negInfInterval.merge(cNegInfInterval).empty); - assert(!negInfInterval.merge(iNegInfInterval).empty); - assert(!cNegInfInterval.merge(interval).empty); - assert(!cNegInfInterval.merge(cInterval).empty); - assert(!cNegInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); - assert(!cNegInfInterval.merge(negInfInterval).empty); - assert(!cNegInfInterval.merge(cNegInfInterval).empty); - assert(!cNegInfInterval.merge(iNegInfInterval).empty); - assert(!iNegInfInterval.merge(interval).empty); - assert(!iNegInfInterval.merge(cInterval).empty); - assert(!iNegInfInterval.merge(iInterval).empty); - static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); - assert(!iNegInfInterval.merge(negInfInterval).empty); - assert(!iNegInfInterval.merge(cNegInfInterval).empty); - assert(!iNegInfInterval.merge(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, - 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, - 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9, 2))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, - 7, 6))) == NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, - 1, 12))) == NegInfInterval!Date(Date(2013, 1, 12))); -} - -//Test NegInfInterval's span(). -@safe unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.span(negInfInterval) == - negInfInterval); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == - NegInfInterval!Date(Date(2013, 7, 3))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == - NegInfInterval!Date(Date(2012, 1, 9))); - - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == - NegInfInterval!Date(Date(2012, 1, 8))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 7))); - assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!negInfInterval.span(interval).empty); - assert(!negInfInterval.span(cInterval).empty); - assert(!negInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); - assert(!negInfInterval.span(negInfInterval).empty); - assert(!negInfInterval.span(cNegInfInterval).empty); - assert(!negInfInterval.span(iNegInfInterval).empty); - assert(!cNegInfInterval.span(interval).empty); - assert(!cNegInfInterval.span(cInterval).empty); - assert(!cNegInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); - assert(!cNegInfInterval.span(negInfInterval).empty); - assert(!cNegInfInterval.span(cNegInfInterval).empty); - assert(!cNegInfInterval.span(iNegInfInterval).empty); - assert(!iNegInfInterval.span(interval).empty); - assert(!iNegInfInterval.span(cInterval).empty); - assert(!iNegInfInterval.span(iInterval).empty); - static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); - assert(!iNegInfInterval.span(negInfInterval).empty); - assert(!iNegInfInterval.span(cNegInfInterval).empty); - assert(!iNegInfInterval.span(iNegInfInterval).empty); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, - 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, - 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9, 2))); - assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, - 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, - 7, 6))) == NegInfInterval!Date(Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, - 1, 12))) == NegInfInterval!Date(Date(2013, 1, 12))); -} - -//Test NegInfInterval's shift(). -@safe unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); - auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - - interval1.shift(dur!"days"(50)); - assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - - interval2.shift(dur!"days"(-50)); - assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); -} - -//Test NegInfInterval's shift(int, int, AllowDayOverflow). -@safe unittest -{ - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testIntervalFail(I)(I interval, int years, int months) - { - interval.shift(years, months); - } - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, No.allowDayOverflow, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, No.allowDayOverflow, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, No.allowDayOverflow, NegInfInterval!Date(Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.shift(1))); - static assert(!__traits(compiles, iNegInfInterval.shift(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.shift(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.shift(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's expand(). -@safe unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - assert(interval == expected); - } - - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(dur!"days"(2)); - assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - - interval2.expand(dur!"days"(-2)); - assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); -} - -//Test NegInfInterval's expand(int, int, AllowDayOverflow). -@safe unittest -{ - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, - in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - assert(interval == expected); - } - - testInterval(interval, 5, 0, Yes.allowDayOverflow, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, Yes.allowDayOverflow, NegInfInterval!Date(Date(2007, 1, 7))); - - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - - testInterval(interval2, 1, 1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, Yes.allowDayOverflow, NegInfInterval!Date(Date(2009, 7, 1))); - - testInterval(interval2, 1, 1, No.allowDayOverflow, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, No.allowDayOverflow, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, No.allowDayOverflow, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, No.allowDayOverflow, NegInfInterval!Date( Date(2009, 6, 30))); - } - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.expand(1))); - static assert(!__traits(compiles, iNegInfInterval.expand(1))); - - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - - interval1.expand(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - - interval2.expand(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); -} - -//Test NegInfInterval's bwdRange(). -@system unittest -{ - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - static void testInterval(NegInfInterval!Date negInfInterval) - { - negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } - - assertThrown!DateTimeException(testInterval(negInfInterval)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, - Direction.bwd)(DayOfWeek.fri)).front == Date(2010, 10, 1)); - - assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, - Direction.bwd)(DayOfWeek.fri), Yes.popFirst).front == Date(2010, 9, 24)); - - //Verify Examples. - auto interval = NegInfInterval!Date(Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if ((date.day & 1) == 0) - return date - dur!"days"(2); - - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); - - //An odd day. Using Yes.popFirst would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); - - range.popFront(); - assert(range.front == Date(2010, 9, 8)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 4)); - - range.popFront(); - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(!range.empty); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(!cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(!iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); -} - -//Test NegInfInterval's toString(). -@safe unittest -{ - assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); - - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - assert(cNegInfInterval.toString()); - assert(iNegInfInterval.toString()); -} - - /++ Range-generating function. @@ -21882,6 +19643,7 @@ public: private: +package: // temporary /+ Params: From 92622c0f4c049a3c7ccc0771f0c28b7af9144324 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 22:30:14 +0200 Subject: [PATCH 087/262] Move interval functions to std.datetime.interval. --- std/datetime/interval.d | 448 +++++++++++++++++++++++++++++++++++++- std/datetime/package.d | 460 ---------------------------------------- 2 files changed, 447 insertions(+), 461 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 1acee7f82dd..d184f3ec5ff 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -20,7 +20,6 @@ version(unittest) import std.exception : assertThrown; import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary import std.datetime : IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary -import std.datetime : everyDayOfWeek; // temporary /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s @@ -7395,3 +7394,450 @@ package: // temporary assert(cNegInfInterval.toString()); assert(iNegInfInterval.toString()); } + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point with the given + $(D DayOfWeek) in a range. + + Using this delegate allows iteration over successive time points which + are all the same day of the week. e.g. passing $(D DayOfWeek.mon) to + $(D everyDayOfWeek) would result in a delegate which could be used to + iterate over all of the Mondays in a range. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + dayOfWeek = The week that each time point in the range will be. + +/ +TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow +if (isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "dayOfWeek") && + !__traits(isStaticFunction, TP.dayOfWeek) && + is(typeof(TP.dayOfWeek) == DayOfWeek)) +{ + TP func(in TP tp) + { + TP retval = cast(TP) tp; + immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); + + static if (dir == Direction.fwd) + immutable adjustedDays = days == 0 ? 7 : days; + else + immutable adjustedDays = days == 0 ? -7 : days - 7; + + return retval += dur!"days"(adjustedDays); + } + + return &func; +} + +/// +@system unittest +{ + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDayOfWeek!Date(DayOfWeek.mon); + auto range = interval.fwdRange(func); + + // A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 6)); + + range.popFront(); + assert(range.front == Date(2010, 9, 13)); + + range.popFront(); + assert(range.front == Date(2010, 9, 20)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); + auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); + + assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); + assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); + + assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); + + static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); + assert(everyDayOfWeek!(DateTime)(DayOfWeek.mon) !is null); + assert(everyDayOfWeek!(SysTime)(DayOfWeek.mon) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point with the given month + which would be reached by adding months to the given time point. + + So, using this delegate allows iteration over successive time points + which are in the same month but different years. For example, + iterate over each successive December 25th in an interval by starting with a + date which had the 25th as its day and passed $(D Month.dec) to + $(D everyMonth) to create the delegate. + + Since it wouldn't really make sense to be iterating over a specific month + and end up with some of the time points in the succeeding month or two years + after the previous time point, $(D AllowDayOverflow.no) is always used when + calculating the next time point. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + month = The month that each time point in the range will be in. + +/ +TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) +if (isTimePoint!TP && + (dir == Direction.fwd || dir == Direction.bwd) && + __traits(hasMember, TP, "month") && + !__traits(isStaticFunction, TP.month) && + is(typeof(TP.month) == Month)) +{ + enforceValid!"months"(month); + + TP func(in TP tp) + { + TP retval = cast(TP) tp; + immutable months = monthsToMonth(retval.month, month); + + static if (dir == Direction.fwd) + immutable adjustedMonths = months == 0 ? 12 : months; + else + immutable adjustedMonths = months == 0 ? -12 : months - 12; + + retval.add!"months"(adjustedMonths, AllowDayOverflow.no); + + if (retval.month != month) + { + retval.add!"months"(-1); + assert(retval.month == month); + } + + return retval; + } + + return &func; +} + +/// +@system unittest +{ + auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); + auto func = everyMonth!Date(Month.feb); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2010, 2, 29). + assert(range.front == Date(2000, 1, 30)); + + range.popFront(); + assert(range.front == Date(2000, 2, 29)); + + range.popFront(); + assert(range.front == Date(2001, 2, 28)); + + range.popFront(); + assert(range.front == Date(2002, 2, 28)); + + range.popFront(); + assert(range.front == Date(2003, 2, 28)); + + range.popFront(); + assert(range.front == Date(2004, 2, 28)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + auto funcFwd = everyMonth!Date(Month.jun); + auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); + + assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); + assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); + assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); + assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); + + assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); + assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); + + static assert(!__traits(compiles, everyMonth!TimeOfDay(Month.jan))); + assert(everyMonth!DateTime(Month.jan) !is null); + assert(everyMonth!SysTime(Month.jan) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point which is the given + duration later. + + Using this delegate allows iteration over successive time points which + are apart by the given duration e.g. passing $(D dur!"days"(3)) to + $(D everyDuration) would result in a delegate which could be used to iterate + over a range of days which are each 3 days apart. + + Params: + dir = The direction to iterate in. If passing the return value to + $(D fwdRange), use $(D Direction.fwd). If passing it to + $(D bwdRange), use $(D Direction.bwd). + duration = The duration which separates each successive time point in + the range. + +/ +TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D)(D duration) nothrow +if (isTimePoint!TP && + __traits(compiles, TP.init + duration) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if (dir == Direction.fwd) + return tp + duration; + else + return tp - duration; + } + + return &func; +} + +/// +@system unittest +{ + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDuration!Date(dur!"days"(8)); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2010, 9, 10). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2010, 9, 10)); + + range.popFront(); + assert(range.front == Date(2010, 9, 18)); + + range.popFront(); + assert(range.front == Date(2010, 9, 26)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + auto funcFwd = everyDuration!Date(dur!"days"(27)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); + + assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); + + assert(everyDuration!Date(dur!"hnsecs"(1)) !is null); + assert(everyDuration!TimeOfDay(dur!"hnsecs"(1)) !is null); + assert(everyDuration!DateTime(dur!"hnsecs"(1)) !is null); + assert(everyDuration!SysTime(dur!"hnsecs"(1)) !is null); +} + + +/++ + Range-generating function. + + Returns a delegate which returns the next time point which is the given + number of years, month, and duration later. + + The difference between this version of $(D everyDuration) and the version + which just takes a $(REF Duration, core,time) is that this one also takes the number of + years and months (along with an $(D AllowDayOverflow) to indicate whether + adding years and months should allow the days to overflow). + + Note that if iterating forward, $(D add!"years"()) is called on the given + time point, then $(D add!"months"()), and finally the duration is added + to it. However, if iterating backwards, the duration is added first, then + $(D add!"months"()) is called, and finally $(D add!"years"()) is called. + That way, going backwards generates close to the same time points that + iterating forward does, but since adding years and months is not entirely + reversible (due to possible day overflow, regardless of whether + $(D AllowDayOverflow.yes) or $(D AllowDayOverflow.no) is used), it can't be + guaranteed that iterating backwards will give the same time points as + iterating forward would have (even assuming that the end of the range is a + time point which would be returned by the delegate when iterating forward + from $(D begin)). + + Params: + dir = The direction to iterate in. If passing the return + value to $(D fwdRange), use $(D Direction.fwd). If + passing it to $(D bwdRange), use $(D Direction.bwd). + years = The number of years to add to the time point passed to + the delegate. + months = The number of months to add to the time point passed to + the delegate. + allowOverflow = Whether the days should be allowed to overflow on + $(D begin) and $(D end), causing their month to + increment. + duration = The duration to add to the time point passed to the + delegate. + +/ +TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) + (int years, + int months = 0, + AllowDayOverflow allowOverflow = AllowDayOverflow.yes, + D duration = dur!"days"(0)) nothrow +if (isTimePoint!TP && + __traits(compiles, TP.init + duration) && + __traits(compiles, TP.init.add!"years"(years)) && + __traits(compiles, TP.init.add!"months"(months)) && + (dir == Direction.fwd || dir == Direction.bwd)) +{ + TP func(in TP tp) + { + static if (dir == Direction.fwd) + { + TP retval = cast(TP) tp; + + retval.add!"years"(years, allowOverflow); + retval.add!"months"(months, allowOverflow); + + return retval + duration; + } + else + { + TP retval = tp - duration; + + retval.add!"months"(-months, allowOverflow); + retval.add!"years"(-years, allowOverflow); + + return retval; + } + } + + return &func; +} + +/// +@system unittest +{ + import std.typecons : Yes; + + auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); + auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); + auto range = interval.fwdRange(func); + + // Using PopFirst.yes would have made this Date(2014, 10, 12). + assert(range.front == Date(2010, 9, 2)); + + range.popFront(); + assert(range.front == Date(2014, 10, 4)); + + range.popFront(); + assert(range.front == Date(2018, 11, 6)); + + range.popFront(); + assert(range.front == Date(2022, 12, 8)); + + range.popFront(); + assert(range.empty); +} + +@system unittest +{ + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); + + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); + } + + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); + + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); + } + + assert(everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); + static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + assert(everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); + assert(everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index 4818126f5df..fdaaf9c449e 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18215,466 +18215,6 @@ private: } -//============================================================================== -// Section with intervals. -//============================================================================== - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given - $(D DayOfWeek) in a range. - - Using this delegate allows iteration over successive time points which - are all the same day of the week. e.g. passing $(D DayOfWeek.mon) to - $(D everyDayOfWeek) would result in a delegate which could be used to - iterate over all of the Mondays in a range. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - dayOfWeek = The week that each time point in the range will be. - +/ -static TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow -if (isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "dayOfWeek") && - !__traits(isStaticFunction, TP.dayOfWeek) && - is(typeof(TP.dayOfWeek) == DayOfWeek)) -{ - TP func(in TP tp) - { - TP retval = cast(TP) tp; - immutable days = daysToDayOfWeek(retval.dayOfWeek, dayOfWeek); - - static if (dir == Direction.fwd) - immutable adjustedDays = days == 0 ? 7 : days; - else - immutable adjustedDays = days == 0 ? -7 : days - 7; - - return retval += dur!"days"(adjustedDays); - } - - return &func; -} - -/// -@system unittest -{ - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDayOfWeek!Date(DayOfWeek.mon); - auto range = interval.fwdRange(func); - - //A Thursday. Using Yes.popFirst would have made this Date(2010, 9, 6). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 6)); - - range.popFront(); - assert(range.front == Date(2010, 9, 13)); - - range.popFront(); - assert(range.front == Date(2010, 9, 20)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); - auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); - - assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); - assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); - assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); - assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); - - assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); - assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); - - static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); - assert(everyDayOfWeek!(DateTime)(DayOfWeek.mon) !is null); - assert(everyDayOfWeek!(SysTime)(DayOfWeek.mon) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point with the given month - which would be reached by adding months to the given time point. - - So, using this delegate allows iteration over successive time points - which are in the same month but different years. For example, - iterate over each successive December 25th in an interval by starting with a - date which had the 25th as its day and passed $(D Month.dec) to - $(D everyMonth) to create the delegate. - - Since it wouldn't really make sense to be iterating over a specific month - and end up with some of the time points in the succeeding month or two years - after the previous time point, $(D No.allowDayOverflow) is always used when - calculating the next time point. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - month = The month that each time point in the range will be in. - +/ -static TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) -if (isTimePoint!TP && - (dir == Direction.fwd || dir == Direction.bwd) && - __traits(hasMember, TP, "month") && - !__traits(isStaticFunction, TP.month) && - is(typeof(TP.month) == Month)) -{ - enforceValid!"months"(month); - - TP func(in TP tp) - { - TP retval = cast(TP) tp; - immutable months = monthsToMonth(retval.month, month); - - static if (dir == Direction.fwd) - immutable adjustedMonths = months == 0 ? 12 : months; - else - immutable adjustedMonths = months == 0 ? -12 : months - 12; - - retval.add!"months"(adjustedMonths, No.allowDayOverflow); - - if (retval.month != month) - { - retval.add!"months"(-1); - assert(retval.month == month); - } - - return retval; - } - - return &func; -} - -/// -@system unittest -{ - auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); - auto func = everyMonth!(Date)(Month.feb); - auto range = interval.fwdRange(func); - - //Using Yes.popFirst would have made this Date(2010, 2, 29). - assert(range.front == Date(2000, 1, 30)); - - range.popFront(); - assert(range.front == Date(2000, 2, 29)); - - range.popFront(); - assert(range.front == Date(2001, 2, 28)); - - range.popFront(); - assert(range.front == Date(2002, 2, 28)); - - range.popFront(); - assert(range.front == Date(2003, 2, 28)); - - range.popFront(); - assert(range.front == Date(2004, 2, 28)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - auto funcFwd = everyMonth!Date(Month.jun); - auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); - - assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); - assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); - assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); - assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); - assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); - - assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); - assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); - assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); - assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); - - static assert(!__traits(compiles, everyMonth!(TimeOfDay)(Month.jan))); - assert(everyMonth!(DateTime)(Month.jan) !is null); - assert(everyMonth!(SysTime)(Month.jan) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - duration later. - - Using this delegate allows iteration over successive time points which - are apart by the given duration e.g. passing $(D dur!"days"(3)) to - $(D everyDuration) would result in a delegate which could be used to iterate - over a range of days which are each 3 days apart. - - Params: - dir = The direction to iterate in. If passing the return value to - $(D fwdRange), use $(D Direction.fwd). If passing it to - $(D bwdRange), use $(D Direction.bwd). - duration = The duration which separates each successive time point in - the range. - +/ -static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) - (D duration) nothrow -if (isTimePoint!TP && - __traits(compiles, TP.init + duration) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(in TP tp) - { - static if (dir == Direction.fwd) - return tp + duration; - else - return tp - duration; - } - - return &func; -} - -/// -@system unittest -{ - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDuration!Date(dur!"days"(8)); - auto range = interval.fwdRange(func); - - //Using Yes.popFirst would have made this Date(2010, 9, 10). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2010, 9, 10)); - - range.popFront(); - assert(range.front == Date(2010, 9, 18)); - - range.popFront(); - assert(range.front == Date(2010, 9, 26)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - auto funcFwd = everyDuration!Date(dur!"days"(27)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); - - assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); - - assert(everyDuration!Date(dur!"hnsecs"(1)) !is null); - assert(everyDuration!TimeOfDay(dur!"hnsecs"(1)) !is null); - assert(everyDuration!DateTime(dur!"hnsecs"(1)) !is null); - assert(everyDuration!SysTime(dur!"hnsecs"(1)) !is null); -} - - -/++ - Range-generating function. - - Returns a delegate which returns the next time point which is the given - number of years, month, and duration later. - - The difference between this version of $(D everyDuration) and the version - which just takes a $(REF Duration, core,time) is that this one also takes the number of - years and months (along with an $(D AllowDayOverflow) to indicate whether - adding years and months should allow the days to overflow). - - Note that if iterating forward, $(D add!"years"()) is called on the given - time point, then $(D add!"months"()), and finally the duration is added - to it. However, if iterating backwards, the duration is added first, then - $(D add!"months"()) is called, and finally $(D add!"years"()) is called. - That way, going backwards generates close to the same time points that - iterating forward does, but since adding years and months is not entirely - reversible (due to possible day overflow, regardless of whether - $(D Yes.allowDayOverflow) or $(D No.allowDayOverflow) is used), it can't be - guaranteed that iterating backwards will give the same time points as - iterating forward would have (even assuming that the end of the range is a - time point which would be returned by the delegate when iterating forward - from $(D begin)). - - Params: - dir = The direction to iterate in. If passing the return - value to $(D fwdRange), use $(D Direction.fwd). If - passing it to $(D bwdRange), use $(D Direction.bwd). - years = The number of years to add to the time point passed to - the delegate. - months = The number of months to add to the time point passed to - the delegate. - allowOverflow = Whether the days should be allowed to overflow on - $(D begin) and $(D end), causing their month to - increment. - duration = The duration to add to the time point passed to the - delegate. - +/ -static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) - (int years, - int months = 0, - AllowDayOverflow allowOverflow = Yes.allowDayOverflow, - D duration = dur!"days"(0)) nothrow -if (isTimePoint!TP && - __traits(compiles, TP.init + duration) && - __traits(compiles, TP.init.add!"years"(years)) && - __traits(compiles, TP.init.add!"months"(months)) && - (dir == Direction.fwd || dir == Direction.bwd)) -{ - TP func(in TP tp) - { - static if (dir == Direction.fwd) - { - TP retval = cast(TP) tp; - - retval.add!"years"(years, allowOverflow); - retval.add!"months"(months, allowOverflow); - - return retval + duration; - } - else - { - TP retval = tp - duration; - - retval.add!"months"(-months, allowOverflow); - retval.add!"years"(-years, allowOverflow); - - return retval; - } - } - - return &func; -} - -/// -@system unittest -{ - import std.typecons : Yes; - - auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); - auto func = everyDuration!Date(4, 1, Yes.allowDayOverflow, dur!"days"(2)); - auto range = interval.fwdRange(func); - - //Using Yes.popFirst would have made this Date(2014, 10, 12). - assert(range.front == Date(2010, 9, 2)); - - range.popFront(); - assert(range.front == Date(2014, 10, 4)); - - range.popFront(); - assert(range.front == Date(2018, 11, 6)); - - range.popFront(); - assert(range.front == Date(2022, 12, 8)); - - range.popFront(); - assert(range.empty); -} - -@system unittest -{ - { - auto funcFwd = everyDuration!Date(1, 2, Yes.allowDayOverflow, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, Yes.allowDayOverflow, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - { - auto funcFwd = everyDuration!Date(1, 2, No.allowDayOverflow, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, Yes.allowDayOverflow, dur!"days"(3)); - - assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); - assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); - assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); - assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); - assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); - - assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); - assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); - assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); - assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); - assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); - } - - assert(everyDuration!Date(1, 2, Yes.allowDayOverflow, dur!"hnsecs"(1)) !is null); - static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, Yes.allowDayOverflow, dur!"hnsecs"(1)))); - assert(everyDuration!DateTime(1, 2, Yes.allowDayOverflow, dur!"hnsecs"(1)) !is null); - assert(everyDuration!SysTime(1, 2, Yes.allowDayOverflow, dur!"hnsecs"(1)) !is null); -} - - -//TODO Add function to create a range generating function based on a date recurrence pattern string. -// This may or may not involve creating a date recurrence pattern class of some sort - probably -// yes if we want to make it easy to build them. However, there is a standard recurrence -// pattern string format which we'd want to support with a range generator (though if we have -// the class/struct, we'd probably want a version of the range generating function which took -// that rather than a string). - - //============================================================================== // Section with ranges. //============================================================================== From c53a6178a6cb6c96774f25830d258aa6e8d69dde Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 22:59:09 +0200 Subject: [PATCH 088/262] Move IntervalRange to std.datetime.interval. --- std/datetime/interval.d | 518 +++++++++++++++++++++++++++++++++++- std/datetime/package.d | 564 ---------------------------------------- 2 files changed, 515 insertions(+), 567 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index d184f3ec5ff..5ec8b6ec757 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -19,7 +19,7 @@ version(unittest) import std.exception : assertThrown; import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary -import std.datetime : IntervalRange, PosInfIntervalRange, NegInfIntervalRange; // temporary +import std.datetime : PosInfIntervalRange, NegInfIntervalRange; // temporary /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s @@ -7778,8 +7778,6 @@ if (isTimePoint!TP && /// @system unittest { - import std.typecons : Yes; - auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); auto range = interval.fwdRange(func); @@ -7841,3 +7839,517 @@ if (isTimePoint!TP && assert(everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); assert(everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)) !is null); } + + +/++ + A range over an $(LREF2 .Interval, Interval). + + $(D IntervalRange) is only ever constructed by $(LREF2 .Interval, Interval). However, when + it is constructed, it is given a function, $(D func), which is used to + generate the time points which are iterated over. $(D func) takes a time + point and returns a time point of the same type. For instance, + to iterate over all of the days in + the interval $(D Interval!Date), pass a function to $(LREF2 .Interval, Interval)'s $(D fwdRange) + where that function took a $(LREF Date) and returned a $(LREF Date) which was one + day later. That function would then be used by $(D IntervalRange)'s + $(D popFront) to iterate over the $(LREF Date)s in the interval. + + If $(D dir == Direction.fwd), then a range iterates forward in time, whereas + if $(D dir == Direction.bwd), then it iterates backwards in time. So, if + $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if + $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must + generate a time point going in the proper direction of iteration, or a + $(LREF DateTimeException) will be thrown. So, to iterate forward in + time, the time point that $(D func) generates must be later in time than the + one passed to it. If it's either identical or earlier in time, then a + $(LREF DateTimeException) will be thrown. To iterate backwards, then + the generated time point must be before the time point which was passed in. + + If the generated time point is ever passed the edge of the range in the + proper direction, then the edge of that range will be used instead. So, if + iterating forward, and the generated time point is past the interval's + $(D end), then $(D front) becomes $(D end). If iterating backwards, and the + generated time point is before $(D begin), then $(D front) becomes + $(D begin). In either case, the range would then be empty. + + Also note that while normally the $(D begin) of an interval is included in + it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then + $(D begin) is treated as excluded and $(D end) is treated as included. This + allows for the same behavior in both directions. This works because none of + $(LREF2 .Interval, Interval)'s functions which care about whether $(D begin) or $(D end) is + included or excluded are ever called by $(D IntervalRange). $(D interval) + returns a normal interval, regardless of whether $(D dir == Direction.fwd) + or if $(D dir == Direction.bwd), so any $(LREF2 .Interval, Interval) functions which are + called on it which care about whether $(D begin) or $(D end) are included or + excluded will treat $(D begin) as included and $(D end) as excluded. + +/ +struct IntervalRange(TP, Direction dir) +if (isTimePoint!TP && dir != Direction.both) +{ +public: + + /++ + Params: + rhs = The $(D IntervalRange) to assign to this one. + +/ + ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + return this; + } + + + /++ Ditto +/ + ref IntervalRange opAssign(IntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + Whether this $(D IntervalRange) is empty. + +/ + @property bool empty() const pure nothrow + { + return _interval.empty; + } + + + /++ + The first time point in the range. + + Throws: + $(LREF DateTimeException) if the range is empty. + +/ + @property TP front() const pure + { + _enforceNotEmpty(); + + static if (dir == Direction.fwd) + return _interval.begin; + else + return _interval.end; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. If the generated time point is beyond the edge + of the range, then $(D front) is set to that edge, and the range is then + empty. So, if iterating forwards, and the generated time point is + greater than the interval's $(D end), then $(D front) is set to + $(D end). If iterating backwards, and the generated time point is less + than the interval's $(D begin), then $(D front) is set to $(D begin). + + Throws: + $(LREF DateTimeException) if the range is empty or if the generated + time point is in the wrong direction (i.e. if iterating + forward and the generated time point is before $(D front), or if + iterating backwards and the generated time point is after + $(D front)). + +/ + void popFront() + { + _enforceNotEmpty(); + + static if (dir == Direction.fwd) + { + auto begin = _func(_interval.begin); + + if (begin > _interval.end) + begin = _interval.end; + + _enforceCorrectDirection(begin); + + _interval.begin = begin; + } + else + { + auto end = _func(_interval.end); + + if (end < _interval.begin) + end = _interval.begin; + + _enforceCorrectDirection(end); + + _interval.end = end; + } + } + + + /++ + Returns a copy of $(D this). + +/ + @property IntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this $(D IntervalRange) currently covers. + +/ + @property Interval!TP interval() const pure nothrow + { + return cast(Interval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + + /++ + The $(D Direction) that this range iterates in. + +/ + @property Direction direction() const pure nothrow + { + return dir; + } + + +private: +package: // temporary + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(LREF DateTimeException) if this interval is empty. + +/ + void _enforceNotEmpty(size_t line = __LINE__) const pure + { + if (empty) + throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); + } + + + /+ + Throws: + $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong + direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + static if (dir == Direction.fwd) + { + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + else + { + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + } + + + Interval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.range.primitives; + static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); + static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); + + static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); + static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); + + static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); + static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); + static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); + static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); +} + +//Test construction of IntervalRange. +@safe unittest +{ + { + Date dateFunc(in Date date) { return date; } + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); + auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); + auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), + SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); + } +} + +//Test IntervalRange's empty(). +@system unittest +{ + //fwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(!cRange.empty); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } + + //bwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21) + ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + + assert(!range.empty); + range.popFront(); + assert(range.empty); + + const cRange = Interval!Date( Date(2010, 7, 4), Date(2012, 1, 7) + ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(!cRange.empty); + + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } +} + +//Test IntervalRange's front. +@system unittest +{ + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); + + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } +} + +//Test IntervalRange's popFront(). +@system unittest +{ + import std.range.primitives : walkLength; + //fwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange( + everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; range) + { + assert(date == expected); + expected += dur!"days"(7); + } + + assert(walkLength(range) == 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); + } + + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); + + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; range) + { + assert(date == expected); + expected += dur!"days"(-7); + } + + assert(walkLength(range) == 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); + } +} + +//Test IntervalRange's save. +@system unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); + } +} + +//Test IntervalRange's interval. +@system unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); + } +} + +//Test IntervalRange's func. +@system unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); + } +} + +//Test IntervalRange's direction. +@system unittest +{ + //fwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.direction == Direction.fwd); + + const cRange = range; + assert(cRange.direction == Direction.fwd); + } + + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.direction == Direction.bwd); + + const cRange = range; + assert(cRange.direction == Direction.bwd); + } +} diff --git a/std/datetime/package.d b/std/datetime/package.d index fdaaf9c449e..1b931a2db6b 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,570 +18219,6 @@ private: // Section with ranges. //============================================================================== - -/++ - A range over an $(LREF2 .Interval, Interval). - - $(D IntervalRange) is only ever constructed by $(LREF2 .Interval, Interval). However, when - it is constructed, it is given a function, $(D func), which is used to - generate the time points which are iterated over. $(D func) takes a time - point and returns a time point of the same type. For instance, - to iterate over all of the days in - the interval $(D Interval!Date), pass a function to $(LREF2 .Interval, Interval)'s $(D fwdRange) - where that function took a $(LREF Date) and returned a $(LREF Date) which was one - day later. That function would then be used by $(D IntervalRange)'s - $(D popFront) to iterate over the $(LREF Date)s in the interval. - - If $(D dir == Direction.fwd), then a range iterates forward in time, whereas - if $(D dir == Direction.bwd), then it iterates backwards in time. So, if - $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if - $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must - generate a time point going in the proper direction of iteration, or a - $(LREF DateTimeException) will be thrown. So, to iterate forward in - time, the time point that $(D func) generates must be later in time than the - one passed to it. If it's either identical or earlier in time, then a - $(LREF DateTimeException) will be thrown. To iterate backwards, then - the generated time point must be before the time point which was passed in. - - If the generated time point is ever passed the edge of the range in the - proper direction, then the edge of that range will be used instead. So, if - iterating forward, and the generated time point is past the interval's - $(D end), then $(D front) becomes $(D end). If iterating backwards, and the - generated time point is before $(D begin), then $(D front) becomes - $(D begin). In either case, the range would then be empty. - - Also note that while normally the $(D begin) of an interval is included in - it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then - $(D begin) is treated as excluded and $(D end) is treated as included. This - allows for the same behavior in both directions. This works because none of - $(LREF2 .Interval, Interval)'s functions which care about whether $(D begin) or $(D end) is - included or excluded are ever called by $(D IntervalRange). $(D interval) - returns a normal interval, regardless of whether $(D dir == Direction.fwd) - or if $(D dir == Direction.bwd), so any $(LREF2 .Interval, Interval) functions which are - called on it which care about whether $(D begin) or $(D end) are included or - excluded will treat $(D begin) as included and $(D end) as excluded. - +/ -struct IntervalRange(TP, Direction dir) -if (isTimePoint!TP && dir != Direction.both) -{ -public: - - /++ - Params: - rhs = The $(D IntervalRange) to assign to this one. - +/ - ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - return this; - } - - - /++ Ditto +/ - ref IntervalRange opAssign(IntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - Whether this $(D IntervalRange) is empty. - +/ - @property bool empty() const pure nothrow - { - return _interval.empty; - } - - - /++ - The first time point in the range. - - Throws: - $(LREF DateTimeException) if the range is empty. - +/ - @property TP front() const pure - { - _enforceNotEmpty(); - - static if (dir == Direction.fwd) - return _interval.begin; - else - return _interval.end; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. If the generated time point is beyond the edge - of the range, then $(D front) is set to that edge, and the range is then - empty. So, if iterating forwards, and the generated time point is - greater than the interval's $(D end), then $(D front) is set to - $(D end). If iterating backwards, and the generated time point is less - than the interval's $(D begin), then $(D front) is set to $(D begin). - - Throws: - $(LREF DateTimeException) if the range is empty or if the generated - time point is in the wrong direction (i.e. if iterating - forward and the generated time point is before $(D front), or if - iterating backwards and the generated time point is after - $(D front)). - +/ - void popFront() - { - _enforceNotEmpty(); - - static if (dir == Direction.fwd) - { - auto begin = _func(_interval.begin); - - if (begin > _interval.end) - begin = _interval.end; - - _enforceCorrectDirection(begin); - - _interval.begin = begin; - } - else - { - auto end = _func(_interval.end); - - if (end < _interval.begin) - end = _interval.begin; - - _enforceCorrectDirection(end); - - _interval.end = end; - } - } - - - /++ - Returns a copy of $(D this). - +/ - @property IntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this $(D IntervalRange) currently covers. - +/ - @property Interval!TP interval() const pure nothrow - { - return cast(Interval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - - /++ - The $(D Direction) that this range iterates in. - +/ - @property Direction direction() const pure nothrow - { - return dir; - } - - -private: -package: // temporary - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in Interval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if this interval is empty. - +/ - void _enforceNotEmpty(size_t line = __LINE__) const pure - { - if (empty) - throw new DateTimeException("Invalid operation for an empty IntervalRange.", __FILE__, line); - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - static if (dir == Direction.fwd) - { - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - else - { - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is after previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - } - - - Interval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.range.primitives : hasAssignableElements, hasSwappableElements, - isBidirectionalRange, isForwardRange, isInfinite, isInputRange; - static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); - static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); - - static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); - static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); - - static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); - static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); - static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); - static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); -} - -//Test construction of IntervalRange. -@safe unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - - auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); - - auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); - - auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime) st; - } - - auto interval = Interval!SysTime( - SysTime(DateTime(2010, 7, 4, 12, 1, 7)), - SysTime(DateTime(2012, 1, 7, 14, 0, 0)) - ); - - auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); - } -} - -//Test IntervalRange's empty(). -@system unittest -{ - //fwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(!cRange.empty); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } - - //bwd - { - auto range = Interval!Date( - Date(2010, 9, 19), - Date(2010, 9, 21) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - - assert(!range.empty); - range.popFront(); - assert(range.empty); - - const cRange = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(!cRange.empty); - - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } -} - -//Test IntervalRange's front. -@system unittest -{ - //fwd - { - auto emptyRange = Interval!Date( - Date(2010, 9, 19), - Date(2010, 9, 20) - ).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } - - //bwd - { - auto emptyRange = Interval!Date( - Date(2010, 9, 19), - Date(2010, 9, 20) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); - - auto range = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } -} - -//Test IntervalRange's popFront(). -@system unittest -{ - import std.range.primitives : walkLength; - //fwd - { - auto emptyRange = Interval!Date( - Date(2010, 9, 19), - Date(2010, 9, 20) - ).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - auto expected = range.front; - - foreach (date; range) - { - assert(date == expected); - expected += dur!"days"(7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); - } - - //bwd - { - auto emptyRange = Interval!Date( - Date(2010, 9, 19), - Date(2010, 9, 20) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); - - auto range = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - auto expected = range.front; - - foreach (date; range) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - assert(walkLength(range) == 79); - - const cRange = Interval!Date( - Date(2010, 7, 4), - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); - } -} - -//Test IntervalRange's save. -@system unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); - } -} - -//Test IntervalRange's interval. -@system unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); - } -} - -//Test IntervalRange's func. -@system unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); - } -} - -//Test IntervalRange's direction. -@system unittest -{ - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.direction == Direction.fwd); - - const cRange = range; - assert(cRange.direction == Direction.fwd); - } - - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.direction == Direction.bwd); - - const cRange = range; - assert(cRange.direction == Direction.bwd); - } -} - - /++ A range over a $(D PosInfInterval). It is an infinite range. From d29326842793b28e8687a4d89e134a381536c5a6 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 23:18:59 +0200 Subject: [PATCH 089/262] Move PosInfIntervalRange to std.datetime.interval. --- std/datetime/interval.d | 260 ++++++++++++++++++++++++++++++++++++- std/datetime/package.d | 281 ---------------------------------------- 2 files changed, 259 insertions(+), 282 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 5ec8b6ec757..4b13d7c1cde 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -19,7 +19,7 @@ version(unittest) import std.exception : assertThrown; import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary -import std.datetime : PosInfIntervalRange, NegInfIntervalRange; // temporary +import std.datetime : NegInfIntervalRange; // temporary /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s @@ -8353,3 +8353,261 @@ package: // temporary assert(cRange.direction == Direction.bwd); } } + + +/++ + A range over a $(D PosInfInterval). It is an infinite range. + + $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval). + However, when it is constructed, it is given a function, $(D func), which + is used to generate the time points which are iterated over. $(D func) + takes a time point and returns a time point of the same type. For + instance, to iterate + over all of the days in the interval $(D PosInfInterval!Date), pass a function to + $(D PosInfInterval)'s $(D fwdRange) where that function took a $(LREF Date) and + returned a $(LREF Date) which was one day later. That function would then be + used by $(D PosInfIntervalRange)'s $(D popFront) to iterate over the + $(LREF Date)s in the interval - though obviously, since the range is infinite, + use a function such as $(D std.range.take) with it rather than + iterating over $(I all) of the dates. + + As the interval goes to positive infinity, the range is always iterated over + forwards, never backwards. $(D func) must generate a time point going in + the proper direction of iteration, or a $(LREF DateTimeException) will be + thrown. So, the time points that $(D func) generates must be later in time + than the one passed to it. If it's either identical or earlier in time, then + a $(LREF DateTimeException) will be thrown. + +/ +struct PosInfIntervalRange(TP) +if (isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The $(D PosInfIntervalRange) to assign to this one. + +/ + ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + return this; + } + + + /++ Ditto +/ + ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.begin; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. + + Throws: + $(LREF DateTimeException) if the generated time point is less than + $(D front). + +/ + void popFront() + { + auto begin = _func(_interval.begin); + _enforceCorrectDirection(begin); + _interval.begin = begin; + } + + + /++ + Returns a copy of $(D this). + +/ + @property PosInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property PosInfInterval!TP interval() const pure nothrow + { + return cast(PosInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: +package: // temporary + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong + direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + enforce(newTP > _interval._begin, + new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", + interval._begin, + newTP), + __FILE__, + line)); + } + + + PosInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.range.primitives; + static assert(isInputRange!(PosInfIntervalRange!Date)); + static assert(isForwardRange!(PosInfIntervalRange!Date)); + static assert(isInfinite!(PosInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); + static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); + static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); + static assert(!hasLength!(PosInfIntervalRange!Date)); + static assert(!hasSlicing!(PosInfIntervalRange!Date)); + + static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); + static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); +} + +//Test construction of PosInfIntervalRange. +@safe unittest +{ + { + Date dateFunc(in Date date) { return date; } + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); + auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); + auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime) st; } + auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); + auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); + } +} + +//Test PosInfIntervalRange's front. +@system unittest +{ + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); + + auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + assert(cRange.front != Date.init); +} + +//Test PosInfIntervalRange's popFront(). +@system unittest +{ + import std.range : take; + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(7); + } + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test PosInfIntervalRange's save. +@system unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.save == range); +} + +//Test PosInfIntervalRange's interval. +@system unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); +} + +//Test PosInfIntervalRange's func. +@system unittest +{ + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); + + assert(range.func == func); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index 1b931a2db6b..fa7123a54f7 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,287 +18219,6 @@ private: // Section with ranges. //============================================================================== -/++ - A range over a $(D PosInfInterval). It is an infinite range. - - $(D PosInfIntervalRange) is only ever constructed by $(D PosInfInterval). - However, when it is constructed, it is given a function, $(D func), which - is used to generate the time points which are iterated over. $(D func) - takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval $(D PosInfInterval!Date), pass a function to - $(D PosInfInterval)'s $(D fwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day later. That function would then be - used by $(D PosInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. - - As the interval goes to positive infinity, the range is always iterated over - forwards, never backwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be later in time - than the one passed to it. If it's either identical or earlier in time, then - a $(LREF DateTimeException) will be thrown. - +/ -struct PosInfIntervalRange(TP) -if (isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The $(D PosInfIntervalRange) to assign to this one. - +/ - ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - - return this; - } - - - /++ Ditto +/ - ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.begin; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. - - Throws: - $(LREF DateTimeException) if the generated time point is less than - $(D front). - +/ - void popFront() - { - auto begin = _func(_interval.begin); - - _enforceCorrectDirection(begin); - - _interval.begin = begin; - } - - - /++ - Returns a copy of $(D this). - +/ - @property PosInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property PosInfInterval!TP interval() const pure nothrow - { - return cast(PosInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - -private: -package: // temporary - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in PosInfInterval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAME newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP > _interval._begin, - new DateTimeException(format("Generated time point is before previous begin: prev [%s] new [%s]", - interval._begin, - newTP), - __FILE__, - line)); - } - - - PosInfInterval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.range.primitives : hasAssignableElements, hasSwappableElements, - isBidirectionalRange, isForwardRange, isInfinite, isInputRange; - static assert(isInputRange!(PosInfIntervalRange!Date)); - static assert(isForwardRange!(PosInfIntervalRange!Date)); - static assert(isInfinite!(PosInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); - static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); - static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); - static assert(!hasLength!(PosInfIntervalRange!Date)); - static assert(!hasSlicing!(PosInfIntervalRange!Date)); - - static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); - static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); -} - -//Test construction of PosInfIntervalRange. -@safe unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - - auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); - - auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); - - auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime) st; - } - - auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); - - auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); - } -} - -//Test PosInfIntervalRange's front. -@system unittest -{ - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - assert(range.front == Date(2010, 7, 4)); - - auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - assert(poppedRange.front == Date(2010, 7, 7)); - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(cRange.front != Date.init); -} - -//Test PosInfIntervalRange's popFront(). -@system unittest -{ - import std.range : take; - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), Yes.popFirst); - auto expected = range.front; - - foreach (date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(7); - } - - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test PosInfIntervalRange's save. -@system unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.save == range); -} - -//Test PosInfIntervalRange's interval. -@system unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); -} - -//Test PosInfIntervalRange's func. -@system unittest -{ - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); - - assert(range.func == func); -} - /++ A range over a $(D NegInfInterval). It is an infinite range. From dcb04f4241868643f783973fe151545650ce94fd Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Wed, 3 May 2017 23:29:42 +0200 Subject: [PATCH 090/262] Move NegInfInterval to std.datetime.interval. --- std/datetime/interval.d | 279 +++++++++++++++++++++++++++++++++++-- std/datetime/package.d | 300 ---------------------------------------- 2 files changed, 271 insertions(+), 308 deletions(-) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 4b13d7c1cde..74473fb7c31 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -9,7 +9,7 @@ +/ module std.datetime.interval; -import core.time : Duration; +import core.time : Duration, dur; import std.datetime.common; import std.exception : enforce; import std.traits : isIntegral, Unqual; @@ -17,9 +17,8 @@ import std.typecons : Flag; version(unittest) import std.exception : assertThrown; -import core.time : dur; // temporary import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary -import std.datetime : NegInfIntervalRange; // temporary + /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s @@ -1513,7 +1512,6 @@ public: private: -package: // temporary /+ Since we have two versions of toString, we have _toStringImpl @@ -4052,7 +4050,6 @@ assert(!range.empty); } private: -package: // temporary /+ Since we have two versions of toString(), we have _toStringImpl() @@ -6226,7 +6223,6 @@ assert(!range.empty); } private: -package: // temporary /+ Since we have two versions of toString(), we have _toStringImpl() @@ -8015,7 +8011,6 @@ public: private: -package: // temporary /+ Params: @@ -8461,7 +8456,6 @@ public: private: -package: // temporary /+ Params: @@ -8611,3 +8605,272 @@ package: // temporary assert(range.func == func); } + + +/++ + A range over a $(D NegInfInterval). It is an infinite range. + + $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval). + However, when it is constructed, it is given a function, $(D func), which + is used to generate the time points which are iterated over. $(D func) + takes a time point and returns a time point of the same type. For + instance, to iterate + over all of the days in the interval $(D NegInfInterval!Date), pass a function to + $(D NegInfInterval)'s $(D bwdRange) where that function took a $(LREF Date) and + returned a $(LREF Date) which was one day earlier. That function would then be + used by $(D NegInfIntervalRange)'s $(D popFront) to iterate over the + $(LREF Date)s in the interval - though obviously, since the range is infinite, + use a function such as $(D std.range.take) with it rather than + iterating over $(I all) of the dates. + + As the interval goes to negative infinity, the range is always iterated over + backwards, never forwards. $(D func) must generate a time point going in + the proper direction of iteration, or a $(LREF DateTimeException) will be + thrown. So, the time points that $(D func) generates must be earlier in time + than the one passed to it. If it's either identical or later in time, then a + $(LREF DateTimeException) will be thrown. + + Also note that while normally the $(D end) of an interval is excluded from + it, $(D NegInfIntervalRange) treats it as if it were included. This allows + for the same behavior as with $(D PosInfIntervalRange). This works + because none of $(D NegInfInterval)'s functions which care about whether + $(D end) is included or excluded are ever called by + $(D NegInfIntervalRange). $(D interval) returns a normal interval, so any + $(D NegInfInterval) functions which are called on it which care about + whether $(D end) is included or excluded will treat $(D end) as excluded. + +/ +struct NegInfIntervalRange(TP) +if (isTimePoint!TP) +{ +public: + + /++ + Params: + rhs = The $(D NegInfIntervalRange) to assign to this one. + +/ + ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow + { + _interval = rhs._interval; + _func = rhs._func; + + return this; + } + + + /++ Ditto +/ + ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow + { + return this = rhs; + } + + + /++ + This is an infinite range, so it is never empty. + +/ + enum bool empty = false; + + + /++ + The first time point in the range. + +/ + @property TP front() const pure nothrow + { + return _interval.end; + } + + + /++ + Pops $(D front) from the range, using $(D func) to generate the next + time point in the range. + + Throws: + $(LREF DateTimeException) if the generated time point is greater than + $(D front). + +/ + void popFront() + { + auto end = _func(_interval.end); + _enforceCorrectDirection(end); + _interval.end = end; + } + + + /++ + Returns a copy of $(D this). + +/ + @property NegInfIntervalRange save() pure nothrow + { + return this; + } + + + /++ + The interval that this range currently covers. + +/ + @property NegInfInterval!TP interval() const pure nothrow + { + return cast(NegInfInterval!TP)_interval; + } + + + /++ + The function used to generate the next time point in the range. + +/ + TP delegate(in TP) func() pure nothrow @property + { + return _func; + } + + +private: + + /+ + Params: + interval = The interval that this range covers. + func = The function used to generate the time points which are + iterated over. + +/ + this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow + { + _func = func; + _interval = interval; + } + + + /+ + Throws: + $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong + direction. + +/ + void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const + { + import std.format : format; + + enforce(newTP < _interval._end, + new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", + interval._end, + newTP), + __FILE__, + line)); + } + + + NegInfInterval!TP _interval; + TP delegate(in TP) _func; +} + +//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. +@safe unittest +{ + import std.range.primitives; + static assert(isInputRange!(NegInfIntervalRange!Date)); + static assert(isForwardRange!(NegInfIntervalRange!Date)); + static assert(isInfinite!(NegInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); + static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); + static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); + static assert(!hasLength!(NegInfIntervalRange!Date)); + static assert(!hasSlicing!(NegInfIntervalRange!Date)); + + static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); +} + +//Test construction of NegInfIntervalRange. +@safe unittest +{ + { + Date dateFunc(in Date date) { return date; } + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); + } + + { + TimeOfDay todFunc(in TimeOfDay tod) { return tod; } + auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); + auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); + } + + { + DateTime dtFunc(in DateTime dt) { return dt; } + auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); + auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); + } + + { + SysTime stFunc(in SysTime st) { return cast(SysTime)(st); } + auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); + } +} + +//Test NegInfIntervalRange's front. +@system unittest +{ + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); + + auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + assert(cRange.front != Date.init); +} + +//Test NegInfIntervalRange's popFront(). +@system unittest +{ + import std.range : take; + + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; + + foreach (date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(-7); + } + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); +} + +//Test NegInfIntervalRange's save. +@system unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.save == range); +} + +//Test NegInfIntervalRange's interval. +@system unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.interval == interval); + + const cRange = range; + assert(!cRange.interval.empty); +} + +//Test NegInfIntervalRange's func. +@system unittest +{ + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); + + assert(range.func == func); +} diff --git a/std/datetime/package.d b/std/datetime/package.d index fa7123a54f7..e54b882f9e5 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18215,306 +18215,6 @@ private: } -//============================================================================== -// Section with ranges. -//============================================================================== - - -/++ - A range over a $(D NegInfInterval). It is an infinite range. - - $(D NegInfIntervalRange) is only ever constructed by $(D NegInfInterval). - However, when it is constructed, it is given a function, $(D func), which - is used to generate the time points which are iterated over. $(D func) - takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval $(D NegInfInterval!Date), pass a function to - $(D NegInfInterval)'s $(D bwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day earlier. That function would then be - used by $(D NegInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. - - As the interval goes to negative infinity, the range is always iterated over - backwards, never forwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be earlier in time - than the one passed to it. If it's either identical or later in time, then a - $(LREF DateTimeException) will be thrown. - - Also note that while normally the $(D end) of an interval is excluded from - it, $(D NegInfIntervalRange) treats it as if it were included. This allows - for the same behavior as with $(D PosInfIntervalRange). This works - because none of $(D NegInfInterval)'s functions which care about whether - $(D end) is included or excluded are ever called by - $(D NegInfIntervalRange). $(D interval) returns a normal interval, so any - $(D NegInfInterval) functions which are called on it which care about - whether $(D end) is included or excluded will treat $(D end) as excluded. - +/ -struct NegInfIntervalRange(TP) -if (isTimePoint!TP) -{ -public: - - /++ - Params: - rhs = The $(D NegInfIntervalRange) to assign to this one. - +/ - ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow - { - _interval = rhs._interval; - _func = rhs._func; - - return this; - } - - - /++ Ditto +/ - ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow - { - return this = rhs; - } - - - /++ - This is an infinite range, so it is never empty. - +/ - enum bool empty = false; - - - /++ - The first time point in the range. - +/ - @property TP front() const pure nothrow - { - return _interval.end; - } - - - /++ - Pops $(D front) from the range, using $(D func) to generate the next - time point in the range. - - Throws: - $(LREF DateTimeException) if the generated time point is greater than - $(D front). - +/ - void popFront() - { - auto end = _func(_interval.end); - - _enforceCorrectDirection(end); - - _interval.end = end; - } - - - /++ - Returns a copy of $(D this). - +/ - @property NegInfIntervalRange save() pure nothrow - { - return this; - } - - - /++ - The interval that this range currently covers. - +/ - @property NegInfInterval!TP interval() const pure nothrow - { - return cast(NegInfInterval!TP)_interval; - } - - - /++ - The function used to generate the next time point in the range. - +/ - TP delegate(in TP) func() pure nothrow @property - { - return _func; - } - - -private: -package: // temporary - - /+ - Params: - interval = The interval that this range covers. - func = The function used to generate the time points which are - iterated over. - +/ - this(in NegInfInterval!TP interval, TP delegate(in TP) func) pure nothrow - { - _func = func; - _interval = interval; - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. - +/ - void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const - { - import std.format : format; - - enforce(newTP < _interval._end, - new DateTimeException(format("Generated time point is before previous end: prev [%s] new [%s]", - interval._end, - newTP), - __FILE__, - line)); - } - - - NegInfInterval!TP _interval; - TP delegate(in TP) _func; -} - -//Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. -@safe unittest -{ - import std.range.primitives : hasAssignableElements, hasSwappableElements, - isBidirectionalRange, isForwardRange, isInfinite, isInputRange; - static assert(isInputRange!(NegInfIntervalRange!Date)); - static assert(isForwardRange!(NegInfIntervalRange!Date)); - static assert(isInfinite!(NegInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); - static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); - static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); - static assert(!hasLength!(NegInfIntervalRange!Date)); - static assert(!hasSlicing!(NegInfIntervalRange!Date)); - - static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); -} - -//Test construction of NegInfIntervalRange. -@safe unittest -{ - { - Date dateFunc(in Date date) - { - return date; - } - - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); - } - - { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } - - auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); - - auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); - } - - { - DateTime dtFunc(in DateTime dt) - { - return dt; - } - - auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); - - auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); - } - - { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)(st); - } - - auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - - auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); - } -} - -//Test NegInfIntervalRange's front. -@system unittest -{ - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - assert(range.front == Date(2012, 1, 7)); - - auto poppedRange = NegInfInterval!Date( - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - assert(poppedRange.front == Date(2012, 1, 4)); - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(cRange.front != Date.init); -} - -//Test NegInfIntervalRange's popFront(). -@system unittest -{ - import std.range : take; - - auto range = NegInfInterval!Date( - Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), Yes.popFirst); - auto expected = range.front; - - foreach (date; take(range, 79)) - { - assert(date == expected); - expected += dur!"days"(-7); - } - - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); -} - -//Test NegInfIntervalRange's save. -@system unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.save == range); -} - -//Test NegInfIntervalRange's interval. -@system unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.interval == interval); - - const cRange = range; - assert(!cRange.interval.empty); -} - -//Test NegInfIntervalRange's func. -@system unittest -{ - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); - - assert(range.func == func); -} - - //============================================================================== // Section with time zones. //============================================================================== From 93940b4374f58d198f3778e965b6e39c4981c547 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 09:58:04 +0200 Subject: [PATCH 091/262] Move TimeZone to std.datetime.timezone. --- std/datetime/package.d | 584 -------------------------------------- std/datetime/timezone.d | 612 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 612 insertions(+), 584 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index e54b882f9e5..cf8afd5c06d 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,590 +18219,6 @@ private: // Section with time zones. //============================================================================== -/++ - Represents a time zone. It is used with $(LREF SysTime) to indicate the time - zone of a $(LREF SysTime). - +/ -abstract class TimeZone -{ -public: - - /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - +/ - @property string name() @safe const nothrow - { - return _name; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - +/ - @property string stdName() @safe const nothrow - { - return _stdName; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - +/ - @property string dstName() @safe const nothrow - { - return _dstName; - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current dates - but will still return true for $(D hasDST) because the time zone did at - some point have DST. - +/ - @property abstract bool hasDST() @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - abstract bool dstInEffect(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - abstract long utcToTZ(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - abstract long tzToUTC(long adjTime) @safe const nothrow; - - - /++ - Returns what the offset from UTC is at the given std time. - It includes the DST offset in effect at that time (if any). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); - } - - // @@@DEPRECATED_2017-07@@@ - /++ - $(RED Deprecated. Use either PosixTimeZone.getTimeZone or - WindowsTimeZone.getTimeZone. ($(LREF parseTZConversions) can be - used to convert time zone names if necessary). Microsoft changes - their time zones too often for us to compile the conversions into - Phobos and have them be properly up-to-date. TimeZone.getTimeZone - will be removed in July 2017.) - - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. - - This returns a $(LREF PosixTimeZone) on Posix systems and a - $(LREF WindowsTimeZone) on Windows systems. For - $(LREF PosixTimeZone) on Windows, call $(D PosixTimeZone.getTimeZone) - directly and give it the location of the TZ Database time zone files on - disk. - - On Windows, the given TZ Database name is converted to the corresponding - time zone name on Windows prior to calling - $(D WindowsTimeZone.getTimeZone). This function allows for - the same time zone names on both Windows and Posix systems. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones)
- $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - name = The TZ Database name of the desired time zone - - Throws: - $(LREF DateTimeException) if the given time zone could not be found. - +/ - deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") - static immutable(TimeZone) getTimeZone(string name) @safe - { - version(Posix) - return PosixTimeZone.getTimeZone(name); - else version(Windows) - { - import std.format : format; - auto windowsTZName = tzDatabaseNameToWindowsTZName(name); - if (windowsTZName != null) - { - try - return WindowsTimeZone.getTimeZone(windowsTZName); - catch (DateTimeException dte) - { - auto oldName = _getOldName(windowsTZName); - if (oldName != null) - return WindowsTimeZone.getTimeZone(oldName); - throw dte; - } - } - else - throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name)); - } - } - - /// - deprecated @safe unittest - { - auto tz = TimeZone.getTimeZone("America/Los_Angeles"); - } - - // The purpose of this is to handle the case where a Windows time zone is - // new and exists on an up-to-date Windows box but does not exist on Windows - // boxes which have not been properly updated. The "date added" is included - // on the theory that we'll be able to remove them at some point in the - // the future once enough time has passed, and that way, we know how much - // time has passed. - private static string _getOldName(string windowsTZName) @safe pure nothrow - { - switch (windowsTZName) - { - case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08 - case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08 - default: return null; - } - } - - //Since reading in the time zone files could be expensive, most unit tests - //are consolidated into this one unittest block which minimizes how often it - //reads a time zone file. - @system unittest - { - import std.conv : to; - import std.file : exists, isFile; - import std.format : format; - import std.path : chainPath; - import std.stdio : writefln; - import std.typecons : tuple; - - version(Posix) alias getTimeZone = PosixTimeZone.getTimeZone; - else version(Windows) alias getTimeZone = WindowsTimeZone.getTimeZone; - - version(Posix) scope(exit) clearTZEnvVar(); - - static immutable(TimeZone) testTZ(string tzName, - string stdName, - string dstName, - Duration utcOffset, - Duration dstOffset, - bool north = true) - { - scope(failure) writefln("Failed time zone: %s", tzName); - - version(Posix) - { - immutable tz = PosixTimeZone.getTimeZone(tzName); - assert(tz.name == tzName); - } - else version(Windows) - { - immutable tz = WindowsTimeZone.getTimeZone(tzName); - assert(tz.name == stdName); - } - - immutable hasDST = dstOffset != Duration.zero; - - //assert(tz.stdName == stdName); //Locale-dependent - //assert(tz.dstName == dstName); //Locale-dependent - assert(tz.hasDST == hasDST); - - immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); - immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); - auto std = SysTime(stdDate, tz); - auto dst = SysTime(dstDate, tz); - auto stdUTC = SysTime(stdDate - utcOffset, UTC()); - auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); - - assert(!std.dstInEffect); - assert(dst.dstInEffect == hasDST); - assert(tz.utcOffsetAt(std.stdTime) == utcOffset); - assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); - - assert(cast(DateTime) std == stdDate); - assert(cast(DateTime) dst == dstDate); - assert(std == stdUTC); - - version(Posix) - { - setTZEnvVar(tzName); - - static void testTM(in SysTime st) - { - import core.stdc.time : localtime, tm; - time_t unixTime = st.toUnixTime(); - tm* osTimeInfo = localtime(&unixTime); - tm ourTimeInfo = st.toTM(); - - assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); - assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); - assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); - assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); - assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); - assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); - assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); - assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); - assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); - assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); - assert(to!string(ourTimeInfo.tm_zone) == - to!string(osTimeInfo.tm_zone)); - } - - testTM(std); - testTM(dst); - - //Apparently, right/ does not exist on Mac OS X. I don't know - //whether or not it exists on FreeBSD. It's rather pointless - //normally, since the Posix standard requires that leap seconds - //be ignored, so it does make some sense that right/ wouldn't - //be there, but since PosixTimeZone _does_ use leap seconds if - //the time zone file does, we'll test that functionality if the - //appropriate files exist. - if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) - { - auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); - - assert(leapTZ.name == "right/" ~ tzName); - //assert(leapTZ.stdName == stdName); //Locale-dependent - //assert(leapTZ.dstName == dstName); //Locale-dependent - assert(leapTZ.hasDST == hasDST); - - auto leapSTD = SysTime(std.stdTime, leapTZ); - auto leapDST = SysTime(dst.stdTime, leapTZ); - - assert(!leapSTD.dstInEffect); - assert(leapDST.dstInEffect == hasDST); - - assert(leapSTD.stdTime == std.stdTime); - assert(leapDST.stdTime == dst.stdTime); - - //Whenever a leap second is added/removed, - //this will have to be adjusted. - //enum leapDiff = convert!("seconds", "hnsecs")(25); - //assert(leapSTD.adjTime - leapDiff == std.adjTime); - //assert(leapDST.adjTime - leapDiff == dst.adjTime); - } - } - - return tz; - } - - auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - version(Posix) - { - version(FreeBSD) enum utcZone = "Etc/UTC"; - else version(NetBSD) enum utcZone = "UTC"; - else version(linux) enum utcZone = "UTC"; - else version(OSX) enum utcZone = "UTC"; - else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); - - auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)), - testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)), - testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)), - //Per www.timeanddate.com, it should be "CST" and "CDT", - //but the OS insists that it's "CST" for both. We should - //probably figure out how to report an error in the TZ - //database and report it. - testTZ("Australia/Adelaide", "CST", "CST", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world")); - } - else version(Windows) - { - auto tzs = [testTZ("Pacific Standard Time", "Pacific Standard Time", - "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)), - testTZ("Eastern Standard Time", "Eastern Standard Time", - "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)), - //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time", - //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false), - testTZ("GMT Standard Time", "GMT Standard Time", - "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)), - testTZ("Romance Standard Time", "Romance Standard Time", - "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)), - testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time", - "Cen. Australia Daylight Time", - dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; - - testTZ("Greenwich Standard Time", "Greenwich Standard Time", - "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0)); - assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world")); - } - else - assert(0, "OS not supported."); - - foreach (i; 0 .. tzs.length) - { - auto tz = tzs[i]; - immutable spring = dstSwitches[i][2]; - immutable fall = dstSwitches[i][3]; - auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - //Verify that creating a SysTime in the given time zone results - //in a SysTime with the correct std time during and surrounding - //a DST switch. - foreach (hour; -12 .. 13) - { - auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tz.name, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if (hour == spring) - { - testHour(st, spring + 1, tz.name); - testHour(st + dur!"minutes"(1), spring + 1, tz.name); - } - else - { - testHour(st, targetHour, tz.name); - testHour(st + dur!"minutes"(1), targetHour, tz.name); - } - - if (hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz); - testHour(st, targetHour, tz.name); - - //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if (hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tz.name); - - if (hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - //Verify that converting a time in UTC to a time in another - //time zone results in the correct time during and surrounding - //a DST switch. - bool first = true; - auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset; - //@@@BUG@@@ 3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach (hour; -24 .. 25) - { - auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC()); - auto local = utc.toOtherTZ(tz); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if (utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC()); - local = utc.toOtherTZ(tz); - - if (utc == fallSwitch || utc == fallSwitchMinus1) - { - if (first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if (utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - - - // @@@DEPRECATED_2017-07@@@ - /++ - $(RED Deprecated. Use either PosixTimeZone.getInstalledTZNames or - WindowsTimeZone.getInstalledTZNames. ($(LREF parseTZConversions) - can be used to convert time zone names if necessary). Microsoft - changes their time zones too often for us to compile the - conversions into Phobos and have them be properly up-to-date. - TimeZone.getInstalledTZNames will be removed in July 2017.) - - Returns a list of the names of the time zones installed on the system. - - Providing a sub-name narrows down the list of time zones (which - can number in the thousands). For example, - passing in "America" as the sub-name returns only the time zones which - begin with "America". - - On Windows, this function will convert the Windows time zone names to - the corresponding TZ Database names with - $(D windowsTZNameToTZDatabaseName). To get the actual Windows time - zone names, use $(D WindowsTimeZone.getInstalledTZNames) directly. - - Params: - subName = The first part of the time zones desired. - - Throws: - $(D FileException) on Posix systems if it fails to read from disk. - $(LREF DateTimeException) on Windows systems if it fails to read the - registry. - +/ - deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") - static string[] getInstalledTZNames(string subName = "") @safe - { - version(Posix) - return PosixTimeZone.getInstalledTZNames(subName); - else version(Windows) - { - import std.algorithm.searching : startsWith; - import std.algorithm.sorting : sort; - import std.array : appender; - - auto windowsNames = WindowsTimeZone.getInstalledTZNames(); - auto retval = appender!(string[])(); - - foreach (winName; windowsNames) - { - auto tzName = windowsTZNameToTZDatabaseName(winName); - if (tzName !is null && tzName.startsWith(subName)) - retval.put(tzName); - } - - sort(retval.data); - return retval.data; - } - } - - deprecated @safe unittest - { - import std.exception : assertNotThrown; - import std.stdio : writefln; - static void testPZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - TimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - // This was not previously tested, and it's currently failing, so I'm - // leaving it commented out until I can sort it out. - //assert(equal(tzNames, tzNames.uniq())); - - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testPZSuccess(tzName)); - } - - -private: - - /+ - Params: - name = The TZ Database name for the time zone. - stdName = The abbreviation for the time zone during std time. - dstName = The abbreviation for the time zone during DST. - +/ - this(string name, string stdName, string dstName) @safe immutable pure - { - _name = name; - _stdName = stdName; - _dstName = dstName; - } - - - immutable string _name; - immutable string _stdName; - immutable string _dstName; -} - - /++ A TimeZone which represents the current local time zone on the system running your program. diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 761a68471c9..02f69bfa772 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -8,3 +8,615 @@ LREF2=$(D $2) +/ module std.datetime.timezone; + +import core.time : Duration, dur; +import std.datetime.common; +import std.exception : enforce; + +version(Windows) +{ + import core.stdc.time : time_t; + import core.sys.windows.windows; + import core.sys.windows.winsock2; + import std.windows.registry; + + // Uncomment and run unittests to print missing Windows TZ translations. + // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog + // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible + // for updating the translations. + // version = UpdateWindowsTZTranslations; +} +else version(Posix) +{ + import core.sys.posix.signal : timespec; + import core.sys.posix.sys.types : time_t; +} + +version(unittest) import std.exception : assertThrown; + +import std.datetime : clearTZEnvVar, Date, DateTime, PosixTimeZone, setTZEnvVar, SysTime, TimeOfDay, UTC; // temporary + +/++ + Represents a time zone. It is used with $(LREF SysTime) to indicate the time + zone of a $(LREF SysTime). + +/ +abstract class TimeZone +{ +public: + + /++ + The name of the time zone per the TZ Database. This is the name used to + get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones) + +/ + @property string name() @safe const nothrow + { + return _name; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + +/ + @property string stdName() @safe const nothrow + { + return _stdName; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + +/ + @property string dstName() @safe const nothrow + { + return _dstName; + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current dates + but will still return true for $(D hasDST) because the time zone did at + some point have DST. + +/ + @property abstract bool hasDST() @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + abstract bool dstInEffect(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + abstract long utcToTZ(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + abstract long tzToUTC(long adjTime) @safe const nothrow; + + + /++ + Returns what the offset from UTC is at the given std time. + It includes the DST offset in effect at that time (if any). + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); + } + + // @@@DEPRECATED_2017-07@@@ + /++ + $(RED Deprecated. Use either PosixTimeZone.getTimeZone or + WindowsTimeZone.getTimeZone. ($(LREF parseTZConversions) can be + used to convert time zone names if necessary). Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. TimeZone.getTimeZone + will be removed in July 2017.) + + Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. + + This returns a $(LREF PosixTimeZone) on Posix systems and a + $(LREF WindowsTimeZone) on Windows systems. For + $(LREF PosixTimeZone) on Windows, call $(D PosixTimeZone.getTimeZone) + directly and give it the location of the TZ Database time zone files on + disk. + + On Windows, the given TZ Database name is converted to the corresponding + time zone name on Windows prior to calling + $(D WindowsTimeZone.getTimeZone). This function allows for + the same time zone names on both Windows and Posix systems. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones)
+ $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + name = The TZ Database name of the desired time zone + + Throws: + $(LREF DateTimeException) if the given time zone could not be found. + +/ + deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") + static immutable(TimeZone) getTimeZone(string name) @safe + { + version(Posix) + return PosixTimeZone.getTimeZone(name); + else version(Windows) + { + import std.format : format; + auto windowsTZName = tzDatabaseNameToWindowsTZName(name); + if (windowsTZName != null) + { + try + return WindowsTimeZone.getTimeZone(windowsTZName); + catch (DateTimeException dte) + { + auto oldName = _getOldName(windowsTZName); + if (oldName != null) + return WindowsTimeZone.getTimeZone(oldName); + throw dte; + } + } + else + throw new DateTimeException(format("%s does not have an equivalent Windows time zone.", name)); + } + } + + /// + deprecated @safe unittest + { + auto tz = TimeZone.getTimeZone("America/Los_Angeles"); + } + + // The purpose of this is to handle the case where a Windows time zone is + // new and exists on an up-to-date Windows box but does not exist on Windows + // boxes which have not been properly updated. The "date added" is included + // on the theory that we'll be able to remove them at some point in the + // the future once enough time has passed, and that way, we know how much + // time has passed. + private static string _getOldName(string windowsTZName) @safe pure nothrow + { + switch (windowsTZName) + { + case "Belarus Standard Time": return "Kaliningrad Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 10": return "Magadan Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 11": return "Magadan Standard Time"; // Added 2014-10-08 + case "Russia Time Zone 3": return "Russian Standard Time"; // Added 2014-10-08 + default: return null; + } + } + + // Since reading in the time zone files could be expensive, most unit tests + // are consolidated into this one unittest block which minimizes how often + // it reads a time zone file. + @system unittest + { + import core.exception : AssertError; + import std.conv : to; + import std.file : exists, isFile; + import std.format : format; + import std.path : chainPath; + import std.stdio : writefln; + import std.typecons : tuple; + + version(Posix) alias getTimeZone = PosixTimeZone.getTimeZone; + else version(Windows) alias getTimeZone = WindowsTimeZone.getTimeZone; + + version(Posix) scope(exit) clearTZEnvVar(); + + static immutable(TimeZone) testTZ(string tzName, + string stdName, + string dstName, + Duration utcOffset, + Duration dstOffset, + bool north = true) + { + scope(failure) writefln("Failed time zone: %s", tzName); + + version(Posix) + { + immutable tz = PosixTimeZone.getTimeZone(tzName); + assert(tz.name == tzName); + } + else version(Windows) + { + immutable tz = WindowsTimeZone.getTimeZone(tzName); + assert(tz.name == stdName); + } + + immutable hasDST = dstOffset != Duration.zero; + + //assert(tz.stdName == stdName); //Locale-dependent + //assert(tz.dstName == dstName); //Locale-dependent + assert(tz.hasDST == hasDST); + + immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); + immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); + auto std = SysTime(stdDate, tz); + auto dst = SysTime(dstDate, tz); + auto stdUTC = SysTime(stdDate - utcOffset, UTC()); + auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); + + assert(!std.dstInEffect); + assert(dst.dstInEffect == hasDST); + assert(tz.utcOffsetAt(std.stdTime) == utcOffset); + assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); + + assert(cast(DateTime) std == stdDate); + assert(cast(DateTime) dst == dstDate); + assert(std == stdUTC); + + version(Posix) + { + setTZEnvVar(tzName); + + static void testTM(in SysTime st) + { + import core.stdc.time : localtime, tm; + time_t unixTime = st.toUnixTime(); + tm* osTimeInfo = localtime(&unixTime); + tm ourTimeInfo = st.toTM(); + + assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); + assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); + assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); + assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); + assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); + assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); + assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); + assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); + assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); + assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); + assert(to!string(ourTimeInfo.tm_zone) == to!string(osTimeInfo.tm_zone)); + } + + testTM(std); + testTM(dst); + + // Apparently, right/ does not exist on Mac OS X. I don't know + // whether or not it exists on FreeBSD. It's rather pointless + // normally, since the Posix standard requires that leap seconds + // be ignored, so it does make some sense that right/ wouldn't + // be there, but since PosixTimeZone _does_ use leap seconds if + // the time zone file does, we'll test that functionality if the + // appropriate files exist. + if (chainPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) + { + auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); + + assert(leapTZ.name == "right/" ~ tzName); + //assert(leapTZ.stdName == stdName); //Locale-dependent + //assert(leapTZ.dstName == dstName); //Locale-dependent + assert(leapTZ.hasDST == hasDST); + + auto leapSTD = SysTime(std.stdTime, leapTZ); + auto leapDST = SysTime(dst.stdTime, leapTZ); + + assert(!leapSTD.dstInEffect); + assert(leapDST.dstInEffect == hasDST); + + assert(leapSTD.stdTime == std.stdTime); + assert(leapDST.stdTime == dst.stdTime); + + // Whenever a leap second is added/removed, + // this will have to be adjusted. + //enum leapDiff = convert!("seconds", "hnsecs")(25); + //assert(leapSTD.adjTime - leapDiff == std.adjTime); + //assert(leapDST.adjTime - leapDiff == dst.adjTime); + } + } + + return tz; + } + + auto dstSwitches = [/+America/Los_Angeles+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + /+America/New_York+/ tuple(DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + ///+America/Santiago+/ tuple(DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), + /+Europe/London+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), + /+Europe/Paris+/ tuple(DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), + /+Australia/Adelaide+/ tuple(DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; + + version(Posix) + { + version(FreeBSD) enum utcZone = "Etc/UTC"; + else version(NetBSD) enum utcZone = "UTC"; + else version(linux) enum utcZone = "UTC"; + else version(OSX) enum utcZone = "UTC"; + else static assert(0, "The location of the UTC timezone file on this Posix platform must be set."); + + auto tzs = [testTZ("America/Los_Angeles", "PST", "PDT", dur!"hours"(-8), dur!"hours"(1)), + testTZ("America/New_York", "EST", "EDT", dur!"hours"(-5), dur!"hours"(1)), + //testTZ("America/Santiago", "CLT", "CLST", dur!"hours"(-4), dur!"hours"(1), false), + testTZ("Europe/London", "GMT", "BST", dur!"hours"(0), dur!"hours"(1)), + testTZ("Europe/Paris", "CET", "CEST", dur!"hours"(1), dur!"hours"(1)), + // Per www.timeanddate.com, it should be "CST" and "CDT", + // but the OS insists that it's "CST" for both. We should + // probably figure out how to report an error in the TZ + // database and report it. + testTZ("Australia/Adelaide", "CST", "CST", + dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; + + testTZ(utcZone, "UTC", "UTC", dur!"hours"(0), dur!"hours"(0)); + assertThrown!DateTimeException(PosixTimeZone.getTimeZone("hello_world")); + } + else version(Windows) + { + auto tzs = [testTZ("Pacific Standard Time", "Pacific Standard Time", + "Pacific Daylight Time", dur!"hours"(-8), dur!"hours"(1)), + testTZ("Eastern Standard Time", "Eastern Standard Time", + "Eastern Daylight Time", dur!"hours"(-5), dur!"hours"(1)), + //testTZ("Pacific SA Standard Time", "Pacific SA Standard Time", + //"Pacific SA Daylight Time", dur!"hours"(-4), dur!"hours"(1), false), + testTZ("GMT Standard Time", "GMT Standard Time", + "GMT Daylight Time", dur!"hours"(0), dur!"hours"(1)), + testTZ("Romance Standard Time", "Romance Standard Time", + "Romance Daylight Time", dur!"hours"(1), dur!"hours"(1)), + testTZ("Cen. Australia Standard Time", "Cen. Australia Standard Time", + "Cen. Australia Daylight Time", + dur!"hours"(9) + dur!"minutes"(30), dur!"hours"(1), false)]; + + testTZ("Greenwich Standard Time", "Greenwich Standard Time", + "Greenwich Daylight Time", dur!"hours"(0), dur!"hours"(0)); + assertThrown!DateTimeException(WindowsTimeZone.getTimeZone("hello_world")); + } + else + assert(0, "OS not supported."); + + foreach (i; 0 .. tzs.length) + { + auto tz = tzs[i]; + immutable spring = dstSwitches[i][2]; + immutable fall = dstSwitches[i][3]; + auto stdOffset = SysTime(dstSwitches[i][0] + dur!"days"(-1), tz).utcOffset; + auto dstOffset = stdOffset + dur!"hours"(1); + + // Verify that creating a SysTime in the given time zone results + // in a SysTime with the correct std time during and surrounding + // a DST switch. + foreach (hour; -12 .. 13) + { + auto st = SysTime(dstSwitches[i][0] + dur!"hours"(hour), tz); + immutable targetHour = hour < 0 ? hour + 24 : hour; + + static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) + { + enforce(st.hour == hour, + new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), + __FILE__, line)); + } + + void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", + tag, st, tz.name, st.utcOffset, stdOffset, dstOffset), + __FILE__, line); + } + + enforce(st.dstInEffect == dstInEffect, msg("1")); + enforce(st.utcOffset == offset, msg("2")); + enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); + } + + if (hour == spring) + { + testHour(st, spring + 1, tz.name); + testHour(st + dur!"minutes"(1), spring + 1, tz.name); + } + else + { + testHour(st, targetHour, tz.name); + testHour(st + dur!"minutes"(1), targetHour, tz.name); + } + + if (hour < spring) + testOffset1(stdOffset, false); + else + testOffset1(dstOffset, true); + + st = SysTime(dstSwitches[i][1] + dur!"hours"(hour), tz); + testHour(st, targetHour, tz.name); + + // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). + if (hour == fall - 1) + testHour(st + dur!"hours"(1), targetHour, tz.name); + + if (hour < fall) + testOffset1(dstOffset, true); + else + testOffset1(stdOffset, false); + } + + // Verify that converting a time in UTC to a time in another + // time zone results in the correct time during and surrounding + // a DST switch. + bool first = true; + auto springSwitch = SysTime(dstSwitches[i][0] + dur!"hours"(spring), UTC()) - stdOffset; + auto fallSwitch = SysTime(dstSwitches[i][1] + dur!"hours"(fall), UTC()) - dstOffset; + // @@@BUG@@@ 3659 makes this necessary. + auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); + + foreach (hour; -24 .. 25) + { + auto utc = SysTime(dstSwitches[i][0] + dur!"hours"(hour), UTC()); + auto local = utc.toOtherTZ(tz); + + void testOffset2(Duration offset, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tz.name, utc, local), + __FILE__, line); + } + + enforce((utc + offset).hour == local.hour, msg("1")); + enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + } + + if (utc < springSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + + utc = SysTime(dstSwitches[i][1] + dur!"hours"(hour), UTC()); + local = utc.toOtherTZ(tz); + + if (utc == fallSwitch || utc == fallSwitchMinus1) + { + if (first) + { + testOffset2(dstOffset); + first = false; + } + else + testOffset2(stdOffset); + } + else if (utc > fallSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + } + } + } + + + // @@@DEPRECATED_2017-07@@@ + /++ + $(RED Deprecated. Use either PosixTimeZone.getInstalledTZNames or + WindowsTimeZone.getInstalledTZNames. ($(LREF parseTZConversions) + can be used to convert time zone names if necessary). Microsoft + changes their time zones too often for us to compile the + conversions into Phobos and have them be properly up-to-date. + TimeZone.getInstalledTZNames will be removed in July 2017.) + + Returns a list of the names of the time zones installed on the system. + + Providing a sub-name narrows down the list of time zones (which + can number in the thousands). For example, + passing in "America" as the sub-name returns only the time zones which + begin with "America". + + On Windows, this function will convert the Windows time zone names to + the corresponding TZ Database names with + $(D windowsTZNameToTZDatabaseName). To get the actual Windows time + zone names, use $(D WindowsTimeZone.getInstalledTZNames) directly. + + Params: + subName = The first part of the time zones desired. + + Throws: + $(D FileException) on Posix systems if it fails to read from disk. + $(LREF DateTimeException) on Windows systems if it fails to read the + registry. + +/ + deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") + static string[] getInstalledTZNames(string subName = "") @safe + { + version(Posix) + return PosixTimeZone.getInstalledTZNames(subName); + else version(Windows) + { + import std.algorithm.searching : startsWith; + import std.algorithm.sorting : sort; + import std.array : appender; + + auto windowsNames = WindowsTimeZone.getInstalledTZNames(); + auto retval = appender!(string[])(); + + foreach (winName; windowsNames) + { + auto tzName = windowsTZNameToTZDatabaseName(winName); + if (tzName !is null && tzName.startsWith(subName)) + retval.put(tzName); + } + + sort(retval.data); + return retval.data; + } + } + + deprecated @safe unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testPZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + TimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + // This was not previously tested, and it's currently failing, so I'm + // leaving it commented out until I can sort it out. + //assert(equal(tzNames, tzNames.uniq())); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testPZSuccess(tzName)); + } + + +protected: + + /++ + Params: + name = The name of the time zone. + stdName = The abbreviation for the time zone during std time. + dstName = The abbreviation for the time zone during DST. + +/ + this(string name, string stdName, string dstName) @safe immutable pure + { + _name = name; + _stdName = stdName; + _dstName = dstName; + } + + +private: + + immutable string _name; + immutable string _stdName; + immutable string _dstName; +} From c98a74391c3abb2a242f363916350534dfb729e9 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 10:41:10 +0200 Subject: [PATCH 092/262] Move LocalTime to std.datetime.timezone. --- std/datetime/package.d | 579 --------------------------------------- std/datetime/timezone.d | 585 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 583 insertions(+), 581 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index cf8afd5c06d..cc5622695d1 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,585 +18219,6 @@ private: // Section with time zones. //============================================================================== -/++ - A TimeZone which represents the current local time zone on - the system running your program. - - This uses the underlying C calls to adjust the time rather than using - specific D code based off of system settings to calculate the time such as - $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that it will - use whatever the current time zone is on the system, even if the system's - time zone changes while the program is running. - +/ -final class LocalTime : TimeZone -{ -public: - - /++ - $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its only - instance. - +/ - static immutable(LocalTime) opCall() @trusted pure nothrow - { - alias FuncType = @safe pure nothrow immutable(LocalTime) function(); - return (cast(FuncType)&singleton)(); - } - - - version(StdDdoc) - { - /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). - - Note that this always returns the empty string. This is because time - zones cannot be uniquely identified by the attributes given by the - OS (such as the $(D stdName) and $(D dstName)), and neither Posix - systems nor Windows systems provide an easy way to get the TZ - Database name of the local time zone. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - +/ - @property override string name() @safe const nothrow; - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Standard Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string stdName() @trusted const nothrow - { - version(Posix) - { - import core.stdc.time : tzname; - import std.conv : to; - try - return to!string(tzname[0]); - catch (Exception e) - assert(0, "to!string(tzname[0]) failed."); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 - //return to!string(tzInfo.StandardName); - - wchar[32] str; - - foreach (i, ref wchar c; str) - c = tzInfo.StandardName[i]; - - string retval; - - try - { - foreach (dchar c; str) - { - if (c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch (Exception e) - assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); - } - } - - @safe unittest - { - assert(LocalTime().stdName !is null); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().stdName == "PST"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().stdName == "EST"); - } - } - - - /++ - Typically, the abbreviation (generally 3 or 4 letters) for the time zone - when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. - - However, on Windows, it may be the unabbreviated name (e.g. Pacific - Daylight Time). Regardless, it is not the same as name. - - This property is overridden because the local time of the system could - change while the program is running and we need to determine it - dynamically rather than it being fixed like it would be with most time - zones. - +/ - @property override string dstName() @trusted const nothrow - { - version(Posix) - { - import core.stdc.time : tzname; - import std.conv : to; - try - return to!string(tzname[1]); - catch (Exception e) - assert(0, "to!string(tzname[1]) failed."); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - //Cannot use to!string() like this should, probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 - //return to!string(tzInfo.DaylightName); - - wchar[32] str; - - foreach (i, ref wchar c; str) - c = tzInfo.DaylightName[i]; - - string retval; - - try - { - foreach (dchar c; str) - { - if (c == '\0') - break; - - retval ~= c; - } - - return retval; - } - catch (Exception e) - assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); - } - } - - @safe unittest - { - assert(LocalTime().dstName !is null); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - version(FreeBSD) - { - // A bug on FreeBSD 9+ makes it so that this test fails. - // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 - } - else version(NetBSD) - { - // The same bug on NetBSD 7+ - } - else - { - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().dstName == "PDT"); - - setTZEnvVar("America/New_York"); - assert(LocalTime().dstName == "EDT"); - } - } - } - - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for $(D hasDST) because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @trusted const nothrow - { - version(Posix) - { - static if (is(typeof(daylight))) - return cast(bool)(daylight); - else - { - try - { - auto currYear = (cast(Date) Clock.currTime()).year; - auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable) this).stdTime - - SysTime(Date(currYear, 1, 4), UTC()).stdTime; - auto julyOffset = SysTime(Date(currYear, 7, 4), cast(immutable) this).stdTime - - SysTime(Date(currYear, 7, 4), UTC()).stdTime; - - return janOffset != julyOffset; - } - catch (Exception e) - assert(0, "Clock.currTime() threw."); - } - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return tzInfo.DaylightDate.wMonth != 0; - } - } - - @safe unittest - { - LocalTime().hasDST; - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().hasDST); - - setTZEnvVar("America/New_York"); - assert(LocalTime().hasDST); - - setTZEnvVar("UTC"); - assert(!LocalTime().hasDST); - } - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @trusted const nothrow - { - import core.stdc.time : localtime, tm; - time_t unixTime = stdTimeToUnixTime(stdTime); - - version(Posix) - { - tm* timeInfo = localtime(&unixTime); - - return cast(bool)(timeInfo.tm_isdst); - } - else version(Windows) - { - //Apparently Windows isn't smart enough to deal with negative time_t. - if (unixTime >= 0) - { - tm* timeInfo = localtime(&unixTime); - - if (timeInfo) - return cast(bool)(timeInfo.tm_isdst); - } - - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); - } - } - - @safe unittest - { - auto currTime = Clock.currStdTime; - LocalTime().dstInEffect(currTime); - } - - - /++ - Returns hnsecs in the local time zone using the standard C function - calls on Posix systems and the standard Windows system calls on Windows - systems to adjust the time to the appropriate time zone from std time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - $(D TimeZone.utcToTZ) - +/ - override long utcToTZ(long stdTime) @trusted const nothrow - { - version(Solaris) - { - return stdTime + convert!("seconds", "hnsecs")(tm_gmtoff(stdTime)); - } - else version(Posix) - { - import core.stdc.time : localtime, tm; - time_t unixTime = stdTimeToUnixTime(stdTime); - tm* timeInfo = localtime(&unixTime); - - return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); - } - } - - @safe unittest - { - LocalTime().utcToTZ(0); - } - - - /++ - Returns std time using the standard C function calls on Posix systems - and the standard Windows system calls on Windows systems to adjust the - time to UTC from the appropriate time zone. - - See_Also: - $(D TimeZone.tzToUTC) - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @trusted const nothrow - { - version(Posix) - { - import core.stdc.time : localtime, tm; - time_t unixTime = stdTimeToUnixTime(adjTime); - - immutable past = unixTime - cast(time_t) convert!("days", "seconds")(1); - tm* timeInfo = localtime(past < unixTime ? &past : &unixTime); - immutable pastOffset = timeInfo.tm_gmtoff; - - immutable future = unixTime + cast(time_t) convert!("days", "seconds")(1); - timeInfo = localtime(future > unixTime ? &future : &unixTime); - immutable futureOffset = timeInfo.tm_gmtoff; - - if (pastOffset == futureOffset) - return adjTime - convert!("seconds", "hnsecs")(pastOffset); - - if (pastOffset < futureOffset) - unixTime -= cast(time_t) convert!("hours", "seconds")(1); - - unixTime -= pastOffset; - timeInfo = localtime(&unixTime); - - return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); - } - else version(Windows) - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); - } - } - - @safe unittest - { - import std.format : format; - import std.typecons : tuple; - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), - tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - - foreach (i; 0 .. tzInfos.length) - { - auto tzName = tzInfos[i][0]; - setTZEnvVar(tzName); - immutable spring = tzInfos[i][3]; - immutable fall = tzInfos[i][4]; - auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - //Verify that creating a SysTime in the given time zone results - //in a SysTime with the correct std time during and surrounding - //a DST switch. - foreach (hour; -12 .. 13) - { - auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); - immutable targetHour = hour < 0 ? hour + 24 : hour; - - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) - { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); - } - - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tzName, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } - - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } - - if (hour == spring) - { - testHour(st, spring + 1, tzName); - testHour(st + dur!"minutes"(1), spring + 1, tzName); - } - else - { - testHour(st, targetHour, tzName); - testHour(st + dur!"minutes"(1), targetHour, tzName); - } - - if (hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); - - st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); - testHour(st, targetHour, tzName); - - //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if (hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tzName); - - if (hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } - - //Verify that converting a time in UTC to a time in another - //time zone results in the correct time during and surrounding - //a DST switch. - bool first = true; - auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; - //@@@BUG@@@ 3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - - foreach (hour; -24 .. 25) - { - auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); - auto local = utc.toLocalTime(); - - void testOffset2(Duration offset, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); - } - - if (utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - - utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); - local = utc.toLocalTime(); - - if (utc == fallSwitch || utc == fallSwitchMinus1) - { - if (first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); - } - else if (utc > fallSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); - } - } - } - } - - -private: - - this() @safe immutable pure - { - super("", "", ""); - } - - - // This is done so that we can maintain purity in spite of doing an impure - // operation the first time that LocalTime() is called. - static immutable(LocalTime) singleton() @trusted - { - import core.stdc.time : tzset; - import std.concurrency : initOnce; - static instance = new immutable(LocalTime)(); - static shared bool guard; - initOnce!guard({tzset(); return true;}()); - return instance; - } - - - // The Solaris version of struct tm has no tm_gmtoff field, so do it here - version(Solaris) - { - long tm_gmtoff(long stdTime) @trusted const nothrow - { - import core.stdc.time : localtime, gmtime, tm; - - time_t unixTime = stdTimeToUnixTime(stdTime); - tm* buf = localtime(&unixTime); - tm timeInfo = *buf; - buf = gmtime(&unixTime); - tm timeInfoGmt = *buf; - - return (timeInfo.tm_sec - timeInfoGmt.tm_sec) + - convert!("minutes", "seconds")(timeInfo.tm_min - timeInfoGmt.tm_min) + - convert!("hours", "seconds")(timeInfo.tm_hour - timeInfoGmt.tm_hour); - } - } -} - - /++ A $(LREF2 .TimeZone, TimeZone) which represents UTC. +/ diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 02f69bfa772..c67ae80a7f0 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -9,7 +9,7 @@ +/ module std.datetime.timezone; -import core.time : Duration, dur; +import core.time : convert, Duration, dur; import std.datetime.common; import std.exception : enforce; @@ -34,7 +34,8 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : clearTZEnvVar, Date, DateTime, PosixTimeZone, setTZEnvVar, SysTime, TimeOfDay, UTC; // temporary +import std.datetime : clearTZEnvVar, Clock, Date, DateTime, PosixTimeZone, setTZEnvVar, stdTimeToUnixTime, SysTime, + TimeOfDay, UTC; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time @@ -620,3 +621,583 @@ private: immutable string _stdName; immutable string _dstName; } + + +/++ + A TimeZone which represents the current local time zone on + the system running your program. + + This uses the underlying C calls to adjust the time rather than using + specific D code based off of system settings to calculate the time such as + $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that it will + use whatever the current time zone is on the system, even if the system's + time zone changes while the program is running. + +/ +final class LocalTime : TimeZone +{ +public: + + /++ + $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its only + instance. + +/ + static immutable(LocalTime) opCall() @trusted pure nothrow + { + alias FuncType = @safe pure nothrow immutable(LocalTime) function(); + return (cast(FuncType)&singleton)(); + } + + + version(StdDdoc) + { + /++ + The name of the time zone per the TZ Database. This is the name used to + get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). + + Note that this always returns the empty string. This is because time + zones cannot be uniquely identified by the attributes given by the + OS (such as the $(D stdName) and $(D dstName)), and neither Posix + systems nor Windows systems provide an easy way to get the TZ + Database name of the local time zone. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List + of Time Zones) + +/ + @property override string name() @safe const nothrow; + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST is $(I not) in effect (e.g. PST). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Standard Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it + dynamically rather than it being fixed like it would be with most time + zones. + +/ + @property override string stdName() @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : tzname; + import std.conv : to; + try + return to!string(tzname[0]); + catch (Exception e) + assert(0, "to!string(tzname[0]) failed."); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + // Cannot use to!string() like this should, probably due to bug + // http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.StandardName); + + wchar[32] str; + + foreach (i, ref wchar c; str) + c = tzInfo.StandardName[i]; + + string retval; + + try + { + foreach (dchar c; str) + { + if (c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch (Exception e) + assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); + } + } + + @safe unittest + { + assert(LocalTime().stdName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().stdName == "PST"); + + setTZEnvVar("America/New_York"); + assert(LocalTime().stdName == "EST"); + } + } + + + /++ + Typically, the abbreviation (generally 3 or 4 letters) for the time zone + when DST $(I is) in effect (e.g. PDT). It is not necessarily unique. + + However, on Windows, it may be the unabbreviated name (e.g. Pacific + Daylight Time). Regardless, it is not the same as name. + + This property is overridden because the local time of the system could + change while the program is running and we need to determine it + dynamically rather than it being fixed like it would be with most time + zones. + +/ + @property override string dstName() @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : tzname; + import std.conv : to; + try + return to!string(tzname[1]); + catch (Exception e) + assert(0, "to!string(tzname[1]) failed."); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + // Cannot use to!string() like this should, probably due to bug + // http://d.puremagic.com/issues/show_bug.cgi?id=5016 + //return to!string(tzInfo.DaylightName); + + wchar[32] str; + + foreach (i, ref wchar c; str) + c = tzInfo.DaylightName[i]; + + string retval; + + try + { + foreach (dchar c; str) + { + if (c == '\0') + break; + + retval ~= c; + } + + return retval; + } + catch (Exception e) + assert(0, "GetTimeZoneInformation() returned invalid UTF-16."); + } + } + + @safe unittest + { + assert(LocalTime().dstName !is null); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + version(FreeBSD) + { + // A bug on FreeBSD 9+ makes it so that this test fails. + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 + } + else version(NetBSD) + { + // The same bug on NetBSD 7+ + } + else + { + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().dstName == "PDT"); + + setTZEnvVar("America/New_York"); + assert(LocalTime().dstName == "EDT"); + } + } + } + + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for $(D hasDST) because the time zone + did at some point have DST. + +/ + @property override bool hasDST() @trusted const nothrow + { + version(Posix) + { + static if (is(typeof(daylight))) + return cast(bool)(daylight); + else + { + try + { + auto currYear = (cast(Date) Clock.currTime()).year; + auto janOffset = SysTime(Date(currYear, 1, 4), cast(immutable) this).stdTime - + SysTime(Date(currYear, 1, 4), UTC()).stdTime; + auto julyOffset = SysTime(Date(currYear, 7, 4), cast(immutable) this).stdTime - + SysTime(Date(currYear, 7, 4), UTC()).stdTime; + + return janOffset != julyOffset; + } + catch (Exception e) + assert(0, "Clock.currTime() threw."); + } + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return tzInfo.DaylightDate.wMonth != 0; + } + } + + @safe unittest + { + LocalTime().hasDST; + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().hasDST); + + setTZEnvVar("America/New_York"); + assert(LocalTime().hasDST); + + setTZEnvVar("UTC"); + assert(!LocalTime().hasDST); + } + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is in effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + override bool dstInEffect(long stdTime) @trusted const nothrow + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(stdTime); + + version(Posix) + { + tm* timeInfo = localtime(&unixTime); + + return cast(bool)(timeInfo.tm_isdst); + } + else version(Windows) + { + // Apparently Windows isn't smart enough to deal with negative time_t. + if (unixTime >= 0) + { + tm* timeInfo = localtime(&unixTime); + + if (timeInfo) + return cast(bool)(timeInfo.tm_isdst); + } + + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._dstInEffect(&tzInfo, stdTime); + } + } + + @safe unittest + { + auto currTime = Clock.currStdTime; + LocalTime().dstInEffect(currTime); + } + + + /++ + Returns hnsecs in the local time zone using the standard C function + calls on Posix systems and the standard Windows system calls on Windows + systems to adjust the time to the appropriate time zone from std time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + $(D TimeZone.utcToTZ) + +/ + override long utcToTZ(long stdTime) @trusted const nothrow + { + version(Solaris) + return stdTime + convert!("seconds", "hnsecs")(tm_gmtoff(stdTime)); + else version(Posix) + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(stdTime); + tm* timeInfo = localtime(&unixTime); + + return stdTime + convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._utcToTZ(&tzInfo, stdTime, hasDST); + } + } + + @safe unittest + { + LocalTime().utcToTZ(0); + } + + + /++ + Returns std time using the standard C function calls on Posix systems + and the standard Windows system calls on Windows systems to adjust the + time to UTC from the appropriate time zone. + + See_Also: + $(D TimeZone.tzToUTC) + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @trusted const nothrow + { + version(Posix) + { + import core.stdc.time : localtime, tm; + time_t unixTime = stdTimeToUnixTime(adjTime); + + immutable past = unixTime - cast(time_t) convert!("days", "seconds")(1); + tm* timeInfo = localtime(past < unixTime ? &past : &unixTime); + immutable pastOffset = timeInfo.tm_gmtoff; + + immutable future = unixTime + cast(time_t) convert!("days", "seconds")(1); + timeInfo = localtime(future > unixTime ? &future : &unixTime); + immutable futureOffset = timeInfo.tm_gmtoff; + + if (pastOffset == futureOffset) + return adjTime - convert!("seconds", "hnsecs")(pastOffset); + + if (pastOffset < futureOffset) + unixTime -= cast(time_t) convert!("hours", "seconds")(1); + + unixTime -= pastOffset; + timeInfo = localtime(&unixTime); + + return adjTime - convert!("seconds", "hnsecs")(timeInfo.tm_gmtoff); + } + else version(Windows) + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + return WindowsTimeZone._tzToUTC(&tzInfo, adjTime, hasDST); + } + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + import std.typecons : tuple; + + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); + + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), + tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), + tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), + tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), + tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; + + foreach (i; 0 .. tzInfos.length) + { + auto tzName = tzInfos[i][0]; + setTZEnvVar(tzName); + immutable spring = tzInfos[i][3]; + immutable fall = tzInfos[i][4]; + auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; + auto dstOffset = stdOffset + dur!"hours"(1); + + // Verify that creating a SysTime in the given time zone results + // in a SysTime with the correct std time during and surrounding + // a DST switch. + foreach (hour; -12 .. 13) + { + auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); + immutable targetHour = hour < 0 ? hour + 24 : hour; + + static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) + { + enforce(st.hour == hour, + new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), + __FILE__, line)); + } + + void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", + tag, st, tzName, st.utcOffset, stdOffset, dstOffset), + __FILE__, line); + } + + enforce(st.dstInEffect == dstInEffect, msg("1")); + enforce(st.utcOffset == offset, msg("2")); + enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); + } + + if (hour == spring) + { + testHour(st, spring + 1, tzName); + testHour(st + dur!"minutes"(1), spring + 1, tzName); + } + else + { + testHour(st, targetHour, tzName); + testHour(st + dur!"minutes"(1), targetHour, tzName); + } + + if (hour < spring) + testOffset1(stdOffset, false); + else + testOffset1(dstOffset, true); + + st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); + testHour(st, targetHour, tzName); + + // Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). + if (hour == fall - 1) + testHour(st + dur!"hours"(1), targetHour, tzName); + + if (hour < fall) + testOffset1(dstOffset, true); + else + testOffset1(stdOffset, false); + } + + // Verify that converting a time in UTC to a time in another + // time zone results in the correct time during and surrounding + // a DST switch. + bool first = true; + auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; + auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; + // @@@BUG@@@ 3659 makes this necessary. + auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); + + foreach (hour; -24 .. 25) + { + auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); + auto local = utc.toLocalTime(); + + void testOffset2(Duration offset, size_t line = __LINE__) + { + AssertError msg(string tag) + { + return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), + __FILE__, line); + } + + enforce((utc + offset).hour == local.hour, msg("1")); + enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + } + + if (utc < springSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + + utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); + local = utc.toLocalTime(); + + if (utc == fallSwitch || utc == fallSwitchMinus1) + { + if (first) + { + testOffset2(dstOffset); + first = false; + } + else + testOffset2(stdOffset); + } + else if (utc > fallSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + } + } + } + } + + +private: + + this() @safe immutable pure + { + super("", "", ""); + } + + + // This is done so that we can maintain purity in spite of doing an impure + // operation the first time that LocalTime() is called. + static immutable(LocalTime) singleton() @trusted + { + import core.stdc.time : tzset; + import std.concurrency : initOnce; + static instance = new immutable(LocalTime)(); + static shared bool guard; + initOnce!guard({tzset(); return true;}()); + return instance; + } + + + // The Solaris version of struct tm has no tm_gmtoff field, so do it here + version(Solaris) + { + long tm_gmtoff(long stdTime) @trusted const nothrow + { + import core.stdc.time : localtime, gmtime, tm; + + time_t unixTime = stdTimeToUnixTime(stdTime); + tm* buf = localtime(&unixTime); + tm timeInfo = *buf; + buf = gmtime(&unixTime); + tm timeInfoGmt = *buf; + + return timeInfo.tm_sec - timeInfoGmt.tm_sec + + convert!("minutes", "seconds")(timeInfo.tm_min - timeInfoGmt.tm_min) + + convert!("hours", "seconds")(timeInfo.tm_hour - timeInfoGmt.tm_hour); + } + } +} From b2a1bcfe96b074c47df02e57561580f8d7952340 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 10:44:20 +0200 Subject: [PATCH 093/262] Move UTC to std.datetime.timezone. --- std/datetime/package.d | 123 --------------------------------------- std/datetime/timezone.d | 125 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 124 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index cc5622695d1..d118c36839f 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,129 +18219,6 @@ private: // Section with time zones. //============================================================================== -/++ - A $(LREF2 .TimeZone, TimeZone) which represents UTC. - +/ -final class UTC : TimeZone -{ -public: - - /++ - $(D UTC) is a singleton class. $(D UTC) returns its only instance. - +/ - static immutable(UTC) opCall() @safe pure nothrow - { - return _utc; - } - - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - return false; - } - - - /++ - Returns the given hnsecs without changing them at all. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - - See_Also: - $(D TimeZone.utcToTZ) - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - return stdTime; - } - - @safe unittest - { - assert(UTC().utcToTZ(0) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().utcToTZ(std.stdTime) == std.stdTime); - assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns the given hnsecs without changing them at all. - - See_Also: - $(D TimeZone.tzToUTC) - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - return adjTime; - } - - @safe unittest - { - assert(UTC().tzToUTC(0) == 0); - - version(Posix) - { - scope(exit) clearTZEnvVar(); - - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - assert(UTC().tzToUTC(std.stdTime) == std.stdTime); - assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); - } - } - - - /++ - Returns a $(REF Duration, core,time) of 0. - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return dur!"hnsecs"(0); - } - - -private: - - this() @safe immutable pure - { - super("UTC", "UTC", "UTC"); - } - - - static immutable UTC _utc = new immutable(UTC)(); -} - - /++ Represents a time zone with an offset (in minutes, west is negative) from UTC but no DST. diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index c67ae80a7f0..6fae78701df 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -35,7 +35,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; import std.datetime : clearTZEnvVar, Clock, Date, DateTime, PosixTimeZone, setTZEnvVar, stdTimeToUnixTime, SysTime, - TimeOfDay, UTC; // temporary + TimeOfDay; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time @@ -1201,3 +1201,126 @@ private: } } } + + +/++ + A $(LREF2 .TimeZone, TimeZone) which represents UTC. + +/ +final class UTC : TimeZone +{ +public: + + /++ + $(D UTC) is a singleton class. $(D UTC) returns its only instance. + +/ + static immutable(UTC) opCall() @safe pure nothrow + { + return _utc; + } + + + /++ + Always returns false. + +/ + @property override bool hasDST() @safe const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + return false; + } + + + /++ + Returns the given hnsecs without changing them at all. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + + See_Also: + $(D TimeZone.utcToTZ) + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + return stdTime; + } + + @safe unittest + { + assert(UTC().utcToTZ(0) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().utcToTZ(std.stdTime) == std.stdTime); + assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); + } + } + + + /++ + Returns the given hnsecs without changing them at all. + + See_Also: + $(D TimeZone.tzToUTC) + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + return adjTime; + } + + @safe unittest + { + assert(UTC().tzToUTC(0) == 0); + + version(Posix) + { + scope(exit) clearTZEnvVar(); + + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().tzToUTC(std.stdTime) == std.stdTime); + assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); + } + } + + + /++ + Returns a $(REF Duration, core,time) of 0. + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + override Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return dur!"hnsecs"(0); + } + + +private: + + this() @safe immutable pure + { + super("UTC", "UTC", "UTC"); + } + + + static immutable UTC _utc = new immutable(UTC)(); +} From caa0233e205e379db7b124a29b8e5761cb38cb36 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 10:54:10 +0200 Subject: [PATCH 094/262] Move SimpleTimeZone to std.datetime.timezone. --- std/datetime/package.d | 545 --------------------------------------- std/datetime/timezone.d | 548 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 547 insertions(+), 546 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index d118c36839f..c997a536b67 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,551 +18219,6 @@ private: // Section with time zones. //============================================================================== -/++ - Represents a time zone with an offset (in minutes, west is negative) from - UTC but no DST. - - It's primarily used as the time zone in the result of $(LREF SysTime)'s - $(D fromISOString), $(D fromISOExtString), and $(D fromSimpleString). - - $(D name) and $(D dstName) are always the empty string since this time zone - has no DST, and while it may be meant to represent a time zone which is in - the TZ Database, obviously it's not likely to be following the exact rules - of any of the time zones in the TZ Database, so it makes no sense to set it. - +/ -final class SimpleTimeZone : TimeZone -{ -public: - - /++ - Always returns false. - +/ - @property override bool hasDST() @safe const nothrow - { - return false; - } - - - /++ - Always returns false. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - return false; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - return stdTime + _utcOffset.total!"hnsecs"; - } - - @safe unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.utcToTZ(0) == -288_000_000_000L); - assert(east.utcToTZ(0) == 288_000_000_000L); - assert(west.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L); - - const cstz = west; - assert(cstz.utcToTZ(50002) == west.utcToTZ(50002)); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - return adjTime - _utcOffset.total!"hnsecs"; - } - - @safe unittest - { - auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto east = new immutable SimpleTimeZone(dur!"hours"(8)); - - assert(west.tzToUTC(-288_000_000_000L) == 0); - assert(east.tzToUTC(288_000_000_000L) == 0); - assert(west.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L); - - const cstz = west; - assert(cstz.tzToUTC(20005) == west.tzToUTC(20005)); - } - - - /++ - Returns utcOffset as a $(REF Duration, core,time). - - Params: - stdTime = The UTC time for which to get the offset from UTC for this - time zone. - +/ - override Duration utcOffsetAt(long stdTime) @safe const nothrow - { - return _utcOffset; - } - - - /++ - Params: - utcOffset = This time zone's offset from UTC with west of UTC being - negative (it is added to UTC to get the adjusted time). - stdName = The $(D stdName) for this time zone. - +/ - this(Duration utcOffset, string stdName = "") @safe immutable pure - { - //FIXME This probably needs to be changed to something like (-12 - 13). - enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - - super("", stdName, ""); - this._utcOffset = utcOffset; - } - - @safe unittest - { - auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); - assert(stz.name == ""); - assert(stz.stdName == "PST"); - assert(stz.dstName == ""); - assert(stz.utcOffset == dur!"hours"(-8)); - } - - - /++ - The amount of time the offset from UTC is (negative is west of UTC, - positive is east). - +/ - @property Duration utcOffset() @safe const pure nothrow - { - return _utcOffset; - } - - -private: - - /+ - Returns a time zone as a string with an offset from UTC. - - Time zone offsets will be in the form +HHMM or -HHMM. - - Params: - utcOffset = The number of minutes offset from UTC (negative means - west). - +/ - static string toISOString(Duration utcOffset) @safe pure - { - import std.format : format; - immutable absOffset = abs(utcOffset); - enforce!DateTimeException(absOffset < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - int hours; - int minutes; - absOffset.split!("hours", "minutes")(hours, minutes); - return format(utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", hours, minutes); - } - - @safe unittest - { - static string testSTZInvalid(Duration offset) - { - return SimpleTimeZone.toISOString(offset); - } - - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); - - assert(toISOString(dur!"minutes"(0)) == "+0000"); - assert(toISOString(dur!"minutes"(1)) == "+0001"); - assert(toISOString(dur!"minutes"(10)) == "+0010"); - assert(toISOString(dur!"minutes"(59)) == "+0059"); - assert(toISOString(dur!"minutes"(60)) == "+0100"); - assert(toISOString(dur!"minutes"(90)) == "+0130"); - assert(toISOString(dur!"minutes"(120)) == "+0200"); - assert(toISOString(dur!"minutes"(480)) == "+0800"); - assert(toISOString(dur!"minutes"(1439)) == "+2359"); - - assert(toISOString(dur!"minutes"(-1)) == "-0001"); - assert(toISOString(dur!"minutes"(-10)) == "-0010"); - assert(toISOString(dur!"minutes"(-59)) == "-0059"); - assert(toISOString(dur!"minutes"(-60)) == "-0100"); - assert(toISOString(dur!"minutes"(-90)) == "-0130"); - assert(toISOString(dur!"minutes"(-120)) == "-0200"); - assert(toISOString(dur!"minutes"(-480)) == "-0800"); - assert(toISOString(dur!"minutes"(-1439)) == "-2359"); - } - - - /+ - Returns a time zone as a string with an offset from UTC. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - - Params: - utcOffset = The number of minutes offset from UTC (negative means - west). - +/ - static string toISOExtString(Duration utcOffset) @safe pure - { - import std.format : format; - - immutable absOffset = abs(utcOffset); - enforce!DateTimeException(absOffset < dur!"minutes"(1440), - "Offset from UTC must be within range (-24:00 - 24:00)."); - int hours; - int minutes; - absOffset.split!("hours", "minutes")(hours, minutes); - return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes); - } - - @safe unittest - { - static string testSTZInvalid(Duration offset) - { - return SimpleTimeZone.toISOExtString(offset); - } - - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); - assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); - - assert(toISOExtString(dur!"minutes"(0)) == "+00:00"); - assert(toISOExtString(dur!"minutes"(1)) == "+00:01"); - assert(toISOExtString(dur!"minutes"(10)) == "+00:10"); - assert(toISOExtString(dur!"minutes"(59)) == "+00:59"); - assert(toISOExtString(dur!"minutes"(60)) == "+01:00"); - assert(toISOExtString(dur!"minutes"(90)) == "+01:30"); - assert(toISOExtString(dur!"minutes"(120)) == "+02:00"); - assert(toISOExtString(dur!"minutes"(480)) == "+08:00"); - assert(toISOExtString(dur!"minutes"(1439)) == "+23:59"); - - assert(toISOExtString(dur!"minutes"(-1)) == "-00:01"); - assert(toISOExtString(dur!"minutes"(-10)) == "-00:10"); - assert(toISOExtString(dur!"minutes"(-59)) == "-00:59"); - assert(toISOExtString(dur!"minutes"(-60)) == "-01:00"); - assert(toISOExtString(dur!"minutes"(-90)) == "-01:30"); - assert(toISOExtString(dur!"minutes"(-120)) == "-02:00"); - assert(toISOExtString(dur!"minutes"(-480)) == "-08:00"); - assert(toISOExtString(dur!"minutes"(-1439)) == "-23:59"); - } - - - /+ - Takes a time zone as a string with an offset from UTC and returns a - $(LREF SimpleTimeZone) which matches. - - The accepted formats for time zone offsets are +HH, -HH, +HHMM, and - -HHMM. - - Params: - isoString = A string which represents a time zone in the ISO format. - +/ - static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : startsWith, countUntil, all; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - - auto dstr = to!dstring(isoString); - - enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); - - auto sign = dstr.startsWith('-') ? -1 : 1; - - dstr.popFront(); - enforce!DateTimeException(all!isDigit(dstr), format("Invalid ISO String: %s", dstr)); - - int hours; - int minutes; - - if (dstr.length == 2) - hours = to!int(dstr); - else if (dstr.length == 4) - { - hours = to!int(dstr[0 .. 2]); - minutes = to!int(dstr[2 .. 4]); - } - else - throw new DateTimeException(format("Invalid ISO String: %s", dstr)); - - enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); - - return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); - } - - @safe unittest - { - import std.format : format; - - foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", - "-24:00", "+24:00", "-24", "+24", "-2400", "+2400", - "1", "+1", "-1", "+9", "-9", - "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", - "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", - "000", "00000", "0160", "-0160", - " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", - " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", - " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", - " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", - "+ab:cd", "+abcd", "+0Z:00", "+Z", "+00Z", - "-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z", - "01:00", "12:00", "23:59"]) - { - assertThrown!DateTimeException(SimpleTimeZone.fromISOString(str), format("[%s]", str)); - } - - static void test(string str, Duration utcOffset, size_t line = __LINE__) - { - if (SimpleTimeZone.fromISOString(str).utcOffset != - (new immutable SimpleTimeZone(utcOffset)).utcOffset) - { - throw new AssertError("unittest failure", __FILE__, line); - } - } - - test("+0000", Duration.zero); - test("+0001", minutes(1)); - test("+0010", minutes(10)); - test("+0059", minutes(59)); - test("+0100", hours(1)); - test("+0130", hours(1) + minutes(30)); - test("+0200", hours(2)); - test("+0800", hours(8)); - test("+2359", hours(23) + minutes(59)); - - test("-0001", minutes(-1)); - test("-0010", minutes(-10)); - test("-0059", minutes(-59)); - test("-0100", hours(-1)); - test("-0130", hours(-1) - minutes(30)); - test("-0200", hours(-2)); - test("-0800", hours(-8)); - test("-2359", hours(-23) - minutes(59)); - - test("+00", Duration.zero); - test("+01", hours(1)); - test("+02", hours(2)); - test("+12", hours(12)); - test("+23", hours(23)); - - test("-00", Duration.zero); - test("-01", hours(-1)); - test("-02", hours(-2)); - test("-12", hours(-12)); - test("-23", hours(-23)); - } - - @safe unittest - { - import std.format : format; - - static void test(in string isoString, int expectedOffset, size_t line = __LINE__) - { - auto stz = SimpleTimeZone.fromISOExtString(isoString); - if (stz.utcOffset != dur!"minutes"(expectedOffset)) - throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); - - auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); - if (result != isoString) - throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoString), __FILE__, line); - } - - test("+00:00", 0); - test("+00:01", 1); - test("+00:10", 10); - test("+00:59", 59); - test("+01:00", 60); - test("+01:30", 90); - test("+02:00", 120); - test("+08:00", 480); - test("+08:00", 480); - test("+23:59", 1439); - - test("-00:01", -1); - test("-00:10", -10); - test("-00:59", -59); - test("-01:00", -60); - test("-01:30", -90); - test("-02:00", -120); - test("-08:00", -480); - test("-08:00", -480); - test("-23:59", -1439); - } - - - /+ - Takes a time zone as a string with an offset from UTC and returns a - $(LREF SimpleTimeZone) which matches. - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - isoExtString = A string which represents a time zone in the ISO format. - +/ - static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : startsWith, countUntil, all; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - - auto dstr = to!dstring(isoExtString); - - enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); - - auto sign = dstr.startsWith('-') ? -1 : 1; - - dstr.popFront(); - enforce!DateTimeException(!dstr.empty, "Invalid ISO String"); - - immutable colon = dstr.countUntil(':'); - - dstring hoursStr; - dstring minutesStr; - - if (colon != -1) - { - hoursStr = dstr[0 .. colon]; - minutesStr = dstr[colon + 1 .. $]; - enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", dstr)); - } - else - hoursStr = dstr; - - enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", dstr)); - enforce!DateTimeException(all!isDigit(hoursStr), format("Invalid ISO String: %s", dstr)); - enforce!DateTimeException(all!isDigit(minutesStr), format("Invalid ISO String: %s", dstr)); - - immutable hours = to!int(hoursStr); - immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); - enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); - - return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); - } - - @safe unittest - { - import std.format : format; - - foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", - "-24:00", "+24:00", "-24", "+24", "-2400", "-2400", - "1", "+1", "-1", "+9", "-9", - "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", - "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", - "000", "00000", "0160", "-0160", - " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", - " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", - " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", - " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", - "+ab:cd", "abcd", "+0Z:00", "+Z", "+00Z", - "-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z", - "0100", "1200", "2359"]) - { - assertThrown!DateTimeException(SimpleTimeZone.fromISOExtString(str), format("[%s]", str)); - } - - static void test(string str, Duration utcOffset, size_t line = __LINE__) - { - if (SimpleTimeZone.fromISOExtString(str).utcOffset != - (new immutable SimpleTimeZone(utcOffset)).utcOffset) - { - throw new AssertError("unittest failure", __FILE__, line); - } - } - - test("+00:00", Duration.zero); - test("+00:01", minutes(1)); - test("+00:10", minutes(10)); - test("+00:59", minutes(59)); - test("+01:00", hours(1)); - test("+01:30", hours(1) + minutes(30)); - test("+02:00", hours(2)); - test("+08:00", hours(8)); - test("+23:59", hours(23) + minutes(59)); - - test("-00:01", minutes(-1)); - test("-00:10", minutes(-10)); - test("-00:59", minutes(-59)); - test("-01:00", hours(-1)); - test("-01:30", hours(-1) - minutes(30)); - test("-02:00", hours(-2)); - test("-08:00", hours(-8)); - test("-23:59", hours(-23) - minutes(59)); - - test("+00", Duration.zero); - test("+01", hours(1)); - test("+02", hours(2)); - test("+12", hours(12)); - test("+23", hours(23)); - - test("-00", Duration.zero); - test("-01", hours(-1)); - test("-02", hours(-2)); - test("-12", hours(-12)); - test("-23", hours(-23)); - } - - @safe unittest - { - import std.format : format; - - static void test(in string isoExtString, int expectedOffset, size_t line = __LINE__) - { - auto stz = SimpleTimeZone.fromISOExtString(isoExtString); - if (stz.utcOffset != dur!"minutes"(expectedOffset)) - throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); - - auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); - if (result != isoExtString) - throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoExtString), __FILE__, line); - } - - test("+00:00", 0); - test("+00:01", 1); - test("+00:10", 10); - test("+00:59", 59); - test("+01:00", 60); - test("+01:30", 90); - test("+02:00", 120); - test("+08:00", 480); - test("+08:00", 480); - test("+23:59", 1439); - - test("-00:01", -1); - test("-00:10", -10); - test("-00:59", -59); - test("-01:00", -60); - test("-01:30", -90); - test("-02:00", -120); - test("-08:00", -480); - test("-08:00", -480); - test("-23:59", -1439); - } - - - immutable Duration _utcOffset; -} - - /++ Represents a time zone from a TZ Database time zone file. Files from the TZ Database are how Posix systems hold their time zone information. diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 6fae78701df..9898aa0e765 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -9,9 +9,10 @@ +/ module std.datetime.timezone; -import core.time : convert, Duration, dur; +import core.time; import std.datetime.common; import std.exception : enforce; +import std.traits : isSomeString; version(Windows) { @@ -1324,3 +1325,548 @@ private: static immutable UTC _utc = new immutable(UTC)(); } + + +/++ + Represents a time zone with an offset (in minutes, west is negative) from + UTC but no DST. + + It's primarily used as the time zone in the result of $(LREF SysTime)'s + $(D fromISOString), $(D fromISOExtString), and $(D fromSimpleString). + + $(D name) and $(D dstName) are always the empty string since this time zone + has no DST, and while it may be meant to represent a time zone which is in + the TZ Database, obviously it's not likely to be following the exact rules + of any of the time zones in the TZ Database, so it makes no sense to set it. + +/ +final class SimpleTimeZone : TimeZone +{ +public: + + /++ + Always returns false. + +/ + @property override bool hasDST() @safe const nothrow + { + return false; + } + + + /++ + Always returns false. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + return false; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + return stdTime + _utcOffset.total!"hnsecs"; + } + + @safe unittest + { + auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto east = new immutable SimpleTimeZone(dur!"hours"(8)); + + assert(west.utcToTZ(0) == -288_000_000_000L); + assert(east.utcToTZ(0) == 288_000_000_000L); + assert(west.utcToTZ(54_321_234_567_890L) == 54_033_234_567_890L); + + const cstz = west; + assert(cstz.utcToTZ(50002) == west.utcToTZ(50002)); + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + return adjTime - _utcOffset.total!"hnsecs"; + } + + @safe unittest + { + auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto east = new immutable SimpleTimeZone(dur!"hours"(8)); + + assert(west.tzToUTC(-288_000_000_000L) == 0); + assert(east.tzToUTC(288_000_000_000L) == 0); + assert(west.tzToUTC(54_033_234_567_890L) == 54_321_234_567_890L); + + const cstz = west; + assert(cstz.tzToUTC(20005) == west.tzToUTC(20005)); + } + + + /++ + Returns utcOffset as a $(REF Duration, core,time). + + Params: + stdTime = The UTC time for which to get the offset from UTC for this + time zone. + +/ + override Duration utcOffsetAt(long stdTime) @safe const nothrow + { + return _utcOffset; + } + + + /++ + Params: + utcOffset = This time zone's offset from UTC with west of UTC being + negative (it is added to UTC to get the adjusted time). + stdName = The $(D stdName) for this time zone. + +/ + this(Duration utcOffset, string stdName = "") @safe immutable pure + { + // FIXME This probably needs to be changed to something like (-12 - 13). + enforce!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + super("", stdName, ""); + this._utcOffset = utcOffset; + } + + @safe unittest + { + auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); + assert(stz.name == ""); + assert(stz.stdName == "PST"); + assert(stz.dstName == ""); + assert(stz.utcOffset == dur!"hours"(-8)); + } + + + /++ + The amount of time the offset from UTC is (negative is west of UTC, + positive is east). + +/ + @property Duration utcOffset() @safe const pure nothrow + { + return _utcOffset; + } + + +private: +package: // temporary + + /+ + Returns a time zone as a string with an offset from UTC. + + Time zone offsets will be in the form +HHMM or -HHMM. + + Params: + utcOffset = The number of minutes offset from UTC (negative means + west). + +/ + static string toISOString(Duration utcOffset) @safe pure + { + import std.format : format; + immutable absOffset = abs(utcOffset); + enforce!DateTimeException(absOffset < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + int hours; + int minutes; + absOffset.split!("hours", "minutes")(hours, minutes); + return format(utcOffset < Duration.zero ? "-%02d%02d" : "+%02d%02d", hours, minutes); + } + + @safe unittest + { + static string testSTZInvalid(Duration offset) + { + return SimpleTimeZone.toISOString(offset); + } + + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); + + assert(toISOString(dur!"minutes"(0)) == "+0000"); + assert(toISOString(dur!"minutes"(1)) == "+0001"); + assert(toISOString(dur!"minutes"(10)) == "+0010"); + assert(toISOString(dur!"minutes"(59)) == "+0059"); + assert(toISOString(dur!"minutes"(60)) == "+0100"); + assert(toISOString(dur!"minutes"(90)) == "+0130"); + assert(toISOString(dur!"minutes"(120)) == "+0200"); + assert(toISOString(dur!"minutes"(480)) == "+0800"); + assert(toISOString(dur!"minutes"(1439)) == "+2359"); + + assert(toISOString(dur!"minutes"(-1)) == "-0001"); + assert(toISOString(dur!"minutes"(-10)) == "-0010"); + assert(toISOString(dur!"minutes"(-59)) == "-0059"); + assert(toISOString(dur!"minutes"(-60)) == "-0100"); + assert(toISOString(dur!"minutes"(-90)) == "-0130"); + assert(toISOString(dur!"minutes"(-120)) == "-0200"); + assert(toISOString(dur!"minutes"(-480)) == "-0800"); + assert(toISOString(dur!"minutes"(-1439)) == "-2359"); + } + + + /+ + Returns a time zone as a string with an offset from UTC. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + + Params: + utcOffset = The number of minutes offset from UTC (negative means + west). + +/ + static string toISOExtString(Duration utcOffset) @safe pure + { + import std.format : format; + + immutable absOffset = abs(utcOffset); + enforce!DateTimeException(absOffset < dur!"minutes"(1440), + "Offset from UTC must be within range (-24:00 - 24:00)."); + int hours; + int minutes; + absOffset.split!("hours", "minutes")(hours, minutes); + return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes); + } + + @safe unittest + { + static string testSTZInvalid(Duration offset) + { + return SimpleTimeZone.toISOExtString(offset); + } + + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(1440))); + assertThrown!DateTimeException(testSTZInvalid(dur!"minutes"(-1440))); + + assert(toISOExtString(dur!"minutes"(0)) == "+00:00"); + assert(toISOExtString(dur!"minutes"(1)) == "+00:01"); + assert(toISOExtString(dur!"minutes"(10)) == "+00:10"); + assert(toISOExtString(dur!"minutes"(59)) == "+00:59"); + assert(toISOExtString(dur!"minutes"(60)) == "+01:00"); + assert(toISOExtString(dur!"minutes"(90)) == "+01:30"); + assert(toISOExtString(dur!"minutes"(120)) == "+02:00"); + assert(toISOExtString(dur!"minutes"(480)) == "+08:00"); + assert(toISOExtString(dur!"minutes"(1439)) == "+23:59"); + + assert(toISOExtString(dur!"minutes"(-1)) == "-00:01"); + assert(toISOExtString(dur!"minutes"(-10)) == "-00:10"); + assert(toISOExtString(dur!"minutes"(-59)) == "-00:59"); + assert(toISOExtString(dur!"minutes"(-60)) == "-01:00"); + assert(toISOExtString(dur!"minutes"(-90)) == "-01:30"); + assert(toISOExtString(dur!"minutes"(-120)) == "-02:00"); + assert(toISOExtString(dur!"minutes"(-480)) == "-08:00"); + assert(toISOExtString(dur!"minutes"(-1439)) == "-23:59"); + } + + + /+ + Takes a time zone as a string with an offset from UTC and returns a + $(LREF SimpleTimeZone) which matches. + + The accepted formats for time zone offsets are +HH, -HH, +HHMM, and + -HHMM. + + Params: + isoString = A string which represents a time zone in the ISO format. + +/ + static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, countUntil, all; + import std.ascii : isDigit; + import std.conv : to; + import std.format : format; + import std.range.primitives; + + auto dstr = to!dstring(isoString); + + enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); + + auto sign = dstr.startsWith('-') ? -1 : 1; + + dstr.popFront(); + enforce!DateTimeException(all!isDigit(dstr), format("Invalid ISO String: %s", dstr)); + + int hours; + int minutes; + + if (dstr.length == 2) + hours = to!int(dstr); + else if (dstr.length == 4) + { + hours = to!int(dstr[0 .. 2]); + minutes = to!int(dstr[2 .. 4]); + } + else + throw new DateTimeException(format("Invalid ISO String: %s", dstr)); + + enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); + + return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", + "-24:00", "+24:00", "-24", "+24", "-2400", "+2400", + "1", "+1", "-1", "+9", "-9", + "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", + "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", + "000", "00000", "0160", "-0160", + " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", + " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", + " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", + " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", + "+ab:cd", "+abcd", "+0Z:00", "+Z", "+00Z", + "-ab:cd", "+abcd", "-0Z:00", "-Z", "-00Z", + "01:00", "12:00", "23:59"]) + { + assertThrown!DateTimeException(SimpleTimeZone.fromISOString(str), format("[%s]", str)); + } + + static void test(string str, Duration utcOffset, size_t line = __LINE__) + { + if (SimpleTimeZone.fromISOString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("+0000", Duration.zero); + test("+0001", minutes(1)); + test("+0010", minutes(10)); + test("+0059", minutes(59)); + test("+0100", hours(1)); + test("+0130", hours(1) + minutes(30)); + test("+0200", hours(2)); + test("+0800", hours(8)); + test("+2359", hours(23) + minutes(59)); + + test("-0001", minutes(-1)); + test("-0010", minutes(-10)); + test("-0059", minutes(-59)); + test("-0100", hours(-1)); + test("-0130", hours(-1) - minutes(30)); + test("-0200", hours(-2)); + test("-0800", hours(-8)); + test("-2359", hours(-23) - minutes(59)); + + test("+00", Duration.zero); + test("+01", hours(1)); + test("+02", hours(2)); + test("+12", hours(12)); + test("+23", hours(23)); + + test("-00", Duration.zero); + test("-01", hours(-1)); + test("-02", hours(-2)); + test("-12", hours(-12)); + test("-23", hours(-23)); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + static void test(in string isoString, int expectedOffset, size_t line = __LINE__) + { + auto stz = SimpleTimeZone.fromISOExtString(isoString); + if (stz.utcOffset != dur!"minutes"(expectedOffset)) + throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); + + auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); + if (result != isoString) + throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoString), __FILE__, line); + } + + test("+00:00", 0); + test("+00:01", 1); + test("+00:10", 10); + test("+00:59", 59); + test("+01:00", 60); + test("+01:30", 90); + test("+02:00", 120); + test("+08:00", 480); + test("+08:00", 480); + test("+23:59", 1439); + + test("-00:01", -1); + test("-00:10", -10); + test("-00:59", -59); + test("-01:00", -60); + test("-01:30", -90); + test("-02:00", -120); + test("-08:00", -480); + test("-08:00", -480); + test("-23:59", -1439); + } + + + /+ + Takes a time zone as a string with an offset from UTC and returns a + $(LREF SimpleTimeZone) which matches. + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + isoExtString = A string which represents a time zone in the ISO format. + +/ + static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, countUntil, all; + import std.ascii : isDigit; + import std.conv : to; + import std.format : format; + import std.range.primitives; + + auto dstr = to!dstring(isoExtString); + + enforce!DateTimeException(dstr.startsWith('-', '+'), "Invalid ISO String"); + + auto sign = dstr.startsWith('-') ? -1 : 1; + + dstr.popFront(); + enforce!DateTimeException(!dstr.empty, "Invalid ISO String"); + + immutable colon = dstr.countUntil(':'); + + dstring hoursStr; + dstring minutesStr; + + if (colon != -1) + { + hoursStr = dstr[0 .. colon]; + minutesStr = dstr[colon + 1 .. $]; + enforce!DateTimeException(minutesStr.length == 2, format("Invalid ISO String: %s", dstr)); + } + else + hoursStr = dstr; + + enforce!DateTimeException(hoursStr.length == 2, format("Invalid ISO String: %s", dstr)); + enforce!DateTimeException(all!isDigit(hoursStr), format("Invalid ISO String: %s", dstr)); + enforce!DateTimeException(all!isDigit(minutesStr), format("Invalid ISO String: %s", dstr)); + + immutable hours = to!int(hoursStr); + immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); + enforce!DateTimeException(hours < 24 && minutes < 60, format("Invalid ISO String: %s", dstr)); + + return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + foreach (str; ["", "Z", "-", "+", "-:", "+:", "-1:", "+1:", "+1", "-1", + "-24:00", "+24:00", "-24", "+24", "-2400", "-2400", + "1", "+1", "-1", "+9", "-9", + "+1:0", "+01:0", "+1:00", "+01:000", "+01:60", + "-1:0", "-01:0", "-1:00", "-01:000", "-01:60", + "000", "00000", "0160", "-0160", + " +08:00", "+ 08:00", "+08 :00", "+08: 00", "+08:00 ", + " -08:00", "- 08:00", "-08 :00", "-08: 00", "-08:00 ", + " +0800", "+ 0800", "+08 00", "+08 00", "+0800 ", + " -0800", "- 0800", "-08 00", "-08 00", "-0800 ", + "+ab:cd", "abcd", "+0Z:00", "+Z", "+00Z", + "-ab:cd", "abcd", "-0Z:00", "-Z", "-00Z", + "0100", "1200", "2359"]) + { + assertThrown!DateTimeException(SimpleTimeZone.fromISOExtString(str), format("[%s]", str)); + } + + static void test(string str, Duration utcOffset, size_t line = __LINE__) + { + if (SimpleTimeZone.fromISOExtString(str).utcOffset != (new immutable SimpleTimeZone(utcOffset)).utcOffset) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("+00:00", Duration.zero); + test("+00:01", minutes(1)); + test("+00:10", minutes(10)); + test("+00:59", minutes(59)); + test("+01:00", hours(1)); + test("+01:30", hours(1) + minutes(30)); + test("+02:00", hours(2)); + test("+08:00", hours(8)); + test("+23:59", hours(23) + minutes(59)); + + test("-00:01", minutes(-1)); + test("-00:10", minutes(-10)); + test("-00:59", minutes(-59)); + test("-01:00", hours(-1)); + test("-01:30", hours(-1) - minutes(30)); + test("-02:00", hours(-2)); + test("-08:00", hours(-8)); + test("-23:59", hours(-23) - minutes(59)); + + test("+00", Duration.zero); + test("+01", hours(1)); + test("+02", hours(2)); + test("+12", hours(12)); + test("+23", hours(23)); + + test("-00", Duration.zero); + test("-01", hours(-1)); + test("-02", hours(-2)); + test("-12", hours(-12)); + test("-23", hours(-23)); + } + + @safe unittest + { + import core.exception : AssertError; + import std.format : format; + + static void test(in string isoExtString, int expectedOffset, size_t line = __LINE__) + { + auto stz = SimpleTimeZone.fromISOExtString(isoExtString); + if (stz.utcOffset != dur!"minutes"(expectedOffset)) + throw new AssertError(format("unittest failure: wrong offset [%s]", stz.utcOffset), __FILE__, line); + + auto result = SimpleTimeZone.toISOExtString(stz.utcOffset); + if (result != isoExtString) + throw new AssertError(format("unittest failure: [%s] != [%s]", result, isoExtString), __FILE__, line); + } + + test("+00:00", 0); + test("+00:01", 1); + test("+00:10", 10); + test("+00:59", 59); + test("+01:00", 60); + test("+01:30", 90); + test("+02:00", 120); + test("+08:00", 480); + test("+08:00", 480); + test("+23:59", 1439); + + test("-00:01", -1); + test("-00:10", -10); + test("-00:59", -59); + test("-01:00", -60); + test("-01:30", -90); + test("-02:00", -120); + test("-08:00", -480); + test("-08:00", -480); + test("-23:59", -1439); + } + + + immutable Duration _utcOffset; +} From 13716774f4dee87a7b730336fa857d8487855417 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 11:23:00 +0200 Subject: [PATCH 095/262] Move PosixTimeZone to std.datetime.timezone. --- std/datetime/package.d | 936 --------------------------------------- std/datetime/timezone.d | 944 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 940 insertions(+), 940 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index c997a536b67..f35d8d58af5 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,942 +18219,6 @@ private: // Section with time zones. //============================================================================== -/++ - Represents a time zone from a TZ Database time zone file. Files from the TZ - Database are how Posix systems hold their time zone information. - Unfortunately, Windows does not use the TZ Database. To use the TZ Database, - use $(D PosixTimeZone) (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - $(D PosixTimeZone.getTimeZone) where the directory holding them is. - - To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone) - (which allows specifying the location the time zone files) or call - $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix - systems and a $(LREF WindowsTimeZone) on Windows systems). - - Note: - Unless your system's local time zone deals with leap seconds (which is - highly unlikely), then the only way to get a time zone which - takes leap seconds into account is to use $(D PosixTimeZone) with a - time zone whose name starts with "right/". Those time zone files do - include leap seconds, and $(D PosixTimeZone) will take them into account - (though posix systems which use a "right/" time zone as their local time - zone will $(I not) take leap seconds into account even though they're - in the file). - - See_Also: - $(HTTP www.iana.org/time-zones, Home of the TZ Database files)
- $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time - Zones) - +/ -final class PosixTimeZone : TimeZone -{ - import std.algorithm.searching : countUntil, canFind, startsWith; - import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; - import std.path : extension; - import std.stdio : File; - import std.string : strip, representation; - import std.traits : isArray, isSomeChar; -public: - - /++ - Whether this time zone has Daylight Savings Time at any point in time. - Note that for some time zone types it may not have DST for current - dates but will still return true for $(D hasDST) because the time zone - did at some point have DST. - +/ - @property override bool hasDST() @safe const nothrow - { - return _hasDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and returns whether DST is in effect in this - time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this time - zone. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if (found == -1) - return _transitions.back.ttInfo.isDST; - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return transition.ttInfo.isDST; - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in UTC time (i.e. std time) and converts it to this time zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time zone's - time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable leapSecs = calculateLeapSeconds(stdTime); - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - - if (found == -1) - return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - - return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. - in this time zone's time and converts it to UTC (i.e. std time). - - Params: - adjTime = The time in this time zone that needs to be adjusted to - UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow - { - assert(!_transitions.empty); - - immutable leapSecs = calculateLeapSeconds(adjTime); - time_t unixTime = stdTimeToUnixTime(adjTime); - immutable past = unixTime - convert!("days", "seconds")(1); - immutable future = unixTime + convert!("days", "seconds")(1); - - immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); - - if (pastFound == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); - immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; - - if (futureFound == 0) - return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); - - immutable futureTrans = futureFound == -1 ? _transitions.back - : _transitions[pastFound + futureFound - 1]; - immutable pastOffset = pastTrans.ttInfo.utcOffset; - - if (pastOffset < futureTrans.ttInfo.utcOffset) - unixTime -= convert!("hours", "seconds")(1); - - immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); - - if (found == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - - immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; - - return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - - - version(Android) - { - // Android concatenates all time zone data into a single file and stores it here. - enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/"; - } - else version(Posix) - { - /++ - The default directory where the TZ Database files are. It's empty - for Windows, since Windows doesn't have them. - +/ - enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; - } - else version(Windows) - { - /++ The default directory where the TZ Database files are. It's empty - for Windows, since Windows doesn't have them. - +/ - enum defaultTZDatabaseDir = ""; - } - - - /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. The time - zone information is fetched from the TZ Database time zone files in the - given directory. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of - Time Zones) - - Params: - name = The TZ Database name of the desired time zone - tzDatabaseDir = The directory where the TZ Database files are - located. Because these files are not located on - Windows systems, provide them - and give their location here to - use $(LREF PosixTimeZone)s. - - Throws: - $(LREF DateTimeException) if the given time zone could not be found or - $(D FileException) if the TZ Database file could not be opened. - +/ - //TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed - // directory. - static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted - { - import std.algorithm.sorting : sort; - import std.range : retro; - import std.format : format; - import std.path : asNormalizedPath, chainPath; - import std.conv : to; - - name = strip(name); - - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - version(Android) - { - auto tzfileOffset = name in tzdataIndex(tzDatabaseDir); - enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name))); - string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata"; - const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string; - } - else - const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string; - - enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file))); - enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); - - auto tzFile = File(file); - version(Android) tzFile.seek(*tzfileOffset); - immutable gmtZone = name.representation().canFind("GMT"); - - try - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion = readVal!char(tzFile); - _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2' || tzFileVersion == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach (val; zeroBlock) - { - if (val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - //The number of UTC/local indicators stored in the file. - auto tzh_ttisgmtcnt = readVal!int(tzFile); - - //The number of standard/wall indicators stored in the file. - auto tzh_ttisstdcnt = readVal!int(tzFile); - - //The number of leap seconds for which data is stored in the file. - auto tzh_leapcnt = readVal!int(tzFile); - - //The number of "transition times" for which data is stored in the file. - auto tzh_timecnt = readVal!int(tzFile); - - //The number of "local time types" for which data is stored in the file (must not be zero). - auto tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - //The number of characters of "timezone abbreviation strings" stored in the file. - auto tzh_charcnt = readVal!int(tzFile); - - //time_ts where DST transitions occur. - auto transitionTimeTs = new long[](tzh_timecnt); - foreach (ref transition; transitionTimeTs) - transition = readVal!int(tzFile); - - //Indices into ttinfo structs indicating the changes - //to be made at the corresponding DST transition. - auto ttInfoIndices = new ubyte[](tzh_timecnt); - foreach (ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - //ttinfos which give info on DST transitions. - auto tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach (ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - //The array of time zone abbreviation characters. - auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - auto leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach (ref leapSecond; leapSeconds) - { - //The time_t when the leap second occurs. - auto timeT = readVal!int(tzFile); - - //The total number of leap seconds to be applied after - //the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - //Indicate whether each corresponding DST transition were specified - //in standard time or wall clock time. - auto transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach (ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - //Indicate whether each corresponding DST transition associated with - //local time types are specified in UTC or local time. - auto transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach (ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - - _enforceValidTZFile(!tzFile.eof); - - //If version 2 or 3, the information is duplicated in 64-bit. - if (tzFileVersion == '2' || tzFileVersion == '3') - { - _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); - - immutable char tzFileVersion2 = readVal!(char)(tzFile); - _enforceValidTZFile(tzFileVersion2 == '2' || tzFileVersion2 == '3'); - - { - auto zeroBlock = readVal!(ubyte[])(tzFile, 15); - bool allZeroes = true; - - foreach (val; zeroBlock) - { - if (val != 0) - { - allZeroes = false; - break; - } - } - - _enforceValidTZFile(allZeroes); - } - - - //The number of UTC/local indicators stored in the file. - tzh_ttisgmtcnt = readVal!int(tzFile); - - //The number of standard/wall indicators stored in the file. - tzh_ttisstdcnt = readVal!int(tzFile); - - //The number of leap seconds for which data is stored in the file. - tzh_leapcnt = readVal!int(tzFile); - - //The number of "transition times" for which data is stored in the file. - tzh_timecnt = readVal!int(tzFile); - - //The number of "local time types" for which data is stored in the file (must not be zero). - tzh_typecnt = readVal!int(tzFile); - _enforceValidTZFile(tzh_typecnt != 0); - - //The number of characters of "timezone abbreviation strings" stored in the file. - tzh_charcnt = readVal!int(tzFile); - - //time_ts where DST transitions occur. - transitionTimeTs = new long[](tzh_timecnt); - foreach (ref transition; transitionTimeTs) - transition = readVal!long(tzFile); - - //Indices into ttinfo structs indicating the changes - //to be made at the corresponding DST transition. - ttInfoIndices = new ubyte[](tzh_timecnt); - foreach (ref ttInfoIndex; ttInfoIndices) - ttInfoIndex = readVal!ubyte(tzFile); - - //ttinfos which give info on DST transitions. - tempTTInfos = new TempTTInfo[](tzh_typecnt); - foreach (ref ttInfo; tempTTInfos) - ttInfo = readVal!TempTTInfo(tzFile); - - //The array of time zone abbreviation characters. - tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); - - leapSeconds = new LeapSecond[](tzh_leapcnt); - foreach (ref leapSecond; leapSeconds) - { - //The time_t when the leap second occurs. - auto timeT = readVal!long(tzFile); - - //The total number of leap seconds to be applied after - //the corresponding leap second. - auto total = readVal!int(tzFile); - - leapSecond = LeapSecond(timeT, total); - } - - //Indicate whether each corresponding DST transition were specified - //in standard time or wall clock time. - transitionIsStd = new bool[](tzh_ttisstdcnt); - foreach (ref isStd; transitionIsStd) - isStd = readVal!bool(tzFile); - - //Indicate whether each corresponding DST transition associated with - //local time types are specified in UTC or local time. - transitionInUTC = new bool[](tzh_ttisgmtcnt); - foreach (ref inUTC; transitionInUTC) - inUTC = readVal!bool(tzFile); - } - - _enforceValidTZFile(tzFile.readln().strip().empty); - - cast(void) tzFile.readln(); - - version(Android) - { - // Android uses a single file for all timezone data, so the file - // doesn't end here. - } - else - { - _enforceValidTZFile(tzFile.readln().strip().empty); - _enforceValidTZFile(tzFile.eof); - } - - - auto transitionTypes = new TransitionType*[](tempTTInfos.length); - - foreach (i, ref ttype; transitionTypes) - { - bool isStd = false; - - if (i < transitionIsStd.length && !transitionIsStd.empty) - isStd = transitionIsStd[i]; - - bool inUTC = false; - - if (i < transitionInUTC.length && !transitionInUTC.empty) - inUTC = transitionInUTC[i]; - - ttype = new TransitionType(isStd, inUTC); - } - - auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); - foreach (i, ref ttInfo; ttInfos) - { - auto tempTTInfo = tempTTInfos[i]; - - if (gmtZone) - tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; - - auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; - string abbrev = abbrevChars[0 .. abbrevChars.countUntil('\0')].idup; - - ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); - } - - auto tempTransitions = new TempTransition[](transitionTimeTs.length); - foreach (i, ref tempTransition; tempTransitions) - { - immutable ttiIndex = ttInfoIndices[i]; - auto transitionTimeT = transitionTimeTs[i]; - auto ttype = transitionTypes[ttiIndex]; - auto ttInfo = ttInfos[ttiIndex]; - - tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); - } - - if (tempTransitions.empty) - { - _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); - tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); - } - - sort!"a.timeT < b.timeT"(tempTransitions); - sort!"a.timeT < b.timeT"(leapSeconds); - - auto transitions = new Transition[](tempTransitions.length); - foreach (i, ref transition; transitions) - { - auto tempTransition = tempTransitions[i]; - auto transitionTimeT = tempTransition.timeT; - auto ttInfo = tempTransition.ttInfo; - - _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); - - transition = Transition(transitionTimeT, ttInfo); - } - - string stdName; - string dstName; - bool hasDST = false; - - foreach (transition; retro(transitions)) - { - auto ttInfo = transition.ttInfo; - - if (ttInfo.isDST) - { - if (dstName.empty) - dstName = ttInfo.abbrev; - - hasDST = true; - } - else - { - if (stdName.empty) - stdName = ttInfo.abbrev; - } - - if (!stdName.empty && !dstName.empty) - break; - } - - return new immutable PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); - } - catch (DateTimeException dte) - throw dte; - catch (Exception e) - throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); - } - - /// - @safe unittest - { - version(Posix) - { - auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); - - assert(tz.name == "America/Los_Angeles"); - assert(tz.stdName == "PST"); - assert(tz.dstName == "PDT"); - } - } - - /++ - Returns a list of the names of the time zones installed on the system. - - Providing a sub-name narrows down the list of time zones (which - can number in the thousands). For example, - passing in "America" as the sub-name returns only the time zones which - begin with "America". - - Params: - subName = The first part of the desired time zones. - tzDatabaseDir = The directory where the TZ Database files are - located. - - Throws: - $(D FileException) if it fails to read from disk. - +/ - static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted - { - import std.algorithm.sorting : sort; - import std.array : appender; - import std.format : format; - - version(Posix) - subName = strip(subName); - else version(Windows) - { - import std.array : replace; - import std.path : dirSeparator; - subName = replace(strip(subName), "/", dirSeparator); - } - - enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); - enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - - auto timezones = appender!(string[])(); - - version(Android) - { - import std.algorithm.iteration : filter; - import std.algorithm.mutation : copy; - tzdataIndex(tzDatabaseDir) - .byKey - .filter!(a => a.startsWith(subName)) - .copy(timezones); - } - else - { - foreach (DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth)) - { - if (dentry.isFile) - { - auto tzName = dentry.name[tzDatabaseDir.length .. $]; - - if (!tzName.extension().empty || - !tzName.startsWith(subName) || - tzName == "leapseconds" || - tzName == "+VERSION") - { - continue; - } - - timezones.put(tzName); - } - } - } - - sort(timezones.data); - - return timezones.data; - } - - version(Posix) @system unittest - { - import std.exception : assertNotThrown; - import std.stdio : writefln; - static void testPTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - static void testPTZFailure(string tzName) - { - scope(success) writefln("TZName which was supposed to throw: %s", tzName); - - PosixTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testPTZSuccess(tzName)); - - // No timezone directories on Android, just a single tzdata file - version(Android) {} else - foreach (DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) - { - if (dentry.isFile) - { - auto tzName = dentry.name[defaultTZDatabaseDir.length .. $]; - - if (!canFind(tzNames, tzName)) - assertThrown!DateTimeException(testPTZFailure(tzName)); - } - } - } - - -private: - - /+ - Holds information on when a time transition occures (usually a - transition to or from DST) as well as a pointer to the $(D TTInfo) which - holds information on the utc offset past the transition. - +/ - struct Transition - { - this(long timeT, immutable (TTInfo)* ttInfo) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - } - - long timeT; - immutable (TTInfo)* ttInfo; - } - - - /+ - Holds information on when a leap second occurs. - +/ - struct LeapSecond - { - this(long timeT, int total) @safe pure - { - this.timeT = timeT; - this.total = total; - } - - long timeT; - int total; - } - - /+ - Holds information on the utc offset after a transition as well as - whether DST is in effect after that transition. - +/ - struct TTInfo - { - this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure - { - utcOffset = tempTTInfo.tt_gmtoff; - isDST = tempTTInfo.tt_isdst; - this.abbrev = abbrev; - } - - immutable int utcOffset; /// Offset from UTC. - immutable bool isDST; /// Whether DST is in effect. - immutable string abbrev; /// The current abbreviation for the time zone. - } - - - /+ - Struct used to hold information relating to $(D TTInfo) while organizing - the time zone information prior to putting it in its final form. - +/ - struct TempTTInfo - { - this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure - { - tt_gmtoff = gmtOff; - tt_isdst = isDST; - tt_abbrind = abbrInd; - } - - int tt_gmtoff; - bool tt_isdst; - ubyte tt_abbrind; - } - - - /+ - Struct used to hold information relating to $(D Transition) while - organizing the time zone information prior to putting it in its final - form. - +/ - struct TempTransition - { - this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure - { - this.timeT = timeT; - this.ttInfo = ttInfo; - this.ttype = ttype; - } - - long timeT; - immutable (TTInfo)* ttInfo; - TransitionType* ttype; - } - - - /+ - Struct used to hold information relating to $(D Transition) and - $(D TTInfo) while organizing the time zone information prior to putting - it in its final form. - +/ - struct TransitionType - { - this(bool isStd, bool inUTC) @safe pure - { - this.isStd = isStd; - this.inUTC = inUTC; - } - - /// Whether the transition is in std time (as opposed to wall clock time). - bool isStd; - - /// Whether the transition is in UTC (as opposed to local time). - bool inUTC; - } - - - /+ - Reads an int from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @trusted - if ((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool)) - { - import std.bitmanip : bigEndianToNative; - T[1] buff; - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return bigEndianToNative!T(cast(ubyte[T.sizeof]) buff); - } - - /+ - Reads an array of values from a TZ file. - +/ - static T readVal(T)(ref File tzFile, size_t length) @trusted - if (isArray!T) - { - auto buff = new T(length); - - _enforceValidTZFile(!tzFile.eof); - tzFile.rawRead(buff); - - return buff; - } - - - /+ - Reads a $(D TempTTInfo) from a TZ file. - +/ - static T readVal(T)(ref File tzFile) @safe - if (is(T == TempTTInfo)) - { - return TempTTInfo(readVal!int(tzFile), - readVal!bool(tzFile), - readVal!ubyte(tzFile)); - } - - - /+ - Throws: - $(LREF DateTimeException) if $(D result) is false. - +/ - static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure - { - if (!result) - throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); - } - - - int calculateLeapSeconds(long stdTime) @safe const pure nothrow - { - if (_leapSeconds.empty) - return 0; - - immutable unixTime = stdTimeToUnixTime(stdTime); - - if (_leapSeconds.front.timeT >= unixTime) - return 0; - - immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); - - if (found == -1) - return _leapSeconds.back.total; - - immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; - - return leapSecond.total; - } - - - this(immutable Transition[] transitions, - immutable LeapSecond[] leapSeconds, - string name, - string stdName, - string dstName, - bool hasDST) @safe immutable pure - { - if (dstName.empty && !stdName.empty) - dstName = stdName; - else if (stdName.empty && !dstName.empty) - stdName = dstName; - - super(name, stdName, dstName); - - if (!transitions.empty) - { - foreach (i, transition; transitions[0 .. $-1]) - _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); - } - - foreach (i, leapSecond; leapSeconds) - _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); - - _transitions = transitions; - _leapSeconds = leapSeconds; - _hasDST = hasDST; - } - - // Android concatenates the usual timezone directories into a single file, - // tzdata, along with an index to jump to each timezone's offset. In older - // versions of Android, the index was stored in a separate file, zoneinfo.idx, - // whereas now it's stored at the beginning of tzdata. - version(Android) - { - // Keep track of whether there's a separate index, zoneinfo.idx. Only - // check this after calling tzdataIndex, as it's initialized there. - static shared bool separate_index; - - // Extracts the name of each time zone and the offset where its data is - // located in the tzdata file from the index and caches it for later. - static const(uint[string]) tzdataIndex(string tzDir) - { - import std.concurrency : initOnce; - - static __gshared uint[string] _tzIndex; - - // _tzIndex is initialized once and then shared across all threads. - initOnce!_tzIndex( - { - import std.conv : to; - import std.format : format; - import std.path : asNormalizedPath, chainPath; - - enum indexEntrySize = 52; - const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string; - const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string; - File tzFile; - uint indexEntries, dataOffset; - uint[string] initIndex; - - // Check for the combined file tzdata, which stores the index - // and the time zone data together. - if (combinedFile.exists() && combinedFile.isFile) - { - tzFile = File(combinedFile); - _enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata"); - auto tzDataVersion = readVal!(char[])(tzFile, 6); - _enforceValidTZFile(tzDataVersion[5] == '\0'); - - uint indexOffset = readVal!uint(tzFile); - dataOffset = readVal!uint(tzFile); - readVal!uint(tzFile); - - indexEntries = (dataOffset - indexOffset)/indexEntrySize; - separate_index = false; - } - else if (indexFile.exists() && indexFile.isFile) - { - tzFile = File(indexFile); - indexEntries = to!(uint)(tzFile.size/indexEntrySize); - separate_index = true; - } - else - throw new DateTimeException(format("Both timezone files %s and %s do not exist.", - combinedFile, indexFile)); - - foreach (Unused; 0 .. indexEntries) - { - string tzName = to!string(readVal!(char[])(tzFile, 40).ptr); - uint tzOffset = readVal!uint(tzFile); - readVal!(uint[])(tzFile, 2); - initIndex[tzName] = dataOffset + tzOffset; - } - initIndex.rehash; - return initIndex; - }()); - return _tzIndex; - } - } - - /// List of times when the utc offset changes. - immutable Transition[] _transitions; - - /// List of leap second occurrences. - immutable LeapSecond[] _leapSeconds; - - /// Whether DST is in effect for this time zone at any point in time. - immutable bool _hasDST; -} - - - version(StdDdoc) { /++ diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 9898aa0e765..99cebe9121f 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -12,7 +12,8 @@ module std.datetime.timezone; import core.time; import std.datetime.common; import std.exception : enforce; -import std.traits : isSomeString; +import std.range.primitives; +import std.traits : isIntegral, isSomeString, Unqual; version(Windows) { @@ -35,7 +36,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : clearTZEnvVar, Clock, Date, DateTime, PosixTimeZone, setTZEnvVar, stdTimeToUnixTime, SysTime, +import std.datetime : clearTZEnvVar, Clock, Date, DateTime, setTZEnvVar, stdTimeToUnixTime, SysTime, TimeOfDay; // temporary /++ @@ -1588,7 +1589,6 @@ package: // temporary import std.ascii : isDigit; import std.conv : to; import std.format : format; - import std.range.primitives; auto dstr = to!dstring(isoString); @@ -1733,7 +1733,6 @@ package: // temporary import std.ascii : isDigit; import std.conv : to; import std.format : format; - import std.range.primitives; auto dstr = to!dstring(isoExtString); @@ -1870,3 +1869,940 @@ package: // temporary immutable Duration _utcOffset; } + + +/++ + Represents a time zone from a TZ Database time zone file. Files from the TZ + Database are how Posix systems hold their time zone information. + Unfortunately, Windows does not use the TZ Database. To use the TZ Database, + use $(D PosixTimeZone) (which reads its information from the TZ Database + files on disk) on Windows by providing the TZ Database files and telling + $(D PosixTimeZone.getTimeZone) where the directory holding them is. + + To get a $(D PosixTimeZone), either call $(D PosixTimeZone.getTimeZone) + (which allows specifying the location the time zone files) or call + $(D TimeZone.getTimeZone) (which will give a $(D PosixTimeZone) on Posix + systems and a $(LREF WindowsTimeZone) on Windows systems). + + Note: + Unless your system's local time zone deals with leap seconds (which is + highly unlikely), then the only way to get a time zone which + takes leap seconds into account is to use $(D PosixTimeZone) with a + time zone whose name starts with "right/". Those time zone files do + include leap seconds, and $(D PosixTimeZone) will take them into account + (though posix systems which use a "right/" time zone as their local time + zone will $(I not) take leap seconds into account even though they're + in the file). + + See_Also: + $(HTTP www.iana.org/time-zones, Home of the TZ Database files)
+ $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time + Zones) + +/ +final class PosixTimeZone : TimeZone +{ + import std.algorithm.searching : countUntil, canFind, startsWith; + import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; + import std.path : extension; + import std.stdio : File; + import std.string : strip, representation; + import std.traits : isArray, isSomeChar; +public: + + /++ + Whether this time zone has Daylight Savings Time at any point in time. + Note that for some time zone types it may not have DST for current + dates but will still return true for $(D hasDST) because the time zone + did at some point have DST. + +/ + @property override bool hasDST() @safe const nothrow + { + return _hasDST; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and returns whether DST is in effect in this + time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this time + zone. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); + + if (found == -1) + return _transitions.back.ttInfo.isDST; + + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return transition.ttInfo.isDST; + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in UTC time (i.e. std time) and converts it to this time zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time zone's + time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable leapSecs = calculateLeapSeconds(stdTime); + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); + + if (found == -1) + return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + + return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, 1 A.D. + in this time zone's time and converts it to UTC (i.e. std time). + + Params: + adjTime = The time in this time zone that needs to be adjusted to + UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow + { + assert(!_transitions.empty); + + immutable leapSecs = calculateLeapSeconds(adjTime); + time_t unixTime = stdTimeToUnixTime(adjTime); + immutable past = unixTime - convert!("days", "seconds")(1); + immutable future = unixTime + convert!("days", "seconds")(1); + + immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); + + if (pastFound == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); + immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; + + if (futureFound == 0) + return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); + + immutable futureTrans = futureFound == -1 ? _transitions.back + : _transitions[pastFound + futureFound - 1]; + immutable pastOffset = pastTrans.ttInfo.utcOffset; + + if (pastOffset < futureTrans.ttInfo.utcOffset) + unixTime -= convert!("hours", "seconds")(1); + + immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); + + if (found == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + + immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; + + return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); + } + + + version(Android) + { + // Android concatenates all time zone data into a single file and stores it here. + enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/"; + } + else version(Posix) + { + /++ + The default directory where the TZ Database files are. It's empty + for Windows, since Windows doesn't have them. + +/ + enum defaultTZDatabaseDir = "/usr/share/zoneinfo/"; + } + else version(Windows) + { + /++ The default directory where the TZ Database files are. It's empty + for Windows, since Windows doesn't have them. + +/ + enum defaultTZDatabaseDir = ""; + } + + + /++ + Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. The time + zone information is fetched from the TZ Database time zone files in the + given directory. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of + Time Zones) + + Params: + name = The TZ Database name of the desired time zone + tzDatabaseDir = The directory where the TZ Database files are + located. Because these files are not located on + Windows systems, provide them + and give their location here to + use $(LREF PosixTimeZone)s. + + Throws: + $(LREF DateTimeException) if the given time zone could not be found or + $(D FileException) if the TZ Database file could not be opened. + +/ + // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed + // directory. + static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted + { + import std.algorithm.sorting : sort; + import std.range : retro; + import std.format : format; + import std.path : asNormalizedPath, chainPath; + import std.conv : to; + + name = strip(name); + + enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + version(Android) + { + auto tzfileOffset = name in tzdataIndex(tzDatabaseDir); + enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name))); + string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata"; + const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string; + } + else + const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string; + + enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file))); + enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); + + auto tzFile = File(file); + version(Android) tzFile.seek(*tzfileOffset); + immutable gmtZone = name.representation().canFind("GMT"); + + try + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + immutable char tzFileVersion = readVal!char(tzFile); + _enforceValidTZFile(tzFileVersion == '\0' || tzFileVersion == '2' || tzFileVersion == '3'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach (val; zeroBlock) + { + if (val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + // The number of UTC/local indicators stored in the file. + auto tzh_ttisgmtcnt = readVal!int(tzFile); + + // The number of standard/wall indicators stored in the file. + auto tzh_ttisstdcnt = readVal!int(tzFile); + + // The number of leap seconds for which data is stored in the file. + auto tzh_leapcnt = readVal!int(tzFile); + + // The number of "transition times" for which data is stored in the file. + auto tzh_timecnt = readVal!int(tzFile); + + // The number of "local time types" for which data is stored in the file (must not be zero). + auto tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + // The number of characters of "timezone abbreviation strings" stored in the file. + auto tzh_charcnt = readVal!int(tzFile); + + // time_ts where DST transitions occur. + auto transitionTimeTs = new long[](tzh_timecnt); + foreach (ref transition; transitionTimeTs) + transition = readVal!int(tzFile); + + // Indices into ttinfo structs indicating the changes + // to be made at the corresponding DST transition. + auto ttInfoIndices = new ubyte[](tzh_timecnt); + foreach (ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + // ttinfos which give info on DST transitions. + auto tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach (ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + // The array of time zone abbreviation characters. + auto tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + auto leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach (ref leapSecond; leapSeconds) + { + // The time_t when the leap second occurs. + auto timeT = readVal!int(tzFile); + + // The total number of leap seconds to be applied after + // the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + // Indicate whether each corresponding DST transition were specified + // in standard time or wall clock time. + auto transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach (ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + // Indicate whether each corresponding DST transition associated with + // local time types are specified in UTC or local time. + auto transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach (ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + + _enforceValidTZFile(!tzFile.eof); + + // If version 2 or 3, the information is duplicated in 64-bit. + if (tzFileVersion == '2' || tzFileVersion == '3') + { + _enforceValidTZFile(readVal!(char[])(tzFile, 4) == "TZif"); + + immutable char tzFileVersion2 = readVal!(char)(tzFile); + _enforceValidTZFile(tzFileVersion2 == '2' || tzFileVersion2 == '3'); + + { + auto zeroBlock = readVal!(ubyte[])(tzFile, 15); + bool allZeroes = true; + + foreach (val; zeroBlock) + { + if (val != 0) + { + allZeroes = false; + break; + } + } + + _enforceValidTZFile(allZeroes); + } + + + // The number of UTC/local indicators stored in the file. + tzh_ttisgmtcnt = readVal!int(tzFile); + + // The number of standard/wall indicators stored in the file. + tzh_ttisstdcnt = readVal!int(tzFile); + + // The number of leap seconds for which data is stored in the file. + tzh_leapcnt = readVal!int(tzFile); + + // The number of "transition times" for which data is stored in the file. + tzh_timecnt = readVal!int(tzFile); + + // The number of "local time types" for which data is stored in the file (must not be zero). + tzh_typecnt = readVal!int(tzFile); + _enforceValidTZFile(tzh_typecnt != 0); + + // The number of characters of "timezone abbreviation strings" stored in the file. + tzh_charcnt = readVal!int(tzFile); + + // time_ts where DST transitions occur. + transitionTimeTs = new long[](tzh_timecnt); + foreach (ref transition; transitionTimeTs) + transition = readVal!long(tzFile); + + // Indices into ttinfo structs indicating the changes + // to be made at the corresponding DST transition. + ttInfoIndices = new ubyte[](tzh_timecnt); + foreach (ref ttInfoIndex; ttInfoIndices) + ttInfoIndex = readVal!ubyte(tzFile); + + // ttinfos which give info on DST transitions. + tempTTInfos = new TempTTInfo[](tzh_typecnt); + foreach (ref ttInfo; tempTTInfos) + ttInfo = readVal!TempTTInfo(tzFile); + + // The array of time zone abbreviation characters. + tzAbbrevChars = readVal!(char[])(tzFile, tzh_charcnt); + + leapSeconds = new LeapSecond[](tzh_leapcnt); + foreach (ref leapSecond; leapSeconds) + { + // The time_t when the leap second occurs. + auto timeT = readVal!long(tzFile); + + // The total number of leap seconds to be applied after + // the corresponding leap second. + auto total = readVal!int(tzFile); + + leapSecond = LeapSecond(timeT, total); + } + + // Indicate whether each corresponding DST transition were specified + // in standard time or wall clock time. + transitionIsStd = new bool[](tzh_ttisstdcnt); + foreach (ref isStd; transitionIsStd) + isStd = readVal!bool(tzFile); + + // Indicate whether each corresponding DST transition associated with + // local time types are specified in UTC or local time. + transitionInUTC = new bool[](tzh_ttisgmtcnt); + foreach (ref inUTC; transitionInUTC) + inUTC = readVal!bool(tzFile); + } + + _enforceValidTZFile(tzFile.readln().strip().empty); + + cast(void) tzFile.readln(); + + version(Android) + { + // Android uses a single file for all timezone data, so the file + // doesn't end here. + } + else + { + _enforceValidTZFile(tzFile.readln().strip().empty); + _enforceValidTZFile(tzFile.eof); + } + + + auto transitionTypes = new TransitionType*[](tempTTInfos.length); + + foreach (i, ref ttype; transitionTypes) + { + bool isStd = false; + + if (i < transitionIsStd.length && !transitionIsStd.empty) + isStd = transitionIsStd[i]; + + bool inUTC = false; + + if (i < transitionInUTC.length && !transitionInUTC.empty) + inUTC = transitionInUTC[i]; + + ttype = new TransitionType(isStd, inUTC); + } + + auto ttInfos = new immutable(TTInfo)*[](tempTTInfos.length); + foreach (i, ref ttInfo; ttInfos) + { + auto tempTTInfo = tempTTInfos[i]; + + if (gmtZone) + tempTTInfo.tt_gmtoff = -tempTTInfo.tt_gmtoff; + + auto abbrevChars = tzAbbrevChars[tempTTInfo.tt_abbrind .. $]; + string abbrev = abbrevChars[0 .. abbrevChars.countUntil('\0')].idup; + + ttInfo = new immutable(TTInfo)(tempTTInfos[i], abbrev); + } + + auto tempTransitions = new TempTransition[](transitionTimeTs.length); + foreach (i, ref tempTransition; tempTransitions) + { + immutable ttiIndex = ttInfoIndices[i]; + auto transitionTimeT = transitionTimeTs[i]; + auto ttype = transitionTypes[ttiIndex]; + auto ttInfo = ttInfos[ttiIndex]; + + tempTransition = TempTransition(transitionTimeT, ttInfo, ttype); + } + + if (tempTransitions.empty) + { + _enforceValidTZFile(ttInfos.length == 1 && transitionTypes.length == 1); + tempTransitions ~= TempTransition(0, ttInfos[0], transitionTypes[0]); + } + + sort!"a.timeT < b.timeT"(tempTransitions); + sort!"a.timeT < b.timeT"(leapSeconds); + + auto transitions = new Transition[](tempTransitions.length); + foreach (i, ref transition; transitions) + { + auto tempTransition = tempTransitions[i]; + auto transitionTimeT = tempTransition.timeT; + auto ttInfo = tempTransition.ttInfo; + + _enforceValidTZFile(i == 0 || transitionTimeT > tempTransitions[i - 1].timeT); + + transition = Transition(transitionTimeT, ttInfo); + } + + string stdName; + string dstName; + bool hasDST = false; + + foreach (transition; retro(transitions)) + { + auto ttInfo = transition.ttInfo; + + if (ttInfo.isDST) + { + if (dstName.empty) + dstName = ttInfo.abbrev; + hasDST = true; + } + else + { + if (stdName.empty) + stdName = ttInfo.abbrev; + } + + if (!stdName.empty && !dstName.empty) + break; + } + + return new immutable PosixTimeZone(transitions.idup, leapSeconds.idup, name, stdName, dstName, hasDST); + } + catch (DateTimeException dte) + throw dte; + catch (Exception e) + throw new DateTimeException("Not a valid TZ data file", __FILE__, __LINE__, e); + } + + /// + @safe unittest + { + version(Posix) + { + auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + + assert(tz.name == "America/Los_Angeles"); + assert(tz.stdName == "PST"); + assert(tz.dstName == "PDT"); + } + } + + /++ + Returns a list of the names of the time zones installed on the system. + + Providing a sub-name narrows down the list of time zones (which + can number in the thousands). For example, + passing in "America" as the sub-name returns only the time zones which + begin with "America". + + Params: + subName = The first part of the desired time zones. + tzDatabaseDir = The directory where the TZ Database files are + located. + + Throws: + $(D FileException) if it fails to read from disk. + +/ + static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted + { + import std.algorithm.sorting : sort; + import std.array : appender; + import std.format : format; + + version(Posix) + subName = strip(subName); + else version(Windows) + { + import std.array : replace; + import std.path : dirSeparator; + subName = replace(strip(subName), "/", dirSeparator); + } + + enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); + enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); + + auto timezones = appender!(string[])(); + + version(Android) + { + import std.algorithm.iteration : filter; + import std.algorithm.mutation : copy; + tzdataIndex(tzDatabaseDir).byKey.filter!(a => a.startsWith(subName)).copy(timezones); + } + else + { + foreach (DirEntry de; dirEntries(tzDatabaseDir, SpanMode.depth)) + { + if (de.isFile) + { + auto tzName = de.name[tzDatabaseDir.length .. $]; + + if (!tzName.extension().empty || + !tzName.startsWith(subName) || + tzName == "leapseconds" || + tzName == "+VERSION") + { + continue; + } + + timezones.put(tzName); + } + } + } + + sort(timezones.data); + + return timezones.data; + } + + version(Posix) @system unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testPTZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + static void testPTZFailure(string tzName) + { + scope(success) writefln("TZName which was supposed to throw: %s", tzName); + + PosixTimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testPTZSuccess(tzName)); + + // No timezone directories on Android, just a single tzdata file + version(Android) + {} + else + { + foreach (DirEntry de; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) + { + if (de.isFile) + { + auto tzName = de.name[defaultTZDatabaseDir.length .. $]; + + if (!canFind(tzNames, tzName)) + assertThrown!DateTimeException(testPTZFailure(tzName)); + } + } + } + } + + +private: + + /+ + Holds information on when a time transition occures (usually a + transition to or from DST) as well as a pointer to the $(D TTInfo) which + holds information on the utc offset past the transition. + +/ + struct Transition + { + this(long timeT, immutable (TTInfo)* ttInfo) @safe pure + { + this.timeT = timeT; + this.ttInfo = ttInfo; + } + + long timeT; + immutable (TTInfo)* ttInfo; + } + + + /+ + Holds information on when a leap second occurs. + +/ + struct LeapSecond + { + this(long timeT, int total) @safe pure + { + this.timeT = timeT; + this.total = total; + } + + long timeT; + int total; + } + + /+ + Holds information on the utc offset after a transition as well as + whether DST is in effect after that transition. + +/ + struct TTInfo + { + this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure + { + utcOffset = tempTTInfo.tt_gmtoff; + isDST = tempTTInfo.tt_isdst; + this.abbrev = abbrev; + } + + immutable int utcOffset; // Offset from UTC. + immutable bool isDST; // Whether DST is in effect. + immutable string abbrev; // The current abbreviation for the time zone. + } + + + /+ + Struct used to hold information relating to $(D TTInfo) while organizing + the time zone information prior to putting it in its final form. + +/ + struct TempTTInfo + { + this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure + { + tt_gmtoff = gmtOff; + tt_isdst = isDST; + tt_abbrind = abbrInd; + } + + int tt_gmtoff; + bool tt_isdst; + ubyte tt_abbrind; + } + + + /+ + Struct used to hold information relating to $(D Transition) while + organizing the time zone information prior to putting it in its final + form. + +/ + struct TempTransition + { + this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure + { + this.timeT = timeT; + this.ttInfo = ttInfo; + this.ttype = ttype; + } + + long timeT; + immutable (TTInfo)* ttInfo; + TransitionType* ttype; + } + + + /+ + Struct used to hold information relating to $(D Transition) and + $(D TTInfo) while organizing the time zone information prior to putting + it in its final form. + +/ + struct TransitionType + { + this(bool isStd, bool inUTC) @safe pure + { + this.isStd = isStd; + this.inUTC = inUTC; + } + + // Whether the transition is in std time (as opposed to wall clock time). + bool isStd; + + // Whether the transition is in UTC (as opposed to local time). + bool inUTC; + } + + + /+ + Reads an int from a TZ file. + +/ + static T readVal(T)(ref File tzFile) @trusted + if ((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool)) + { + import std.bitmanip : bigEndianToNative; + T[1] buff; + + _enforceValidTZFile(!tzFile.eof); + tzFile.rawRead(buff); + + return bigEndianToNative!T(cast(ubyte[T.sizeof]) buff); + } + + /+ + Reads an array of values from a TZ file. + +/ + static T readVal(T)(ref File tzFile, size_t length) @trusted + if (isArray!T) + { + auto buff = new T(length); + + _enforceValidTZFile(!tzFile.eof); + tzFile.rawRead(buff); + + return buff; + } + + + /+ + Reads a $(D TempTTInfo) from a TZ file. + +/ + static T readVal(T)(ref File tzFile) @safe + if (is(T == TempTTInfo)) + { + return TempTTInfo(readVal!int(tzFile), + readVal!bool(tzFile), + readVal!ubyte(tzFile)); + } + + + /+ + Throws: + $(LREF DateTimeException) if $(D result) is false. + +/ + static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure + { + if (!result) + throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); + } + + + int calculateLeapSeconds(long stdTime) @safe const pure nothrow + { + if (_leapSeconds.empty) + return 0; + + immutable unixTime = stdTimeToUnixTime(stdTime); + + if (_leapSeconds.front.timeT >= unixTime) + return 0; + + immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); + + if (found == -1) + return _leapSeconds.back.total; + + immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; + + return leapSecond.total; + } + + + this(immutable Transition[] transitions, + immutable LeapSecond[] leapSeconds, + string name, + string stdName, + string dstName, + bool hasDST) @safe immutable pure + { + if (dstName.empty && !stdName.empty) + dstName = stdName; + else if (stdName.empty && !dstName.empty) + stdName = dstName; + + super(name, stdName, dstName); + + if (!transitions.empty) + { + foreach (i, transition; transitions[0 .. $-1]) + _enforceValidTZFile(transition.timeT < transitions[i + 1].timeT); + } + + foreach (i, leapSecond; leapSeconds) + _enforceValidTZFile(i == leapSeconds.length - 1 || leapSecond.timeT < leapSeconds[i + 1].timeT); + + _transitions = transitions; + _leapSeconds = leapSeconds; + _hasDST = hasDST; + } + + // Android concatenates the usual timezone directories into a single file, + // tzdata, along with an index to jump to each timezone's offset. In older + // versions of Android, the index was stored in a separate file, zoneinfo.idx, + // whereas now it's stored at the beginning of tzdata. + version(Android) + { + // Keep track of whether there's a separate index, zoneinfo.idx. Only + // check this after calling tzdataIndex, as it's initialized there. + static shared bool separate_index; + + // Extracts the name of each time zone and the offset where its data is + // located in the tzdata file from the index and caches it for later. + static const(uint[string]) tzdataIndex(string tzDir) + { + import std.concurrency : initOnce; + + static __gshared uint[string] _tzIndex; + + // _tzIndex is initialized once and then shared across all threads. + initOnce!_tzIndex( + { + import std.conv : to; + import std.format : format; + import std.path : asNormalizedPath, chainPath; + + enum indexEntrySize = 52; + const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string; + const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string; + File tzFile; + uint indexEntries, dataOffset; + uint[string] initIndex; + + // Check for the combined file tzdata, which stores the index + // and the time zone data together. + if (combinedFile.exists() && combinedFile.isFile) + { + tzFile = File(combinedFile); + _enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata"); + auto tzDataVersion = readVal!(char[])(tzFile, 6); + _enforceValidTZFile(tzDataVersion[5] == '\0'); + + uint indexOffset = readVal!uint(tzFile); + dataOffset = readVal!uint(tzFile); + readVal!uint(tzFile); + + indexEntries = (dataOffset - indexOffset) / indexEntrySize; + separate_index = false; + } + else if (indexFile.exists() && indexFile.isFile) + { + tzFile = File(indexFile); + indexEntries = to!uint(tzFile.size/indexEntrySize); + separate_index = true; + } + else + { + throw new DateTimeException(format("Both timezone files %s and %s do not exist.", + combinedFile, indexFile)); + } + + foreach (_; 0 .. indexEntries) + { + string tzName = to!string(readVal!(char[])(tzFile, 40).ptr); + uint tzOffset = readVal!uint(tzFile); + readVal!(uint[])(tzFile, 2); + initIndex[tzName] = dataOffset + tzOffset; + } + initIndex.rehash; + return initIndex; + }()); + return _tzIndex; + } + } + + // List of times when the utc offset changes. + immutable Transition[] _transitions; + + // List of leap second occurrences. + immutable LeapSecond[] _leapSeconds; + + // Whether DST is in effect for this time zone at any point in time. + immutable bool _hasDST; +} From abf9fb12c90d8aef8c63dee8d655971406d52bd8 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 11:23:34 +0200 Subject: [PATCH 096/262] Move WindowsTimeZone to std.datetime.timezone. --- std/datetime/package.d | 453 ---------------------------------------- std/datetime/timezone.d | 451 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 451 insertions(+), 453 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index f35d8d58af5..5d410f2b8d6 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18219,459 +18219,6 @@ private: // Section with time zones. //============================================================================== -version(StdDdoc) -{ - /++ - $(BLUE This class is Windows-Only.) - - Represents a time zone from the Windows registry. Unfortunately, Windows - does not use the TZ Database. To use the TZ Database, use - $(LREF PosixTimeZone) (which reads its information from the TZ Database - files on disk) on Windows by providing the TZ Database files and telling - $(D PosixTimeZone.getTimeZone) where the directory holding them is. - - The TZ Database files and Windows' time zone information frequently - do not match. Windows has many errors with regards to when DST switches - occur (especially for historical dates). Also, the TZ Database files - include far more time zones than Windows does. So, for accurate - time zone information, use the TZ Database files with - $(LREF PosixTimeZone) rather than $(D WindowsTimeZone). However, because - $(D WindowsTimeZone) uses Windows system calls to deal with the time, - it's far more likely to match the behavior of other Windows programs. - Be aware of the differences when selecting a method. - - $(D WindowsTimeZone) does not exist on Posix systems. - - To get a $(D WindowsTimeZone), either call - $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone) - (which will give a $(LREF PosixTimeZone) on Posix systems and a - $(D WindowsTimeZone) on Windows systems). - - See_Also: - $(HTTP www.iana.org/time-zones, Home of the TZ Database files) - +/ - final class WindowsTimeZone : TimeZone - { - public: - - /++ - Whether this time zone has Daylight Savings Time at any point in - time. Note that for some time zone types it may not have DST for - current dates but will still return true for $(D hasDST) because the - time zone did at some point have DST. - +/ - @property override bool hasDST() @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and returns whether DST is in - effect in this time zone at the given point in time. - - Params: - stdTime = The UTC time that needs to be checked for DST in this - time zone. - +/ - override bool dstInEffect(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in UTC time (i.e. std time) and converts it to this time - zone's time. - - Params: - stdTime = The UTC time that needs to be adjusted to this time - zone's time. - +/ - override long utcToTZ(long stdTime) @safe const nothrow; - - - /++ - Takes the number of hnsecs (100 ns) since midnight, January 1st, - 1 A.D. in this time zone's time and converts it to UTC (i.e. std - time). - - Params: - adjTime = The time in this time zone that needs to be adjusted - to UTC time. - +/ - override long tzToUTC(long adjTime) @safe const nothrow; - - - /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the given name per the Windows time - zone names. The time zone information is fetched from the Windows - registry. - - See_Also: - $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ - Database)
- $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List - of Time Zones) - - Params: - name = The TZ Database name of the desired time zone. - - Throws: - $(LREF DateTimeException) if the given time zone could not be - found. - - Example: - -------------------- - auto tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); - -------------------- - +/ - static immutable(WindowsTimeZone) getTimeZone(string name) @safe; - - - /++ - Returns a list of the names of the time zones installed on the - system. The list returned by WindowsTimeZone contains the Windows - TZ names, not the TZ Database names. However, - $(D TimeZone.getinstalledTZNames) will return the TZ Database names - which are equivalent to the Windows TZ names. - +/ - static string[] getInstalledTZNames() @safe; - - private: - - version(Windows) {} - else - alias TIME_ZONE_INFORMATION = void*; - - static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow; - static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow; - static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow; - - this() immutable pure - { - super("", "", ""); - } - } - -} -else version(Windows) -{ - final class WindowsTimeZone : TimeZone - { - import std.algorithm.sorting : sort; - import std.array : appender; - import std.conv : to; - import std.format : format; - - public: - - @property override bool hasDST() @safe const nothrow - { - return _tzInfo.DaylightDate.wMonth != 0; - } - - - override bool dstInEffect(long stdTime) @safe const nothrow - { - return _dstInEffect(&_tzInfo, stdTime); - } - - - override long utcToTZ(long stdTime) @safe const nothrow - { - return _utcToTZ(&_tzInfo, stdTime, hasDST); - } - - - override long tzToUTC(long adjTime) @safe const nothrow - { - return _tzToUTC(&_tzInfo, adjTime, hasDST); - } - - - static immutable(WindowsTimeZone) getTimeZone(string name) @trusted - { - import std.utf : toUTF16; - - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - { - if (tzKeyName != name) - continue; - - scope tzKey = baseKey.getKey(tzKeyName); - - scope stdVal = tzKey.getValue("Std"); - auto stdName = stdVal.value_SZ; - - scope dstVal = tzKey.getValue("Dlt"); - auto dstName = dstVal.value_SZ; - - scope tziVal = tzKey.getValue("TZI"); - auto binVal = tziVal.value_BINARY; - assert(binVal.length == REG_TZI_FORMAT.sizeof); - auto tziFmt = cast(REG_TZI_FORMAT*) binVal.ptr; - - TIME_ZONE_INFORMATION tzInfo; - - auto wstdName = toUTF16(stdName); - auto wdstName = toUTF16(dstName); - auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length; - auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length; - - tzInfo.Bias = tziFmt.Bias; - tzInfo.StandardName[0 .. wstdNameLen] = wstdName[0 .. wstdNameLen]; - tzInfo.StandardName[wstdNameLen .. $] = '\0'; - tzInfo.StandardDate = tziFmt.StandardDate; - tzInfo.StandardBias = tziFmt.StandardBias; - tzInfo.DaylightName[0 .. wdstNameLen] = wdstName[0 .. wdstNameLen]; - tzInfo.DaylightName[wdstNameLen .. $] = '\0'; - tzInfo.DaylightDate = tziFmt.DaylightDate; - tzInfo.DaylightBias = tziFmt.DaylightBias; - - return new immutable WindowsTimeZone(name, tzInfo); - } - throw new DateTimeException(format("Failed to find time zone: %s", name)); - } - - static string[] getInstalledTZNames() @trusted - { - auto timezones = appender!(string[])(); - - scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); - - foreach (tzKeyName; baseKey.keyNames) - { - timezones.put(tzKeyName); - } - sort(timezones.data); - - return timezones.data; - } - - @safe unittest - { - import std.exception : assertNotThrown; - import std.stdio : writefln; - static void testWTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - WindowsTimeZone.getTimeZone(tzName); - } - - auto tzNames = getInstalledTZNames(); - - foreach (tzName; tzNames) - assertNotThrown!DateTimeException(testWTZSuccess(tzName)); - } - - - private: - - static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow - { - try - { - if (tzInfo.DaylightDate.wMonth == 0) - return false; - - auto utcDateTime = cast(DateTime) SysTime(stdTime, UTC()); - - //The limits of what SystemTimeToTzSpecificLocalTime will accept. - if (utcDateTime.year < 1601) - { - if (utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - - utcDateTime.year = 1601; - } - else if (utcDateTime.year > 30_827) - { - if (utcDateTime.month == Month.feb && utcDateTime.day == 29) - utcDateTime.day = 28; - - utcDateTime.year = 30_827; - } - - //SystemTimeToTzSpecificLocalTime doesn't act correctly at the - //beginning or end of the year (bleh). Unless some bizarre time - //zone changes DST on January 1st or December 31st, this should - //fix the problem. - if (utcDateTime.month == Month.jan) - { - if (utcDateTime.day == 1) - utcDateTime.day = 2; - } - else if (utcDateTime.month == Month.dec && utcDateTime.day == 31) - utcDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME otherTime = void; - - utcTime.wYear = utcDateTime.year; - utcTime.wMonth = utcDateTime.month; - utcTime.wDay = utcDateTime.day; - utcTime.wHour = utcDateTime.hour; - utcTime.wMinute = utcDateTime.minute; - utcTime.wSecond = utcDateTime.second; - utcTime.wMilliseconds = 0; - - immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*) tzInfo, - &utcTime, - &otherTime); - assert(result); - - immutable otherDateTime = DateTime(otherTime.wYear, - otherTime.wMonth, - otherTime.wDay, - otherTime.wHour, - otherTime.wMinute, - otherTime.wSecond); - immutable diff = utcDateTime - otherDateTime; - immutable minutes = diff.total!"minutes" - tzInfo.Bias; - - if (minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias); - - return false; - } - catch (Exception e) - assert(0, "DateTime's constructor threw."); - } - - @system unittest - { - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - - foreach (year; [1600, 1601, 30_827, 30_828]) - WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime); - } - - - static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow - { - if (hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - - return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow - { - if (hasDST) - { - try - { - bool dstInEffectForLocalDateTime(DateTime localDateTime) - { - //The limits of what SystemTimeToTzSpecificLocalTime will accept. - if (localDateTime.year < 1601) - { - if (localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 1601; - } - else if (localDateTime.year > 30_827) - { - if (localDateTime.month == Month.feb && localDateTime.day == 29) - localDateTime.day = 28; - - localDateTime.year = 30_827; - } - - //SystemTimeToTzSpecificLocalTime doesn't act correctly at the - //beginning or end of the year (bleh). Unless some bizarre time - //zone changes DST on January 1st or December 31st, this should - //fix the problem. - if (localDateTime.month == Month.jan) - { - if (localDateTime.day == 1) - localDateTime.day = 2; - } - else if (localDateTime.month == Month.dec && localDateTime.day == 31) - localDateTime.day = 30; - - SYSTEMTIME utcTime = void; - SYSTEMTIME localTime = void; - - localTime.wYear = localDateTime.year; - localTime.wMonth = localDateTime.month; - localTime.wDay = localDateTime.day; - localTime.wHour = localDateTime.hour; - localTime.wMinute = localDateTime.minute; - localTime.wSecond = localDateTime.second; - localTime.wMilliseconds = 0; - - immutable result = TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION*) tzInfo, - &localTime, - &utcTime); - assert(result); - - immutable utcDateTime = DateTime(utcTime.wYear, - utcTime.wMonth, - utcTime.wDay, - utcTime.wHour, - utcTime.wMinute, - utcTime.wSecond); - - immutable diff = localDateTime - utcDateTime; - immutable minutes = -tzInfo.Bias - diff.total!"minutes"; - - if (minutes == tzInfo.DaylightBias) - return true; - - assert(minutes == tzInfo.StandardBias); - - return false; - } - - auto localDateTime = cast(DateTime) SysTime(adjTime, UTC()); - auto localDateTimeBefore = localDateTime - dur!"hours"(1); - auto localDateTimeAfter = localDateTime + dur!"hours"(1); - - auto dstInEffectNow = dstInEffectForLocalDateTime(localDateTime); - auto dstInEffectBefore = dstInEffectForLocalDateTime(localDateTimeBefore); - auto dstInEffectAfter = dstInEffectForLocalDateTime(localDateTimeAfter); - - bool isDST; - - if (dstInEffectBefore && dstInEffectNow && dstInEffectAfter) - isDST = true; - else if (!dstInEffectBefore && !dstInEffectNow && !dstInEffectAfter) - isDST = false; - else if (!dstInEffectBefore && dstInEffectAfter) - isDST = false; - else if (dstInEffectBefore && !dstInEffectAfter) - isDST = dstInEffectNow; - else - assert(0, "Bad Logic."); - - if (isDST) - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); - } - catch (Exception e) - assert(0, "SysTime's constructor threw."); - } - - return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); - } - - - this(string name, TIME_ZONE_INFORMATION tzInfo) @trusted immutable pure - { - super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); - _tzInfo = tzInfo; - } - - - TIME_ZONE_INFORMATION _tzInfo; - } -} - version(StdDdoc) { diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 99cebe9121f..961b55df84c 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -2806,3 +2806,454 @@ private: // Whether DST is in effect for this time zone at any point in time. immutable bool _hasDST; } + + +version(StdDdoc) +{ + /++ + $(BLUE This class is Windows-Only.) + + Represents a time zone from the Windows registry. Unfortunately, Windows + does not use the TZ Database. To use the TZ Database, use + $(LREF PosixTimeZone) (which reads its information from the TZ Database + files on disk) on Windows by providing the TZ Database files and telling + $(D PosixTimeZone.getTimeZone) where the directory holding them is. + + The TZ Database files and Windows' time zone information frequently + do not match. Windows has many errors with regards to when DST switches + occur (especially for historical dates). Also, the TZ Database files + include far more time zones than Windows does. So, for accurate + time zone information, use the TZ Database files with + $(LREF PosixTimeZone) rather than $(D WindowsTimeZone). However, because + $(D WindowsTimeZone) uses Windows system calls to deal with the time, + it's far more likely to match the behavior of other Windows programs. + Be aware of the differences when selecting a method. + + $(D WindowsTimeZone) does not exist on Posix systems. + + To get a $(D WindowsTimeZone), either call + $(D WindowsTimeZone.getTimeZone) or call $(D TimeZone.getTimeZone) + (which will give a $(LREF PosixTimeZone) on Posix systems and a + $(D WindowsTimeZone) on Windows systems). + + See_Also: + $(HTTP www.iana.org/time-zones, Home of the TZ Database files) + +/ + final class WindowsTimeZone : TimeZone + { + public: + + /++ + Whether this time zone has Daylight Savings Time at any point in + time. Note that for some time zone types it may not have DST for + current dates but will still return true for $(D hasDST) because the + time zone did at some point have DST. + +/ + @property override bool hasDST() @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and returns whether DST is in + effect in this time zone at the given point in time. + + Params: + stdTime = The UTC time that needs to be checked for DST in this + time zone. + +/ + override bool dstInEffect(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in UTC time (i.e. std time) and converts it to this time + zone's time. + + Params: + stdTime = The UTC time that needs to be adjusted to this time + zone's time. + +/ + override long utcToTZ(long stdTime) @safe const nothrow; + + + /++ + Takes the number of hnsecs (100 ns) since midnight, January 1st, + 1 A.D. in this time zone's time and converts it to UTC (i.e. std + time). + + Params: + adjTime = The time in this time zone that needs to be adjusted + to UTC time. + +/ + override long tzToUTC(long adjTime) @safe const nothrow; + + + /++ + Returns a $(LREF2 .TimeZone, TimeZone) with the given name per the Windows time + zone names. The time zone information is fetched from the Windows + registry. + + See_Also: + $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ + Database)
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones, List + of Time Zones) + + Params: + name = The TZ Database name of the desired time zone. + + Throws: + $(LREF DateTimeException) if the given time zone could not be + found. + + Example: + -------------------- + auto tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); + -------------------- + +/ + static immutable(WindowsTimeZone) getTimeZone(string name) @safe; + + + /++ + Returns a list of the names of the time zones installed on the + system. The list returned by WindowsTimeZone contains the Windows + TZ names, not the TZ Database names. However, + $(D TimeZone.getinstalledTZNames) will return the TZ Database names + which are equivalent to the Windows TZ names. + +/ + static string[] getInstalledTZNames() @safe; + + private: + + version(Windows) + {} + else + alias TIME_ZONE_INFORMATION = void*; + + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow; + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow; + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow; + + this() immutable pure + { + super("", "", ""); + } + } + +} +else version(Windows) +{ + final class WindowsTimeZone : TimeZone + { + import std.algorithm.sorting : sort; + import std.array : appender; + import std.conv : to; + import std.format : format; + + public: + + @property override bool hasDST() @safe const nothrow + { + return _tzInfo.DaylightDate.wMonth != 0; + } + + + override bool dstInEffect(long stdTime) @safe const nothrow + { + return _dstInEffect(&_tzInfo, stdTime); + } + + + override long utcToTZ(long stdTime) @safe const nothrow + { + return _utcToTZ(&_tzInfo, stdTime, hasDST); + } + + + override long tzToUTC(long adjTime) @safe const nothrow + { + return _tzToUTC(&_tzInfo, adjTime, hasDST); + } + + + static immutable(WindowsTimeZone) getTimeZone(string name) @trusted + { + import std.utf : toUTF16; + + scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); + + foreach (tzKeyName; baseKey.keyNames) + { + if (tzKeyName != name) + continue; + + scope tzKey = baseKey.getKey(tzKeyName); + + scope stdVal = tzKey.getValue("Std"); + auto stdName = stdVal.value_SZ; + + scope dstVal = tzKey.getValue("Dlt"); + auto dstName = dstVal.value_SZ; + + scope tziVal = tzKey.getValue("TZI"); + auto binVal = tziVal.value_BINARY; + assert(binVal.length == REG_TZI_FORMAT.sizeof); + auto tziFmt = cast(REG_TZI_FORMAT*) binVal.ptr; + + TIME_ZONE_INFORMATION tzInfo; + + auto wstdName = toUTF16(stdName); + auto wdstName = toUTF16(dstName); + auto wstdNameLen = wstdName.length > 32 ? 32 : wstdName.length; + auto wdstNameLen = wdstName.length > 32 ? 32 : wdstName.length; + + tzInfo.Bias = tziFmt.Bias; + tzInfo.StandardName[0 .. wstdNameLen] = wstdName[0 .. wstdNameLen]; + tzInfo.StandardName[wstdNameLen .. $] = '\0'; + tzInfo.StandardDate = tziFmt.StandardDate; + tzInfo.StandardBias = tziFmt.StandardBias; + tzInfo.DaylightName[0 .. wdstNameLen] = wdstName[0 .. wdstNameLen]; + tzInfo.DaylightName[wdstNameLen .. $] = '\0'; + tzInfo.DaylightDate = tziFmt.DaylightDate; + tzInfo.DaylightBias = tziFmt.DaylightBias; + + return new immutable WindowsTimeZone(name, tzInfo); + } + throw new DateTimeException(format("Failed to find time zone: %s", name)); + } + + static string[] getInstalledTZNames() @trusted + { + auto timezones = appender!(string[])(); + + scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); + + foreach (tzKeyName; baseKey.keyNames) + timezones.put(tzKeyName); + sort(timezones.data); + + return timezones.data; + } + + @safe unittest + { + import std.exception : assertNotThrown; + import std.stdio : writefln; + static void testWTZSuccess(string tzName) + { + scope(failure) writefln("TZName which threw: %s", tzName); + + WindowsTimeZone.getTimeZone(tzName); + } + + auto tzNames = getInstalledTZNames(); + + foreach (tzName; tzNames) + assertNotThrown!DateTimeException(testWTZSuccess(tzName)); + } + + + private: + + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow + { + try + { + if (tzInfo.DaylightDate.wMonth == 0) + return false; + + auto utcDateTime = cast(DateTime) SysTime(stdTime, UTC()); + + //The limits of what SystemTimeToTzSpecificLocalTime will accept. + if (utcDateTime.year < 1601) + { + if (utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + utcDateTime.year = 1601; + } + else if (utcDateTime.year > 30_827) + { + if (utcDateTime.month == Month.feb && utcDateTime.day == 29) + utcDateTime.day = 28; + utcDateTime.year = 30_827; + } + + //SystemTimeToTzSpecificLocalTime doesn't act correctly at the + //beginning or end of the year (bleh). Unless some bizarre time + //zone changes DST on January 1st or December 31st, this should + //fix the problem. + if (utcDateTime.month == Month.jan) + { + if (utcDateTime.day == 1) + utcDateTime.day = 2; + } + else if (utcDateTime.month == Month.dec && utcDateTime.day == 31) + utcDateTime.day = 30; + + SYSTEMTIME utcTime = void; + SYSTEMTIME otherTime = void; + + utcTime.wYear = utcDateTime.year; + utcTime.wMonth = utcDateTime.month; + utcTime.wDay = utcDateTime.day; + utcTime.wHour = utcDateTime.hour; + utcTime.wMinute = utcDateTime.minute; + utcTime.wSecond = utcDateTime.second; + utcTime.wMilliseconds = 0; + + immutable result = SystemTimeToTzSpecificLocalTime(cast(TIME_ZONE_INFORMATION*) tzInfo, + &utcTime, + &otherTime); + assert(result); + + immutable otherDateTime = DateTime(otherTime.wYear, + otherTime.wMonth, + otherTime.wDay, + otherTime.wHour, + otherTime.wMinute, + otherTime.wSecond); + immutable diff = utcDateTime - otherDateTime; + immutable minutes = diff.total!"minutes" - tzInfo.Bias; + + if (minutes == tzInfo.DaylightBias) + return true; + + assert(minutes == tzInfo.StandardBias); + + return false; + } + catch (Exception e) + assert(0, "DateTime's constructor threw."); + } + + @system unittest + { + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + + foreach (year; [1600, 1601, 30_827, 30_828]) + WindowsTimeZone._dstInEffect(&tzInfo, SysTime(DateTime(year, 1, 1)).stdTime); + } + + + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow + { + if (hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + + return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow + { + if (hasDST) + { + try + { + bool dstInEffectForLocalDateTime(DateTime localDateTime) + { + // The limits of what SystemTimeToTzSpecificLocalTime will accept. + if (localDateTime.year < 1601) + { + if (localDateTime.month == Month.feb && localDateTime.day == 29) + localDateTime.day = 28; + + localDateTime.year = 1601; + } + else if (localDateTime.year > 30_827) + { + if (localDateTime.month == Month.feb && localDateTime.day == 29) + localDateTime.day = 28; + + localDateTime.year = 30_827; + } + + // SystemTimeToTzSpecificLocalTime doesn't act correctly at the + // beginning or end of the year (bleh). Unless some bizarre time + // zone changes DST on January 1st or December 31st, this should + // fix the problem. + if (localDateTime.month == Month.jan) + { + if (localDateTime.day == 1) + localDateTime.day = 2; + } + else if (localDateTime.month == Month.dec && localDateTime.day == 31) + localDateTime.day = 30; + + SYSTEMTIME utcTime = void; + SYSTEMTIME localTime = void; + + localTime.wYear = localDateTime.year; + localTime.wMonth = localDateTime.month; + localTime.wDay = localDateTime.day; + localTime.wHour = localDateTime.hour; + localTime.wMinute = localDateTime.minute; + localTime.wSecond = localDateTime.second; + localTime.wMilliseconds = 0; + + immutable result = TzSpecificLocalTimeToSystemTime(cast(TIME_ZONE_INFORMATION*) tzInfo, + &localTime, + &utcTime); + assert(result); + + immutable utcDateTime = DateTime(utcTime.wYear, + utcTime.wMonth, + utcTime.wDay, + utcTime.wHour, + utcTime.wMinute, + utcTime.wSecond); + + immutable diff = localDateTime - utcDateTime; + immutable minutes = -tzInfo.Bias - diff.total!"minutes"; + + if (minutes == tzInfo.DaylightBias) + return true; + + assert(minutes == tzInfo.StandardBias); + + return false; + } + + auto localDateTime = cast(DateTime) SysTime(adjTime, UTC()); + auto localDateTimeBefore = localDateTime - dur!"hours"(1); + auto localDateTimeAfter = localDateTime + dur!"hours"(1); + + auto dstInEffectNow = dstInEffectForLocalDateTime(localDateTime); + auto dstInEffectBefore = dstInEffectForLocalDateTime(localDateTimeBefore); + auto dstInEffectAfter = dstInEffectForLocalDateTime(localDateTimeAfter); + + bool isDST; + + if (dstInEffectBefore && dstInEffectNow && dstInEffectAfter) + isDST = true; + else if (!dstInEffectBefore && !dstInEffectNow && !dstInEffectAfter) + isDST = false; + else if (!dstInEffectBefore && dstInEffectAfter) + isDST = false; + else if (dstInEffectBefore && !dstInEffectAfter) + isDST = dstInEffectNow; + else + assert(0, "Bad Logic."); + + if (isDST) + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); + } + catch (Exception e) + assert(0, "SysTime's constructor threw."); + } + + return adjTime + convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.StandardBias); + } + + + this(string name, TIME_ZONE_INFORMATION tzInfo) @trusted immutable pure + { + super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); + _tzInfo = tzInfo; + } + + + TIME_ZONE_INFORMATION _tzInfo; + } +} From 858acecf5cc0c89f1b514a2c13b36f3496745740 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 11:33:44 +0200 Subject: [PATCH 097/262] Move timezone-related free functions to std.datetime.timezone. --- std/datetime/package.d | 1080 -------------------------------------- std/datetime/timezone.d | 1082 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 1078 insertions(+), 1084 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index 5d410f2b8d6..88f00119cf6 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -18215,940 +18215,6 @@ private: } -//============================================================================== -// Section with time zones. -//============================================================================== - - -version(StdDdoc) -{ - /++ - $(BLUE This function is Posix-Only.) - - Sets the local time zone on Posix systems with the TZ - Database name by setting the TZ environment variable. - - Unfortunately, there is no way to do it on Windows using the TZ - Database name, so this function only exists on Posix systems. - +/ - void setTZEnvVar(string tzDatabaseName) @safe nothrow; - - - /++ - $(BLUE This function is Posix-Only.) - - Clears the TZ environment variable. - +/ - void clearTZEnvVar() @safe nothrow; -} -else version(Posix) -{ - void setTZEnvVar(string tzDatabaseName) @trusted nothrow - { - import core.stdc.time : tzset; - import core.sys.posix.stdlib : setenv; - import std.internal.cstring : tempCString; - import std.path : asNormalizedPath, chainPath; - - version(Android) - auto value = asNormalizedPath(tzDatabaseName); - else - auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName)); - setenv("TZ", value.tempCString(), 1); - tzset(); - } - - - void clearTZEnvVar() @trusted nothrow - { - import core.stdc.time : tzset; - import core.sys.posix.stdlib : unsetenv; - - unsetenv("TZ"); - tzset(); - } -} - - -/++ - Provides the conversions between the IANA time zone database time zone names - (which POSIX systems use) and the time zone names that Windows uses. - - Windows uses a different set of time zone names than the IANA time zone - database does, and how they correspond to one another changes over time - (particularly when Microsoft updates Windows). - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) - provides the current conversions (which may or may not match up with what's - on a particular Windows box depending on how up-to-date it is), and - parseTZConversions reads in those conversions from windowsZones.xml so that - a D program can use those conversions. - - However, it should be noted that the time zone information on Windows is - frequently less accurate than that in the IANA time zone database, and if - someone really wants accurate time zone information, they should use the - IANA time zone database files with $(LREF PosixTimeZone) on Windows rather - than $(LREF WindowsTimeZone), whereas $(LREF WindowsTimeZone) makes more - sense when trying to match what Windows will think the time is in a specific - time zone. - - Also, the IANA time zone database has a lot more time zones than Windows - does. - - Params: - windowsZonesXMLFileText The text from - $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) - - Throws: - Exception if there is an error while parsing the given XML. - --------------------- - // Parse the conversions from a local file. - auto text = std.file.readText("path/to/windowsZones.xml"); - auto conversions = parseTZConversions(text); - - // Alternatively, grab the XML file from the web at runtime - // and parse it so that it's guaranteed to be up-to-date, though - // that has the downside that the code needs to worry about the - // site being down or unicode.org changing the URL. - auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"; - auto conversions2 = parseTZConversions(std.net.curl.get(url)); --------------------- - +/ -struct TZConversions -{ - /++ - The key is the Windows time zone name, and the value is a list of - IANA TZ database names which are close (currently only ever one, but - it allows for multiple in case it's ever necessary). - +/ - string[][string] toWindows; - - /++ - The key is the IANA time zone database name, and the value is a list of - Windows time zone names which are close (usually only one, but it could - be multiple). - +/ - string[][string] fromWindows; -} - -/++ ditto +/ -TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure -{ - // This is a bit hacky, since it doesn't properly read XML, but it avoids - // needing to pull in std.xml (which we're theoretically replacing at some - // point anyway. - import std.algorithm.iteration : uniq; - import std.algorithm.searching : find; - import std.algorithm.sorting : sort; - import std.array : array, split; - import std.string : lineSplitter; - - string[][string] win2Nix; - string[][string] nix2Win; - - immutable f1 = ` - - line = line.find(f1); - if (line.empty) - continue; - line = line[f1.length .. $]; - auto next = line.find('"'); - enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - auto win = line[0 .. $ - next.length]; - line = next.find(f2); - enforce(!line.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - line = line[f2.length .. $]; - next = line.find('"'); - enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); - auto nixes = line[0 .. $ - next.length].split(); - - if (auto n = win in win2Nix) - *n ~= nixes; - else - win2Nix[win] = nixes; - - foreach (nix; nixes) - { - if (auto w = nix in nix2Win) - *w ~= win; - else - nix2Win[nix] = [win]; - } - } - - foreach (key, ref value; nix2Win) - value = value.sort().uniq().array(); - foreach (key, ref value; win2Nix) - value = value.sort().uniq().array(); - - return TZConversions(nix2Win, win2Nix); -} - -@safe unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : uniq; - import std.algorithm.sorting : isSorted; - - // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - auto sampleFileText = -` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`; - - auto tzConversions = parseTZConversions(sampleFileText); - assert(tzConversions.toWindows.length == 15); - assert(tzConversions.toWindows["America/Anchorage"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Juneau"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Nome"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Sitka"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["America/Yakutat"] == ["Alaskan Standard Time"]); - assert(tzConversions.toWindows["Etc/GMT+10"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Etc/GMT+11"] == ["UTC-11"]); - assert(tzConversions.toWindows["Etc/GMT+12"] == ["Dateline Standard Time"]); - assert(tzConversions.toWindows["Pacific/Honolulu"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Johnston"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Midway"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Niue"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Pago_Pago"] == ["UTC-11"]); - assert(tzConversions.toWindows["Pacific/Rarotonga"] == ["Hawaiian Standard Time"]); - assert(tzConversions.toWindows["Pacific/Tahiti"] == ["Hawaiian Standard Time"]); - - assert(tzConversions.fromWindows.length == 4); - assert(tzConversions.fromWindows["Alaskan Standard Time"] == - ["America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat"]); - assert(tzConversions.fromWindows["Dateline Standard Time"] == ["Etc/GMT+12"]); - assert(tzConversions.fromWindows["Hawaiian Standard Time"] == - ["Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti"]); - assert(tzConversions.fromWindows["UTC-11"] == - ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"]); - - foreach (key, value; tzConversions.fromWindows) - { - assert(value.isSorted, key); - assert(equal(value.uniq(), value), key); - } -} - - -// @@@DEPRECATED_2017-07@@@ -/++ - $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes - their time zones too often for us to compile the conversions into - Phobos and have them be properly up-to-date. - tzDatabaseNameToWindowsTZName will be removed in July 2017.) - - Converts the given TZ Database name to the corresponding Windows time zone - name. - - Note that in a few cases, a TZ Dabatase name corresponds to two different - Windows time zone names. So, while in most cases converting from one to the - other and back again will result in the same time zone name started - with, in a few case, it'll get a different name. - - Also, there are far more TZ Database names than Windows time zones, so some - of the more exotic TZ Database names don't have corresponding Windows time - zone names. - - Returns null if the given time zone name cannot be converted. - - See_Also: - $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - tzName = The TZ Database name to convert. - +/ -deprecated("Use parseTZConversions instead") -string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc -{ - switch (tzName) - { - case "Africa/Abidjan": return "Greenwich Standard Time"; - case "Africa/Accra": return "Greenwich Standard Time"; - case "Africa/Addis_Ababa": return "E. Africa Standard Time"; - case "Africa/Algiers": return "W. Central Africa Standard Time"; - case "Africa/Asmera": return "E. Africa Standard Time"; - case "Africa/Bamako": return "Greenwich Standard Time"; - case "Africa/Bangui": return "W. Central Africa Standard Time"; - case "Africa/Banjul": return "Greenwich Standard Time"; - case "Africa/Bissau": return "Greenwich Standard Time"; - case "Africa/Blantyre": return "South Africa Standard Time"; - case "Africa/Brazzaville": return "W. Central Africa Standard Time"; - case "Africa/Bujumbura": return "South Africa Standard Time"; - case "Africa/Cairo": return "Egypt Standard Time"; - case "Africa/Casablanca": return "Morocco Standard Time"; - case "Africa/Ceuta": return "Romance Standard Time"; - case "Africa/Conakry": return "Greenwich Standard Time"; - case "Africa/Dakar": return "Greenwich Standard Time"; - case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; - case "Africa/Djibouti": return "E. Africa Standard Time"; - case "Africa/Douala": return "W. Central Africa Standard Time"; - case "Africa/El_Aaiun": return "Morocco Standard Time"; - case "Africa/Freetown": return "Greenwich Standard Time"; - case "Africa/Gaborone": return "South Africa Standard Time"; - case "Africa/Harare": return "South Africa Standard Time"; - case "Africa/Johannesburg": return "South Africa Standard Time"; - case "Africa/Juba": return "E. Africa Standard Time"; - case "Africa/Kampala": return "E. Africa Standard Time"; - case "Africa/Khartoum": return "E. Africa Standard Time"; - case "Africa/Kigali": return "South Africa Standard Time"; - case "Africa/Kinshasa": return "W. Central Africa Standard Time"; - case "Africa/Lagos": return "W. Central Africa Standard Time"; - case "Africa/Libreville": return "W. Central Africa Standard Time"; - case "Africa/Lome": return "Greenwich Standard Time"; - case "Africa/Luanda": return "W. Central Africa Standard Time"; - case "Africa/Lubumbashi": return "South Africa Standard Time"; - case "Africa/Lusaka": return "South Africa Standard Time"; - case "Africa/Malabo": return "W. Central Africa Standard Time"; - case "Africa/Maputo": return "South Africa Standard Time"; - case "Africa/Maseru": return "South Africa Standard Time"; - case "Africa/Mbabane": return "South Africa Standard Time"; - case "Africa/Mogadishu": return "E. Africa Standard Time"; - case "Africa/Monrovia": return "Greenwich Standard Time"; - case "Africa/Nairobi": return "E. Africa Standard Time"; - case "Africa/Ndjamena": return "W. Central Africa Standard Time"; - case "Africa/Niamey": return "W. Central Africa Standard Time"; - case "Africa/Nouakchott": return "Greenwich Standard Time"; - case "Africa/Ouagadougou": return "Greenwich Standard Time"; - case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; - case "Africa/Sao_Tome": return "Greenwich Standard Time"; - case "Africa/Tripoli": return "Libya Standard Time"; - case "Africa/Tunis": return "W. Central Africa Standard Time"; - case "Africa/Windhoek": return "Namibia Standard Time"; - case "America/Adak": return "Aleutian Standard Time"; - case "America/Anchorage": return "Alaskan Standard Time"; - case "America/Anguilla": return "SA Western Standard Time"; - case "America/Antigua": return "SA Western Standard Time"; - case "America/Araguaina": return "SA Eastern Standard Time"; - case "America/Argentina/La_Rioja": return "Argentina Standard Time"; - case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; - case "America/Argentina/Salta": return "Argentina Standard Time"; - case "America/Argentina/San_Juan": return "Argentina Standard Time"; - case "America/Argentina/San_Luis": return "Argentina Standard Time"; - case "America/Argentina/Tucuman": return "Argentina Standard Time"; - case "America/Argentina/Ushuaia": return "Argentina Standard Time"; - case "America/Arguaina": return "Tocantins Standard Time"; - case "America/Aruba": return "SA Western Standard Time"; - case "America/Asuncion": return "Paraguay Standard Time"; - case "America/Bahia": return "Bahia Standard Time"; - case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; - case "America/Barbados": return "SA Western Standard Time"; - case "America/Belem": return "SA Eastern Standard Time"; - case "America/Belize": return "Central America Standard Time"; - case "America/Blanc-Sablon": return "SA Western Standard Time"; - case "America/Boa_Vista": return "SA Western Standard Time"; - case "America/Bogota": return "SA Pacific Standard Time"; - case "America/Boise": return "Mountain Standard Time"; - case "America/Buenos_Aires": return "Argentina Standard Time"; - case "America/Cambridge_Bay": return "Mountain Standard Time"; - case "America/Campo_Grande": return "Central Brazilian Standard Time"; - case "America/Cancun": return "Eastern Standard Time (Mexico)"; - case "America/Caracas": return "Venezuela Standard Time"; - case "America/Catamarca": return "Argentina Standard Time"; - case "America/Cayenne": return "SA Eastern Standard Time"; - case "America/Cayman": return "SA Pacific Standard Time"; - case "America/Chicago": return "Central Standard Time"; - case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; - case "America/Coral_Harbour": return "SA Pacific Standard Time"; - case "America/Cordoba": return "Argentina Standard Time"; - case "America/Costa_Rica": return "Central America Standard Time"; - case "America/Creston": return "US Mountain Standard Time"; - case "America/Cuiaba": return "Central Brazilian Standard Time"; - case "America/Curacao": return "SA Western Standard Time"; - case "America/Danmarkshavn": return "UTC"; - case "America/Dawson": return "Pacific Standard Time"; - case "America/Dawson_Creek": return "US Mountain Standard Time"; - case "America/Denver": return "Mountain Standard Time"; - case "America/Detroit": return "Eastern Standard Time"; - case "America/Dominica": return "SA Western Standard Time"; - case "America/Edmonton": return "Mountain Standard Time"; - case "America/Eirunepe": return "SA Pacific Standard Time"; - case "America/El_Salvador": return "Central America Standard Time"; - case "America/Fortaleza": return "SA Eastern Standard Time"; - case "America/Glace_Bay": return "Atlantic Standard Time"; - case "America/Godthab": return "Greenland Standard Time"; - case "America/Goose_Bay": return "Atlantic Standard Time"; - case "America/Grand_Turk": return "Turks And Caicos Standard Time"; - case "America/Grenada": return "SA Western Standard Time"; - case "America/Guadeloupe": return "SA Western Standard Time"; - case "America/Guatemala": return "Central America Standard Time"; - case "America/Guayaquil": return "SA Pacific Standard Time"; - case "America/Guyana": return "SA Western Standard Time"; - case "America/Halifax": return "Atlantic Standard Time"; - case "America/Havana": return "Cuba Standard Time"; - case "America/Hermosillo": return "US Mountain Standard Time"; - case "America/Indiana/Knox": return "Central Standard Time"; - case "America/Indiana/Marengo": return "US Eastern Standard Time"; - case "America/Indiana/Petersburg": return "Eastern Standard Time"; - case "America/Indiana/Tell_City": return "Central Standard Time"; - case "America/Indiana/Vevay": return "US Eastern Standard Time"; - case "America/Indiana/Vincennes": return "Eastern Standard Time"; - case "America/Indiana/Winamac": return "Eastern Standard Time"; - case "America/Indianapolis": return "US Eastern Standard Time"; - case "America/Inuvik": return "Mountain Standard Time"; - case "America/Iqaluit": return "Eastern Standard Time"; - case "America/Jamaica": return "SA Pacific Standard Time"; - case "America/Jujuy": return "Argentina Standard Time"; - case "America/Juneau": return "Alaskan Standard Time"; - case "America/Kentucky/Monticello": return "Eastern Standard Time"; - case "America/Kralendijk": return "SA Western Standard Time"; - case "America/La_Paz": return "SA Western Standard Time"; - case "America/Lima": return "SA Pacific Standard Time"; - case "America/Los_Angeles": return "Pacific Standard Time"; - case "America/Louisville": return "Eastern Standard Time"; - case "America/Lower_Princes": return "SA Western Standard Time"; - case "America/Maceio": return "SA Eastern Standard Time"; - case "America/Managua": return "Central America Standard Time"; - case "America/Manaus": return "SA Western Standard Time"; - case "America/Marigot": return "SA Western Standard Time"; - case "America/Martinique": return "SA Western Standard Time"; - case "America/Matamoros": return "Central Standard Time"; - case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; - case "America/Mendoza": return "Argentina Standard Time"; - case "America/Menominee": return "Central Standard Time"; - case "America/Merida": return "Central Standard Time (Mexico)"; - case "America/Mexico_City": return "Central Standard Time (Mexico)"; - case "America/Miquelon": return "Saint Pierre Standard Time"; - case "America/Moncton": return "Atlantic Standard Time"; - case "America/Monterrey": return "Central Standard Time (Mexico)"; - case "America/Montevideo": return "Montevideo Standard Time"; - case "America/Montreal": return "Eastern Standard Time"; - case "America/Montserrat": return "SA Western Standard Time"; - case "America/Nassau": return "Eastern Standard Time"; - case "America/New_York": return "Eastern Standard Time"; - case "America/Nipigon": return "Eastern Standard Time"; - case "America/Nome": return "Alaskan Standard Time"; - case "America/Noronha": return "UTC-02"; - case "America/North_Dakota/Beulah": return "Central Standard Time"; - case "America/North_Dakota/Center": return "Central Standard Time"; - case "America/North_Dakota/New_Salem": return "Central Standard Time"; - case "America/Ojinaga": return "Mountain Standard Time"; - case "America/Panama": return "SA Pacific Standard Time"; - case "America/Pangnirtung": return "Eastern Standard Time"; - case "America/Paramaribo": return "SA Eastern Standard Time"; - case "America/Phoenix": return "US Mountain Standard Time"; - case "America/Port-au-Prince": return "Haiti Standard Time"; - case "America/Port_of_Spain": return "SA Western Standard Time"; - case "America/Porto_Velho": return "SA Western Standard Time"; - case "America/Puerto_Rico": return "SA Western Standard Time"; - case "America/Rainy_River": return "Central Standard Time"; - case "America/Rankin_Inlet": return "Central Standard Time"; - case "America/Recife": return "SA Eastern Standard Time"; - case "America/Regina": return "Canada Central Standard Time"; - case "America/Resolute": return "Central Standard Time"; - case "America/Rio_Branco": return "SA Pacific Standard Time"; - case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; - case "America/Santarem": return "SA Eastern Standard Time"; - case "America/Santiago": return "Pacific SA Standard Time"; - case "America/Santo_Domingo": return "SA Western Standard Time"; - case "America/Sao_Paulo": return "E. South America Standard Time"; - case "America/Scoresbysund": return "Azores Standard Time"; - case "America/Sitka": return "Alaskan Standard Time"; - case "America/St_Barthelemy": return "SA Western Standard Time"; - case "America/St_Johns": return "Newfoundland Standard Time"; - case "America/St_Kitts": return "SA Western Standard Time"; - case "America/St_Lucia": return "SA Western Standard Time"; - case "America/St_Thomas": return "SA Western Standard Time"; - case "America/St_Vincent": return "SA Western Standard Time"; - case "America/Swift_Current": return "Canada Central Standard Time"; - case "America/Tegucigalpa": return "Central America Standard Time"; - case "America/Thule": return "Atlantic Standard Time"; - case "America/Thunder_Bay": return "Eastern Standard Time"; - case "America/Tijuana": return "Pacific Standard Time"; - case "America/Toronto": return "Eastern Standard Time"; - case "America/Tortola": return "SA Western Standard Time"; - case "America/Vancouver": return "Pacific Standard Time"; - case "America/Whitehorse": return "Pacific Standard Time"; - case "America/Winnipeg": return "Central Standard Time"; - case "America/Yakutat": return "Alaskan Standard Time"; - case "America/Yellowknife": return "Mountain Standard Time"; - case "Antarctica/Casey": return "W. Australia Standard Time"; - case "Antarctica/Davis": return "SE Asia Standard Time"; - case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; - case "Antarctica/Macquarie": return "Central Pacific Standard Time"; - case "Antarctica/Mawson": return "West Asia Standard Time"; - case "Antarctica/McMurdo": return "New Zealand Standard Time"; - case "Antarctica/Palmer": return "Pacific SA Standard Time"; - case "Antarctica/Rothera": return "SA Eastern Standard Time"; - case "Antarctica/Syowa": return "E. Africa Standard Time"; - case "Antarctica/Vostok": return "Central Asia Standard Time"; - case "Arctic/Longyearbyen": return "W. Europe Standard Time"; - case "Asia/Aden": return "Arab Standard Time"; - case "Asia/Almaty": return "Central Asia Standard Time"; - case "Asia/Amman": return "Jordan Standard Time"; - case "Asia/Anadyr": return "Russia Time Zone 11"; - case "Asia/Aqtau": return "West Asia Standard Time"; - case "Asia/Aqtobe": return "West Asia Standard Time"; - case "Asia/Ashgabat": return "West Asia Standard Time"; - case "Asia/Baghdad": return "Arabic Standard Time"; - case "Asia/Bahrain": return "Arab Standard Time"; - case "Asia/Baku": return "Azerbaijan Standard Time"; - case "Asia/Bangkok": return "SE Asia Standard Time"; - case "Asia/Barnaul": return "Altai Standard Time"; - case "Asia/Beirut": return "Middle East Standard Time"; - case "Asia/Bishkek": return "Central Asia Standard Time"; - case "Asia/Brunei": return "Singapore Standard Time"; - case "Asia/Calcutta": return "India Standard Time"; - case "Asia/Chita": return "Transbaikal Standard Time"; - case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; - case "Asia/Colombo": return "Sri Lanka Standard Time"; - case "Asia/Damascus": return "Syria Standard Time"; - case "Asia/Dhaka": return "Bangladesh Standard Time"; - case "Asia/Dili": return "Tokyo Standard Time"; - case "Asia/Dubai": return "Arabian Standard Time"; - case "Asia/Dushanbe": return "West Asia Standard Time"; - case "Asia/Hebron": return "West Bank Standard Time"; - case "Asia/Hong_Kong": return "China Standard Time"; - case "Asia/Hovd": return "W. Mongolia Standard Time"; - case "Asia/Irkutsk": return "North Asia East Standard Time"; - case "Asia/Jakarta": return "SE Asia Standard Time"; - case "Asia/Jayapura": return "Tokyo Standard Time"; - case "Asia/Jerusalem": return "Israel Standard Time"; - case "Asia/Kabul": return "Afghanistan Standard Time"; - case "Asia/Kamchatka": return "Russia Time Zone 11"; - case "Asia/Karachi": return "Pakistan Standard Time"; - case "Asia/Katmandu": return "Nepal Standard Time"; - case "Asia/Khandyga": return "Yakutsk Standard Time"; - case "Asia/Krasnoyarsk": return "North Asia Standard Time"; - case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; - case "Asia/Kuching": return "Singapore Standard Time"; - case "Asia/Kuwait": return "Arab Standard Time"; - case "Asia/Macau": return "China Standard Time"; - case "Asia/Magadan": return "Magadan Standard Time"; - case "Asia/Makassar": return "Singapore Standard Time"; - case "Asia/Manila": return "Singapore Standard Time"; - case "Asia/Muscat": return "Arabian Standard Time"; - case "Asia/Nicosia": return "GTB Standard Time"; - case "Asia/Novokuznetsk": return "North Asia Standard Time"; - case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; - case "Asia/Omsk": return "N. Central Asia Standard Time"; - case "Asia/Oral": return "West Asia Standard Time"; - case "Asia/Phnom_Penh": return "SE Asia Standard Time"; - case "Asia/Pontianak": return "SE Asia Standard Time"; - case "Asia/Pyongyang": return "North Korea Standard Time"; - case "Asia/Qatar": return "Arab Standard Time"; - case "Asia/Qyzylorda": return "Central Asia Standard Time"; - case "Asia/Rangoon": return "Myanmar Standard Time"; - case "Asia/Riyadh": return "Arab Standard Time"; - case "Asia/Saigon": return "SE Asia Standard Time"; - case "Asia/Sakhalin": return "Sakhalin Standard Time"; - case "Asia/Samarkand": return "West Asia Standard Time"; - case "Asia/Seoul": return "Korea Standard Time"; - case "Asia/Shanghai": return "China Standard Time"; - case "Asia/Singapore": return "Singapore Standard Time"; - case "Asia/Srednekolymsk": return "Russia Time Zone 10"; - case "Asia/Taipei": return "Taipei Standard Time"; - case "Asia/Tashkent": return "West Asia Standard Time"; - case "Asia/Tbilisi": return "Georgian Standard Time"; - case "Asia/Tehran": return "Iran Standard Time"; - case "Asia/Thimphu": return "Bangladesh Standard Time"; - case "Asia/Tokyo": return "Tokyo Standard Time"; - case "Asia/Tomsk": return "Tomsk Standard Time"; - case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; - case "Asia/Urumqi": return "Central Asia Standard Time"; - case "Asia/Ust-Nera": return "Vladivostok Standard Time"; - case "Asia/Vientiane": return "SE Asia Standard Time"; - case "Asia/Vladivostok": return "Vladivostok Standard Time"; - case "Asia/Yakutsk": return "Yakutsk Standard Time"; - case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; - case "Asia/Yerevan": return "Caucasus Standard Time"; - case "Atlantic/Azores": return "Azores Standard Time"; - case "Atlantic/Bermuda": return "Atlantic Standard Time"; - case "Atlantic/Canary": return "GMT Standard Time"; - case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; - case "Atlantic/Faeroe": return "GMT Standard Time"; - case "Atlantic/Madeira": return "GMT Standard Time"; - case "Atlantic/Reykjavik": return "Greenwich Standard Time"; - case "Atlantic/South_Georgia": return "UTC-02"; - case "Atlantic/St_Helena": return "Greenwich Standard Time"; - case "Atlantic/Stanley": return "SA Eastern Standard Time"; - case "Australia/Adelaide": return "Cen. Australia Standard Time"; - case "Australia/Brisbane": return "E. Australia Standard Time"; - case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; - case "Australia/Currie": return "Tasmania Standard Time"; - case "Australia/Darwin": return "AUS Central Standard Time"; - case "Australia/Eucla": return "Aus Central W. Standard Time"; - case "Australia/Hobart": return "Tasmania Standard Time"; - case "Australia/Lindeman": return "E. Australia Standard Time"; - case "Australia/Lord_Howe": return "Lord Howe Standard Time"; - case "Australia/Melbourne": return "AUS Eastern Standard Time"; - case "Australia/Perth": return "W. Australia Standard Time"; - case "Australia/Sydney": return "AUS Eastern Standard Time"; - case "CST6CDT": return "Central Standard Time"; - case "EST5EDT": return "Eastern Standard Time"; - case "Etc/GMT": return "UTC"; - case "Etc/GMT+1": return "Cape Verde Standard Time"; - case "Etc/GMT+10": return "Hawaiian Standard Time"; - case "Etc/GMT+11": return "UTC-11"; - case "Etc/GMT+12": return "Dateline Standard Time"; - case "Etc/GMT+2": return "UTC-02"; - case "Etc/GMT+3": return "SA Eastern Standard Time"; - case "Etc/GMT+4": return "SA Western Standard Time"; - case "Etc/GMT+5": return "SA Pacific Standard Time"; - case "Etc/GMT+6": return "Central America Standard Time"; - case "Etc/GMT+7": return "US Mountain Standard Time"; - case "Etc/GMT+8": return "UTC-08"; - case "Etc/GMT+9": return "UTC-09"; - case "Etc/GMT-1": return "W. Central Africa Standard Time"; - case "Etc/GMT-10": return "West Pacific Standard Time"; - case "Etc/GMT-11": return "Central Pacific Standard Time"; - case "Etc/GMT-12": return "UTC+12"; - case "Etc/GMT-13": return "Tonga Standard Time"; - case "Etc/GMT-14": return "Line Islands Standard Time"; - case "Etc/GMT-2": return "South Africa Standard Time"; - case "Etc/GMT-3": return "E. Africa Standard Time"; - case "Etc/GMT-4": return "Arabian Standard Time"; - case "Etc/GMT-5": return "West Asia Standard Time"; - case "Etc/GMT-6": return "Central Asia Standard Time"; - case "Etc/GMT-7": return "SE Asia Standard Time"; - case "Etc/GMT-8": return "Singapore Standard Time"; - case "Etc/GMT-9": return "Tokyo Standard Time"; - case "Europe/Amsterdam": return "W. Europe Standard Time"; - case "Europe/Andorra": return "W. Europe Standard Time"; - case "Europe/Astrakhan": return "Astrakhan Standard Time"; - case "Europe/Athens": return "GTB Standard Time"; - case "Europe/Belgrade": return "Central Europe Standard Time"; - case "Europe/Berlin": return "W. Europe Standard Time"; - case "Europe/Bratislava": return "Central Europe Standard Time"; - case "Europe/Brussels": return "Romance Standard Time"; - case "Europe/Bucharest": return "GTB Standard Time"; - case "Europe/Budapest": return "Central Europe Standard Time"; - case "Europe/Busingen": return "W. Europe Standard Time"; - case "Europe/Chisinau": return "GTB Standard Time"; - case "Europe/Copenhagen": return "Romance Standard Time"; - case "Europe/Dublin": return "GMT Standard Time"; - case "Europe/Gibraltar": return "W. Europe Standard Time"; - case "Europe/Guernsey": return "GMT Standard Time"; - case "Europe/Helsinki": return "FLE Standard Time"; - case "Europe/Isle_of_Man": return "GMT Standard Time"; - case "Europe/Istanbul": return "Turkey Standard Time"; - case "Europe/Jersey": return "GMT Standard Time"; - case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; - case "Europe/Kiev": return "FLE Standard Time"; - case "Europe/Lisbon": return "GMT Standard Time"; - case "Europe/Ljubljana": return "Central Europe Standard Time"; - case "Europe/London": return "GMT Standard Time"; - case "Europe/Luxembourg": return "W. Europe Standard Time"; - case "Europe/Madrid": return "Romance Standard Time"; - case "Europe/Malta": return "W. Europe Standard Time"; - case "Europe/Mariehamn": return "FLE Standard Time"; - case "Europe/Minsk": return "Belarus Standard Time"; - case "Europe/Monaco": return "W. Europe Standard Time"; - case "Europe/Moscow": return "Russian Standard Time"; - case "Europe/Oslo": return "W. Europe Standard Time"; - case "Europe/Paris": return "Romance Standard Time"; - case "Europe/Podgorica": return "Central Europe Standard Time"; - case "Europe/Prague": return "Central Europe Standard Time"; - case "Europe/Riga": return "FLE Standard Time"; - case "Europe/Rome": return "W. Europe Standard Time"; - case "Europe/Samara": return "Russia Time Zone 3"; - case "Europe/San_Marino": return "W. Europe Standard Time"; - case "Europe/Sarajevo": return "Central European Standard Time"; - case "Europe/Simferopol": return "Russian Standard Time"; - case "Europe/Skopje": return "Central European Standard Time"; - case "Europe/Sofia": return "FLE Standard Time"; - case "Europe/Stockholm": return "W. Europe Standard Time"; - case "Europe/Tallinn": return "FLE Standard Time"; - case "Europe/Tirane": return "Central Europe Standard Time"; - case "Europe/Uzhgorod": return "FLE Standard Time"; - case "Europe/Vaduz": return "W. Europe Standard Time"; - case "Europe/Vatican": return "W. Europe Standard Time"; - case "Europe/Vienna": return "W. Europe Standard Time"; - case "Europe/Vilnius": return "FLE Standard Time"; - case "Europe/Volgograd": return "Russian Standard Time"; - case "Europe/Warsaw": return "Central European Standard Time"; - case "Europe/Zagreb": return "Central European Standard Time"; - case "Europe/Zaporozhye": return "FLE Standard Time"; - case "Europe/Zurich": return "W. Europe Standard Time"; - case "Indian/Antananarivo": return "E. Africa Standard Time"; - case "Indian/Chagos": return "Central Asia Standard Time"; - case "Indian/Christmas": return "SE Asia Standard Time"; - case "Indian/Cocos": return "Myanmar Standard Time"; - case "Indian/Comoro": return "E. Africa Standard Time"; - case "Indian/Kerguelen": return "West Asia Standard Time"; - case "Indian/Mahe": return "Mauritius Standard Time"; - case "Indian/Maldives": return "West Asia Standard Time"; - case "Indian/Mauritius": return "Mauritius Standard Time"; - case "Indian/Mayotte": return "E. Africa Standard Time"; - case "Indian/Reunion": return "Mauritius Standard Time"; - case "MST7MDT": return "Mountain Standard Time"; - case "PST8PDT": return "Pacific Standard Time"; - case "Pacific/Apia": return "Samoa Standard Time"; - case "Pacific/Auckland": return "New Zealand Standard Time"; - case "Pacific/Bougainville": return "Bougainville Standard Time"; - case "Pacific/Chatham": return "Chatham Islands Standard Time"; - case "Pacific/Easter": return "Easter Island Standard Time"; - case "Pacific/Efate": return "Central Pacific Standard Time"; - case "Pacific/Enderbury": return "Tonga Standard Time"; - case "Pacific/Fakaofo": return "Tonga Standard Time"; - case "Pacific/Fiji": return "Fiji Standard Time"; - case "Pacific/Funafuti": return "UTC+12"; - case "Pacific/Galapagos": return "Central America Standard Time"; - case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; - case "Pacific/Guam": return "West Pacific Standard Time"; - case "Pacific/Honolulu": return "Hawaiian Standard Time"; - case "Pacific/Johnston": return "Hawaiian Standard Time"; - case "Pacific/Kiritimati": return "Line Islands Standard Time"; - case "Pacific/Kosrae": return "Central Pacific Standard Time"; - case "Pacific/Kwajalein": return "UTC+12"; - case "Pacific/Majuro": return "UTC+12"; - case "Pacific/Marquesas": return "Marquesas Standard Time"; - case "Pacific/Midway": return "UTC-11"; - case "Pacific/Nauru": return "UTC+12"; - case "Pacific/Niue": return "UTC-11"; - case "Pacific/Noumea": return "Central Pacific Standard Time"; - case "Pacific/Norfolk": return "Norfolk Standard Time"; - case "Pacific/Pago_Pago": return "UTC-11"; - case "Pacific/Palau": return "Tokyo Standard Time"; - case "Pacific/Ponape": return "Central Pacific Standard Time"; - case "Pacific/Port_Moresby": return "West Pacific Standard Time"; - case "Pacific/Rarotonga": return "Hawaiian Standard Time"; - case "Pacific/Saipan": return "West Pacific Standard Time"; - case "Pacific/Tahiti": return "Hawaiian Standard Time"; - case "Pacific/Tarawa": return "UTC+12"; - case "Pacific/Tongatapu": return "Tonga Standard Time"; - case "Pacific/Truk": return "West Pacific Standard Time"; - case "Pacific/Wake": return "UTC+12"; - case "Pacific/Wallis": return "UTC+12"; - default: return null; - } -} - -version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest -{ - import std.stdio : stderr; - - foreach (tzName; TimeZone.getInstalledTZNames()) - { - if (tzDatabaseNameToWindowsTZName(tzName) is null) - stderr.writeln("Missing TZName to Windows translation: ", tzName); - } -} - - -// @@@DEPRECATED_2017-07@@@ -/++ - $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes - their time zones too often for us to compile the conversions into - Phobos and have them be properly up-to-date. - windowsTZNameToTZDatabaseName will be removed in July 2017.) - - Converts the given Windows time zone name to a corresponding TZ Database - name. - - Returns null if the given time zone name cannot be converted. - - See_Also: - $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, - Windows <-> TZ Database Name Conversion Table) - - Params: - tzName = The TZ Database name to convert. - +/ -deprecated("Use parseTZConversions instead") -string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc -{ - switch (tzName) - { - case "AUS Central Standard Time": return "Australia/Darwin"; - case "AUS Eastern Standard Time": return "Australia/Sydney"; - case "Aus Central W. Standard Time": return "Australia/Eucla"; - case "Afghanistan Standard Time": return "Asia/Kabul"; - case "Haiti Standard Time": return "America/Port-au-Prince"; - case "Alaskan Standard Time": return "America/Anchorage"; - case "Aleutian Standard Time": return "America/Adak"; - case "Altai Standard Time": return "Asia/Barnaul"; - case "Arab Standard Time": return "Asia/Riyadh"; - case "Arabian Standard Time": return "Asia/Dubai"; - case "Arabic Standard Time": return "Asia/Baghdad"; - case "Argentina Standard Time": return "America/Buenos_Aires"; - case "Astrakhan Standard Time": return "Europe/Astrakhan"; - case "Atlantic Standard Time": return "America/Halifax"; - case "Azerbaijan Standard Time": return "Asia/Baku"; - case "Azores Standard Time": return "Atlantic/Azores"; - case "Bahia Standard Time": return "America/Bahia"; - case "Bangladesh Standard Time": return "Asia/Dhaka"; - case "Belarus Standard Time": return "Europe/Minsk"; - case "Bougainville Standard Time": return "Pacific/Bougainville"; - case "Canada Central Standard Time": return "America/Regina"; - case "Cape Verde Standard Time": return "Atlantic/Cape_Verde"; - case "Caucasus Standard Time": return "Asia/Yerevan"; - case "Cen. Australia Standard Time": return "Australia/Adelaide"; - case "Central America Standard Time": return "America/Guatemala"; - case "Central Asia Standard Time": return "Asia/Almaty"; - case "Central Brazilian Standard Time": return "America/Cuiaba"; - case "Central Europe Standard Time": return "Europe/Budapest"; - case "Central European Standard Time": return "Europe/Warsaw"; - case "Central Pacific Standard Time": return "Pacific/Guadalcanal"; - case "Central Standard Time": return "America/Chicago"; - case "Central Standard Time (Mexico)": return "America/Mexico_City"; - case "Chatham Islands Standard Time": return "Pacific/Chatham"; - case "China Standard Time": return "Asia/Shanghai"; - case "Cuba Standard Time": return "America/Havana"; - case "Dateline Standard Time": return "Etc/GMT+12"; - case "E. Africa Standard Time": return "Africa/Nairobi"; - case "E. Australia Standard Time": return "Australia/Brisbane"; - // This doesn't appear to be in the current stuff from MS, but the autotester - // is failing without it (probably because its time zone data hasn't been - // updated recently enough). - case "E. Europe Standard Time": return "Europe/Minsk"; - case "E. South America Standard Time": return "America/Sao_Paulo"; - case "Easter Island Standard Time": return "Pacific/Easter"; - case "Eastern Standard Time": return "America/New_York"; - case "Eastern Standard Time (Mexico)": return "America/Cancun"; - case "Egypt Standard Time": return "Africa/Cairo"; - case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg"; - case "FLE Standard Time": return "Europe/Kiev"; - case "Fiji Standard Time": return "Pacific/Fiji"; - case "GMT Standard Time": return "Europe/London"; - case "GTB Standard Time": return "Europe/Athens"; - case "Georgian Standard Time": return "Asia/Tbilisi"; - case "Greenland Standard Time": return "America/Godthab"; - case "Greenwich Standard Time": return "Atlantic/Reykjavik"; - case "Hawaiian Standard Time": return "Pacific/Honolulu"; - case "India Standard Time": return "Asia/Calcutta"; - case "Iran Standard Time": return "Asia/Tehran"; - case "Israel Standard Time": return "Asia/Jerusalem"; - case "Jordan Standard Time": return "Asia/Amman"; - case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; - // Same as with E. Europe Standard Time. - case "Kamchatka Standard Time": return "Asia/Kamchatka"; - case "Korea Standard Time": return "Asia/Seoul"; - case "Libya Standard Time": return "Africa/Tripoli"; - case "Line Islands Standard Time": return "Pacific/Kiritimati"; - case "Lord Howe Standard Time": return "Australia/Lord_Howe"; - case "Magadan Standard Time": return "Asia/Magadan"; - case "Marquesas Standard Time": return "Pacific/Marquesas"; - case "Mauritius Standard Time": return "Indian/Mauritius"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time": return "America/Mexico_City"; - // Same as with E. Europe Standard Time. - case "Mexico Standard Time 2": return "America/Chihuahua"; - // Same as with E. Europe Standard Time. - case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; - case "Middle East Standard Time": return "Asia/Beirut"; - case "Montevideo Standard Time": return "America/Montevideo"; - case "Morocco Standard Time": return "Africa/Casablanca"; - case "Mountain Standard Time": return "America/Denver"; - case "Mountain Standard Time (Mexico)": return "America/Chihuahua"; - case "Myanmar Standard Time": return "Asia/Rangoon"; - case "N. Central Asia Standard Time": return "Asia/Novosibirsk"; - case "Namibia Standard Time": return "Africa/Windhoek"; - case "Nepal Standard Time": return "Asia/Katmandu"; - case "New Zealand Standard Time": return "Pacific/Auckland"; - case "Newfoundland Standard Time": return "America/St_Johns"; - case "Norfolk Standard Time": return "Pacific/Norfolk"; - case "North Asia East Standard Time": return "Asia/Irkutsk"; - case "North Asia Standard Time": return "Asia/Krasnoyarsk"; - case "North Korea Standard Time": return "Asia/Pyongyang"; - case "Pacific SA Standard Time": return "America/Santiago"; - case "Pacific Standard Time": return "America/Los_Angeles"; - case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel"; - case "Pakistan Standard Time": return "Asia/Karachi"; - case "Paraguay Standard Time": return "America/Asuncion"; - case "Romance Standard Time": return "Europe/Paris"; - case "Russia Time Zone 10": return "Asia/Srednekolymsk"; - case "Russia Time Zone 11": return "Asia/Anadyr"; - case "Russia Time Zone 3": return "Europe/Samara"; - case "Russian Standard Time": return "Europe/Moscow"; - case "SA Eastern Standard Time": return "America/Cayenne"; - case "SA Pacific Standard Time": return "America/Bogota"; - case "SA Western Standard Time": return "America/La_Paz"; - case "SE Asia Standard Time": return "Asia/Bangkok"; - case "Sakhalin Standard Time": return "Asia/Sakhalin"; - case "Saint Pierre Standard Time": return "America/Miquelon"; - case "Samoa Standard Time": return "Pacific/Apia"; - case "Singapore Standard Time": return "Asia/Singapore"; - case "South Africa Standard Time": return "Africa/Johannesburg"; - case "Sri Lanka Standard Time": return "Asia/Colombo"; - case "Syria Standard Time": return "Asia/Damascus"; - case "Taipei Standard Time": return "Asia/Taipei"; - case "Tasmania Standard Time": return "Australia/Hobart"; - case "Tocantins Standard Time": return "America/Arguaina"; - case "Tokyo Standard Time": return "Asia/Tokyo"; - case "Tomsk Standard Time": return "Asia/Tomsk"; - case "Tonga Standard Time": return "Pacific/Tongatapu"; - case "Transbaikal Standard Time": return "Asia/Chita"; - case "Turkey Standard Time": return "Europe/Istanbul"; - case "Turks And Caicos Standard Time": return "America/Grand_Turk"; - case "US Eastern Standard Time": return "America/Indianapolis"; - case "US Mountain Standard Time": return "America/Phoenix"; - case "UTC": return "Etc/GMT"; - case "UTC+12": return "Etc/GMT-12"; - case "UTC-02": return "Etc/GMT+2"; - case "UTC-08": return "Etc/GMT+8"; - case "UTC-09": return "Etc/GMT+9"; - case "UTC-11": return "Etc/GMT+11"; - case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar"; - case "Venezuela Standard Time": return "America/Caracas"; - case "Vladivostok Standard Time": return "Asia/Vladivostok"; - case "W. Australia Standard Time": return "Australia/Perth"; - case "W. Central Africa Standard Time": return "Africa/Lagos"; - case "W. Europe Standard Time": return "Europe/Berlin"; - case "W. Mongolia Standard Time": return "Asia/Hovd"; - case "West Asia Standard Time": return "Asia/Tashkent"; - case "West Bank Standard Time": return "Asia/Hebron"; - case "West Pacific Standard Time": return "Pacific/Port_Moresby"; - case "Yakutsk Standard Time": return "Asia/Yakutsk"; - default: return null; - } -} - -version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest -{ - import std.stdio : stderr; - - foreach (winName; WindowsTimeZone.getInstalledTZNames()) - { - if (windowsTZNameToTZDatabaseName(winName) is null) - stderr.writeln("Missing Windows to TZName translation: ", winName); - } -} - - //============================================================================== // Section with StopWatch and Benchmark Code. //============================================================================== @@ -22255,149 +21321,3 @@ version(unittest) static assert(!hasUnsharedAliasing!DateTime); static assert(!hasUnsharedAliasing!SysTime); } - - - -// This script is for regenerating tzDatabaseNameToWindowsTZName and -// windowsTZNameToTZDatabaseName from -// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - -/+ -#!/bin/rdmd - -import std.algorithm; -import std.array; -import std.conv; -import std.datetime; -import std.exception; -import std.path; -import std.stdio; -import std.string; - -int main(string[] args) -{ - if (args.length != 4 || args[1].baseName != "windowsZones.xml") - { - stderr.writeln("genTZs.d windowsZones.xml "); - return -1; - } - - string[][string] win2Nix; - string[][string] nix2Win; - immutable f1 = ` %s", nix, wins)); - - // We'll try to eliminate multiples by favoring a conversion if it's already - // in Phobos, but if it's new, then the correct one will have to be chosen - // manually from the results. - string[] haveMultiple; - foreach (win, nixes; win2Nix) - { - if (nixes.length > 1) - haveMultiple ~= win; - } - bool[string] haveConflicts; - foreach (win; haveMultiple) - { - if (auto curr = windowsTZNameToTZDatabaseName(win)) - { - if (auto other = curr in nix2Win) - { - if ((*other)[0] == win) - { - win2Nix[win] = [curr]; - continue; - } - } - } - haveConflicts[win] = true; - writefln("Warning: %s -> %s", win, win2Nix[win]); - } - - - string[] nix2WinLines = [ - `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch (tzName)`, - ` {`]; - - foreach (nix; nix2Win.keys.sort()) - nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); - - nix2WinLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - string[] win2NixLines = [ - `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, - `{`, - ` switch (tzName)`, - ` {`]; - foreach (win; win2Nix.keys.sort()) - { - immutable hasMultiple = cast(bool)(win in haveConflicts); - foreach (nix; win2Nix[win]) - win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); - } - - win2NixLines ~= [ - ` default: return null;`, - ` }`, - `}`]; - - - auto nix2WinFile = args[2]; - std.file.write(nix2WinFile, nix2WinLines.join("\n")); - - auto win2NixFile = args[3]; - std.file.write(win2NixFile, win2NixLines.join("\n")); - - return 0; -} -+/ diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 961b55df84c..a9646aa7144 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -36,8 +36,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : clearTZEnvVar, Clock, Date, DateTime, setTZEnvVar, stdTimeToUnixTime, SysTime, - TimeOfDay; // temporary +import std.datetime : Clock, Date, DateTime, stdTimeToUnixTime, SysTime, TimeOfDay; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time @@ -1464,8 +1463,7 @@ public: } -private: -package: // temporary +package: /+ Returns a time zone as a string with an offset from UTC. @@ -1867,6 +1865,8 @@ package: // temporary } +private: + immutable Duration _utcOffset; } @@ -3257,3 +3257,1077 @@ else version(Windows) TIME_ZONE_INFORMATION _tzInfo; } } + + +version(StdDdoc) +{ + /++ + $(BLUE This function is Posix-Only.) + + Sets the local time zone on Posix systems with the TZ + Database name by setting the TZ environment variable. + + Unfortunately, there is no way to do it on Windows using the TZ + Database name, so this function only exists on Posix systems. + +/ + void setTZEnvVar(string tzDatabaseName) @safe nothrow; + + + /++ + $(BLUE This function is Posix-Only.) + + Clears the TZ environment variable. + +/ + void clearTZEnvVar() @safe nothrow; +} +else version(Posix) +{ + void setTZEnvVar(string tzDatabaseName) @trusted nothrow + { + import core.stdc.time : tzset; + import core.sys.posix.stdlib : setenv; + import std.internal.cstring : tempCString; + import std.path : asNormalizedPath, chainPath; + + version(Android) + auto value = asNormalizedPath(tzDatabaseName); + else + auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName)); + setenv("TZ", value.tempCString(), 1); + tzset(); + } + + + void clearTZEnvVar() @trusted nothrow + { + import core.stdc.time : tzset; + import core.sys.posix.stdlib : unsetenv; + + unsetenv("TZ"); + tzset(); + } +} + + +/++ + Provides the conversions between the IANA time zone database time zone names + (which POSIX systems use) and the time zone names that Windows uses. + + Windows uses a different set of time zone names than the IANA time zone + database does, and how they correspond to one another changes over time + (particularly when Microsoft updates Windows). + $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + provides the current conversions (which may or may not match up with what's + on a particular Windows box depending on how up-to-date it is), and + parseTZConversions reads in those conversions from windowsZones.xml so that + a D program can use those conversions. + + However, it should be noted that the time zone information on Windows is + frequently less accurate than that in the IANA time zone database, and if + someone really wants accurate time zone information, they should use the + IANA time zone database files with $(LREF PosixTimeZone) on Windows rather + than $(LREF WindowsTimeZone), whereas $(LREF WindowsTimeZone) makes more + sense when trying to match what Windows will think the time is in a specific + time zone. + + Also, the IANA time zone database has a lot more time zones than Windows + does. + + Params: + windowsZonesXMLFileText The text from + $(HTTP unicode.org/cldr/data/common/supplemental/windowsZones.xml, windowsZones.xml) + + Throws: + Exception if there is an error while parsing the given XML. + +-------------------- + // Parse the conversions from a local file. + auto text = std.file.readText("path/to/windowsZones.xml"); + auto conversions = parseTZConversions(text); + + // Alternatively, grab the XML file from the web at runtime + // and parse it so that it's guaranteed to be up-to-date, though + // that has the downside that the code needs to worry about the + // site being down or unicode.org changing the URL. + auto url = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml"; + auto conversions2 = parseTZConversions(std.net.curl.get(url)); +-------------------- + +/ +struct TZConversions +{ + /++ + The key is the Windows time zone name, and the value is a list of + IANA TZ database names which are close (currently only ever one, but + it allows for multiple in case it's ever necessary). + +/ + string[][string] toWindows; + + /++ + The key is the IANA time zone database name, and the value is a list of + Windows time zone names which are close (usually only one, but it could + be multiple). + +/ + string[][string] fromWindows; +} + +/++ ditto +/ +TZConversions parseTZConversions(string windowsZonesXMLText) @safe pure +{ + // This is a bit hacky, since it doesn't properly read XML, but it avoids + // needing to pull in std.xml (which we're theoretically replacing at some + // point anyway). + import std.algorithm.iteration : uniq; + import std.algorithm.searching : find; + import std.algorithm.sorting : sort; + import std.array : array, split; + import std.string : lineSplitter; + + string[][string] win2Nix; + string[][string] nix2Win; + + immutable f1 = ` + + line = line.find(f1); + if (line.empty) + continue; + line = line[f1.length .. $]; + auto next = line.find('"'); + enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + auto win = line[0 .. $ - next.length]; + line = next.find(f2); + enforce(!line.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + line = line[f2.length .. $]; + next = line.find('"'); + enforce(!next.empty, "Error parsing. Text does not appear to be from windowsZones.xml"); + auto nixes = line[0 .. $ - next.length].split(); + + if (auto n = win in win2Nix) + *n ~= nixes; + else + win2Nix[win] = nixes; + + foreach (nix; nixes) + { + if (auto w = nix in nix2Win) + *w ~= win; + else + nix2Win[nix] = [win]; + } + } + + foreach (key, ref value; nix2Win) + value = value.sort().uniq().array(); + foreach (key, ref value; win2Nix) + value = value.sort().uniq().array(); + + return TZConversions(nix2Win, win2Nix); +} + +@safe unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : uniq; + import std.algorithm.sorting : isSorted; + + // Reduced text from http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + auto sampleFileText = +` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + + auto tzConversions = parseTZConversions(sampleFileText); + assert(tzConversions.toWindows.length == 15); + assert(tzConversions.toWindows["America/Anchorage"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Juneau"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Nome"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Sitka"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["America/Yakutat"] == ["Alaskan Standard Time"]); + assert(tzConversions.toWindows["Etc/GMT+10"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Etc/GMT+11"] == ["UTC-11"]); + assert(tzConversions.toWindows["Etc/GMT+12"] == ["Dateline Standard Time"]); + assert(tzConversions.toWindows["Pacific/Honolulu"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Johnston"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Midway"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Niue"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Pago_Pago"] == ["UTC-11"]); + assert(tzConversions.toWindows["Pacific/Rarotonga"] == ["Hawaiian Standard Time"]); + assert(tzConversions.toWindows["Pacific/Tahiti"] == ["Hawaiian Standard Time"]); + + assert(tzConversions.fromWindows.length == 4); + assert(tzConversions.fromWindows["Alaskan Standard Time"] == + ["America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat"]); + assert(tzConversions.fromWindows["Dateline Standard Time"] == ["Etc/GMT+12"]); + assert(tzConversions.fromWindows["Hawaiian Standard Time"] == + ["Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti"]); + assert(tzConversions.fromWindows["UTC-11"] == + ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"]); + + foreach (key, value; tzConversions.fromWindows) + { + assert(value.isSorted, key); + assert(equal(value.uniq(), value), key); + } +} + + +// @@@DEPRECATED_2017-07@@@ +/++ + $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. + tzDatabaseNameToWindowsTZName will be removed in July 2017.) + + Converts the given TZ Database name to the corresponding Windows time zone + name. + + Note that in a few cases, a TZ Dabatase name corresponds to two different + Windows time zone names. So, while in most cases converting from one to the + other and back again will result in the same time zone name started + with, in a few case, it'll get a different name. + + Also, there are far more TZ Database names than Windows time zones, so some + of the more exotic TZ Database names don't have corresponding Windows time + zone names. + + Returns null if the given time zone name cannot be converted. + + See_Also: + $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + tzName = The TZ Database name to convert. + +/ +deprecated("Use parseTZConversions instead") +string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc +{ + switch (tzName) + { + case "Africa/Abidjan": return "Greenwich Standard Time"; + case "Africa/Accra": return "Greenwich Standard Time"; + case "Africa/Addis_Ababa": return "E. Africa Standard Time"; + case "Africa/Algiers": return "W. Central Africa Standard Time"; + case "Africa/Asmera": return "E. Africa Standard Time"; + case "Africa/Bamako": return "Greenwich Standard Time"; + case "Africa/Bangui": return "W. Central Africa Standard Time"; + case "Africa/Banjul": return "Greenwich Standard Time"; + case "Africa/Bissau": return "Greenwich Standard Time"; + case "Africa/Blantyre": return "South Africa Standard Time"; + case "Africa/Brazzaville": return "W. Central Africa Standard Time"; + case "Africa/Bujumbura": return "South Africa Standard Time"; + case "Africa/Cairo": return "Egypt Standard Time"; + case "Africa/Casablanca": return "Morocco Standard Time"; + case "Africa/Ceuta": return "Romance Standard Time"; + case "Africa/Conakry": return "Greenwich Standard Time"; + case "Africa/Dakar": return "Greenwich Standard Time"; + case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; + case "Africa/Djibouti": return "E. Africa Standard Time"; + case "Africa/Douala": return "W. Central Africa Standard Time"; + case "Africa/El_Aaiun": return "Morocco Standard Time"; + case "Africa/Freetown": return "Greenwich Standard Time"; + case "Africa/Gaborone": return "South Africa Standard Time"; + case "Africa/Harare": return "South Africa Standard Time"; + case "Africa/Johannesburg": return "South Africa Standard Time"; + case "Africa/Juba": return "E. Africa Standard Time"; + case "Africa/Kampala": return "E. Africa Standard Time"; + case "Africa/Khartoum": return "E. Africa Standard Time"; + case "Africa/Kigali": return "South Africa Standard Time"; + case "Africa/Kinshasa": return "W. Central Africa Standard Time"; + case "Africa/Lagos": return "W. Central Africa Standard Time"; + case "Africa/Libreville": return "W. Central Africa Standard Time"; + case "Africa/Lome": return "Greenwich Standard Time"; + case "Africa/Luanda": return "W. Central Africa Standard Time"; + case "Africa/Lubumbashi": return "South Africa Standard Time"; + case "Africa/Lusaka": return "South Africa Standard Time"; + case "Africa/Malabo": return "W. Central Africa Standard Time"; + case "Africa/Maputo": return "South Africa Standard Time"; + case "Africa/Maseru": return "South Africa Standard Time"; + case "Africa/Mbabane": return "South Africa Standard Time"; + case "Africa/Mogadishu": return "E. Africa Standard Time"; + case "Africa/Monrovia": return "Greenwich Standard Time"; + case "Africa/Nairobi": return "E. Africa Standard Time"; + case "Africa/Ndjamena": return "W. Central Africa Standard Time"; + case "Africa/Niamey": return "W. Central Africa Standard Time"; + case "Africa/Nouakchott": return "Greenwich Standard Time"; + case "Africa/Ouagadougou": return "Greenwich Standard Time"; + case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; + case "Africa/Sao_Tome": return "Greenwich Standard Time"; + case "Africa/Tripoli": return "Libya Standard Time"; + case "Africa/Tunis": return "W. Central Africa Standard Time"; + case "Africa/Windhoek": return "Namibia Standard Time"; + case "America/Adak": return "Aleutian Standard Time"; + case "America/Anchorage": return "Alaskan Standard Time"; + case "America/Anguilla": return "SA Western Standard Time"; + case "America/Antigua": return "SA Western Standard Time"; + case "America/Araguaina": return "SA Eastern Standard Time"; + case "America/Argentina/La_Rioja": return "Argentina Standard Time"; + case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; + case "America/Argentina/Salta": return "Argentina Standard Time"; + case "America/Argentina/San_Juan": return "Argentina Standard Time"; + case "America/Argentina/San_Luis": return "Argentina Standard Time"; + case "America/Argentina/Tucuman": return "Argentina Standard Time"; + case "America/Argentina/Ushuaia": return "Argentina Standard Time"; + case "America/Arguaina": return "Tocantins Standard Time"; + case "America/Aruba": return "SA Western Standard Time"; + case "America/Asuncion": return "Paraguay Standard Time"; + case "America/Bahia": return "Bahia Standard Time"; + case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; + case "America/Barbados": return "SA Western Standard Time"; + case "America/Belem": return "SA Eastern Standard Time"; + case "America/Belize": return "Central America Standard Time"; + case "America/Blanc-Sablon": return "SA Western Standard Time"; + case "America/Boa_Vista": return "SA Western Standard Time"; + case "America/Bogota": return "SA Pacific Standard Time"; + case "America/Boise": return "Mountain Standard Time"; + case "America/Buenos_Aires": return "Argentina Standard Time"; + case "America/Cambridge_Bay": return "Mountain Standard Time"; + case "America/Campo_Grande": return "Central Brazilian Standard Time"; + case "America/Cancun": return "Eastern Standard Time (Mexico)"; + case "America/Caracas": return "Venezuela Standard Time"; + case "America/Catamarca": return "Argentina Standard Time"; + case "America/Cayenne": return "SA Eastern Standard Time"; + case "America/Cayman": return "SA Pacific Standard Time"; + case "America/Chicago": return "Central Standard Time"; + case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; + case "America/Coral_Harbour": return "SA Pacific Standard Time"; + case "America/Cordoba": return "Argentina Standard Time"; + case "America/Costa_Rica": return "Central America Standard Time"; + case "America/Creston": return "US Mountain Standard Time"; + case "America/Cuiaba": return "Central Brazilian Standard Time"; + case "America/Curacao": return "SA Western Standard Time"; + case "America/Danmarkshavn": return "UTC"; + case "America/Dawson": return "Pacific Standard Time"; + case "America/Dawson_Creek": return "US Mountain Standard Time"; + case "America/Denver": return "Mountain Standard Time"; + case "America/Detroit": return "Eastern Standard Time"; + case "America/Dominica": return "SA Western Standard Time"; + case "America/Edmonton": return "Mountain Standard Time"; + case "America/Eirunepe": return "SA Pacific Standard Time"; + case "America/El_Salvador": return "Central America Standard Time"; + case "America/Fortaleza": return "SA Eastern Standard Time"; + case "America/Glace_Bay": return "Atlantic Standard Time"; + case "America/Godthab": return "Greenland Standard Time"; + case "America/Goose_Bay": return "Atlantic Standard Time"; + case "America/Grand_Turk": return "Turks And Caicos Standard Time"; + case "America/Grenada": return "SA Western Standard Time"; + case "America/Guadeloupe": return "SA Western Standard Time"; + case "America/Guatemala": return "Central America Standard Time"; + case "America/Guayaquil": return "SA Pacific Standard Time"; + case "America/Guyana": return "SA Western Standard Time"; + case "America/Halifax": return "Atlantic Standard Time"; + case "America/Havana": return "Cuba Standard Time"; + case "America/Hermosillo": return "US Mountain Standard Time"; + case "America/Indiana/Knox": return "Central Standard Time"; + case "America/Indiana/Marengo": return "US Eastern Standard Time"; + case "America/Indiana/Petersburg": return "Eastern Standard Time"; + case "America/Indiana/Tell_City": return "Central Standard Time"; + case "America/Indiana/Vevay": return "US Eastern Standard Time"; + case "America/Indiana/Vincennes": return "Eastern Standard Time"; + case "America/Indiana/Winamac": return "Eastern Standard Time"; + case "America/Indianapolis": return "US Eastern Standard Time"; + case "America/Inuvik": return "Mountain Standard Time"; + case "America/Iqaluit": return "Eastern Standard Time"; + case "America/Jamaica": return "SA Pacific Standard Time"; + case "America/Jujuy": return "Argentina Standard Time"; + case "America/Juneau": return "Alaskan Standard Time"; + case "America/Kentucky/Monticello": return "Eastern Standard Time"; + case "America/Kralendijk": return "SA Western Standard Time"; + case "America/La_Paz": return "SA Western Standard Time"; + case "America/Lima": return "SA Pacific Standard Time"; + case "America/Los_Angeles": return "Pacific Standard Time"; + case "America/Louisville": return "Eastern Standard Time"; + case "America/Lower_Princes": return "SA Western Standard Time"; + case "America/Maceio": return "SA Eastern Standard Time"; + case "America/Managua": return "Central America Standard Time"; + case "America/Manaus": return "SA Western Standard Time"; + case "America/Marigot": return "SA Western Standard Time"; + case "America/Martinique": return "SA Western Standard Time"; + case "America/Matamoros": return "Central Standard Time"; + case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; + case "America/Mendoza": return "Argentina Standard Time"; + case "America/Menominee": return "Central Standard Time"; + case "America/Merida": return "Central Standard Time (Mexico)"; + case "America/Mexico_City": return "Central Standard Time (Mexico)"; + case "America/Miquelon": return "Saint Pierre Standard Time"; + case "America/Moncton": return "Atlantic Standard Time"; + case "America/Monterrey": return "Central Standard Time (Mexico)"; + case "America/Montevideo": return "Montevideo Standard Time"; + case "America/Montreal": return "Eastern Standard Time"; + case "America/Montserrat": return "SA Western Standard Time"; + case "America/Nassau": return "Eastern Standard Time"; + case "America/New_York": return "Eastern Standard Time"; + case "America/Nipigon": return "Eastern Standard Time"; + case "America/Nome": return "Alaskan Standard Time"; + case "America/Noronha": return "UTC-02"; + case "America/North_Dakota/Beulah": return "Central Standard Time"; + case "America/North_Dakota/Center": return "Central Standard Time"; + case "America/North_Dakota/New_Salem": return "Central Standard Time"; + case "America/Ojinaga": return "Mountain Standard Time"; + case "America/Panama": return "SA Pacific Standard Time"; + case "America/Pangnirtung": return "Eastern Standard Time"; + case "America/Paramaribo": return "SA Eastern Standard Time"; + case "America/Phoenix": return "US Mountain Standard Time"; + case "America/Port-au-Prince": return "Haiti Standard Time"; + case "America/Port_of_Spain": return "SA Western Standard Time"; + case "America/Porto_Velho": return "SA Western Standard Time"; + case "America/Puerto_Rico": return "SA Western Standard Time"; + case "America/Rainy_River": return "Central Standard Time"; + case "America/Rankin_Inlet": return "Central Standard Time"; + case "America/Recife": return "SA Eastern Standard Time"; + case "America/Regina": return "Canada Central Standard Time"; + case "America/Resolute": return "Central Standard Time"; + case "America/Rio_Branco": return "SA Pacific Standard Time"; + case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; + case "America/Santarem": return "SA Eastern Standard Time"; + case "America/Santiago": return "Pacific SA Standard Time"; + case "America/Santo_Domingo": return "SA Western Standard Time"; + case "America/Sao_Paulo": return "E. South America Standard Time"; + case "America/Scoresbysund": return "Azores Standard Time"; + case "America/Sitka": return "Alaskan Standard Time"; + case "America/St_Barthelemy": return "SA Western Standard Time"; + case "America/St_Johns": return "Newfoundland Standard Time"; + case "America/St_Kitts": return "SA Western Standard Time"; + case "America/St_Lucia": return "SA Western Standard Time"; + case "America/St_Thomas": return "SA Western Standard Time"; + case "America/St_Vincent": return "SA Western Standard Time"; + case "America/Swift_Current": return "Canada Central Standard Time"; + case "America/Tegucigalpa": return "Central America Standard Time"; + case "America/Thule": return "Atlantic Standard Time"; + case "America/Thunder_Bay": return "Eastern Standard Time"; + case "America/Tijuana": return "Pacific Standard Time"; + case "America/Toronto": return "Eastern Standard Time"; + case "America/Tortola": return "SA Western Standard Time"; + case "America/Vancouver": return "Pacific Standard Time"; + case "America/Whitehorse": return "Pacific Standard Time"; + case "America/Winnipeg": return "Central Standard Time"; + case "America/Yakutat": return "Alaskan Standard Time"; + case "America/Yellowknife": return "Mountain Standard Time"; + case "Antarctica/Casey": return "W. Australia Standard Time"; + case "Antarctica/Davis": return "SE Asia Standard Time"; + case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; + case "Antarctica/Macquarie": return "Central Pacific Standard Time"; + case "Antarctica/Mawson": return "West Asia Standard Time"; + case "Antarctica/McMurdo": return "New Zealand Standard Time"; + case "Antarctica/Palmer": return "Pacific SA Standard Time"; + case "Antarctica/Rothera": return "SA Eastern Standard Time"; + case "Antarctica/Syowa": return "E. Africa Standard Time"; + case "Antarctica/Vostok": return "Central Asia Standard Time"; + case "Arctic/Longyearbyen": return "W. Europe Standard Time"; + case "Asia/Aden": return "Arab Standard Time"; + case "Asia/Almaty": return "Central Asia Standard Time"; + case "Asia/Amman": return "Jordan Standard Time"; + case "Asia/Anadyr": return "Russia Time Zone 11"; + case "Asia/Aqtau": return "West Asia Standard Time"; + case "Asia/Aqtobe": return "West Asia Standard Time"; + case "Asia/Ashgabat": return "West Asia Standard Time"; + case "Asia/Baghdad": return "Arabic Standard Time"; + case "Asia/Bahrain": return "Arab Standard Time"; + case "Asia/Baku": return "Azerbaijan Standard Time"; + case "Asia/Bangkok": return "SE Asia Standard Time"; + case "Asia/Barnaul": return "Altai Standard Time"; + case "Asia/Beirut": return "Middle East Standard Time"; + case "Asia/Bishkek": return "Central Asia Standard Time"; + case "Asia/Brunei": return "Singapore Standard Time"; + case "Asia/Calcutta": return "India Standard Time"; + case "Asia/Chita": return "Transbaikal Standard Time"; + case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; + case "Asia/Colombo": return "Sri Lanka Standard Time"; + case "Asia/Damascus": return "Syria Standard Time"; + case "Asia/Dhaka": return "Bangladesh Standard Time"; + case "Asia/Dili": return "Tokyo Standard Time"; + case "Asia/Dubai": return "Arabian Standard Time"; + case "Asia/Dushanbe": return "West Asia Standard Time"; + case "Asia/Hebron": return "West Bank Standard Time"; + case "Asia/Hong_Kong": return "China Standard Time"; + case "Asia/Hovd": return "W. Mongolia Standard Time"; + case "Asia/Irkutsk": return "North Asia East Standard Time"; + case "Asia/Jakarta": return "SE Asia Standard Time"; + case "Asia/Jayapura": return "Tokyo Standard Time"; + case "Asia/Jerusalem": return "Israel Standard Time"; + case "Asia/Kabul": return "Afghanistan Standard Time"; + case "Asia/Kamchatka": return "Russia Time Zone 11"; + case "Asia/Karachi": return "Pakistan Standard Time"; + case "Asia/Katmandu": return "Nepal Standard Time"; + case "Asia/Khandyga": return "Yakutsk Standard Time"; + case "Asia/Krasnoyarsk": return "North Asia Standard Time"; + case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; + case "Asia/Kuching": return "Singapore Standard Time"; + case "Asia/Kuwait": return "Arab Standard Time"; + case "Asia/Macau": return "China Standard Time"; + case "Asia/Magadan": return "Magadan Standard Time"; + case "Asia/Makassar": return "Singapore Standard Time"; + case "Asia/Manila": return "Singapore Standard Time"; + case "Asia/Muscat": return "Arabian Standard Time"; + case "Asia/Nicosia": return "GTB Standard Time"; + case "Asia/Novokuznetsk": return "North Asia Standard Time"; + case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; + case "Asia/Omsk": return "N. Central Asia Standard Time"; + case "Asia/Oral": return "West Asia Standard Time"; + case "Asia/Phnom_Penh": return "SE Asia Standard Time"; + case "Asia/Pontianak": return "SE Asia Standard Time"; + case "Asia/Pyongyang": return "North Korea Standard Time"; + case "Asia/Qatar": return "Arab Standard Time"; + case "Asia/Qyzylorda": return "Central Asia Standard Time"; + case "Asia/Rangoon": return "Myanmar Standard Time"; + case "Asia/Riyadh": return "Arab Standard Time"; + case "Asia/Saigon": return "SE Asia Standard Time"; + case "Asia/Sakhalin": return "Sakhalin Standard Time"; + case "Asia/Samarkand": return "West Asia Standard Time"; + case "Asia/Seoul": return "Korea Standard Time"; + case "Asia/Shanghai": return "China Standard Time"; + case "Asia/Singapore": return "Singapore Standard Time"; + case "Asia/Srednekolymsk": return "Russia Time Zone 10"; + case "Asia/Taipei": return "Taipei Standard Time"; + case "Asia/Tashkent": return "West Asia Standard Time"; + case "Asia/Tbilisi": return "Georgian Standard Time"; + case "Asia/Tehran": return "Iran Standard Time"; + case "Asia/Thimphu": return "Bangladesh Standard Time"; + case "Asia/Tokyo": return "Tokyo Standard Time"; + case "Asia/Tomsk": return "Tomsk Standard Time"; + case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; + case "Asia/Urumqi": return "Central Asia Standard Time"; + case "Asia/Ust-Nera": return "Vladivostok Standard Time"; + case "Asia/Vientiane": return "SE Asia Standard Time"; + case "Asia/Vladivostok": return "Vladivostok Standard Time"; + case "Asia/Yakutsk": return "Yakutsk Standard Time"; + case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; + case "Asia/Yerevan": return "Caucasus Standard Time"; + case "Atlantic/Azores": return "Azores Standard Time"; + case "Atlantic/Bermuda": return "Atlantic Standard Time"; + case "Atlantic/Canary": return "GMT Standard Time"; + case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; + case "Atlantic/Faeroe": return "GMT Standard Time"; + case "Atlantic/Madeira": return "GMT Standard Time"; + case "Atlantic/Reykjavik": return "Greenwich Standard Time"; + case "Atlantic/South_Georgia": return "UTC-02"; + case "Atlantic/St_Helena": return "Greenwich Standard Time"; + case "Atlantic/Stanley": return "SA Eastern Standard Time"; + case "Australia/Adelaide": return "Cen. Australia Standard Time"; + case "Australia/Brisbane": return "E. Australia Standard Time"; + case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; + case "Australia/Currie": return "Tasmania Standard Time"; + case "Australia/Darwin": return "AUS Central Standard Time"; + case "Australia/Eucla": return "Aus Central W. Standard Time"; + case "Australia/Hobart": return "Tasmania Standard Time"; + case "Australia/Lindeman": return "E. Australia Standard Time"; + case "Australia/Lord_Howe": return "Lord Howe Standard Time"; + case "Australia/Melbourne": return "AUS Eastern Standard Time"; + case "Australia/Perth": return "W. Australia Standard Time"; + case "Australia/Sydney": return "AUS Eastern Standard Time"; + case "CST6CDT": return "Central Standard Time"; + case "EST5EDT": return "Eastern Standard Time"; + case "Etc/GMT": return "UTC"; + case "Etc/GMT+1": return "Cape Verde Standard Time"; + case "Etc/GMT+10": return "Hawaiian Standard Time"; + case "Etc/GMT+11": return "UTC-11"; + case "Etc/GMT+12": return "Dateline Standard Time"; + case "Etc/GMT+2": return "UTC-02"; + case "Etc/GMT+3": return "SA Eastern Standard Time"; + case "Etc/GMT+4": return "SA Western Standard Time"; + case "Etc/GMT+5": return "SA Pacific Standard Time"; + case "Etc/GMT+6": return "Central America Standard Time"; + case "Etc/GMT+7": return "US Mountain Standard Time"; + case "Etc/GMT+8": return "UTC-08"; + case "Etc/GMT+9": return "UTC-09"; + case "Etc/GMT-1": return "W. Central Africa Standard Time"; + case "Etc/GMT-10": return "West Pacific Standard Time"; + case "Etc/GMT-11": return "Central Pacific Standard Time"; + case "Etc/GMT-12": return "UTC+12"; + case "Etc/GMT-13": return "Tonga Standard Time"; + case "Etc/GMT-14": return "Line Islands Standard Time"; + case "Etc/GMT-2": return "South Africa Standard Time"; + case "Etc/GMT-3": return "E. Africa Standard Time"; + case "Etc/GMT-4": return "Arabian Standard Time"; + case "Etc/GMT-5": return "West Asia Standard Time"; + case "Etc/GMT-6": return "Central Asia Standard Time"; + case "Etc/GMT-7": return "SE Asia Standard Time"; + case "Etc/GMT-8": return "Singapore Standard Time"; + case "Etc/GMT-9": return "Tokyo Standard Time"; + case "Europe/Amsterdam": return "W. Europe Standard Time"; + case "Europe/Andorra": return "W. Europe Standard Time"; + case "Europe/Astrakhan": return "Astrakhan Standard Time"; + case "Europe/Athens": return "GTB Standard Time"; + case "Europe/Belgrade": return "Central Europe Standard Time"; + case "Europe/Berlin": return "W. Europe Standard Time"; + case "Europe/Bratislava": return "Central Europe Standard Time"; + case "Europe/Brussels": return "Romance Standard Time"; + case "Europe/Bucharest": return "GTB Standard Time"; + case "Europe/Budapest": return "Central Europe Standard Time"; + case "Europe/Busingen": return "W. Europe Standard Time"; + case "Europe/Chisinau": return "GTB Standard Time"; + case "Europe/Copenhagen": return "Romance Standard Time"; + case "Europe/Dublin": return "GMT Standard Time"; + case "Europe/Gibraltar": return "W. Europe Standard Time"; + case "Europe/Guernsey": return "GMT Standard Time"; + case "Europe/Helsinki": return "FLE Standard Time"; + case "Europe/Isle_of_Man": return "GMT Standard Time"; + case "Europe/Istanbul": return "Turkey Standard Time"; + case "Europe/Jersey": return "GMT Standard Time"; + case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; + case "Europe/Kiev": return "FLE Standard Time"; + case "Europe/Lisbon": return "GMT Standard Time"; + case "Europe/Ljubljana": return "Central Europe Standard Time"; + case "Europe/London": return "GMT Standard Time"; + case "Europe/Luxembourg": return "W. Europe Standard Time"; + case "Europe/Madrid": return "Romance Standard Time"; + case "Europe/Malta": return "W. Europe Standard Time"; + case "Europe/Mariehamn": return "FLE Standard Time"; + case "Europe/Minsk": return "Belarus Standard Time"; + case "Europe/Monaco": return "W. Europe Standard Time"; + case "Europe/Moscow": return "Russian Standard Time"; + case "Europe/Oslo": return "W. Europe Standard Time"; + case "Europe/Paris": return "Romance Standard Time"; + case "Europe/Podgorica": return "Central Europe Standard Time"; + case "Europe/Prague": return "Central Europe Standard Time"; + case "Europe/Riga": return "FLE Standard Time"; + case "Europe/Rome": return "W. Europe Standard Time"; + case "Europe/Samara": return "Russia Time Zone 3"; + case "Europe/San_Marino": return "W. Europe Standard Time"; + case "Europe/Sarajevo": return "Central European Standard Time"; + case "Europe/Simferopol": return "Russian Standard Time"; + case "Europe/Skopje": return "Central European Standard Time"; + case "Europe/Sofia": return "FLE Standard Time"; + case "Europe/Stockholm": return "W. Europe Standard Time"; + case "Europe/Tallinn": return "FLE Standard Time"; + case "Europe/Tirane": return "Central Europe Standard Time"; + case "Europe/Uzhgorod": return "FLE Standard Time"; + case "Europe/Vaduz": return "W. Europe Standard Time"; + case "Europe/Vatican": return "W. Europe Standard Time"; + case "Europe/Vienna": return "W. Europe Standard Time"; + case "Europe/Vilnius": return "FLE Standard Time"; + case "Europe/Volgograd": return "Russian Standard Time"; + case "Europe/Warsaw": return "Central European Standard Time"; + case "Europe/Zagreb": return "Central European Standard Time"; + case "Europe/Zaporozhye": return "FLE Standard Time"; + case "Europe/Zurich": return "W. Europe Standard Time"; + case "Indian/Antananarivo": return "E. Africa Standard Time"; + case "Indian/Chagos": return "Central Asia Standard Time"; + case "Indian/Christmas": return "SE Asia Standard Time"; + case "Indian/Cocos": return "Myanmar Standard Time"; + case "Indian/Comoro": return "E. Africa Standard Time"; + case "Indian/Kerguelen": return "West Asia Standard Time"; + case "Indian/Mahe": return "Mauritius Standard Time"; + case "Indian/Maldives": return "West Asia Standard Time"; + case "Indian/Mauritius": return "Mauritius Standard Time"; + case "Indian/Mayotte": return "E. Africa Standard Time"; + case "Indian/Reunion": return "Mauritius Standard Time"; + case "MST7MDT": return "Mountain Standard Time"; + case "PST8PDT": return "Pacific Standard Time"; + case "Pacific/Apia": return "Samoa Standard Time"; + case "Pacific/Auckland": return "New Zealand Standard Time"; + case "Pacific/Bougainville": return "Bougainville Standard Time"; + case "Pacific/Chatham": return "Chatham Islands Standard Time"; + case "Pacific/Easter": return "Easter Island Standard Time"; + case "Pacific/Efate": return "Central Pacific Standard Time"; + case "Pacific/Enderbury": return "Tonga Standard Time"; + case "Pacific/Fakaofo": return "Tonga Standard Time"; + case "Pacific/Fiji": return "Fiji Standard Time"; + case "Pacific/Funafuti": return "UTC+12"; + case "Pacific/Galapagos": return "Central America Standard Time"; + case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; + case "Pacific/Guam": return "West Pacific Standard Time"; + case "Pacific/Honolulu": return "Hawaiian Standard Time"; + case "Pacific/Johnston": return "Hawaiian Standard Time"; + case "Pacific/Kiritimati": return "Line Islands Standard Time"; + case "Pacific/Kosrae": return "Central Pacific Standard Time"; + case "Pacific/Kwajalein": return "UTC+12"; + case "Pacific/Majuro": return "UTC+12"; + case "Pacific/Marquesas": return "Marquesas Standard Time"; + case "Pacific/Midway": return "UTC-11"; + case "Pacific/Nauru": return "UTC+12"; + case "Pacific/Niue": return "UTC-11"; + case "Pacific/Noumea": return "Central Pacific Standard Time"; + case "Pacific/Norfolk": return "Norfolk Standard Time"; + case "Pacific/Pago_Pago": return "UTC-11"; + case "Pacific/Palau": return "Tokyo Standard Time"; + case "Pacific/Ponape": return "Central Pacific Standard Time"; + case "Pacific/Port_Moresby": return "West Pacific Standard Time"; + case "Pacific/Rarotonga": return "Hawaiian Standard Time"; + case "Pacific/Saipan": return "West Pacific Standard Time"; + case "Pacific/Tahiti": return "Hawaiian Standard Time"; + case "Pacific/Tarawa": return "UTC+12"; + case "Pacific/Tongatapu": return "Tonga Standard Time"; + case "Pacific/Truk": return "West Pacific Standard Time"; + case "Pacific/Wake": return "UTC+12"; + case "Pacific/Wallis": return "UTC+12"; + default: return null; + } +} + +version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest +{ + import std.stdio : stderr; + + foreach (tzName; TimeZone.getInstalledTZNames()) + { + if (tzDatabaseNameToWindowsTZName(tzName) is null) + stderr.writeln("Missing TZName to Windows translation: ", tzName); + } +} + + +// @@@DEPRECATED_2017-07@@@ +/++ + $(RED Deprecated. Use $(LREF parseTZConversions) instead. Microsoft changes + their time zones too often for us to compile the conversions into + Phobos and have them be properly up-to-date. + windowsTZNameToTZDatabaseName will be removed in July 2017.) + + Converts the given Windows time zone name to a corresponding TZ Database + name. + + Returns null if the given time zone name cannot be converted. + + See_Also: + $(HTTP unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, + Windows <-> TZ Database Name Conversion Table) + + Params: + tzName = The TZ Database name to convert. + +/ +deprecated("Use parseTZConversions instead") +string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc +{ + switch (tzName) + { + case "AUS Central Standard Time": return "Australia/Darwin"; + case "AUS Eastern Standard Time": return "Australia/Sydney"; + case "Aus Central W. Standard Time": return "Australia/Eucla"; + case "Afghanistan Standard Time": return "Asia/Kabul"; + case "Haiti Standard Time": return "America/Port-au-Prince"; + case "Alaskan Standard Time": return "America/Anchorage"; + case "Aleutian Standard Time": return "America/Adak"; + case "Altai Standard Time": return "Asia/Barnaul"; + case "Arab Standard Time": return "Asia/Riyadh"; + case "Arabian Standard Time": return "Asia/Dubai"; + case "Arabic Standard Time": return "Asia/Baghdad"; + case "Argentina Standard Time": return "America/Buenos_Aires"; + case "Astrakhan Standard Time": return "Europe/Astrakhan"; + case "Atlantic Standard Time": return "America/Halifax"; + case "Azerbaijan Standard Time": return "Asia/Baku"; + case "Azores Standard Time": return "Atlantic/Azores"; + case "Bahia Standard Time": return "America/Bahia"; + case "Bangladesh Standard Time": return "Asia/Dhaka"; + case "Belarus Standard Time": return "Europe/Minsk"; + case "Bougainville Standard Time": return "Pacific/Bougainville"; + case "Canada Central Standard Time": return "America/Regina"; + case "Cape Verde Standard Time": return "Atlantic/Cape_Verde"; + case "Caucasus Standard Time": return "Asia/Yerevan"; + case "Cen. Australia Standard Time": return "Australia/Adelaide"; + case "Central America Standard Time": return "America/Guatemala"; + case "Central Asia Standard Time": return "Asia/Almaty"; + case "Central Brazilian Standard Time": return "America/Cuiaba"; + case "Central Europe Standard Time": return "Europe/Budapest"; + case "Central European Standard Time": return "Europe/Warsaw"; + case "Central Pacific Standard Time": return "Pacific/Guadalcanal"; + case "Central Standard Time": return "America/Chicago"; + case "Central Standard Time (Mexico)": return "America/Mexico_City"; + case "Chatham Islands Standard Time": return "Pacific/Chatham"; + case "China Standard Time": return "Asia/Shanghai"; + case "Cuba Standard Time": return "America/Havana"; + case "Dateline Standard Time": return "Etc/GMT+12"; + case "E. Africa Standard Time": return "Africa/Nairobi"; + case "E. Australia Standard Time": return "Australia/Brisbane"; + // This doesn't appear to be in the current stuff from MS, but the autotester + // is failing without it (probably because its time zone data hasn't been + // updated recently enough). + case "E. Europe Standard Time": return "Europe/Minsk"; + case "E. South America Standard Time": return "America/Sao_Paulo"; + case "Easter Island Standard Time": return "Pacific/Easter"; + case "Eastern Standard Time": return "America/New_York"; + case "Eastern Standard Time (Mexico)": return "America/Cancun"; + case "Egypt Standard Time": return "Africa/Cairo"; + case "Ekaterinburg Standard Time": return "Asia/Yekaterinburg"; + case "FLE Standard Time": return "Europe/Kiev"; + case "Fiji Standard Time": return "Pacific/Fiji"; + case "GMT Standard Time": return "Europe/London"; + case "GTB Standard Time": return "Europe/Athens"; + case "Georgian Standard Time": return "Asia/Tbilisi"; + case "Greenland Standard Time": return "America/Godthab"; + case "Greenwich Standard Time": return "Atlantic/Reykjavik"; + case "Hawaiian Standard Time": return "Pacific/Honolulu"; + case "India Standard Time": return "Asia/Calcutta"; + case "Iran Standard Time": return "Asia/Tehran"; + case "Israel Standard Time": return "Asia/Jerusalem"; + case "Jordan Standard Time": return "Asia/Amman"; + case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; + // Same as with E. Europe Standard Time. + case "Kamchatka Standard Time": return "Asia/Kamchatka"; + case "Korea Standard Time": return "Asia/Seoul"; + case "Libya Standard Time": return "Africa/Tripoli"; + case "Line Islands Standard Time": return "Pacific/Kiritimati"; + case "Lord Howe Standard Time": return "Australia/Lord_Howe"; + case "Magadan Standard Time": return "Asia/Magadan"; + case "Marquesas Standard Time": return "Pacific/Marquesas"; + case "Mauritius Standard Time": return "Indian/Mauritius"; + // Same as with E. Europe Standard Time. + case "Mexico Standard Time": return "America/Mexico_City"; + // Same as with E. Europe Standard Time. + case "Mexico Standard Time 2": return "America/Chihuahua"; + // Same as with E. Europe Standard Time. + case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; + case "Middle East Standard Time": return "Asia/Beirut"; + case "Montevideo Standard Time": return "America/Montevideo"; + case "Morocco Standard Time": return "Africa/Casablanca"; + case "Mountain Standard Time": return "America/Denver"; + case "Mountain Standard Time (Mexico)": return "America/Chihuahua"; + case "Myanmar Standard Time": return "Asia/Rangoon"; + case "N. Central Asia Standard Time": return "Asia/Novosibirsk"; + case "Namibia Standard Time": return "Africa/Windhoek"; + case "Nepal Standard Time": return "Asia/Katmandu"; + case "New Zealand Standard Time": return "Pacific/Auckland"; + case "Newfoundland Standard Time": return "America/St_Johns"; + case "Norfolk Standard Time": return "Pacific/Norfolk"; + case "North Asia East Standard Time": return "Asia/Irkutsk"; + case "North Asia Standard Time": return "Asia/Krasnoyarsk"; + case "North Korea Standard Time": return "Asia/Pyongyang"; + case "Pacific SA Standard Time": return "America/Santiago"; + case "Pacific Standard Time": return "America/Los_Angeles"; + case "Pacific Standard Time (Mexico)": return "America/Santa_Isabel"; + case "Pakistan Standard Time": return "Asia/Karachi"; + case "Paraguay Standard Time": return "America/Asuncion"; + case "Romance Standard Time": return "Europe/Paris"; + case "Russia Time Zone 10": return "Asia/Srednekolymsk"; + case "Russia Time Zone 11": return "Asia/Anadyr"; + case "Russia Time Zone 3": return "Europe/Samara"; + case "Russian Standard Time": return "Europe/Moscow"; + case "SA Eastern Standard Time": return "America/Cayenne"; + case "SA Pacific Standard Time": return "America/Bogota"; + case "SA Western Standard Time": return "America/La_Paz"; + case "SE Asia Standard Time": return "Asia/Bangkok"; + case "Sakhalin Standard Time": return "Asia/Sakhalin"; + case "Saint Pierre Standard Time": return "America/Miquelon"; + case "Samoa Standard Time": return "Pacific/Apia"; + case "Singapore Standard Time": return "Asia/Singapore"; + case "South Africa Standard Time": return "Africa/Johannesburg"; + case "Sri Lanka Standard Time": return "Asia/Colombo"; + case "Syria Standard Time": return "Asia/Damascus"; + case "Taipei Standard Time": return "Asia/Taipei"; + case "Tasmania Standard Time": return "Australia/Hobart"; + case "Tocantins Standard Time": return "America/Arguaina"; + case "Tokyo Standard Time": return "Asia/Tokyo"; + case "Tomsk Standard Time": return "Asia/Tomsk"; + case "Tonga Standard Time": return "Pacific/Tongatapu"; + case "Transbaikal Standard Time": return "Asia/Chita"; + case "Turkey Standard Time": return "Europe/Istanbul"; + case "Turks And Caicos Standard Time": return "America/Grand_Turk"; + case "US Eastern Standard Time": return "America/Indianapolis"; + case "US Mountain Standard Time": return "America/Phoenix"; + case "UTC": return "Etc/GMT"; + case "UTC+12": return "Etc/GMT-12"; + case "UTC-02": return "Etc/GMT+2"; + case "UTC-08": return "Etc/GMT+8"; + case "UTC-09": return "Etc/GMT+9"; + case "UTC-11": return "Etc/GMT+11"; + case "Ulaanbaatar Standard Time": return "Asia/Ulaanbaatar"; + case "Venezuela Standard Time": return "America/Caracas"; + case "Vladivostok Standard Time": return "Asia/Vladivostok"; + case "W. Australia Standard Time": return "Australia/Perth"; + case "W. Central Africa Standard Time": return "Africa/Lagos"; + case "W. Europe Standard Time": return "Europe/Berlin"; + case "W. Mongolia Standard Time": return "Asia/Hovd"; + case "West Asia Standard Time": return "Asia/Tashkent"; + case "West Bank Standard Time": return "Asia/Hebron"; + case "West Pacific Standard Time": return "Pacific/Port_Moresby"; + case "Yakutsk Standard Time": return "Asia/Yakutsk"; + default: return null; + } +} + +version(Windows) version(UpdateWindowsTZTranslations) deprecated @system unittest +{ + import std.stdio : stderr; + + foreach (winName; WindowsTimeZone.getInstalledTZNames()) + { + if (windowsTZNameToTZDatabaseName(winName) is null) + stderr.writeln("Missing Windows to TZName translation: ", winName); + } +} + + +// This script is for regenerating tzDatabaseNameToWindowsTZName and +// windowsTZNameToTZDatabaseName from +// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + +/+ +#!/bin/rdmd + +import std.algorithm; +import std.array; +import std.conv; +import std.datetime; +import std.exception; +import std.path; +import std.stdio; +import std.string; + +int main(string[] args) +{ + if (args.length != 4 || args[1].baseName != "windowsZones.xml") + { + stderr.writeln("genTZs.d windowsZones.xml "); + return -1; + } + + string[][string] win2Nix; + string[][string] nix2Win; + immutable f1 = ` %s", nix, wins)); + + // We'll try to eliminate multiples by favoring a conversion if it's already + // in Phobos, but if it's new, then the correct one will have to be chosen + // manually from the results. + string[] haveMultiple; + foreach (win, nixes; win2Nix) + { + if (nixes.length > 1) + haveMultiple ~= win; + } + bool[string] haveConflicts; + foreach (win; haveMultiple) + { + if (auto curr = windowsTZNameToTZDatabaseName(win)) + { + if (auto other = curr in nix2Win) + { + if ((*other)[0] == win) + { + win2Nix[win] = [curr]; + continue; + } + } + } + haveConflicts[win] = true; + writefln("Warning: %s -> %s", win, win2Nix[win]); + } + + + string[] nix2WinLines = [ + `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch (tzName)`, + ` {`]; + + foreach (nix; nix2Win.keys.sort()) + nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); + + nix2WinLines ~= [ + ` default: return null;`, + ` }`, + `}`]; + + + string[] win2NixLines = [ + `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch (tzName)`, + ` {`]; + foreach (win; win2Nix.keys.sort()) + { + immutable hasMultiple = cast(bool)(win in haveConflicts); + foreach (nix; win2Nix[win]) + win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); + } + + win2NixLines ~= [ + ` default: return null;`, + ` }`, + `}`]; + + + auto nix2WinFile = args[2]; + std.file.write(nix2WinFile, nix2WinLines.join("\n")); + + auto win2NixFile = args[3]; + std.file.write(win2NixFile, win2NixLines.join("\n")); + + return 0; +} ++/ From 657b356db085e79ddf456b4a7625d7796b7c6b1a Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 11:41:26 +0200 Subject: [PATCH 098/262] Move TimeOfDay to std.datetime.timeofday. --- std/datetime/common.d | 164 ++++- std/datetime/interval.d | 40 +- std/datetime/package.d | 1446 -------------------------------------- std/datetime/timeofday.d | 1316 ++++++++++++++++++++++++++++++++++ std/datetime/timezone.d | 2 +- 5 files changed, 1506 insertions(+), 1462 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index 7f864c3c542..f09fc4547b6 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -215,7 +215,7 @@ int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow return 0; if (currDoW < dow) return dow - currDoW; - return (DayOfWeek.sat - currDoW) + dow + 1; + return DayOfWeek.sat - currDoW + dow + 1; } @safe unittest @@ -295,7 +295,7 @@ int monthsToMonth(int currMonth, int month) @safe pure return 0; if (currMonth < month) return month - currMonth; - return (Month.dec - currMonth) + month; + return Month.dec - currMonth + month; } @safe unittest @@ -373,7 +373,7 @@ bool yearIsLeapYear(int year) @safe pure nothrow { import std.format : format; foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, - 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) + 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) { assert(!yearIsLeapYear(year), format("year: %s.", year)); assert(!yearIsLeapYear(-year), format("year: %s.", year)); @@ -455,14 +455,14 @@ private: @safe unittest { import core.time : Duration; - import std.datetime : Date, DateTime, Interval, SysTime, TimeOfDay; // temporary + import std.datetime : Date, DateTime, Interval, SysTime; // temporary /+ import std.datetime.date : Date; import std.datetime.datetime : DateTime; import std.datetime.interval : Interval; import std.datetime.systime : SysTime; - import std.datetime.timeofday : TimeOfDay; +/ + import std.datetime.timeofday; static assert(isTimePoint!Date); static assert(isTimePoint!DateTime); @@ -477,14 +477,12 @@ private: @safe unittest { import core.time; - import std.datetime : Date, DateTime, Interval, NegInfInterval, PosInfInterval, SysTime, TimeOfDay; // temporary - /+ - import std.datetime.date; - import std.datetime.datetime; + import std.datetime : Date, DateTime, SysTime; // temporary + //import std.datetime.date; + //import std.datetime.datetime; import std.datetime.interval; - import std.datetime.systime; + //import std.datetime.systime; import std.datetime.timeofday; - +/ import std.meta : AliasSeq; foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) @@ -498,6 +496,150 @@ private: } +/++ + Whether all of the given strings are valid units of time. + + $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime + can handle precision greater than hnsecs, and the few functions in core.time + which deal with "nsecs" deal with it explicitly. + +/ +bool validTimeUnits(string[] units...) @safe pure nothrow +{ + import std.algorithm.searching : canFind; + foreach (str; units) + { + if (!canFind(timeStrings[], str)) + return false; + } + return true; +} + + +/++ + Compares two time unit strings. $(D "years") are the largest units and + $(D "hnsecs") are the smallest. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + + Throws: + $(LREF DateTimeException) if either of the given strings is not a valid + time unit string. + +/ +int cmpTimeUnits(string lhs, string rhs) @safe pure +{ + import std.algorithm.searching : countUntil; + import std.exception : enforce; + import std.format : format; + + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); + enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + foreach (i, outerUnits; timeStrings) + { + assert(cmpTimeUnits(outerUnits, outerUnits) == 0); + + // For some reason, $ won't compile. + foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length]) + assert(cmpTimeUnits(outerUnits, innerUnits) == -1); + } + + foreach (i, outerUnits; timeStrings) + { + foreach (innerUnits; timeStrings[0 .. i]) + assert(cmpTimeUnits(outerUnits, innerUnits) == 1); + } +} + + +/++ + Compares two time unit strings at compile time. $(D "years") are the largest + units and $(D "hnsecs") are the smallest. + + This template is used instead of $(D cmpTimeUnits) because exceptions + can't be thrown at compile time and $(D cmpTimeUnits) must enforce that + the strings it's given are valid time unit strings. This template uses a + template constraint instead. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ +template CmpTimeUnits(string lhs, string rhs) +if (validTimeUnits(lhs, rhs)) +{ + enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); +} + + +/+ + Helper function for $(D CmpTimeUnits). + +/ +private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow +{ + import std.algorithm.searching : countUntil; + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + import std.format : format; + import std.meta : AliasSeq; + + static string genTest(size_t index) + { + auto currUnits = timeStrings[index]; + auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); + + foreach (units; timeStrings[index + 1 .. $]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); + + foreach (units; timeStrings[0 .. index]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); + + return test; + } + + static assert(timeStrings.length == 10); + foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + mixin(genTest(n)); +} + + +//============================================================================== +// Section with package-level helper functions and templates. +//============================================================================== + package: /+ diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 74473fb7c31..03c8594d2b5 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -17,7 +17,7 @@ import std.typecons : Flag; version(unittest) import std.exception : assertThrown; -import std.datetime : Date, DateTime, SysTime, TimeOfDay; // temporary +import std.datetime : Date, DateTime, SysTime; // temporary /++ @@ -1564,6 +1564,8 @@ private: // Test Interval's constructors. @safe unittest { + import std.datetime.timeofday; + assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); Interval!Date(Date.init, Date.init); @@ -1622,6 +1624,8 @@ private: // Test Interval's length. @safe unittest { + import std.datetime.timeofday; + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); @@ -1642,6 +1646,8 @@ private: // Test Interval's empty. @safe unittest { + import std.datetime.timeofday; + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); @@ -4071,6 +4077,8 @@ private: //Test PosInfInterval's constructor. @safe unittest { + import std.datetime.timeofday; + PosInfInterval!Date(Date.init); PosInfInterval!TimeOfDay(TimeOfDay.init); PosInfInterval!DateTime(DateTime.init); @@ -4099,6 +4107,8 @@ private: //Test PosInfInterval's empty. @safe unittest { + import std.datetime.timeofday; + assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); @@ -6244,6 +6254,8 @@ private: //Test NegInfInterval's constructor. @safe unittest { + import std.datetime.timeofday; + NegInfInterval!Date(Date.init); NegInfInterval!TimeOfDay(TimeOfDay.init); NegInfInterval!DateTime(DateTime.init); @@ -6269,6 +6281,8 @@ private: //Test NegInfInterval's empty. @safe unittest { + import std.datetime.timeofday; + assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); @@ -7457,6 +7471,8 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.timeofday; + auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); @@ -7484,9 +7500,9 @@ if (isTimePoint!TP && assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); - static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); - assert(everyDayOfWeek!(DateTime)(DayOfWeek.mon) !is null); - assert(everyDayOfWeek!(SysTime)(DayOfWeek.mon) !is null); + static assert(!__traits(compiles, everyDayOfWeek!TimeOfDay(DayOfWeek.mon))); + assert(everyDayOfWeek!DateTime(DayOfWeek.mon) !is null); + assert(everyDayOfWeek!SysTime(DayOfWeek.mon) !is null); } @@ -7677,6 +7693,8 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.timeofday; + auto funcFwd = everyDuration!Date(dur!"days"(27)); auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); @@ -7796,6 +7814,8 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.timeofday; + { auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); @@ -8073,7 +8093,9 @@ private: //Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.timeofday; import std.range.primitives; + static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); @@ -8097,6 +8119,8 @@ private: //Test construction of IntervalRange. @safe unittest { + import std.datetime.timeofday; + { Date dateFunc(in Date date) { return date; } auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -8495,7 +8519,9 @@ private: //Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.timeofday; import std.range.primitives; + static assert(isInputRange!(PosInfIntervalRange!Date)); static assert(isForwardRange!(PosInfIntervalRange!Date)); static assert(isInfinite!(PosInfIntervalRange!Date)); @@ -8518,6 +8544,8 @@ private: //Test construction of PosInfIntervalRange. @safe unittest { + import std.datetime.timeofday; + { Date dateFunc(in Date date) { return date; } auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); @@ -8762,7 +8790,9 @@ private: //Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.timeofday; import std.range.primitives; + static assert(isInputRange!(NegInfIntervalRange!Date)); static assert(isForwardRange!(NegInfIntervalRange!Date)); static assert(isInfinite!(NegInfIntervalRange!Date)); @@ -8784,6 +8814,8 @@ private: //Test construction of NegInfIntervalRange. @safe unittest { + import std.datetime.timeofday; + { Date dateFunc(in Date date) { return date; } auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); diff --git a/std/datetime/package.d b/std/datetime/package.d index 88f00119cf6..165bf458d8b 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -13435,1276 +13435,6 @@ private: } - -/++ - Represents a time of day with hours, minutes, and seconds. It uses 24 hour - time. -+/ -struct TimeOfDay -{ -public: - - /++ - Params: - hour = Hour of the day [0 - 24$(RPAREN). - minute = Minute of the hour [0 - 60$(RPAREN). - second = Second of the minute [0 - 60$(RPAREN). - - Throws: - $(LREF DateTimeException) if the resulting $(LREF TimeOfDay) would be not - be valid. - +/ - this(int hour, int minute, int second = 0) @safe pure - { - enforceValid!"hours"(hour); - enforceValid!"minutes"(minute); - enforceValid!"seconds"(second); - - _hour = cast(ubyte) hour; - _minute = cast(ubyte) minute; - _second = cast(ubyte) second; - } - - @safe unittest - { - assert(TimeOfDay(0, 0) == TimeOfDay.init); - - { - auto tod = TimeOfDay(0, 0); - assert(tod._hour == 0); - assert(tod._minute == 0); - assert(tod._second == 0); - } - - { - auto tod = TimeOfDay(12, 30, 33); - assert(tod._hour == 12); - assert(tod._minute == 30); - assert(tod._second == 33); - } - - { - auto tod = TimeOfDay(23, 59, 59); - assert(tod._hour == 23); - assert(tod._minute == 59); - assert(tod._second == 59); - } - - assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); - } - - - /++ - Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in TimeOfDay rhs) @safe const pure nothrow - { - if (_hour < rhs._hour) - return -1; - if (_hour > rhs._hour) - return 1; - - if (_minute < rhs._minute) - return -1; - if (_minute > rhs._minute) - return 1; - - if (_second < rhs._second) - return -1; - if (_second > rhs._second) - return 1; - - return 0; - } - - @safe unittest - { - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); - - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); - assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); - assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); - - assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); - - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(ctod.opCmp(itod) == 0); - assert(itod.opCmp(ctod) == 0); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _hour; - } - - @safe unittest - { - assert(TimeOfDay.init.hour == 0); - assert(TimeOfDay(12, 0, 0).hour == 12); - - const ctod = TimeOfDay(12, 0, 0); - immutable itod = TimeOfDay(12, 0, 0); - assert(ctod.hour == 12); - assert(itod.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF TimeOfDay). - +/ - @property void hour(int hour) @safe pure - { - enforceValid!"hours"(hour); - _hour = cast(ubyte) hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.hour = 12; - assert(tod == TimeOfDay(12, 0, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.hour = 12)); - static assert(!__traits(compiles, itod.hour = 12)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _minute; - } - - @safe unittest - { - assert(TimeOfDay.init.minute == 0); - assert(TimeOfDay(0, 30, 0).minute == 30); - - const ctod = TimeOfDay(0, 30, 0); - immutable itod = TimeOfDay(0, 30, 0); - assert(ctod.minute == 30); - assert(itod.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF TimeOfDay)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF TimeOfDay). - +/ - @property void minute(int minute) @safe pure - { - enforceValid!"minutes"(minute); - _minute = cast(ubyte) minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.minute = 30; - assert(tod == TimeOfDay(0, 30, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.minute = 30)); - static assert(!__traits(compiles, itod.minute = 30)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _second; - } - - @safe unittest - { - assert(TimeOfDay.init.second == 0); - assert(TimeOfDay(0, 0, 33).second == 33); - - const ctod = TimeOfDay(0, 0, 33); - immutable itod = TimeOfDay(0, 0, 33); - assert(ctod.second == 33); - assert(itod.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF TimeOfDay)'s second to. - - Throws: - $(LREF DateTimeException) if the given second would result in an - invalid $(LREF TimeOfDay). - +/ - @property void second(int second) @safe pure - { - enforceValid!"seconds"(second); - _second = cast(ubyte) second; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.second = 33; - assert(tod == TimeOfDay(0, 0, 33)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.second = 33)); - static assert(!__traits(compiles, itod.second = 33)); - } - - - /++ - Adds the given number of units to this $(LREF TimeOfDay). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF TimeOfDay) - one hours's worth of minutes gets the exact same - $(LREF TimeOfDay). - - Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF TimeOfDay). - +/ - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if (units == "hours") - { - return this += dur!"hours"(value); - } - - /// - @safe unittest - { - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"hours"(1); - assert(tod1 == TimeOfDay(8, 12, 0)); - - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"hours"(-1); - assert(tod2 == TimeOfDay(6, 12, 0)); - - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); - - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); - - auto tod5 = TimeOfDay(23, 59, 59); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(23, 59, 0)); - - auto tod6 = TimeOfDay(0, 0, 0); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(0, 0, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"hours"(22).roll!"hours"(-7); - assert(tod == TimeOfDay(3, 27, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"hours"(53))); - static assert(!__traits(compiles, itod.roll!"hours"(53))); - } - - - //Shares documentation with "hours" version. - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if (units == "minutes" || - units == "seconds") - { - import std.format : format; - - enum memberVarStr = units[0 .. $ - 1]; - value %= 60; - mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); - - if (value < 0) - { - if (newVal < 0) - newVal += 60; - } - else if (newVal >= 60) - newVal -= 60; - - mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); - return this; - } - - //Test roll!"minutes"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); - - testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); - - testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); - - testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); - testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); - testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); - - testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); - testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); - testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); - - testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); - testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); - testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"minutes"(97).roll!"minutes"(-102); - assert(tod == TimeOfDay(12, 22, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"minutes"(7))); - static assert(!__traits(compiles, itod.roll!"minutes"(7))); - } - - //Test roll!"seconds"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"seconds"(105).roll!"seconds"(-77); - assert(tod == TimeOfDay(12, 27, 30)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"seconds"(7))); - static assert(!__traits(compiles, itod.roll!"seconds"(7))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - TimeOfDay retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); - assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); - assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); - assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); - - assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); - assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); - assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); - assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto duration = dur!"hours"(11); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod + duration == TimeOfDay(23, 30, 33)); - assert(ctod + duration == TimeOfDay(23, 30, 33)); - assert(itod + duration == TimeOfDay(23, 30, 33)); - - assert(tod - duration == TimeOfDay(1, 30, 33)); - assert(ctod - duration == TimeOfDay(1, 30, 33)); - assert(itod - duration == TimeOfDay(1, 30, 33)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - TimeOfDay retval = this; - immutable seconds = td.seconds; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto tod = TimeOfDay(12, 30, 33); - - assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - - assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF TimeOfDay), as well as assigning the result to this - $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable seconds = duration.total!"seconds"; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - @safe unittest - { - auto duration = dur!"hours"(12); - - assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto tod = TimeOfDay(19, 17, 22); - (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); - assert(tod == TimeOfDay(17, 15, 59)); - - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(!__traits(compiles, ctod += duration)); - static assert(!__traits(compiles, itod += duration)); - static assert(!__traits(compiles, ctod -= duration)); - static assert(!__traits(compiles, itod -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable seconds = td.seconds; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto tod = TimeOfDay(12, 30, 33); - tod += TickDuration.from!"usecs"(7_000_000); - assert(tod == TimeOfDay(12, 30, 40)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod += TickDuration.from!"usecs"(-7_000_000); - assert(tod == TimeOfDay(12, 30, 26)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod -= TickDuration.from!"usecs"(-7_000_000); - assert(tod == TimeOfDay(12, 30, 40)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod -= TickDuration.from!"usecs"(7_000_000); - assert(tod == TimeOfDay(12, 30, 26)); - } - } - } - - - /++ - Gives the difference between two $(LREF TimeOfDay)s. - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) - ) - - Params: - rhs = The $(LREF TimeOfDay) to subtract from this one. - +/ - Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow - if (op == "-") - { - immutable lhsSec = _hour * 3600 + _minute * 60 + _second; - immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; - - return dur!"seconds"(lhsSec - rhsSec); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); - assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); - assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); - assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod - tod == Duration.zero); - assert(ctod - tod == Duration.zero); - assert(itod - tod == Duration.zero); - - assert(tod - ctod == Duration.zero); - assert(ctod - ctod == Duration.zero); - assert(itod - ctod == Duration.zero); - - assert(tod - itod == Duration.zero); - assert(ctod - itod == Duration.zero); - assert(itod - itod == Duration.zero); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d%02d%02d", _hour, _minute, _second); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); - assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOString() == "123033"); - assert(ctod.toISOString() == "123033"); - assert(itod.toISOString() == "123033"); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d:%02d:%02d", _hour, _minute, _second); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); - assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOExtString() == "12:30:33"); - assert(ctod.toISOExtString() == "12:30:33"); - assert(itod.toISOExtString() == "12:30:33"); - } - - - /++ - Converts this TimeOfDay to a string. - +/ - string toString() @safe const pure nothrow - { - return toISOExtString(); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toString()); - assert(ctod.toString()); - assert(itod.toString()); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF TimeOfDay) would not be valid. - +/ - static TimeOfDay fromISOString(S)(in S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[2 .. 4]; - auto seconds = dstr[4 .. $]; - - enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); - - assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. - Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF TimeOfDay) would not be - valid. - +/ - static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[3 .. 5]; - auto seconds = dstr[6 .. $]; - - enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(hours), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(minutes), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(seconds), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); - - assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Returns midnight. - +/ - @property static TimeOfDay min() @safe pure nothrow - { - return TimeOfDay.init; - } - - @safe unittest - { - assert(TimeOfDay.min.hour == 0); - assert(TimeOfDay.min.minute == 0); - assert(TimeOfDay.min.second == 0); - assert(TimeOfDay.min < TimeOfDay.max); - } - - - /++ - Returns one second short of midnight. - +/ - @property static TimeOfDay max() @safe pure nothrow - { - auto tod = TimeOfDay.init; - tod._hour = maxHour; - tod._minute = maxMinute; - tod._second = maxSecond; - - return tod; - } - - @safe unittest - { - assert(TimeOfDay.max.hour == 23); - assert(TimeOfDay.max.minute == 59); - assert(TimeOfDay.max.second == 59); - assert(TimeOfDay.max > TimeOfDay.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. If the - number of minutes overflows (or underflows), then the minutes will wrap. - If the number of minutes overflows(or underflows), then the hour will - wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). - - Params: - seconds = The number of seconds to add to this TimeOfDay. - +/ - ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_hour); - hnsecs += convert!("minutes", "hnsecs")(_minute); - hnsecs += convert!("seconds", "hnsecs")(_second); - - hnsecs %= convert!("days", "hnsecs")(1); - - if (hnsecs < 0) - hnsecs += convert!("days", "hnsecs")(1); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _hour = cast(ubyte) newHours; - _minute = cast(ubyte) newMinutes; - _second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); - - testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); - testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod._addSeconds(7))); - static assert(!__traits(compiles, itod._addSeconds(7))); - } - - - /+ - Whether the given values form a valid $(LREF TimeOfDay). - +/ - static bool _valid(int hour, int minute, int second) @safe pure nothrow - { - return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); - } - - - @safe pure invariant() - { - import std.format : format; - assert(_valid(_hour, _minute, _second), - format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); - } - - ubyte _hour; - ubyte _minute; - ubyte _second; - - enum ubyte maxHour = 24 - 1; - enum ubyte maxMinute = 60 - 1; - enum ubyte maxSecond = 60 - 1; -} - - /++ Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object which holds both the date and the time. It is optimized for calendar-based @@ -19945,146 +18675,6 @@ version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LIN } -/++ - Whether all of the given strings are valid units of time. - - $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime - can handle precision greater than hnsecs, and the few functions in core.time - which deal with "nsecs" deal with it explicitly. - +/ -bool validTimeUnits(string[] units...) @safe pure nothrow -{ - import std.algorithm.searching : canFind; - foreach (str; units) - { - if (!canFind(timeStrings[], str)) - return false; - } - - return true; -} - - -/++ - Compares two time unit strings. $(D "years") are the largest units and - $(D "hnsecs") are the smallest. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - - Throws: - $(LREF DateTimeException) if either of the given strings is not a valid - time unit string. - +/ -int cmpTimeUnits(string lhs, string rhs) @safe pure -{ - import std.algorithm.searching : countUntil; - import std.format : format; - - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); - enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -@safe unittest -{ - foreach (i, outerUnits; timeStrings) - { - assert(cmpTimeUnits(outerUnits, outerUnits) == 0); - - //For some reason, $ won't compile. - foreach (innerUnits; timeStrings[i+1 .. timeStrings.length]) - assert(cmpTimeUnits(outerUnits, innerUnits) == -1); - } - - foreach (i, outerUnits; timeStrings) - { - foreach (innerUnits; timeStrings[0 .. i]) - assert(cmpTimeUnits(outerUnits, innerUnits) == 1); - } -} - - -/++ - Compares two time unit strings at compile time. $(D "years") are the largest - units and $(D "hnsecs") are the smallest. - - This template is used instead of $(D cmpTimeUnits) because exceptions - can't be thrown at compile time and $(D cmpTimeUnits) must enforce that - the strings it's given are valid time unit strings. This template uses a - template constraint instead. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ -template CmpTimeUnits(string lhs, string rhs) -if (validTimeUnits(lhs, rhs)) -{ - enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); -} - - -/+ - Helper function for $(D CmpTimeUnits). - +/ -private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow -{ - import std.algorithm.searching : countUntil; - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -@safe unittest -{ - import std.format : format; - import std.meta : AliasSeq; - - static string genTest(size_t index) - { - auto currUnits = timeStrings[index]; - auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); - - foreach (units; timeStrings[index + 1 .. $]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); - - foreach (units; timeStrings[0 .. index]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); - - return test; - } - - static assert(timeStrings.length == 10); - foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) - mixin(genTest(n)); -} - - /++ Function for starting to a stop watch time when the function is called and stopping it when its return value goes out of scope and is destroyed. @@ -20297,42 +18887,6 @@ if (CmpTimeUnits!(units, "months") < 0) } -/+ - Splits out a particular unit from hnsecs and gives the value for that - unit and the remaining hnsecs. It really shouldn't be used unless unless - all units larger than the given units have already been split out. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left - after splitting out the given units. - - Returns: - The number of the given units from converting hnsecs to those units. - +/ -long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - immutable value = convert!("hnsecs", units)(hnsecs); - hnsecs -= convert!(units, "hnsecs")(value); - - return value; -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 3000000007); - - immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - assert(minutes == 5); - assert(hnsecs == 7); -} - - /+ This function is used to split out the units without getting the remaining hnsecs. diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d index 8c91bef774a..683728bdb04 100644 --- a/std/datetime/timeofday.d +++ b/std/datetime/timeofday.d @@ -8,3 +8,1319 @@ LREF2=$(D $2) +/ module std.datetime.timeofday; + +import core.time; +import std.datetime.common; +import std.traits : isSomeString; + +version(unittest) import std.exception : assertThrown; + + +/++ + Represents a time of day with hours, minutes, and seconds. It uses 24 hour + time. ++/ +struct TimeOfDay +{ +public: + + /++ + Params: + hour = Hour of the day [0 - 24$(RPAREN). + minute = Minute of the hour [0 - 60$(RPAREN). + second = Second of the minute [0 - 60$(RPAREN). + + Throws: + $(LREF DateTimeException) if the resulting $(LREF TimeOfDay) would be not + be valid. + +/ + this(int hour, int minute, int second = 0) @safe pure + { + enforceValid!"hours"(hour); + enforceValid!"minutes"(minute); + enforceValid!"seconds"(second); + + _hour = cast(ubyte) hour; + _minute = cast(ubyte) minute; + _second = cast(ubyte) second; + } + + @safe unittest + { + assert(TimeOfDay(0, 0) == TimeOfDay.init); + + { + auto tod = TimeOfDay(0, 0); + assert(tod._hour == 0); + assert(tod._minute == 0); + assert(tod._second == 0); + } + + { + auto tod = TimeOfDay(12, 30, 33); + assert(tod._hour == 12); + assert(tod._minute == 30); + assert(tod._second == 33); + } + + { + auto tod = TimeOfDay(23, 59, 59); + assert(tod._hour == 23); + assert(tod._minute == 59); + assert(tod._second == 59); + } + + assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); + } + + + /++ + Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in TimeOfDay rhs) @safe const pure nothrow + { + if (_hour < rhs._hour) + return -1; + if (_hour > rhs._hour) + return 1; + + if (_minute < rhs._minute) + return -1; + if (_minute > rhs._minute) + return 1; + + if (_second < rhs._second) + return -1; + if (_second > rhs._second) + return 1; + + return 0; + } + + @safe unittest + { + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); + + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); + assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); + assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); + + assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); + + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(ctod.opCmp(itod) == 0); + assert(itod.opCmp(ctod) == 0); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _hour; + } + + @safe unittest + { + assert(TimeOfDay.init.hour == 0); + assert(TimeOfDay(12, 0, 0).hour == 12); + + const ctod = TimeOfDay(12, 0, 0); + immutable itod = TimeOfDay(12, 0, 0); + assert(ctod.hour == 12); + assert(itod.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. + + Throws: + $(LREF DateTimeException) if the given hour would result in an invalid + $(LREF TimeOfDay). + +/ + @property void hour(int hour) @safe pure + { + enforceValid!"hours"(hour); + _hour = cast(ubyte) hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.hour = 12; + assert(tod == TimeOfDay(12, 0, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.hour = 12)); + static assert(!__traits(compiles, itod.hour = 12)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _minute; + } + + @safe unittest + { + assert(TimeOfDay.init.minute == 0); + assert(TimeOfDay(0, 30, 0).minute == 30); + + const ctod = TimeOfDay(0, 30, 0); + immutable itod = TimeOfDay(0, 30, 0); + assert(ctod.minute == 30); + assert(itod.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF TimeOfDay)'s minute to. + + Throws: + $(LREF DateTimeException) if the given minute would result in an + invalid $(LREF TimeOfDay). + +/ + @property void minute(int minute) @safe pure + { + enforceValid!"minutes"(minute); + _minute = cast(ubyte) minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.minute = 30; + assert(tod == TimeOfDay(0, 30, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.minute = 30)); + static assert(!__traits(compiles, itod.minute = 30)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _second; + } + + @safe unittest + { + assert(TimeOfDay.init.second == 0); + assert(TimeOfDay(0, 0, 33).second == 33); + + const ctod = TimeOfDay(0, 0, 33); + immutable itod = TimeOfDay(0, 0, 33); + assert(ctod.second == 33); + assert(itod.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF TimeOfDay)'s second to. + + Throws: + $(LREF DateTimeException) if the given second would result in an + invalid $(LREF TimeOfDay). + +/ + @property void second(int second) @safe pure + { + enforceValid!"seconds"(second); + _second = cast(ubyte) second; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.second = 33; + assert(tod == TimeOfDay(0, 0, 33)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.second = 33)); + static assert(!__traits(compiles, itod.second = 33)); + } + + + /++ + Adds the given number of units to this $(LREF TimeOfDay). A negative number + will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF TimeOfDay) + one hours's worth of minutes gets the exact same + $(LREF TimeOfDay). + + Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF TimeOfDay). + +/ + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "hours") + { + return this += dur!"hours"(value); + } + + /// + @safe unittest + { + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"hours"(1); + assert(tod1 == TimeOfDay(8, 12, 0)); + + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"hours"(-1); + assert(tod2 == TimeOfDay(6, 12, 0)); + + auto tod3 = TimeOfDay(23, 59, 0); + tod3.roll!"minutes"(1); + assert(tod3 == TimeOfDay(23, 0, 0)); + + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"minutes"(-1); + assert(tod4 == TimeOfDay(0, 59, 0)); + + auto tod5 = TimeOfDay(23, 59, 59); + tod5.roll!"seconds"(1); + assert(tod5 == TimeOfDay(23, 59, 0)); + + auto tod6 = TimeOfDay(0, 0, 0); + tod6.roll!"seconds"(-1); + assert(tod6 == TimeOfDay(0, 0, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"hours"(22).roll!"hours"(-7); + assert(tod == TimeOfDay(3, 27, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"hours"(53))); + static assert(!__traits(compiles, itod.roll!"hours"(53))); + } + + + // Shares documentation with "hours" version. + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "minutes" || units == "seconds") + { + import std.format : format; + + enum memberVarStr = units[0 .. $ - 1]; + value %= 60; + mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); + + if (value < 0) + { + if (newVal < 0) + newVal += 60; + } + else if (newVal >= 60) + newVal -= 60; + + mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); + return this; + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); + + testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); + + testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); + + testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); + testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); + testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); + + testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); + testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); + testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); + + testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); + testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); + testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"minutes"(97).roll!"minutes"(-102); + assert(tod == TimeOfDay(12, 22, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"minutes"(7))); + static assert(!__traits(compiles, itod.roll!"minutes"(7))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"seconds"(105).roll!"seconds"(-77); + assert(tod == TimeOfDay(12, 27, 30)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"seconds"(7))); + static assert(!__traits(compiles, itod.roll!"seconds"(7))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); + assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); + assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); + assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); + + assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); + assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); + assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); + assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto duration = dur!"hours"(11); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod + duration == TimeOfDay(23, 30, 33)); + assert(ctod + duration == TimeOfDay(23, 30, 33)); + assert(itod + duration == TimeOfDay(23, 30, 33)); + + assert(tod - duration == TimeOfDay(1, 30, 33)); + assert(ctod - duration == TimeOfDay(1, 30, 33)); + assert(itod - duration == TimeOfDay(1, 30, 33)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF TimeOfDay), as well as assigning the result to this + $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = duration.total!"seconds"; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + @safe unittest + { + auto duration = dur!"hours"(12); + + assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto tod = TimeOfDay(19, 17, 22); + (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); + assert(tod == TimeOfDay(17, 15, 59)); + + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(!__traits(compiles, ctod += duration)); + static assert(!__traits(compiles, itod += duration)); + static assert(!__traits(compiles, ctod -= duration)); + static assert(!__traits(compiles, itod -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + } + } + + + /++ + Gives the difference between two $(LREF TimeOfDay)s. + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) + ) + + Params: + rhs = The $(LREF TimeOfDay) to subtract from this one. + +/ + Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow + if (op == "-") + { + immutable lhsSec = _hour * 3600 + _minute * 60 + _second; + immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; + + return dur!"seconds"(lhsSec - rhsSec); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); + assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); + assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); + assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod - tod == Duration.zero); + assert(ctod - tod == Duration.zero); + assert(itod - tod == Duration.zero); + + assert(tod - ctod == Duration.zero); + assert(ctod - ctod == Duration.zero); + assert(itod - ctod == Duration.zero); + + assert(tod - itod == Duration.zero); + assert(ctod - itod == Duration.zero); + assert(itod - itod == Duration.zero); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d%02d%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); + assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOString() == "123033"); + assert(ctod.toISOString() == "123033"); + assert(itod.toISOString() == "123033"); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d:%02d:%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); + assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOExtString() == "12:30:33"); + assert(ctod.toISOExtString() == "12:30:33"); + assert(itod.toISOExtString() == "12:30:33"); + } + + + /++ + Converts this TimeOfDay to a string. + +/ + string toString() @safe const pure nothrow + { + return toISOExtString(); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toString()); + assert(ctod.toString()); + assert(itod.toString()); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for times. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO format + or if the resulting $(LREF TimeOfDay) would not be valid. + +/ + static TimeOfDay fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[2 .. 4]; + auto seconds = dstr[4 .. $]; + + enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); + + assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. + Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for times. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO + Extended format or if the resulting $(LREF TimeOfDay) would not be + valid. + +/ + static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[3 .. 5]; + auto seconds = dstr[6 .. $]; + + enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(hours), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(minutes), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(seconds), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); + + assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Returns midnight. + +/ + @property static TimeOfDay min() @safe pure nothrow + { + return TimeOfDay.init; + } + + @safe unittest + { + assert(TimeOfDay.min.hour == 0); + assert(TimeOfDay.min.minute == 0); + assert(TimeOfDay.min.second == 0); + assert(TimeOfDay.min < TimeOfDay.max); + } + + + /++ + Returns one second short of midnight. + +/ + @property static TimeOfDay max() @safe pure nothrow + { + auto tod = TimeOfDay.init; + tod._hour = maxHour; + tod._minute = maxMinute; + tod._second = maxSecond; + + return tod; + } + + @safe unittest + { + assert(TimeOfDay.max.hour == 23); + assert(TimeOfDay.max.minute == 59); + assert(TimeOfDay.max.second == 59); + assert(TimeOfDay.max > TimeOfDay.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. If the + number of minutes overflows (or underflows), then the minutes will wrap. + If the number of minutes overflows(or underflows), then the hour will + wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). + + Params: + seconds = The number of seconds to add to this TimeOfDay. + +/ + ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_hour); + hnsecs += convert!("minutes", "hnsecs")(_minute); + hnsecs += convert!("seconds", "hnsecs")(_second); + + hnsecs %= convert!("days", "hnsecs")(1); + + if (hnsecs < 0) + hnsecs += convert!("days", "hnsecs")(1); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _hour = cast(ubyte) newHours; + _minute = cast(ubyte) newMinutes; + _second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); + + testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); + testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod._addSeconds(7))); + static assert(!__traits(compiles, itod._addSeconds(7))); + } + + + /+ + Whether the given values form a valid $(LREF TimeOfDay). + +/ + static bool _valid(int hour, int minute, int second) @safe pure nothrow + { + return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); + } + + + @safe pure invariant() + { + import std.format : format; + assert(_valid(_hour, _minute, _second), + format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); + } + + +package: + + ubyte _hour; + ubyte _minute; + ubyte _second; + + enum ubyte maxHour = 24 - 1; + enum ubyte maxMinute = 60 - 1; + enum ubyte maxSecond = 60 - 1; +} + + +package: + +/+ + Splits out a particular unit from hnsecs and gives the value for that + unit and the remaining hnsecs. It really shouldn't be used unless unless + all units larger than the given units have already been split out. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left + after splitting out the given units. + + Returns: + The number of the given units from converting hnsecs to those units. + +/ +long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) +{ + import core.time : convert; + immutable value = convert!("hnsecs", units)(hnsecs); + hnsecs -= convert!(units, "hnsecs")(value); + return value; +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 3000000007); + + immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + assert(minutes == 5); + assert(hnsecs == 7); +} diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index a9646aa7144..2b0a2e3cc1e 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -36,7 +36,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : Clock, Date, DateTime, stdTimeToUnixTime, SysTime, TimeOfDay; // temporary +import std.datetime : Clock, Date, DateTime, stdTimeToUnixTime, SysTime; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time From 2d8e563053949681bc296247114ce1e318e0f7da Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 12:29:12 +0200 Subject: [PATCH 099/262] Move Date to std.datetime.date. --- std/datetime/common.d | 16 + std/datetime/date.d | 4763 +++++++++++++++++++++++++++++++++++++++ std/datetime/interval.d | 180 +- std/datetime/package.d | 4510 +----------------------------------- std/datetime/timezone.d | 3 +- 5 files changed, 4957 insertions(+), 4515 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index f09fc4547b6..ffd1c5be4df 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -642,6 +642,22 @@ private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow package: +/+ + Array of the short (three letter) names of each month. + +/ +immutable string[12] _monthNames = ["Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"]; + /+ The maximum valid Day in the given month in the given year. diff --git a/std/datetime/date.d b/std/datetime/date.d index 39d54c4e457..68d66841a64 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -8,3 +8,4766 @@ LREF2=$(D $2) +/ module std.datetime.date; + +import core.time; +import std.datetime.common; +import std.traits : isSomeString; + +version(unittest) import std.exception : assertThrown; + + +@safe unittest +{ + initializeTests(); +} + + +/++ + Represents a date in the + $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic Gregorian Calendar) + ranging from + 32,768 B.C. to 32,767 A.D. Positive years are A.D. Non-positive years are + B.C. + + Year, month, and day are kept separately internally so that $(D Date) is + optimized for calendar-based operations. + + $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian + leap year calculations for its entire length. As per + $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as + year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. as + a positive integer with 1 B.C. being the year prior to 1 A.D. + + Year 0 is a leap year. + +/ +struct Date +{ +public: + + /++ + Throws: + $(LREF DateTimeException) if the resulting $(LREF Date) would not be valid. + + Params: + year = Year of the Gregorian Calendar. Positive values are A.D. + Non-positive values are B.C. with year 0 being the year + prior to 1 A.D. + month = Month of the year. + day = Day of the month. + +/ + this(int year, int month, int day) @safe pure + { + enforceValid!"months"(cast(Month) month); + enforceValid!"days"(year, cast(Month) month, day); + + _year = cast(short) year; + _month = cast(Month) month; + _day = cast(ubyte) day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + assert(Date(1, 1, 1) == Date.init); + + static void testDate(in Date date, int year, int month, int day) + { + assert(date._year == year); + assert(date._month == month); + assert(date._day == day); + } + + testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); + testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); + testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); + + // Test A.D. + assertThrown!DateTimeException(Date(1, 0, 1)); + assertThrown!DateTimeException(Date(1, 1, 0)); + assertThrown!DateTimeException(Date(1999, 13, 1)); + assertThrown!DateTimeException(Date(1999, 1, 32)); + assertThrown!DateTimeException(Date(1999, 2, 29)); + assertThrown!DateTimeException(Date(2000, 2, 30)); + assertThrown!DateTimeException(Date(1999, 3, 32)); + assertThrown!DateTimeException(Date(1999, 4, 31)); + assertThrown!DateTimeException(Date(1999, 5, 32)); + assertThrown!DateTimeException(Date(1999, 6, 31)); + assertThrown!DateTimeException(Date(1999, 7, 32)); + assertThrown!DateTimeException(Date(1999, 8, 32)); + assertThrown!DateTimeException(Date(1999, 9, 31)); + assertThrown!DateTimeException(Date(1999, 10, 32)); + assertThrown!DateTimeException(Date(1999, 11, 31)); + assertThrown!DateTimeException(Date(1999, 12, 32)); + + assertNotThrown!DateTimeException(Date(1999, 1, 31)); + assertNotThrown!DateTimeException(Date(1999, 2, 28)); + assertNotThrown!DateTimeException(Date(2000, 2, 29)); + assertNotThrown!DateTimeException(Date(1999, 3, 31)); + assertNotThrown!DateTimeException(Date(1999, 4, 30)); + assertNotThrown!DateTimeException(Date(1999, 5, 31)); + assertNotThrown!DateTimeException(Date(1999, 6, 30)); + assertNotThrown!DateTimeException(Date(1999, 7, 31)); + assertNotThrown!DateTimeException(Date(1999, 8, 31)); + assertNotThrown!DateTimeException(Date(1999, 9, 30)); + assertNotThrown!DateTimeException(Date(1999, 10, 31)); + assertNotThrown!DateTimeException(Date(1999, 11, 30)); + assertNotThrown!DateTimeException(Date(1999, 12, 31)); + + // Test B.C. + assertNotThrown!DateTimeException(Date(0, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 12, 31)); + assertNotThrown!DateTimeException(Date(-1, 2, 28)); + assertNotThrown!DateTimeException(Date(-4, 2, 29)); + + assertThrown!DateTimeException(Date(-1, 2, 29)); + assertThrown!DateTimeException(Date(-2, 2, 29)); + assertThrown!DateTimeException(Date(-3, 2, 29)); + } + + + /++ + Params: + day = The Xth day of the Gregorian Calendar that the constructed + $(LREF Date) will be for. + +/ + this(int day) @safe pure nothrow + { + if (day > 0) + { + int years = (day / daysIn400Years) * 400 + 1; + day %= daysIn400Years; + + { + immutable tempYears = day / daysIn100Years; + + if (tempYears == 4) + { + years += 300; + day -= daysIn100Years * 3; + } + else + { + years += tempYears * 100; + day %= daysIn100Years; + } + } + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + { + immutable tempYears = day / daysInYear; + + if (tempYears == 4) + { + years += 3; + day -= daysInYear * 3; + } + else + { + years += tempYears; + day %= daysInYear; + } + } + + if (day == 0) + { + _year = cast(short)(years - 1); + _month = Month.dec; + _day = 31; + } + else + { + _year = cast(short) years; + + try + dayOfYear = day; + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + else if (day <= 0 && -day < daysInLeapYear) + { + _year = 0; + + try + dayOfYear = (daysInLeapYear + day); + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + else + { + day += daysInLeapYear - 1; + int years = (day / daysIn400Years) * 400 - 1; + day %= daysIn400Years; + + { + immutable tempYears = day / daysIn100Years; + + if (tempYears == -4) + { + years -= 300; + day += daysIn100Years * 3; + } + else + { + years += tempYears * 100; + day %= daysIn100Years; + } + } + + years += (day / daysIn4Years) * 4; + day %= daysIn4Years; + + { + immutable tempYears = day / daysInYear; + + if (tempYears == -4) + { + years -= 3; + day += daysInYear * 3; + } + else + { + years += tempYears; + day %= daysInYear; + } + } + + if (day == 0) + { + _year = cast(short)(years + 1); + _month = Month.jan; + _day = 1; + } + else + { + _year = cast(short) years; + immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; + + try + dayOfYear = newDoY; + catch (Exception e) + assert(0, "dayOfYear assignment threw."); + } + } + } + + @safe unittest + { + import std.range : chain; + + // Test A.D. + foreach (gd; chain(testGregDaysBC, testGregDaysAD)) + assert(Date(gd.day) == gd.date); + } + + + /++ + Compares this $(LREF Date) with the given $(LREF Date). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in Date rhs) @safe const pure nothrow + { + if (_year < rhs._year) + return -1; + if (_year > rhs._year) + return 1; + + if (_month < rhs._month) + return -1; + if (_month > rhs._month) + return 1; + + if (_day < rhs._day) + return -1; + if (_day > rhs._day) + return 1; + + return 0; + } + + @safe unittest + { + // Test A.D. + assert(Date(1, 1, 1).opCmp(Date.init) == 0); + + assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); + assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); + assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); + + assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); + + assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); + + assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); + + assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); + + // Test B.C. + assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); + assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); + assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); + assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); + + assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); + + assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); + + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); + assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); + + assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); + + // Test Both + assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); + + assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); + + assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); + + assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); + + assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); + assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.opCmp(date) == 0); + assert(date.opCmp(cdate) == 0); + assert(date.opCmp(idate) == 0); + assert(cdate.opCmp(date) == 0); + assert(cdate.opCmp(cdate) == 0); + assert(cdate.opCmp(idate) == 0); + assert(idate.opCmp(date) == 0); + assert(idate.opCmp(cdate) == 0); + assert(idate.opCmp(idate) == 0); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const pure nothrow + { + return _year; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + @safe unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.year == 1999); + assert(idate.year == 1999); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this Date's year to. + + Throws: + $(LREF DateTimeException) if the new year is not a leap year and the + resulting date would be on February 29th. + +/ + @property void year(int year) @safe pure + { + enforceValid!"days"(year, _month, _day); + _year = cast(short) year; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } + + @safe unittest + { + static void testDateInvalid(Date date, int year) + { + date.year = year; + } + + static void testDate(Date date, int year, in Date expected) + { + date.year = year; + assert(date == expected); + } + + assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); + + testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); + testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); + testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.year = 1999)); + static assert(!__traits(compiles, idate.year = 1999)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(LREF DateTimeException) if $(D isAD) is true. + +/ + @property ushort yearBC() @safe const pure + { + import std.format : format; + + if (isAD) + throw new DateTimeException(format("Year %s is A.D.", _year)); + return cast(ushort)((_year * -1) + 1); + } + + /// + @safe unittest + { + assert(Date(0, 1, 1).yearBC == 1); + assert(Date(-1, 1, 1).yearBC == 2); + assert(Date(-100, 1, 1).yearBC == 101); + } + + @safe unittest + { + assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); + + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + assert(date.yearBC == 1); + assert(cdate.yearBC == 1); + assert(idate.yearBC == 1); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF Date)'s year to. + + Throws: + $(LREF DateTimeException) if a non-positive value is given. + +/ + @property void yearBC(int year) @safe pure + { + if (year <= 0) + throw new DateTimeException("The given year is not a year B.C."); + _year = cast(short)((year - 1) * -1); + } + + /// + @safe unittest + { + auto date = Date(2010, 1, 1); + date.yearBC = 1; + assert(date == Date(0, 1, 1)); + + date.yearBC = 10; + assert(date == Date(-9, 1, 1)); + } + + @safe unittest + { + assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); + + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + date.yearBC = 7; + assert(date.yearBC == 7); + static assert(!__traits(compiles, cdate.yearBC = 7)); + static assert(!__traits(compiles, idate.yearBC = 7)); + } + + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const pure nothrow + { + return _month; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).month == 7); + assert(Date(2010, 10, 4).month == 10); + assert(Date(-7, 4, 5).month == 4); + } + + @safe unittest + { + assert(Date.init.month == 1); + assert(Date(1999, 7, 6).month == 7); + assert(Date(-1999, 7, 6).month == 7); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.month == 7); + assert(idate.month == 7); + } + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF Date)'s month to. + + Throws: + $(LREF DateTimeException) if the given month is not a valid month or if + the current day would not be valid in the given month. + +/ + @property void month(Month month) @safe pure + { + enforceValid!"months"(month); + enforceValid!"days"(_year, month, _day); + _month = cast(Month) month; + } + + @safe unittest + { + static void testDate(Date date, Month month, in Date expected = Date.init) + { + date.month = month; + assert(expected != Date.init); + assert(date == expected); + } + + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13)); + assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2)); + assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2)); + + testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1)); + testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.month = 7)); + static assert(!__traits(compiles, idate.month = 7)); + } + + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const pure nothrow + { + return _day; + } + + /// + @safe unittest + { + assert(Date(1999, 7, 6).day == 6); + assert(Date(2010, 10, 4).day == 4); + assert(Date(-7, 4, 5).day == 5); + } + + @safe unittest + { + import std.format : format; + import std.range : chain; + + static void test(Date date, int expected) + { + assert(date.day == expected, format("Value given: %s", date)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + test(Date(year, md.month, md.day), md.day); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.day == 6); + assert(idate.day == 6); + } + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF Date)'s day to. + + Throws: + $(LREF DateTimeException) if the given day is not a valid day of the + current month. + +/ + @property void day(int day) @safe pure + { + enforceValid!"days"(_year, _month, day); + _day = cast(ubyte) day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + + static void testDate(Date date, int day) + { + date.day = day; + } + + // Test A.D. + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); + + { + auto date = Date(1, 1, 1); + date.day = 6; + assert(date == Date(1, 1, 6)); + } + + // Test B.C. + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); + + { + auto date = Date(-1, 1, 1); + date.day = 6; + assert(date == Date(-1, 1, 6)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.day = 6)); + static assert(!__traits(compiles, idate.day = 6)); + } + + + /++ + Adds the given number of years or months to this $(LREF Date). A negative + number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF Date). + allowOverflow = Whether the day should be allowed to overflow, + causing the month to increment. + +/ + ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years") + { + _year += value; + + if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) + { + if (allowOverflow == AllowDayOverflow.yes) + { + _month = Month.mar; + _day = 1; + } + else + _day = 28; + } + + return this; + } + + /// + @safe unittest + { + auto d1 = Date(2010, 1, 1); + d1.add!"months"(11); + assert(d1 == Date(2010, 12, 1)); + + auto d2 = Date(2010, 1, 1); + d2.add!"months"(-11); + assert(d2 == Date(2009, 2, 1)); + + auto d3 = Date(2000, 2, 29); + d3.add!"years"(1); + assert(d3 == Date(2001, 3, 1)); + + auto d4 = Date(2000, 2, 29); + d4.add!"years"(1, AllowDayOverflow.no); + assert(d4 == Date(2001, 2, 28)); + } + + // Test add!"years"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9); + assert(date == Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1); + assert(date == Date(1999, 3, 1)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9); + assert(date == Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1); + assert(date == Date(-1999, 3, 1)); + } + + // Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5); + assert(date == Date(1, 3, 1)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5); + assert(date == Date(-1, 3, 1)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5).add!"years"(7); + assert(date == Date(6, 3, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"years"(7))); + static assert(!__traits(compiles, idate.add!"years"(7))); + } + + // Test add!"years"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"years"(7, AllowDayOverflow.no); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9, AllowDayOverflow.no); + assert(date == Date(1997, 7, 6)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7, AllowDayOverflow.no); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9, AllowDayOverflow.no); + assert(date == Date(-1997, 7, 6)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } + + // Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(4, 7, 6); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } + + { + auto date = Date(-4, 7, 6); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } + + { + auto date = Date(-4, 2, 29); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(date == Date(6, 2, 28)); + } + } + + + // Shares documentation with "years" version. + ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "months") + { + auto years = months / 12; + months %= 12; + auto newMonth = _month + months; + + if (months < 0) + { + if (newMonth < 1) + { + newMonth += 12; + --years; + } + } + else if (newMonth > 12) + { + newMonth -= 12; + ++years; + } + + _year += years; + _month = cast(Month) newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if (overflow > 0) + { + if (allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte) overflow; + } + else + _day = cast(ubyte) currMaxDay; + } + + return this; + } + + // Test add!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1); + assert(date == Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12); + assert(date == Date(2001, 3, 1)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13); + assert(date == Date(1999, 10, 1)); + date.add!"months"(-13); + assert(date == Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14); + assert(date == Date(1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(1998, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14); + assert(date == Date(2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(1999, 1, 2)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14); + assert(date == Date(2001, 3, 3)); + date.add!"months"(-14); + assert(date == Date(2000, 1, 3)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1); + assert(date == Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12); + assert(date == Date(-2001, 3, 1)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13); + assert(date == Date(-1997, 10, 1)); + date.add!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14); + assert(date == Date(-1995, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-1996, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14); + assert(date == Date(-2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(-2001, 1, 2)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14); + assert(date == Date(-1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-2000, 1, 3)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1); + assert(date == Date(0, 12, 1)); + date.add!"months"(1); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48); + assert(date == Date(0, 1, 1)); + date.add!"months"(48); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49); + assert(date == Date(0, 3, 2)); + date.add!"months"(49); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85); + assert(date == Date(-3, 3, 3)); + date.add!"months"(85); + assert(date == Date(4, 4, 3)); + } + + { + auto date = Date(-3, 3, 31); + date.add!"months"(85).add!"months"(-83); + assert(date == Date(-3, 6, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"months"(3))); + static assert(!__traits(compiles, idate.add!"months"(3))); + } + + // Test add!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.add!"months"(27, AllowDayOverflow.no); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + } + + { + auto date = Date(1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 29)); + } + + { + auto date = Date(1999, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + } + + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1995, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 29)); + } + + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(0, 12, 1)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.add!"months"(-48, AllowDayOverflow.no); + assert(date == Date(0, 1, 1)); + date.add!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-49, AllowDayOverflow.no); + assert(date == Date(0, 2, 29)); + date.add!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.add!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-3, 2, 28)); + date.add!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 28)); + } + + { + auto date = Date(-3, 3, 31); + date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); + } + } + + + /++ + Adds the given number of years or months to this $(LREF Date). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF Date) 12 months gets + the exact same $(LREF Date). However, the days can still be affected due to + the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF Date). + allowOverflow = Whether the day should be allowed to overflow, + causing the month to increment. + +/ + ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years") + { + return add!"years"(value, allowOverflow); + } + + /// + @safe unittest + { + auto d1 = Date(2010, 1, 1); + d1.roll!"months"(1); + assert(d1 == Date(2010, 2, 1)); + + auto d2 = Date(2010, 1, 1); + d2.roll!"months"(-1); + assert(d2 == Date(2010, 12, 1)); + + auto d3 = Date(1999, 1, 29); + d3.roll!"months"(1); + assert(d3 == Date(1999, 3, 1)); + + auto d4 = Date(1999, 1, 29); + d4.roll!"months"(1, AllowDayOverflow.no); + assert(d4 == Date(1999, 2, 28)); + + auto d5 = Date(2000, 2, 29); + d5.roll!"years"(1); + assert(d5 == Date(2001, 3, 1)); + + auto d6 = Date(2000, 2, 29); + d6.roll!"years"(1, AllowDayOverflow.no); + assert(d6 == Date(2001, 2, 28)); + } + + @safe unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"years"(3))); + static assert(!__traits(compiles, idate.rolYears(3))); + } + + + // Shares documentation with "years" version. + ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "months") + { + months %= 12; + auto newMonth = _month + months; + + if (months < 0) + { + if (newMonth < 1) + newMonth += 12; + } + else + { + if (newMonth > 12) + newMonth -= 12; + } + + _month = cast(Month) newMonth; + + immutable currMaxDay = maxDay(_year, _month); + immutable overflow = _day - currMaxDay; + + if (overflow > 0) + { + if (allowOverflow == AllowDayOverflow.yes) + { + ++_month; + _day = cast(ubyte) overflow; + } + else + _day = cast(ubyte) currMaxDay; + } + + return this; + } + + // Test roll!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(1999, 7, 1)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(1999, 5, 1)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(1999, 10, 1)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(1998, 9, 1)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1997, 1, 3)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14); + assert(date == Date(1998, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1998, 1, 3)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14); + assert(date == Date(1999, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1999, 1, 3)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 7, 1)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(-1999, 10, 1)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(-1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(-1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-1997, 1, 3)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2002, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2002, 1, 3)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2001, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2001, 1, 3)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(4, 4, 2)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(-4, 4, 2)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(-4, 4, 2)); + } + + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85).roll!"months"(-83); + assert(date == Date(-3, 6, 1)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"months"(3))); + static assert(!__traits(compiles, idate.roll!"months"(3))); + } + + // Test roll!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } + + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } + + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } + + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } + + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1998, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 28)); + } + + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } + + // Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } + + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } + + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } + + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } + + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2002, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 28)); + } + + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } + + // Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } + + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } + + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1, 1, 1)); + } + + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } + + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } + + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); + } + } + + + /++ + Adds the given number of units to this $(LREF Date). A negative number will + subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF Date) one + year's worth of days gets the exact same $(LREF Date). + + The only accepted units are $(D "days"). + + Params: + units = The units to add. Must be $(D "days"). + days = The number of days to add to this $(LREF Date). + +/ + ref Date roll(string units)(long days) @safe pure nothrow + if (units == "days") + { + immutable limit = maxDay(_year, _month); + days %= limit; + auto newDay = _day + days; + + if (days < 0) + { + if (newDay < 1) + newDay += limit; + } + else if (newDay > limit) + newDay -= limit; + + _day = cast(ubyte) newDay; + return this; + } + + /// + @safe unittest + { + auto d = Date(2010, 1, 1); + d.roll!"days"(1); + assert(d == Date(2010, 1, 2)); + d.roll!"days"(365); + assert(d == Date(2010, 1, 26)); + d.roll!"days"(-32); + assert(d == Date(2010, 1, 25)); + } + + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(1999, 7, 6)); + } + + { + auto date = Date(1999, 2, 6); + date.roll!"days"(365); + assert(date == Date(1999, 2, 7)); + date.roll!"days"(-365); + assert(date == Date(1999, 2, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 2, 8)); + date.roll!"days"(730); + assert(date == Date(1999, 2, 10)); + date.roll!"days"(-1096); + assert(date == Date(1999, 2, 6)); + } + + // Test B.C. + { + auto date = Date(-1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(-1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(-1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(-1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(-1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(-1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(-1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(-1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(-1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(-1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(-1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(-1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(-1999, 7, 6)); + } + + // Test Both + { + auto date = Date(1, 7, 6); + date.roll!"days"(-365); + assert(date == Date(1, 7, 13)); + date.roll!"days"(365); + assert(date == Date(1, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(1, 7, 19)); + date.roll!"days"(730); + assert(date == Date(1, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365); + assert(date == Date(0, 7, 13)); + date.roll!"days"(365); + assert(date == Date(0, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(0, 7, 19)); + date.roll!"days"(730); + assert(date == Date(0, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(date == Date(0, 7, 8)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"days"(12))); + static assert(!__traits(compiles, idate.roll!"days"(12))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF Date). + +/ + Date opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + Date retval = this; + immutable days = duration.total!"days"; + mixin("return retval._addDays(" ~ op ~ "days);"); + } + + /// + @safe unittest + { + assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1)); + assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1)); + + assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31)); + assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26)); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(date + dur!"days"(7) == Date(1999, 7, 13)); + assert(date + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(date + dur!"hours"(24) == Date(1999, 7, 7)); + assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(date - dur!"days"(-7) == Date(1999, 7, 13)); + assert(date - dur!"days"(7) == Date(1999, 6, 29)); + + assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(date - dur!"hours"(24) == Date(1999, 7, 5)); + assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + auto duration = dur!"days"(12); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date + duration == Date(1999, 7, 18)); + assert(cdate + duration == Date(1999, 7, 18)); + assert(idate + duration == Date(1999, 7, 18)); + + assert(date - duration == Date(1999, 6, 24)); + assert(cdate - duration == Date(1999, 6, 24)); + assert(idate - duration == Date(1999, 6, 24)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + Date opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + Date retval = this; + immutable days = convert!("hnsecs", "days")(td.hnsecs); + mixin("return retval._addDays(" ~ op ~ "days);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto date = Date(1999, 7, 6); + + assert(date + TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + + assert(date - TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF Date), as well as assigning the result to this $(LREF Date). + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) + $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF Date). + +/ + ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable days = duration.total!"days"; + mixin("return _addDays(" ~ op ~ "days);"); + } + + @safe unittest + { + assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + { + auto date = Date(0, 1, 31); + (date += dur!"days"(507)) += dur!"days"(-2); + assert(date == Date(1, 6, 19)); + } + + auto duration = dur!"days"(12); + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + date += duration; + static assert(!__traits(compiles, cdate += duration)); + static assert(!__traits(compiles, idate += duration)); + + date -= duration; + static assert(!__traits(compiles, cdate -= duration)); + static assert(!__traits(compiles, idate -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref Date opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable days = convert!("seconds", "days")(td.seconds); + mixin("return _addDays(" ~ op ~ "days);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto date = Date(1999, 7, 6); + date += TickDuration.from!"usecs"(86_400_000_000); + assert(date == Date(1999, 7, 7)); + } + + { + auto date = Date(1999, 7, 6); + date += TickDuration.from!"usecs"(-86_400_000_000); + assert(date == Date(1999, 7, 5)); + } + + { + auto date = Date(1999, 7, 6); + date -= TickDuration.from!"usecs"(-86_400_000_000); + assert(date == Date(1999, 7, 7)); + } + + { + auto date = Date(1999, 7, 6); + date -= TickDuration.from!"usecs"(86_400_000_000); + assert(date == Date(1999, 7, 5)); + } + } + } + + + /++ + Gives the difference between two $(LREF Date)s. + + The legal types of arithmetic for $(LREF Date) using this operator are + + $(BOOKTABLE, + $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in Date rhs) @safe const pure nothrow + if (op == "-") + { + return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); + assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); + assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); + assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); + assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); + assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date - date == Duration.zero); + assert(cdate - date == Duration.zero); + assert(idate - date == Duration.zero); + + assert(date - cdate == Duration.zero); + assert(cdate - cdate == Duration.zero); + assert(idate - cdate == Duration.zero); + + assert(date - idate == Duration.zero); + assert(cdate - idate == Duration.zero); + assert(idate - idate == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF Date)s in months. + + To get the difference in years, subtract the year property + of two $(LREF SysTime)s. To get the difference in days or weeks, + subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) + that results. Because converting between months and smaller + units requires a specific date (which $(REF Duration, core,time)s don't have), + getting the difference in months requires some math using both + the year and month properties, so this is a convenience function for + getting the difference in months. + + Note that the number of days in the months or how far into the month + either $(LREF Date) is is irrelevant. It is the difference in the month + property combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF Date) to subtract from this one. + +/ + int diffMonths(in Date rhs) @safe const pure nothrow + { + immutable yearDiff = _year - rhs._year; + immutable monthDiff = _month - rhs._month; + + return yearDiff * 12 + monthDiff; + } + + /// + @safe unittest + { + assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); + assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); + assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); + assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + + // Test A.D. + assert(date.diffMonths(Date(1998, 6, 5)) == 13); + assert(date.diffMonths(Date(1998, 7, 5)) == 12); + assert(date.diffMonths(Date(1998, 8, 5)) == 11); + assert(date.diffMonths(Date(1998, 9, 5)) == 10); + assert(date.diffMonths(Date(1998, 10, 5)) == 9); + assert(date.diffMonths(Date(1998, 11, 5)) == 8); + assert(date.diffMonths(Date(1998, 12, 5)) == 7); + assert(date.diffMonths(Date(1999, 1, 5)) == 6); + assert(date.diffMonths(Date(1999, 2, 6)) == 5); + assert(date.diffMonths(Date(1999, 3, 6)) == 4); + assert(date.diffMonths(Date(1999, 4, 6)) == 3); + assert(date.diffMonths(Date(1999, 5, 6)) == 2); + assert(date.diffMonths(Date(1999, 6, 6)) == 1); + assert(date.diffMonths(date) == 0); + assert(date.diffMonths(Date(1999, 8, 6)) == -1); + assert(date.diffMonths(Date(1999, 9, 6)) == -2); + assert(date.diffMonths(Date(1999, 10, 6)) == -3); + assert(date.diffMonths(Date(1999, 11, 6)) == -4); + assert(date.diffMonths(Date(1999, 12, 6)) == -5); + assert(date.diffMonths(Date(2000, 1, 6)) == -6); + assert(date.diffMonths(Date(2000, 2, 6)) == -7); + assert(date.diffMonths(Date(2000, 3, 6)) == -8); + assert(date.diffMonths(Date(2000, 4, 6)) == -9); + assert(date.diffMonths(Date(2000, 5, 6)) == -10); + assert(date.diffMonths(Date(2000, 6, 6)) == -11); + assert(date.diffMonths(Date(2000, 7, 6)) == -12); + assert(date.diffMonths(Date(2000, 8, 6)) == -13); + + assert(Date(1998, 6, 5).diffMonths(date) == -13); + assert(Date(1998, 7, 5).diffMonths(date) == -12); + assert(Date(1998, 8, 5).diffMonths(date) == -11); + assert(Date(1998, 9, 5).diffMonths(date) == -10); + assert(Date(1998, 10, 5).diffMonths(date) == -9); + assert(Date(1998, 11, 5).diffMonths(date) == -8); + assert(Date(1998, 12, 5).diffMonths(date) == -7); + assert(Date(1999, 1, 5).diffMonths(date) == -6); + assert(Date(1999, 2, 6).diffMonths(date) == -5); + assert(Date(1999, 3, 6).diffMonths(date) == -4); + assert(Date(1999, 4, 6).diffMonths(date) == -3); + assert(Date(1999, 5, 6).diffMonths(date) == -2); + assert(Date(1999, 6, 6).diffMonths(date) == -1); + assert(Date(1999, 8, 6).diffMonths(date) == 1); + assert(Date(1999, 9, 6).diffMonths(date) == 2); + assert(Date(1999, 10, 6).diffMonths(date) == 3); + assert(Date(1999, 11, 6).diffMonths(date) == 4); + assert(Date(1999, 12, 6).diffMonths(date) == 5); + assert(Date(2000, 1, 6).diffMonths(date) == 6); + assert(Date(2000, 2, 6).diffMonths(date) == 7); + assert(Date(2000, 3, 6).diffMonths(date) == 8); + assert(Date(2000, 4, 6).diffMonths(date) == 9); + assert(Date(2000, 5, 6).diffMonths(date) == 10); + assert(Date(2000, 6, 6).diffMonths(date) == 11); + assert(Date(2000, 7, 6).diffMonths(date) == 12); + assert(Date(2000, 8, 6).diffMonths(date) == 13); + + assert(date.diffMonths(Date(1999, 6, 30)) == 1); + assert(date.diffMonths(Date(1999, 7, 1)) == 0); + assert(date.diffMonths(Date(1999, 7, 6)) == 0); + assert(date.diffMonths(Date(1999, 7, 11)) == 0); + assert(date.diffMonths(Date(1999, 7, 16)) == 0); + assert(date.diffMonths(Date(1999, 7, 21)) == 0); + assert(date.diffMonths(Date(1999, 7, 26)) == 0); + assert(date.diffMonths(Date(1999, 7, 31)) == 0); + assert(date.diffMonths(Date(1999, 8, 1)) == -1); + + assert(date.diffMonths(Date(1990, 6, 30)) == 109); + assert(date.diffMonths(Date(1990, 7, 1)) == 108); + assert(date.diffMonths(Date(1990, 7, 6)) == 108); + assert(date.diffMonths(Date(1990, 7, 11)) == 108); + assert(date.diffMonths(Date(1990, 7, 16)) == 108); + assert(date.diffMonths(Date(1990, 7, 21)) == 108); + assert(date.diffMonths(Date(1990, 7, 26)) == 108); + assert(date.diffMonths(Date(1990, 7, 31)) == 108); + assert(date.diffMonths(Date(1990, 8, 1)) == 107); + + assert(Date(1999, 6, 30).diffMonths(date) == -1); + assert(Date(1999, 7, 1).diffMonths(date) == 0); + assert(Date(1999, 7, 6).diffMonths(date) == 0); + assert(Date(1999, 7, 11).diffMonths(date) == 0); + assert(Date(1999, 7, 16).diffMonths(date) == 0); + assert(Date(1999, 7, 21).diffMonths(date) == 0); + assert(Date(1999, 7, 26).diffMonths(date) == 0); + assert(Date(1999, 7, 31).diffMonths(date) == 0); + assert(Date(1999, 8, 1).diffMonths(date) == 1); + + assert(Date(1990, 6, 30).diffMonths(date) == -109); + assert(Date(1990, 7, 1).diffMonths(date) == -108); + assert(Date(1990, 7, 6).diffMonths(date) == -108); + assert(Date(1990, 7, 11).diffMonths(date) == -108); + assert(Date(1990, 7, 16).diffMonths(date) == -108); + assert(Date(1990, 7, 21).diffMonths(date) == -108); + assert(Date(1990, 7, 26).diffMonths(date) == -108); + assert(Date(1990, 7, 31).diffMonths(date) == -108); + assert(Date(1990, 8, 1).diffMonths(date) == -107); + + // Test B.C. + auto dateBC = Date(-1999, 7, 6); + + assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); + assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); + assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); + assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); + assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); + assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); + assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); + assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); + assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); + assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); + assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); + assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); + assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); + assert(dateBC.diffMonths(dateBC) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); + assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); + assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); + assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); + assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); + assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); + assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); + assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); + assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); + assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); + assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); + assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); + assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); + + assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); + assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); + assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); + assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); + assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); + assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); + assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); + assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); + assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); + assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); + assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); + assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); + assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); + assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); + assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); + assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); + assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); + assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); + assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); + assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); + assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); + assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); + assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); + assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); + assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); + assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); + + assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); + assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); + + assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); + assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); + assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); + + assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); + assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); + assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); + + assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); + assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); + assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); + + // Test Both + assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); + assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.diffMonths(date) == 0); + assert(cdate.diffMonths(date) == 0); + assert(idate.diffMonths(date) == 0); + + assert(date.diffMonths(cdate) == 0); + assert(cdate.diffMonths(cdate) == 0); + assert(idate.diffMonths(cdate) == 0); + + assert(date.diffMonths(idate) == 0); + assert(cdate.diffMonths(idate) == 0); + assert(idate.diffMonths(idate) == 0); + } + + + /++ + Whether this $(LREF Date) is in a leap year. + +/ + @property bool isLeapYear() @safe const pure nothrow + { + return yearIsLeapYear(_year); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, date.isLeapYear = true)); + static assert(!__traits(compiles, cdate.isLeapYear = true)); + static assert(!__traits(compiles, idate.isLeapYear = true)); + } + + + /++ + Day of the week this $(LREF Date) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const pure nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + @safe unittest + { + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.dayOfWeek == DayOfWeek.tue); + static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); + assert(idate.dayOfWeek == DayOfWeek.tue); + static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); + } + + + /++ + Day of the year this $(LREF Date) is on. + +/ + @property ushort dayOfYear() @safe const pure nothrow + { + if (_month >= Month.jan && _month <= Month.dec) + { + immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; + auto monthIndex = _month - Month.jan; + + return cast(ushort)(lastDay[monthIndex] + _day); + } + assert(0, "Invalid month."); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 1).dayOfYear == 1); + assert(Date(1999, 12, 31).dayOfYear == 365); + assert(Date(2000, 12, 31).dayOfYear == 366); + } + + @safe unittest + { + import std.algorithm.iteration : filter; + import std.range : chain; + + foreach (year; filter!((a){return !yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) + { + foreach (doy; testDaysOfYear) + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); + } + + foreach (year; filter!((a){return yearIsLeapYear(a);})(chain(testYearsBC, testYearsAD))) + { + foreach (doy; testDaysOfLeapYear) + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.dayOfYear == 187); + assert(idate.dayOfYear == 187); + } + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF Date) is on. + + Throws: + $(LREF DateTimeException) if the given day is an invalid day of the + year. + +/ + @property void dayOfYear(int day) @safe pure + { + immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; + + if (day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear)) + throw new DateTimeException("Invalid day of the year."); + + foreach (i; 1 .. lastDay.length) + { + if (day <= lastDay[i]) + { + _month = cast(Month)(cast(int) Month.jan + i - 1); + _day = cast(ubyte)(day - lastDay[i - 1]); + return; + } + } + assert(0, "Invalid day of the year."); + } + + @safe unittest + { + static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) + { + date.dayOfYear = day; + assert(date.month == expected.month); + assert(date.day == expected.day); + } + + foreach (doy; testDaysOfYear) + { + test(Date(1999, 1, 1), doy.day, doy.md); + test(Date(-1, 1, 1), doy.day, doy.md); + } + + foreach (doy; testDaysOfLeapYear) + { + test(Date(2000, 1, 1), doy.day, doy.md); + test(Date(-4, 1, 1), doy.day, doy.md); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.dayOfYear = 187)); + static assert(!__traits(compiles, idate.dayOfYear = 187)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF Date) is on. + +/ + @property int dayOfGregorianCal() @safe const pure nothrow + { + if (isAD) + { + if (_year == 1) + return dayOfYear; + + int years = _year - 1; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + days += years * daysInYear; + + days += dayOfYear; + + return days; + } + else if (_year == 0) + return dayOfYear - daysInLeapYear; + else + { + int years = _year; + auto days = (years / 400) * daysIn400Years; + years %= 400; + + days += (years / 100) * daysIn100Years; + years %= 100; + + days += (years / 4) * daysIn4Years; + years %= 4; + + if (years < 0) + { + days -= daysInLeapYear; + ++years; + + days += years * daysInYear; + + days -= daysInYear - dayOfYear; + } + else + days -= daysInLeapYear - dayOfYear; + + return days; + } + } + + /// + @safe unittest + { + assert(Date(1, 1, 1).dayOfGregorianCal == 1); + assert(Date(1, 12, 31).dayOfGregorianCal == 365); + assert(Date(2, 1, 1).dayOfGregorianCal == 366); + + assert(Date(0, 12, 31).dayOfGregorianCal == 0); + assert(Date(0, 1, 1).dayOfGregorianCal == -365); + assert(Date(-1, 12, 31).dayOfGregorianCal == -366); + + assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); + assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + import std.range : chain; + + foreach (gd; chain(testGregDaysBC, testGregDaysAD)) + assert(gd.date.dayOfGregorianCal == gd.day); + + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.dayOfGregorianCal == 729_941); + assert(cdate.dayOfGregorianCal == 729_941); + assert(idate.dayOfGregorianCal == 729_941); + } + + /++ + The Xth day of the Gregorian Calendar that this $(LREF Date) is on. + + Params: + day = The day of the Gregorian Calendar to set this $(LREF Date) to. + +/ + @property void dayOfGregorianCal(int day) @safe pure nothrow + { + this = Date(day); + } + + /// + @safe unittest + { + auto date = Date.init; + date.dayOfGregorianCal = 1; + assert(date == Date(1, 1, 1)); + + date.dayOfGregorianCal = 365; + assert(date == Date(1, 12, 31)); + + date.dayOfGregorianCal = 366; + assert(date == Date(2, 1, 1)); + + date.dayOfGregorianCal = 0; + assert(date == Date(0, 12, 31)); + + date.dayOfGregorianCal = -365; + assert(date == Date(-0, 1, 1)); + + date.dayOfGregorianCal = -366; + assert(date == Date(-1, 12, 31)); + + date.dayOfGregorianCal = 730_120; + assert(date == Date(2000, 1, 1)); + + date.dayOfGregorianCal = 734_137; + assert(date == Date(2010, 12, 31)); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + date.dayOfGregorianCal = 187; + assert(date.dayOfGregorianCal == 187); + static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF Date) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) + +/ + @property ubyte isoWeek() @safe const pure nothrow + { + immutable weekday = dayOfWeek; + immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; + immutable week = (dayOfYear - adjustedWeekday + 10) / 7; + + try + { + if (week == 53) + { + switch (Date(_year + 1, 1, 1).dayOfWeek) + { + case DayOfWeek.mon: + case DayOfWeek.tue: + case DayOfWeek.wed: + case DayOfWeek.thu: + return 1; + case DayOfWeek.fri: + case DayOfWeek.sat: + case DayOfWeek.sun: + return 53; + default: + assert(0, "Invalid ISO Week"); + } + } + else if (week > 0) + return cast(ubyte) week; + else + return Date(_year - 1, 12, 31).isoWeek; + } + catch (Exception e) + assert(0, "Date's constructor threw."); + } + + @safe unittest + { + // Test A.D. + assert(Date(2009, 12, 28).isoWeek == 53); + assert(Date(2009, 12, 29).isoWeek == 53); + assert(Date(2009, 12, 30).isoWeek == 53); + assert(Date(2009, 12, 31).isoWeek == 53); + assert(Date(2010, 1, 1).isoWeek == 53); + assert(Date(2010, 1, 2).isoWeek == 53); + assert(Date(2010, 1, 3).isoWeek == 53); + assert(Date(2010, 1, 4).isoWeek == 1); + assert(Date(2010, 1, 5).isoWeek == 1); + assert(Date(2010, 1, 6).isoWeek == 1); + assert(Date(2010, 1, 7).isoWeek == 1); + assert(Date(2010, 1, 8).isoWeek == 1); + assert(Date(2010, 1, 9).isoWeek == 1); + assert(Date(2010, 1, 10).isoWeek == 1); + assert(Date(2010, 1, 11).isoWeek == 2); + assert(Date(2010, 12, 31).isoWeek == 52); + + assert(Date(2004, 12, 26).isoWeek == 52); + assert(Date(2004, 12, 27).isoWeek == 53); + assert(Date(2004, 12, 28).isoWeek == 53); + assert(Date(2004, 12, 29).isoWeek == 53); + assert(Date(2004, 12, 30).isoWeek == 53); + assert(Date(2004, 12, 31).isoWeek == 53); + assert(Date(2005, 1, 1).isoWeek == 53); + assert(Date(2005, 1, 2).isoWeek == 53); + + assert(Date(2005, 12, 31).isoWeek == 52); + assert(Date(2007, 1, 1).isoWeek == 1); + + assert(Date(2007, 12, 30).isoWeek == 52); + assert(Date(2007, 12, 31).isoWeek == 1); + assert(Date(2008, 1, 1).isoWeek == 1); + + assert(Date(2008, 12, 28).isoWeek == 52); + assert(Date(2008, 12, 29).isoWeek == 1); + assert(Date(2008, 12, 30).isoWeek == 1); + assert(Date(2008, 12, 31).isoWeek == 1); + assert(Date(2009, 1, 1).isoWeek == 1); + assert(Date(2009, 1, 2).isoWeek == 1); + assert(Date(2009, 1, 3).isoWeek == 1); + assert(Date(2009, 1, 4).isoWeek == 1); + + // Test B.C. + // The algorithm should work identically for both A.D. and B.C. since + // it doesn't really take the year into account, so B.C. testing + // probably isn't really needed. + assert(Date(0, 12, 31).isoWeek == 52); + assert(Date(0, 1, 4).isoWeek == 1); + assert(Date(0, 1, 1).isoWeek == 52); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.isoWeek == 27); + static assert(!__traits(compiles, cdate.isoWeek = 3)); + assert(idate.isoWeek == 27); + static assert(!__traits(compiles, idate.isoWeek = 3)); + } + + + /++ + $(LREF Date) for the last day in the month that this $(LREF Date) is in. + +/ + @property Date endOfMonth() @safe const pure nothrow + { + try + return Date(_year, _month, maxDay(_year, _month)); + catch (Exception e) + assert(0, "Date's constructor threw."); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); + assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); + } + + @safe unittest + { + // Test A.D. + assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); + assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); + assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); + assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); + assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); + assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); + assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); + assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); + assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); + assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); + assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); + + // Test B.C. + assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); + assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); + assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); + assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); + assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); + assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); + assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); + assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); + assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); + assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); + assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); + assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); + assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); + static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); + } + + + /++ + The last day in the month that this $(LREF Date) is in. + +/ + @property ubyte daysInMonth() @safe const pure nothrow + { + return maxDay(_year, _month); + } + + /// + @safe unittest + { + assert(Date(1999, 1, 6).daysInMonth == 31); + assert(Date(1999, 2, 7).daysInMonth == 28); + assert(Date(2000, 2, 7).daysInMonth == 29); + assert(Date(2000, 6, 4).daysInMonth == 30); + } + + @safe unittest + { + // Test A.D. + assert(Date(1999, 1, 1).daysInMonth == 31); + assert(Date(1999, 2, 1).daysInMonth == 28); + assert(Date(2000, 2, 1).daysInMonth == 29); + assert(Date(1999, 3, 1).daysInMonth == 31); + assert(Date(1999, 4, 1).daysInMonth == 30); + assert(Date(1999, 5, 1).daysInMonth == 31); + assert(Date(1999, 6, 1).daysInMonth == 30); + assert(Date(1999, 7, 1).daysInMonth == 31); + assert(Date(1999, 8, 1).daysInMonth == 31); + assert(Date(1999, 9, 1).daysInMonth == 30); + assert(Date(1999, 10, 1).daysInMonth == 31); + assert(Date(1999, 11, 1).daysInMonth == 30); + assert(Date(1999, 12, 1).daysInMonth == 31); + + // Test B.C. + assert(Date(-1999, 1, 1).daysInMonth == 31); + assert(Date(-1999, 2, 1).daysInMonth == 28); + assert(Date(-2000, 2, 1).daysInMonth == 29); + assert(Date(-1999, 3, 1).daysInMonth == 31); + assert(Date(-1999, 4, 1).daysInMonth == 30); + assert(Date(-1999, 5, 1).daysInMonth == 31); + assert(Date(-1999, 6, 1).daysInMonth == 30); + assert(Date(-1999, 7, 1).daysInMonth == 31); + assert(Date(-1999, 8, 1).daysInMonth == 31); + assert(Date(-1999, 9, 1).daysInMonth == 30); + assert(Date(-1999, 10, 1).daysInMonth == 31); + assert(Date(-1999, 11, 1).daysInMonth == 30); + assert(Date(-1999, 12, 1).daysInMonth == 31); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.daysInMonth = 30)); + static assert(!__traits(compiles, idate.daysInMonth = 30)); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const pure nothrow + { + return _year > 0; + } + + /// + @safe unittest + { + assert(Date(1, 1, 1).isAD); + assert(Date(2010, 12, 31).isAD); + assert(!Date(0, 12, 31).isAD); + assert(!Date(-2010, 1, 1).isAD); + } + + @safe unittest + { + assert(Date(2010, 7, 4).isAD); + assert(Date(1, 1, 1).isAD); + assert(!Date(0, 1, 1).isAD); + assert(!Date(-1, 1, 1).isAD); + assert(!Date(-2010, 7, 4).isAD); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.isAD); + assert(idate.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF Date) at noon (since the Julian day changes at noon). + +/ + @property long julianDay() @safe const pure nothrow + { + return dayOfGregorianCal + 1_721_425; + } + + @safe unittest + { + assert(Date(-4713, 11, 24).julianDay == 0); + assert(Date(0, 12, 31).julianDay == 1_721_425); + assert(Date(1, 1, 1).julianDay == 1_721_426); + assert(Date(1582, 10, 15).julianDay == 2_299_161); + assert(Date(1858, 11, 17).julianDay == 2_400_001); + assert(Date(1982, 1, 4).julianDay == 2_444_974); + assert(Date(1996, 3, 31).julianDay == 2_450_174); + assert(Date(2010, 8, 24).julianDay == 2_455_433); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.julianDay == 2_451_366); + assert(idate.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for + any time on this date (since, the modified Julian day changes at + midnight). + +/ + @property long modJulianDay() @safe const pure nothrow + { + return julianDay - 2_400_001; + } + + @safe unittest + { + assert(Date(1858, 11, 17).modJulianDay == 0); + assert(Date(2010, 8, 24).modJulianDay == 55_432); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.modJulianDay == 51_365); + assert(idate.modJulianDay == 51_365); + } + + + /++ + Converts this $(LREF Date) to a string with the format YYYYMMDD. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d%02d%02d", _year, _month, _day); + else + return format("+%05d%02d%02d", _year, _month, _day); + } + else if (_year > -10_000) + return format("%05d%02d%02d", _year, _month, _day); + else + return format("%06d%02d%02d", _year, _month, _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toISOString() == "20100704"); + assert(Date(1998, 12, 25).toISOString() == "19981225"); + assert(Date(0, 1, 5).toISOString() == "00000105"); + assert(Date(-4, 1, 5).toISOString() == "-00040105"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toISOString() == "00091204"); + assert(Date(99, 12, 4).toISOString() == "00991204"); + assert(Date(999, 12, 4).toISOString() == "09991204"); + assert(Date(9999, 7, 4).toISOString() == "99990704"); + assert(Date(10000, 10, 20).toISOString() == "+100001020"); + + // Test B.C. + assert(Date(0, 12, 4).toISOString() == "00001204"); + assert(Date(-9, 12, 4).toISOString() == "-00091204"); + assert(Date(-99, 12, 4).toISOString() == "-00991204"); + assert(Date(-999, 12, 4).toISOString() == "-09991204"); + assert(Date(-9999, 7, 4).toISOString() == "-99990704"); + assert(Date(-10000, 10, 20).toISOString() == "-100001020"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toISOString() == "19990706"); + assert(idate.toISOString() == "19990706"); + } + + /++ + Converts this $(LREF Date) to a string with the format YYYY-MM-DD. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d-%02d-%02d", _year, _month, _day); + else + return format("+%05d-%02d-%02d", _year, _month, _day); + } + else if (_year > -10_000) + return format("%05d-%02d-%02d", _year, _month, _day); + else + return format("%06d-%02d-%02d", _year, _month, _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); + assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); + assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); + assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); + assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); + assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); + assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); + assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); + + // Test B.C. + assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); + assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); + assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); + assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); + assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); + assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toISOExtString() == "1999-07-06"); + assert(idate.toISOExtString() == "1999-07-06"); + } + + /++ + Converts this $(LREF Date) to a string with the format YYYY-Mon-DD. + +/ + string toSimpleString() @safe const pure nothrow + { + import std.format : format; + try + { + if (_year >= 0) + { + if (_year < 10_000) + return format("%04d-%s-%02d", _year, monthToString(_month), _day); + else + return format("+%05d-%s-%02d", _year, monthToString(_month), _day); + } + else if (_year > -10_000) + return format("%05d-%s-%02d", _year, monthToString(_month), _day); + else + return format("%06d-%s-%02d", _year, monthToString(_month), _day); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); + assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); + assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); + assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); + } + + @safe unittest + { + // Test A.D. + assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); + assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); + assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); + assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); + assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); + + // Test B.C. + assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); + assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); + assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); + assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); + assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); + assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(cdate.toSimpleString() == "1999-Jul-06"); + assert(idate.toSimpleString() == "1999-Jul-06"); + } + + + /++ + Converts this $(LREF Date) to a string. + +/ + string toString() @safe const pure nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + assert(date.toString()); + assert(cdate.toString()); + assert(idate.toString()); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace + is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO format + or if the resulting $(LREF Date) would not be valid. + +/ + static Date fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-4 .. $-2]; + auto year = dstr[0 .. $-4]; + + enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid ISO String: %s", isoString))); + } + else + enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); + assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); + assert(Date.fromISOString("00000105") == Date(0, 1, 5)); + assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); + assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromISOString("")); + assertThrown!DateTimeException(Date.fromISOString("990704")); + assertThrown!DateTimeException(Date.fromISOString("0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070")); + assertThrown!DateTimeException(Date.fromISOString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOString("120100704")); + assertThrown!DateTimeException(Date.fromISOString("-0100704")); + assertThrown!DateTimeException(Date.fromISOString("+0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070a")); + assertThrown!DateTimeException(Date.fromISOString("20100a04")); + assertThrown!DateTimeException(Date.fromISOString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); + + assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); + assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYY-MM-DD. Whitespace + is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for + dates. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO + Extended format or if the resulting $(LREF Date) would not be valid. + +/ + static Date fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto day = dstr[$-2 .. $]; + auto month = dstr[$-5 .. $-3]; + auto year = dstr[0 .. $-6]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(day), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(month), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + } + else + enforce(all!isDigit(year), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); + assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); + assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); + assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); + assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromISOExtString("")); + assertThrown!DateTimeException(Date.fromISOExtString("990704")); + assertThrown!DateTimeException(Date.fromISOExtString("0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOExtString("120100704")); + assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); + assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOExtString("20100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); + + assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); + assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); + } + + + /++ + Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. + Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString + formats dates. + + Throws: + $(LREF DateTimeException) if the given string is not in the correct + format or if the resulting $(LREF Date) would not be valid. + +/ + static Date fromSimpleString(S)(in S simpleString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : all, startsWith; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString))); + + auto day = dstr[$-2 .. $]; + auto month = monthFromString(to!string(dstr[$-6 .. $-3])); + auto year = dstr[0 .. $-7]; + + enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString))); + + if (year.length > 4) + { + enforce(year.startsWith('-', '+'), + new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(all!isDigit(year[1..$]), + new DateTimeException(format("Invalid string format: %s", simpleString))); + } + else + enforce(all!isDigit(year), + new DateTimeException(format("Invalid string format: %s", simpleString))); + + return Date(to!short(year), month, to!ubyte(day)); + } + + /// + @safe unittest + { + assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); + assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); + assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); + assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); + assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); + } + + @safe unittest + { + assertThrown!DateTimeException(Date.fromSimpleString("")); + assertThrown!DateTimeException(Date.fromSimpleString("990704")); + assertThrown!DateTimeException(Date.fromSimpleString("0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); + assertThrown!DateTimeException(Date.fromSimpleString("120100704")); + assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); + assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromSimpleString("20100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); + + assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); + assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); + } + + + /++ + Returns the $(LREF Date) farthest in the past which is representable by + $(LREF Date). + +/ + @property static Date min() @safe pure nothrow + { + auto date = Date.init; + date._year = short.min; + date._month = Month.jan; + date._day = 1; + + return date; + } + + @safe unittest + { + assert(Date.min.year < 0); + assert(Date.min < Date.max); + } + + + /++ + Returns the $(LREF Date) farthest in the future which is representable by + $(LREF Date). + +/ + @property static Date max() @safe pure nothrow + { + auto date = Date.init; + date._year = short.max; + date._month = Month.dec; + date._day = 31; + + return date; + } + + @safe unittest + { + assert(Date.max.year > 0); + assert(Date.max > Date.min); + } + + +private: + + /+ + Whether the given values form a valid date. + + Params: + year = The year to test. + month = The month of the Gregorian Calendar to test. + day = The day of the month to test. + +/ + static bool _valid(int year, int month, int day) @safe pure nothrow + { + if (!valid!"months"(month)) + return false; + return valid!"days"(year, month, day); + } + + +package: + + /+ + Adds the given number of days to this $(LREF Date). A negative number will + subtract. + + The month will be adjusted along with the day if the number of days + added (or subtracted) would overflow (or underflow) the current month. + The year will be adjusted along with the month if the increase (or + decrease) to the month would cause it to overflow (or underflow) the + current year. + + $(D _addDays(numDays)) is effectively equivalent to + $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). + + Params: + days = The number of days to add to this Date. + +/ + ref Date _addDays(long days) return @safe pure nothrow + { + dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); + return this; + } + + @safe unittest + { + // Test A.D. + { + auto date = Date(1999, 2, 28); + date._addDays(1); + assert(date == Date(1999, 3, 1)); + date._addDays(-1); + assert(date == Date(1999, 2, 28)); + } + + { + auto date = Date(2000, 2, 28); + date._addDays(1); + assert(date == Date(2000, 2, 29)); + date._addDays(1); + assert(date == Date(2000, 3, 1)); + date._addDays(-1); + assert(date == Date(2000, 2, 29)); + } + + { + auto date = Date(1999, 6, 30); + date._addDays(1); + assert(date == Date(1999, 7, 1)); + date._addDays(-1); + assert(date == Date(1999, 6, 30)); + } + + { + auto date = Date(1999, 7, 31); + date._addDays(1); + assert(date == Date(1999, 8, 1)); + date._addDays(-1); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 1, 1); + date._addDays(-1); + assert(date == Date(1998, 12, 31)); + date._addDays(1); + assert(date == Date(1999, 1, 1)); + } + + { + auto date = Date(1999, 7, 6); + date._addDays(9); + assert(date == Date(1999, 7, 15)); + date._addDays(-11); + assert(date == Date(1999, 7, 4)); + date._addDays(30); + assert(date == Date(1999, 8, 3)); + date._addDays(-3); + assert(date == Date(1999, 7, 31)); + } + + { + auto date = Date(1999, 7, 6); + date._addDays(365); + assert(date == Date(2000, 7, 5)); + date._addDays(-365); + assert(date == Date(1999, 7, 6)); + date._addDays(366); + assert(date == Date(2000, 7, 6)); + date._addDays(730); + assert(date == Date(2002, 7, 6)); + date._addDays(-1096); + assert(date == Date(1999, 7, 6)); + } + + // Test B.C. + { + auto date = Date(-1999, 2, 28); + date._addDays(1); + assert(date == Date(-1999, 3, 1)); + date._addDays(-1); + assert(date == Date(-1999, 2, 28)); + } + + { + auto date = Date(-2000, 2, 28); + date._addDays(1); + assert(date == Date(-2000, 2, 29)); + date._addDays(1); + assert(date == Date(-2000, 3, 1)); + date._addDays(-1); + assert(date == Date(-2000, 2, 29)); + } + + { + auto date = Date(-1999, 6, 30); + date._addDays(1); + assert(date == Date(-1999, 7, 1)); + date._addDays(-1); + assert(date == Date(-1999, 6, 30)); + } + + { + auto date = Date(-1999, 7, 31); + date._addDays(1); + assert(date == Date(-1999, 8, 1)); + date._addDays(-1); + assert(date == Date(-1999, 7, 31)); + } + + { + auto date = Date(-1999, 1, 1); + date._addDays(-1); + assert(date == Date(-2000, 12, 31)); + date._addDays(1); + assert(date == Date(-1999, 1, 1)); + } + + { + auto date = Date(-1999, 7, 6); + date._addDays(9); + assert(date == Date(-1999, 7, 15)); + date._addDays(-11); + assert(date == Date(-1999, 7, 4)); + date._addDays(30); + assert(date == Date(-1999, 8, 3)); + date._addDays(-3); + } + + { + auto date = Date(-1999, 7, 6); + date._addDays(365); + assert(date == Date(-1998, 7, 6)); + date._addDays(-365); + assert(date == Date(-1999, 7, 6)); + date._addDays(366); + assert(date == Date(-1998, 7, 7)); + date._addDays(730); + assert(date == Date(-1996, 7, 6)); + date._addDays(-1096); + assert(date == Date(-1999, 7, 6)); + } + + // Test Both + { + auto date = Date(1, 7, 6); + date._addDays(-365); + assert(date == Date(0, 7, 6)); + date._addDays(365); + assert(date == Date(1, 7, 6)); + date._addDays(-731); + assert(date == Date(-1, 7, 6)); + date._addDays(730); + assert(date == Date(1, 7, 5)); + } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate._addDays(12))); + static assert(!__traits(compiles, idate._addDays(12))); + } + + + @safe pure invariant() + { + import std.format : format; + assert(valid!"months"(_month), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); + assert(valid!"days"(_year, _month, _day), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); + } + + short _year = 1; + Month _month = Month.jan; + ubyte _day = 1; +} + + +package: + +/+ + Returns the day of the week for the given day of the Gregorian Calendar. + + Params: + day = The day of the Gregorian Calendar for which to get the day of + the week. + +/ +DayOfWeek getDayOfWeek(int day) @safe pure nothrow +{ + // January 1st, 1 A.D. was a Monday + if (day >= 0) + return cast(DayOfWeek)(day % 7); + else + { + immutable dow = cast(DayOfWeek)((day % 7) + 7); + + if (dow == 7) + return DayOfWeek.sun; + else + return dow; + } +} + +@safe unittest +{ + import std.datetime : SysTime; // temporary + + // Test A.D. + assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); + + // Test B.C. + assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); +} + + +private: + +enum daysInYear = 365; // The number of days in a non-leap year. +enum daysInLeapYear = 366; // The numbef or days in a leap year. +enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. +enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. +enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. + +/+ + Array of integers representing the last days of each month in a year. + +/ +immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + +/+ + Array of integers representing the last days of each month in a leap year. + +/ +immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + + +/+ + Returns the string representation of the given month. + +/ +string monthToString(Month month) @safe pure +{ + import std.format : format; + assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); + return _monthNames[month - Month.jan]; +} + +@safe unittest +{ + assert(monthToString(Month.jan) == "Jan"); + assert(monthToString(Month.feb) == "Feb"); + assert(monthToString(Month.mar) == "Mar"); + assert(monthToString(Month.apr) == "Apr"); + assert(monthToString(Month.may) == "May"); + assert(monthToString(Month.jun) == "Jun"); + assert(monthToString(Month.jul) == "Jul"); + assert(monthToString(Month.aug) == "Aug"); + assert(monthToString(Month.sep) == "Sep"); + assert(monthToString(Month.oct) == "Oct"); + assert(monthToString(Month.nov) == "Nov"); + assert(monthToString(Month.dec) == "Dec"); +} + + +/+ + Returns the Month corresponding to the given string. + + Params: + monthStr = The string representation of the month to get the Month for. + + Throws: + $(LREF DateTimeException) if the given month is not a valid month string. + +/ +Month monthFromString(string monthStr) @safe pure +{ + import std.format : format; + switch (monthStr) + { + case "Jan": + return Month.jan; + case "Feb": + return Month.feb; + case "Mar": + return Month.mar; + case "Apr": + return Month.apr; + case "May": + return Month.may; + case "Jun": + return Month.jun; + case "Jul": + return Month.jul; + case "Aug": + return Month.aug; + case "Sep": + return Month.sep; + case "Oct": + return Month.oct; + case "Nov": + return Month.nov; + case "Dec": + return Month.dec; + default: + throw new DateTimeException(format("Invalid month %s", monthStr)); + } +} + +@safe unittest +{ + import std.stdio : writeln; + import std.traits : EnumMembers; + foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", + "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) + { + scope(failure) writeln(badStr); + assertThrown!DateTimeException(monthFromString(badStr)); + } + + foreach (month; EnumMembers!Month) + { + scope(failure) writeln(month); + assert(monthFromString(monthToString(month)) == month); + } +} + + +version(unittest) +{ + // All of these helper arrays are sorted in ascending order. + auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; + auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct MonthDay + { + Month month; + short day; + + this(int m, short d) + { + month = cast(Month) m; + day = d; + } + } + + MonthDay[] testMonthDays = [MonthDay(1, 1), + MonthDay(1, 2), + MonthDay(3, 17), + MonthDay(7, 4), + MonthDay(10, 27), + MonthDay(12, 30), + MonthDay(12, 31)]; + + auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + + Date[] testDatesBC; + Date[] testDatesAD; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct GregDay { int day; Date date; } + auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar + GregDay(-735_233, Date(-2012, 1, 1)), + GregDay(-735_202, Date(-2012, 2, 1)), + GregDay(-735_175, Date(-2012, 2, 28)), + GregDay(-735_174, Date(-2012, 2, 29)), + GregDay(-735_173, Date(-2012, 3, 1)), + GregDay(-734_502, Date(-2010, 1, 1)), + GregDay(-734_472, Date(-2010, 1, 31)), + GregDay(-734_471, Date(-2010, 2, 1)), + GregDay(-734_444, Date(-2010, 2, 28)), + GregDay(-734_443, Date(-2010, 3, 1)), + GregDay(-734_413, Date(-2010, 3, 31)), + GregDay(-734_412, Date(-2010, 4, 1)), + GregDay(-734_383, Date(-2010, 4, 30)), + GregDay(-734_382, Date(-2010, 5, 1)), + GregDay(-734_352, Date(-2010, 5, 31)), + GregDay(-734_351, Date(-2010, 6, 1)), + GregDay(-734_322, Date(-2010, 6, 30)), + GregDay(-734_321, Date(-2010, 7, 1)), + GregDay(-734_291, Date(-2010, 7, 31)), + GregDay(-734_290, Date(-2010, 8, 1)), + GregDay(-734_260, Date(-2010, 8, 31)), + GregDay(-734_259, Date(-2010, 9, 1)), + GregDay(-734_230, Date(-2010, 9, 30)), + GregDay(-734_229, Date(-2010, 10, 1)), + GregDay(-734_199, Date(-2010, 10, 31)), + GregDay(-734_198, Date(-2010, 11, 1)), + GregDay(-734_169, Date(-2010, 11, 30)), + GregDay(-734_168, Date(-2010, 12, 1)), + GregDay(-734_139, Date(-2010, 12, 30)), + GregDay(-734_138, Date(-2010, 12, 31)), + GregDay(-731_215, Date(-2001, 1, 1)), + GregDay(-730_850, Date(-2000, 1, 1)), + GregDay(-730_849, Date(-2000, 1, 2)), + GregDay(-730_486, Date(-2000, 12, 30)), + GregDay(-730_485, Date(-2000, 12, 31)), + GregDay(-730_484, Date(-1999, 1, 1)), + GregDay(-694_690, Date(-1901, 1, 1)), + GregDay(-694_325, Date(-1900, 1, 1)), + GregDay(-585_118, Date(-1601, 1, 1)), + GregDay(-584_753, Date(-1600, 1, 1)), + GregDay(-584_388, Date(-1600, 12, 31)), + GregDay(-584_387, Date(-1599, 1, 1)), + GregDay(-365_972, Date(-1001, 1, 1)), + GregDay(-365_607, Date(-1000, 1, 1)), + GregDay(-183_351, Date(-501, 1, 1)), + GregDay(-182_986, Date(-500, 1, 1)), + GregDay(-182_621, Date(-499, 1, 1)), + GregDay(-146_827, Date(-401, 1, 1)), + GregDay(-146_462, Date(-400, 1, 1)), + GregDay(-146_097, Date(-400, 12, 31)), + GregDay(-110_302, Date(-301, 1, 1)), + GregDay(-109_937, Date(-300, 1, 1)), + GregDay(-73_778, Date(-201, 1, 1)), + GregDay(-73_413, Date(-200, 1, 1)), + GregDay(-38_715, Date(-105, 1, 1)), + GregDay(-37_254, Date(-101, 1, 1)), + GregDay(-36_889, Date(-100, 1, 1)), + GregDay(-36_524, Date(-99, 1, 1)), + GregDay(-36_160, Date(-99, 12, 31)), + GregDay(-35_794, Date(-97, 1, 1)), + GregDay(-18_627, Date(-50, 1, 1)), + GregDay(-18_262, Date(-49, 1, 1)), + GregDay(-3652, Date(-9, 1, 1)), + GregDay(-2191, Date(-5, 1, 1)), + GregDay(-1827, Date(-5, 12, 31)), + GregDay(-1826, Date(-4, 1, 1)), + GregDay(-1825, Date(-4, 1, 2)), + GregDay(-1462, Date(-4, 12, 30)), + GregDay(-1461, Date(-4, 12, 31)), + GregDay(-1460, Date(-3, 1, 1)), + GregDay(-1096, Date(-3, 12, 31)), + GregDay(-1095, Date(-2, 1, 1)), + GregDay(-731, Date(-2, 12, 31)), + GregDay(-730, Date(-1, 1, 1)), + GregDay(-367, Date(-1, 12, 30)), + GregDay(-366, Date(-1, 12, 31)), + GregDay(-365, Date(0, 1, 1)), + GregDay(-31, Date(0, 11, 30)), + GregDay(-30, Date(0, 12, 1)), + GregDay(-1, Date(0, 12, 30)), + GregDay(0, Date(0, 12, 31))]; + + auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), + GregDay(2, Date(1, 1, 2)), + GregDay(32, Date(1, 2, 1)), + GregDay(365, Date(1, 12, 31)), + GregDay(366, Date(2, 1, 1)), + GregDay(731, Date(3, 1, 1)), + GregDay(1096, Date(4, 1, 1)), + GregDay(1097, Date(4, 1, 2)), + GregDay(1460, Date(4, 12, 30)), + GregDay(1461, Date(4, 12, 31)), + GregDay(1462, Date(5, 1, 1)), + GregDay(17_898, Date(50, 1, 1)), + GregDay(35_065, Date(97, 1, 1)), + GregDay(36_160, Date(100, 1, 1)), + GregDay(36_525, Date(101, 1, 1)), + GregDay(37_986, Date(105, 1, 1)), + GregDay(72_684, Date(200, 1, 1)), + GregDay(73_049, Date(201, 1, 1)), + GregDay(109_208, Date(300, 1, 1)), + GregDay(109_573, Date(301, 1, 1)), + GregDay(145_732, Date(400, 1, 1)), + GregDay(146_098, Date(401, 1, 1)), + GregDay(182_257, Date(500, 1, 1)), + GregDay(182_622, Date(501, 1, 1)), + GregDay(364_878, Date(1000, 1, 1)), + GregDay(365_243, Date(1001, 1, 1)), + GregDay(584_023, Date(1600, 1, 1)), + GregDay(584_389, Date(1601, 1, 1)), + GregDay(693_596, Date(1900, 1, 1)), + GregDay(693_961, Date(1901, 1, 1)), + GregDay(729_755, Date(1999, 1, 1)), + GregDay(730_120, Date(2000, 1, 1)), + GregDay(730_121, Date(2000, 1, 2)), + GregDay(730_484, Date(2000, 12, 30)), + GregDay(730_485, Date(2000, 12, 31)), + GregDay(730_486, Date(2001, 1, 1)), + GregDay(733_773, Date(2010, 1, 1)), + GregDay(733_774, Date(2010, 1, 2)), + GregDay(733_803, Date(2010, 1, 31)), + GregDay(733_804, Date(2010, 2, 1)), + GregDay(733_831, Date(2010, 2, 28)), + GregDay(733_832, Date(2010, 3, 1)), + GregDay(733_862, Date(2010, 3, 31)), + GregDay(733_863, Date(2010, 4, 1)), + GregDay(733_892, Date(2010, 4, 30)), + GregDay(733_893, Date(2010, 5, 1)), + GregDay(733_923, Date(2010, 5, 31)), + GregDay(733_924, Date(2010, 6, 1)), + GregDay(733_953, Date(2010, 6, 30)), + GregDay(733_954, Date(2010, 7, 1)), + GregDay(733_984, Date(2010, 7, 31)), + GregDay(733_985, Date(2010, 8, 1)), + GregDay(734_015, Date(2010, 8, 31)), + GregDay(734_016, Date(2010, 9, 1)), + GregDay(734_045, Date(2010, 9, 30)), + GregDay(734_046, Date(2010, 10, 1)), + GregDay(734_076, Date(2010, 10, 31)), + GregDay(734_077, Date(2010, 11, 1)), + GregDay(734_106, Date(2010, 11, 30)), + GregDay(734_107, Date(2010, 12, 1)), + GregDay(734_136, Date(2010, 12, 30)), + GregDay(734_137, Date(2010, 12, 31)), + GregDay(734_503, Date(2012, 1, 1)), + GregDay(734_534, Date(2012, 2, 1)), + GregDay(734_561, Date(2012, 2, 28)), + GregDay(734_562, Date(2012, 2, 29)), + GregDay(734_563, Date(2012, 3, 1)), + GregDay(734_858, Date(2012, 12, 21))]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct DayOfYear { int day; MonthDay md; } + auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(3, 1)), + DayOfYear(90, MonthDay(3, 31)), + DayOfYear(91, MonthDay(4, 1)), + DayOfYear(120, MonthDay(4, 30)), + DayOfYear(121, MonthDay(5, 1)), + DayOfYear(151, MonthDay(5, 31)), + DayOfYear(152, MonthDay(6, 1)), + DayOfYear(181, MonthDay(6, 30)), + DayOfYear(182, MonthDay(7, 1)), + DayOfYear(212, MonthDay(7, 31)), + DayOfYear(213, MonthDay(8, 1)), + DayOfYear(243, MonthDay(8, 31)), + DayOfYear(244, MonthDay(9, 1)), + DayOfYear(273, MonthDay(9, 30)), + DayOfYear(274, MonthDay(10, 1)), + DayOfYear(304, MonthDay(10, 31)), + DayOfYear(305, MonthDay(11, 1)), + DayOfYear(334, MonthDay(11, 30)), + DayOfYear(335, MonthDay(12, 1)), + DayOfYear(363, MonthDay(12, 29)), + DayOfYear(364, MonthDay(12, 30)), + DayOfYear(365, MonthDay(12, 31))]; + + auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(2, 29)), + DayOfYear(61, MonthDay(3, 1)), + DayOfYear(91, MonthDay(3, 31)), + DayOfYear(92, MonthDay(4, 1)), + DayOfYear(121, MonthDay(4, 30)), + DayOfYear(122, MonthDay(5, 1)), + DayOfYear(152, MonthDay(5, 31)), + DayOfYear(153, MonthDay(6, 1)), + DayOfYear(182, MonthDay(6, 30)), + DayOfYear(183, MonthDay(7, 1)), + DayOfYear(213, MonthDay(7, 31)), + DayOfYear(214, MonthDay(8, 1)), + DayOfYear(244, MonthDay(8, 31)), + DayOfYear(245, MonthDay(9, 1)), + DayOfYear(274, MonthDay(9, 30)), + DayOfYear(275, MonthDay(10, 1)), + DayOfYear(305, MonthDay(10, 31)), + DayOfYear(306, MonthDay(11, 1)), + DayOfYear(335, MonthDay(11, 30)), + DayOfYear(336, MonthDay(12, 1)), + DayOfYear(364, MonthDay(12, 29)), + DayOfYear(365, MonthDay(12, 30)), + DayOfYear(366, MonthDay(12, 31))]; + + void initializeTests() @safe + { + foreach (year; testYearsBC) + { + foreach (md; testMonthDays) + testDatesBC ~= Date(year, md.month, md.day); + } + + foreach (year; testYearsAD) + { + foreach (md; testMonthDays) + testDatesAD ~= Date(year, md.month, md.day); + } + } +} diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 03c8594d2b5..bf2bfbeae30 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -17,7 +17,7 @@ import std.typecons : Flag; version(unittest) import std.exception : assertThrown; -import std.datetime : Date, DateTime, SysTime; // temporary +import std.datetime : DateTime, SysTime; // temporary /++ @@ -1564,6 +1564,7 @@ private: // Test Interval's constructors. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); @@ -1592,6 +1593,8 @@ private: // Test Interval's begin. @safe unittest { + import std.datetime.date; + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); @@ -1608,6 +1611,8 @@ private: // Test Interval's end. @safe unittest { + import std.datetime.date; + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); @@ -1624,6 +1629,7 @@ private: // Test Interval's length. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); @@ -1646,6 +1652,7 @@ private: // Test Interval's empty. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); @@ -1667,6 +1674,8 @@ private: // Test Interval's contains(time point). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); @@ -1697,6 +1706,8 @@ private: // Test Interval's contains(Interval). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); @@ -1797,6 +1808,8 @@ private: // Test Interval's isBefore(time point). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); @@ -1827,6 +1840,8 @@ private: // Test Interval's isBefore(Interval). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); @@ -1928,6 +1943,8 @@ private: // Test Interval's isAfter(time point). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); @@ -1958,6 +1975,8 @@ private: // Test Interval's isAfter(Interval). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); @@ -2058,6 +2077,8 @@ private: // Test Interval's intersects(). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); @@ -2160,6 +2181,8 @@ private: // Test Interval's intersection(). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); @@ -2298,6 +2321,8 @@ private: // Test Interval's isAdjacent(). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); static void testInterval(in Interval!Date interval1, in Interval!Date interval2) @@ -2405,6 +2430,8 @@ private: // Test Interval's merge(). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); static void testInterval(I)(in Interval!Date interval1, in I interval2) @@ -2548,6 +2575,8 @@ private: // Test Interval's span(). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); static void testInterval(in Interval!Date interval1, in Interval!Date interval2) @@ -2681,6 +2710,8 @@ private: // Test Interval's shift(duration). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); static void testIntervalFail(Interval!Date interval, in Duration duration) @@ -2718,6 +2749,8 @@ private: // Test Interval's shift(int, int, AllowDayOverflow). @safe unittest { + import std.datetime.date; + { auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -2770,6 +2803,8 @@ private: // Test Interval's expand(Duration). @safe unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); static void testIntervalFail(I)(I interval, in Duration duration) @@ -2808,6 +2843,8 @@ private: // Test Interval's expand(int, int, AllowDayOverflow, Direction) @safe unittest { + import std.datetime.date; + { auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); @@ -2917,6 +2954,8 @@ private: // Test Interval's fwdRange. @system unittest { + import std.datetime.date; + { auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); @@ -2983,6 +3022,8 @@ private: // Test Interval's bwdRange. @system unittest { + import std.datetime.date; + { auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); @@ -3049,6 +3090,8 @@ private: // Test Interval's toString(). @safe unittest { + import std.datetime.date; + assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -4077,6 +4120,7 @@ private: //Test PosInfInterval's constructor. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; PosInfInterval!Date(Date.init); @@ -4091,6 +4135,8 @@ private: //Test PosInfInterval's begin. @safe unittest { + import std.datetime.date; + assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); @@ -4107,6 +4153,7 @@ private: //Test PosInfInterval's empty. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); @@ -4126,6 +4173,8 @@ private: //Test PosInfInterval's contains(time point). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); assert(!posInfInterval.contains(Date(2009, 7, 4))); @@ -4153,6 +4202,8 @@ private: //Test PosInfInterval's contains(Interval). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4247,6 +4298,8 @@ private: //Test PosInfInterval's isBefore(time point). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); assert(!posInfInterval.isBefore(Date(2009, 7, 3))); @@ -4274,6 +4327,8 @@ private: //Test PosInfInterval's isBefore(Interval). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4367,6 +4422,8 @@ private: //Test PosInfInterval's isAfter(time point). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); assert(posInfInterval.isAfter(Date(2009, 7, 3))); @@ -4394,6 +4451,8 @@ private: //Test PosInfInterval's isAfter(Interval). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4489,6 +4548,8 @@ private: //Test PosInfInterval's intersects(). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4584,6 +4645,8 @@ private: //Test PosInfInterval's intersection(). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(I, J)(in I interval1, in J interval2) @@ -4700,6 +4763,8 @@ private: //Test PosInfInterval's isAdjacent(). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4794,6 +4859,8 @@ private: //Test PosInfInterval's merge(). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -4901,6 +4968,8 @@ private: //Test PosInfInterval's span(). @safe unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) @@ -5010,6 +5079,8 @@ private: //Test PosInfInterval's shift(). @safe unittest { + import std.datetime.date; + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) @@ -5040,6 +5111,8 @@ private: //Test PosInfInterval's shift(int, int, AllowDayOverflow). @safe unittest { + import std.datetime.date; + { auto interval = PosInfInterval!Date(Date(2010, 7, 4)); @@ -5085,6 +5158,8 @@ private: //Test PosInfInterval's expand(). @safe unittest { + import std.datetime.date; + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) @@ -5115,6 +5190,8 @@ private: //Test PosInfInterval's expand(int, int, AllowDayOverflow). @safe unittest { + import std.datetime.date; + { auto interval = PosInfInterval!Date(Date(2000, 7, 4)); @@ -5160,6 +5237,8 @@ private: //Test PosInfInterval's fwdRange(). @system unittest { + import std.datetime.date; + auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); static void testInterval(PosInfInterval!Date posInfInterval) @@ -5211,6 +5290,7 @@ private: //Test PosInfInterval's toString(). @safe unittest { + import std.datetime.date; assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); @@ -6254,6 +6334,7 @@ private: //Test NegInfInterval's constructor. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; NegInfInterval!Date(Date.init); @@ -6265,6 +6346,8 @@ private: //Test NegInfInterval's end. @safe unittest { + import std.datetime.date; + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); @@ -6281,6 +6364,7 @@ private: //Test NegInfInterval's empty. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); @@ -6300,6 +6384,8 @@ private: //Test NegInfInterval's contains(time point). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); assert(negInfInterval.contains(Date(2009, 7, 4))); @@ -6328,6 +6414,8 @@ private: //Test NegInfInterval's contains(Interval). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) @@ -6422,6 +6510,8 @@ private: //Test NegInfInterval's isBefore(time point). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); assert(!negInfInterval.isBefore(Date(2009, 7, 4))); @@ -6450,6 +6540,8 @@ private: //Test NegInfInterval's isBefore(Interval). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) @@ -6545,6 +6637,8 @@ private: //Test NegInfInterval's isAfter(time point). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); assert(!negInfInterval.isAfter(Date(2009, 7, 4))); @@ -6568,6 +6662,8 @@ private: //Test NegInfInterval's isAfter(Interval). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) @@ -6667,6 +6763,8 @@ private: //Test NegInfInterval's intersects(). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) @@ -6762,6 +6860,8 @@ private: //Test NegInfInterval's intersection(). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(I, J)(in I interval1, in J interval2) @@ -6878,6 +6978,8 @@ private: //Test NegInfInterval's isAdjacent(). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) @@ -6974,6 +7076,8 @@ private: //Test NegInfInterval's merge(). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(I, J)(in I interval1, in J interval2) @@ -7081,6 +7185,8 @@ private: //Test NegInfInterval's span(). @safe unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(I, J)(in I interval1, in J interval2) @@ -7190,6 +7296,8 @@ private: //Test NegInfInterval's shift(). @safe unittest { + import std.datetime.date; + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) @@ -7220,6 +7328,8 @@ private: //Test NegInfInterval's shift(int, int, AllowDayOverflow). @safe unittest { + import std.datetime.date; + { auto interval = NegInfInterval!Date(Date(2012, 1, 7)); @@ -7270,6 +7380,8 @@ private: //Test NegInfInterval's expand(). @safe unittest { + import std.datetime.date; + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) @@ -7300,6 +7412,8 @@ private: //Test NegInfInterval's expand(int, int, AllowDayOverflow). @safe unittest { + import std.datetime.date; + { auto interval = NegInfInterval!Date(Date(2012, 1, 7)); @@ -7345,6 +7459,8 @@ private: //Test NegInfInterval's bwdRange(). @system unittest { + import std.datetime.date; + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); static void testInterval(NegInfInterval!Date negInfInterval) @@ -7397,6 +7513,8 @@ private: //Test NegInfInterval's toString(). @safe unittest { + import std.datetime.date; + assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); @@ -7449,6 +7567,8 @@ if (isTimePoint!TP && /// @system unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); auto func = everyDayOfWeek!Date(DayOfWeek.mon); auto range = interval.fwdRange(func); @@ -7471,6 +7591,7 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.date; import std.datetime.timeofday; auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); @@ -7565,6 +7686,8 @@ if (isTimePoint!TP && /// @system unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); auto func = everyMonth!Date(Month.feb); auto range = interval.fwdRange(func); @@ -7593,6 +7716,8 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.date; + auto funcFwd = everyMonth!Date(Month.jun); auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); @@ -7671,6 +7796,8 @@ if (isTimePoint!TP && /// @system unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); auto func = everyDuration!Date(dur!"days"(8)); auto range = interval.fwdRange(func); @@ -7693,6 +7820,7 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.date; import std.datetime.timeofday; auto funcFwd = everyDuration!Date(dur!"days"(27)); @@ -7792,6 +7920,8 @@ if (isTimePoint!TP && /// @system unittest { + import std.datetime.date; + auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); auto range = interval.fwdRange(func); @@ -7814,6 +7944,7 @@ if (isTimePoint!TP && @system unittest { + import std.datetime.date; import std.datetime.timeofday; { @@ -8093,6 +8224,7 @@ private: //Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; import std.range.primitives; @@ -8119,6 +8251,7 @@ private: //Test construction of IntervalRange. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; { @@ -8150,6 +8283,8 @@ private: //Test IntervalRange's empty(). @system unittest { + import std.datetime.date; + //fwd { auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); @@ -8167,15 +8302,15 @@ private: //bwd { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); assert(!range.empty); range.popFront(); assert(range.empty); - const cRange = Interval!Date( Date(2010, 7, 4), Date(2012, 1, 7) - ).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + const cRange = Interval!Date( Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange( + everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); assert(!cRange.empty); //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if @@ -8186,6 +8321,8 @@ private: //Test IntervalRange's front. @system unittest { + import std.datetime.date; + //fwd { auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( @@ -8226,7 +8363,9 @@ private: //Test IntervalRange's popFront(). @system unittest { + import std.datetime.date; import std.range.primitives : walkLength; + //fwd { auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange( @@ -8276,6 +8415,8 @@ private: //Test IntervalRange's save. @system unittest { + import std.datetime.date; + //fwd { auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -8298,6 +8439,8 @@ private: //Test IntervalRange's interval. @system unittest { + import std.datetime.date; + //fwd { auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -8326,6 +8469,8 @@ private: //Test IntervalRange's func. @system unittest { + import std.datetime.date; + //fwd { auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -8348,6 +8493,8 @@ private: //Test IntervalRange's direction. @system unittest { + import std.datetime.date; + //fwd { auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); @@ -8519,6 +8666,7 @@ private: //Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; import std.range.primitives; @@ -8544,6 +8692,7 @@ private: //Test construction of PosInfIntervalRange. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; { @@ -8574,6 +8723,8 @@ private: //Test PosInfIntervalRange's front. @system unittest { + import std.datetime.date; + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); assert(range.front == Date(2010, 7, 4)); @@ -8587,7 +8738,9 @@ private: //Test PosInfIntervalRange's popFront(). @system unittest { + import std.datetime.date; import std.range : take; + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); auto expected = range.front; @@ -8604,6 +8757,8 @@ private: //Test PosInfIntervalRange's save. @system unittest { + import std.datetime.date; + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); auto func = everyDayOfWeek!Date(DayOfWeek.fri); auto range = interval.fwdRange(func); @@ -8614,6 +8769,8 @@ private: //Test PosInfIntervalRange's interval. @system unittest { + import std.datetime.date; + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); auto func = everyDayOfWeek!Date(DayOfWeek.fri); auto range = interval.fwdRange(func); @@ -8627,6 +8784,8 @@ private: //Test PosInfIntervalRange's func. @system unittest { + import std.datetime.date; + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); auto func = everyDayOfWeek!Date(DayOfWeek.fri); auto range = interval.fwdRange(func); @@ -8790,6 +8949,7 @@ private: //Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; import std.range.primitives; @@ -8814,6 +8974,7 @@ private: //Test construction of NegInfIntervalRange. @safe unittest { + import std.datetime.date; import std.datetime.timeofday; { @@ -8844,6 +9005,8 @@ private: //Test NegInfIntervalRange's front. @system unittest { + import std.datetime.date; + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); assert(range.front == Date(2012, 1, 7)); @@ -8858,6 +9021,7 @@ private: //Test NegInfIntervalRange's popFront(). @system unittest { + import std.datetime.date; import std.range : take; auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange( @@ -8877,6 +9041,8 @@ private: //Test NegInfIntervalRange's save. @system unittest { + import std.datetime.date; + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); auto range = interval.bwdRange(func); @@ -8887,6 +9053,8 @@ private: //Test NegInfIntervalRange's interval. @system unittest { + import std.datetime.date; + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); auto range = interval.bwdRange(func); @@ -8900,6 +9068,8 @@ private: //Test NegInfIntervalRange's func. @system unittest { + import std.datetime.date; + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); auto range = interval.bwdRange(func); diff --git a/std/datetime/package.d b/std/datetime/package.d index 165bf458d8b..afc5c1adc9c 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -9114,4327 +9114,6 @@ private: } -/++ - Represents a date in the - $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic Gregorian Calendar) - ranging from - 32,768 B.C. to 32,767 A.D. Positive years are A.D. Non-positive years are - B.C. - - Year, month, and day are kept separately internally so that $(D Date) is - optimized for calendar-based operations. - - $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian - leap year calculations for its entire length. As per - $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as - year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. as - a positive integer with 1 B.C. being the year prior to 1 A.D. - - Year 0 is a leap year. - +/ -struct Date -{ -public: - - /++ - Throws: - $(LREF DateTimeException) if the resulting $(LREF Date) would not be valid. - - Params: - year = Year of the Gregorian Calendar. Positive values are A.D. - Non-positive values are B.C. with year 0 being the year - prior to 1 A.D. - month = Month of the year. - day = Day of the month. - +/ - this(int year, int month, int day) @safe pure - { - enforceValid!"months"(cast(Month) month); - enforceValid!"days"(year, cast(Month) month, day); - - _year = cast(short) year; - _month = cast(Month) month; - _day = cast(ubyte) day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - assert(Date(1, 1, 1) == Date.init); - - static void testDate(in Date date, int year, int month, int day) - { - assert(date._year == year); - assert(date._month == month); - assert(date._day == day); - } - - testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); - testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); - testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); - - //Test A.D. - assertThrown!DateTimeException(Date(1, 0, 1)); - assertThrown!DateTimeException(Date(1, 1, 0)); - assertThrown!DateTimeException(Date(1999, 13, 1)); - assertThrown!DateTimeException(Date(1999, 1, 32)); - assertThrown!DateTimeException(Date(1999, 2, 29)); - assertThrown!DateTimeException(Date(2000, 2, 30)); - assertThrown!DateTimeException(Date(1999, 3, 32)); - assertThrown!DateTimeException(Date(1999, 4, 31)); - assertThrown!DateTimeException(Date(1999, 5, 32)); - assertThrown!DateTimeException(Date(1999, 6, 31)); - assertThrown!DateTimeException(Date(1999, 7, 32)); - assertThrown!DateTimeException(Date(1999, 8, 32)); - assertThrown!DateTimeException(Date(1999, 9, 31)); - assertThrown!DateTimeException(Date(1999, 10, 32)); - assertThrown!DateTimeException(Date(1999, 11, 31)); - assertThrown!DateTimeException(Date(1999, 12, 32)); - - assertNotThrown!DateTimeException(Date(1999, 1, 31)); - assertNotThrown!DateTimeException(Date(1999, 2, 28)); - assertNotThrown!DateTimeException(Date(2000, 2, 29)); - assertNotThrown!DateTimeException(Date(1999, 3, 31)); - assertNotThrown!DateTimeException(Date(1999, 4, 30)); - assertNotThrown!DateTimeException(Date(1999, 5, 31)); - assertNotThrown!DateTimeException(Date(1999, 6, 30)); - assertNotThrown!DateTimeException(Date(1999, 7, 31)); - assertNotThrown!DateTimeException(Date(1999, 8, 31)); - assertNotThrown!DateTimeException(Date(1999, 9, 30)); - assertNotThrown!DateTimeException(Date(1999, 10, 31)); - assertNotThrown!DateTimeException(Date(1999, 11, 30)); - assertNotThrown!DateTimeException(Date(1999, 12, 31)); - - //Test B.C. - assertNotThrown!DateTimeException(Date(0, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 12, 31)); - assertNotThrown!DateTimeException(Date(-1, 2, 28)); - assertNotThrown!DateTimeException(Date(-4, 2, 29)); - - assertThrown!DateTimeException(Date(-1, 2, 29)); - assertThrown!DateTimeException(Date(-2, 2, 29)); - assertThrown!DateTimeException(Date(-3, 2, 29)); - } - - - /++ - Params: - day = The Xth day of the Gregorian Calendar that the constructed - $(LREF Date) will be for. - +/ - this(int day) @safe pure nothrow - { - if (day > 0) - { - int years = (day / daysIn400Years) * 400 + 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if (tempYears == 4) - { - years += 300; - day -= daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if (tempYears == 4) - { - years += 3; - day -= daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if (day == 0) - { - _year = cast(short)(years - 1); - _month = Month.dec; - _day = 31; - } - else - { - _year = cast(short) years; - - try - dayOfYear = day; - catch (Exception e) - assert(0, "dayOfYear assignment threw."); - } - } - else if (day <= 0 && -day < daysInLeapYear) - { - _year = 0; - - try - dayOfYear = (daysInLeapYear + day); - catch (Exception e) - assert(0, "dayOfYear assignment threw."); - } - else - { - day += daysInLeapYear - 1; - int years = (day / daysIn400Years) * 400 - 1; - day %= daysIn400Years; - - { - immutable tempYears = day / daysIn100Years; - - if (tempYears == -4) - { - years -= 300; - day += daysIn100Years * 3; - } - else - { - years += tempYears * 100; - day %= daysIn100Years; - } - } - - years += (day / daysIn4Years) * 4; - day %= daysIn4Years; - - { - immutable tempYears = day / daysInYear; - - if (tempYears == -4) - { - years -= 3; - day += daysInYear * 3; - } - else - { - years += tempYears; - day %= daysInYear; - } - } - - if (day == 0) - { - _year = cast(short)(years + 1); - _month = Month.jan; - _day = 1; - } - else - { - _year = cast(short) years; - immutable newDoY = (yearIsLeapYear(_year) ? daysInLeapYear : daysInYear) + day + 1; - - try - dayOfYear = newDoY; - catch (Exception e) - assert(0, "dayOfYear assignment threw."); - } - } - } - - @safe unittest - { - import std.range : chain; - - //Test A.D. - foreach (gd; chain(testGregDaysBC, testGregDaysAD)) - assert(Date(gd.day) == gd.date); - } - - - /++ - Compares this $(LREF Date) with the given $(LREF Date). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in Date rhs) @safe const pure nothrow - { - if (_year < rhs._year) - return -1; - if (_year > rhs._year) - return 1; - - if (_month < rhs._month) - return -1; - if (_month > rhs._month) - return 1; - - if (_day < rhs._day) - return -1; - if (_day > rhs._day) - return 1; - - return 0; - } - - @safe unittest - { - //Test A.D. - assert(Date(1, 1, 1).opCmp(Date.init) == 0); - - assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); - assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); - assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); - - assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); - - assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); - - assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); - assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); - - assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); - assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); - assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); - assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - - //Test B.C. - assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); - assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); - assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); - assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); - - assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); - - assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); - - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); - assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); - assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); - assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); - assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); - assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); - - //Test Both - assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); - - assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); - - assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); - assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); - - assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); - assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.opCmp(date) == 0); - assert(date.opCmp(cdate) == 0); - assert(date.opCmp(idate) == 0); - assert(cdate.opCmp(date) == 0); - assert(cdate.opCmp(cdate) == 0); - assert(cdate.opCmp(idate) == 0); - assert(idate.opCmp(date) == 0); - assert(idate.opCmp(cdate) == 0); - assert(idate.opCmp(idate) == 0); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const pure nothrow - { - return _year; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - @safe unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.year == 1999); - assert(idate.year == 1999); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this Date's year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - enforceValid!"days"(year, _month, _day); - _year = cast(short) year; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } - - @safe unittest - { - static void testDateInvalid(Date date, int year) - { - date.year = year; - } - - static void testDate(Date date, int year, in Date expected) - { - date.year = year; - assert(date == expected); - } - - assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); - - testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); - testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); - testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.year = 1999)); - static assert(!__traits(compiles, idate.year = 1999)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property ushort yearBC() @safe const pure - { - import std.format : format; - - if (isAD) - throw new DateTimeException(format("Year %s is A.D.", _year)); - return cast(ushort)((_year * -1) + 1); - } - - /// - @safe unittest - { - assert(Date(0, 1, 1).yearBC == 1); - assert(Date(-1, 1, 1).yearBC == 2); - assert(Date(-100, 1, 1).yearBC == 101); - } - - @safe unittest - { - assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - assert(date.yearBC == 1); - assert(cdate.yearBC == 1); - assert(idate.yearBC == 1); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF Date)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe pure - { - if (year <= 0) - throw new DateTimeException("The given year is not a year B.C."); - - _year = cast(short)((year - 1) * -1); - } - - /// - @safe unittest - { - auto date = Date(2010, 1, 1); - date.yearBC = 1; - assert(date == Date(0, 1, 1)); - - date.yearBC = 10; - assert(date == Date(-9, 1, 1)); - } - - @safe unittest - { - assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); - - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - date.yearBC = 7; - assert(date.yearBC == 7); - static assert(!__traits(compiles, cdate.yearBC = 7)); - static assert(!__traits(compiles, idate.yearBC = 7)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const pure nothrow - { - return _month; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).month == 7); - assert(Date(2010, 10, 4).month == 10); - assert(Date(-7, 4, 5).month == 4); - } - - @safe unittest - { - assert(Date.init.month == 1); - assert(Date(1999, 7, 6).month == 7); - assert(Date(-1999, 7, 6).month == 7); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.month == 7); - assert(idate.month == 7); - } - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF Date)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month or if - the current day would not be valid in the given month. - +/ - @property void month(Month month) @safe pure - { - enforceValid!"months"(month); - enforceValid!"days"(_year, month, _day); - _month = cast(Month) month; - } - - @safe unittest - { - static void testDate(Date date, Month month, in Date expected = Date.init) - { - date.month = month; - assert(expected != Date.init); - assert(date == expected); - } - - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month) 13)); - assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month) 2)); - assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month) 2)); - - testDate(Date(1, 1, 1), cast(Month) 7, Date(1, 7, 1)); - testDate(Date(-1, 1, 1), cast(Month) 7, Date(-1, 7, 1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.month = 7)); - static assert(!__traits(compiles, idate.month = 7)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const pure nothrow - { - return _day; - } - - /// - @safe unittest - { - assert(Date(1999, 7, 6).day == 6); - assert(Date(2010, 10, 4).day == 4); - assert(Date(-7, 4, 5).day == 5); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(Date date, int expected) - { - assert(date.day == expected, - format("Value given: %s", date)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - test(Date(year, md.month, md.day), md.day); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.day == 6); - assert(idate.day == 6); - } - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF Date)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe pure - { - enforceValid!"days"(_year, _month, day); - _day = cast(ubyte) day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - static void testDate(Date date, int day) - { - date.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); - - { - auto date = Date(1, 1, 1); - date.day = 6; - assert(date == Date(1, 1, 6)); - } - - //Test B.C. - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); - - { - auto date = Date(-1, 1, 1); - date.day = 6; - assert(date == Date(-1, 1, 6)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.day = 6)); - static assert(!__traits(compiles, idate.day = 6)); - } - - - /++ - Adds the given number of years or months to this $(LREF Date). A negative - number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - +/ - ref Date add(string units)(long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "years") - { - _year += value; - - if (_month == Month.feb && _day == 29 && !yearIsLeapYear(_year)) - { - if (allowOverflow == Yes.allowDayOverflow) - { - _month = Month.mar; - _day = 1; - } - else - _day = 28; - } - - return this; - } - - /// - @safe unittest - { - import std.typecons : No; - - auto d1 = Date(2010, 1, 1); - d1.add!"months"(11); - assert(d1 == Date(2010, 12, 1)); - - auto d2 = Date(2010, 1, 1); - d2.add!"months"(-11); - assert(d2 == Date(2009, 2, 1)); - - auto d3 = Date(2000, 2, 29); - d3.add!"years"(1); - assert(d3 == Date(2001, 3, 1)); - - auto d4 = Date(2000, 2, 29); - d4.add!"years"(1, No.allowDayOverflow); - assert(d4 == Date(2001, 2, 28)); - } - - //Test add!"years"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1); - assert(date == Date(1999, 3, 1)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1); - assert(date == Date(-1999, 3, 1)); - } - - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5); - assert(date == Date(1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5); - assert(date == Date(-1, 3, 1)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5).add!"years"(7); - assert(date == Date(6, 3, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"years"(7))); - static assert(!__traits(compiles, idate.add!"years"(7))); - } - - //Test add!"years"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7, No.allowDayOverflow); - assert(date == Date(2006, 7, 6)); - date.add!"years"(-9, No.allowDayOverflow); - assert(date == Date(1997, 7, 6)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"years"(1, No.allowDayOverflow); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1, No.allowDayOverflow); - assert(date == Date(1999, 2, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7, No.allowDayOverflow); - assert(date == Date(-2006, 7, 6)); - date.add!"years"(9, No.allowDayOverflow); - assert(date == Date(-1997, 7, 6)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1, No.allowDayOverflow); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1, No.allowDayOverflow); - assert(date == Date(-1999, 2, 28)); - } - - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5, No.allowDayOverflow); - assert(date == Date(-1, 7, 6)); - date.add!"years"(5, No.allowDayOverflow); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(5, No.allowDayOverflow); - assert(date == Date(1, 7, 6)); - date.add!"years"(-5, No.allowDayOverflow); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(4, 7, 6); - date.add!"years"(-8, No.allowDayOverflow); - assert(date == Date(-4, 7, 6)); - date.add!"years"(8, No.allowDayOverflow); - assert(date == Date(4, 7, 6)); - } - - { - auto date = Date(-4, 7, 6); - date.add!"years"(8, No.allowDayOverflow); - assert(date == Date(4, 7, 6)); - date.add!"years"(-8, No.allowDayOverflow); - assert(date == Date(-4, 7, 6)); - } - - { - auto date = Date(-4, 2, 29); - date.add!"years"(5, No.allowDayOverflow); - assert(date == Date(1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, No.allowDayOverflow); - assert(date == Date(-1, 2, 28)); - } - - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, No.allowDayOverflow).add!"years"(7, No.allowDayOverflow); - assert(date == Date(6, 2, 28)); - } - } - - - //Shares documentation with "years" version. - ref Date add(string units)(long months, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "months") - { - auto years = months / 12; - months %= 12; - auto newMonth = _month + months; - - if (months < 0) - { - if (newMonth < 1) - { - newMonth += 12; - --years; - } - } - else if (newMonth > 12) - { - newMonth -= 12; - ++years; - } - - _year += years; - _month = cast(Month) newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if (overflow > 0) - { - if (allowOverflow == Yes.allowDayOverflow) - { - ++_month; - _day = cast(ubyte) overflow; - } - else - _day = cast(ubyte) currMaxDay; - } - - return this; - } - - //Test add!"months"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12); - assert(date == Date(2001, 3, 1)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13); - assert(date == Date(1999, 10, 1)); - date.add!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14); - assert(date == Date(1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14); - assert(date == Date(2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(1999, 1, 2)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14); - assert(date == Date(2001, 3, 3)); - date.add!"months"(-14); - assert(date == Date(2000, 1, 3)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12); - assert(date == Date(-2001, 3, 1)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13); - assert(date == Date(-1997, 10, 1)); - date.add!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14); - assert(date == Date(-1995, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-1996, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14); - assert(date == Date(-2000, 3, 2)); - date.add!"months"(-14); - assert(date == Date(-2001, 1, 2)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14); - assert(date == Date(-1999, 3, 3)); - date.add!"months"(-14); - assert(date == Date(-2000, 1, 3)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1); - assert(date == Date(0, 12, 1)); - date.add!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48); - assert(date == Date(0, 1, 1)); - date.add!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49); - assert(date == Date(0, 3, 2)); - date.add!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85); - assert(date == Date(-3, 3, 3)); - date.add!"months"(85); - assert(date == Date(4, 4, 3)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85).add!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"months"(3))); - static assert(!__traits(compiles, idate.add!"months"(3))); - } - - //Test add!"months"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3, No.allowDayOverflow); - assert(date == Date(1999, 10, 6)); - date.add!"months"(-4, No.allowDayOverflow); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(6, No.allowDayOverflow); - assert(date == Date(2000, 1, 6)); - date.add!"months"(-6, No.allowDayOverflow); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.add!"months"(27, No.allowDayOverflow); - assert(date == Date(2001, 10, 6)); - date.add!"months"(-28, No.allowDayOverflow); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1, No.allowDayOverflow); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.add!"months"(12, No.allowDayOverflow); - assert(date == Date(2000, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.add!"months"(12, No.allowDayOverflow); - assert(date == Date(2001, 2, 28)); - } - - { - auto date = Date(1999, 7, 31); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 8, 31)); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.add!"months"(13, No.allowDayOverflow); - assert(date == Date(1999, 9, 30)); - date.add!"months"(-13, No.allowDayOverflow); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(13, No.allowDayOverflow); - assert(date == Date(1999, 1, 31)); - date.add!"months"(-13, No.allowDayOverflow); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(1999, 2, 28)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(2000, 2, 29)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(1998, 12, 29)); - } - - { - auto date = Date(1999, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(2001, 2, 28)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(1999, 12, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3, No.allowDayOverflow); - assert(date == Date(-1999, 10, 6)); - date.add!"months"(-4, No.allowDayOverflow); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6, No.allowDayOverflow); - assert(date == Date(-1998, 1, 6)); - date.add!"months"(-6, No.allowDayOverflow); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27, No.allowDayOverflow); - assert(date == Date(-2001, 4, 6)); - date.add!"months"(28, No.allowDayOverflow); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1, No.allowDayOverflow); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12, No.allowDayOverflow); - assert(date == Date(-2000, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12, No.allowDayOverflow); - assert(date == Date(-2001, 2, 28)); - } - - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 8, 31)); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13, No.allowDayOverflow); - assert(date == Date(-1997, 9, 30)); - date.add!"months"(-13, No.allowDayOverflow); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13, No.allowDayOverflow); - assert(date == Date(-1995, 1, 31)); - date.add!"months"(-13, No.allowDayOverflow); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(-1995, 2, 28)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(-2000, 2, 29)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(-2002, 12, 29)); - } - - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14, No.allowDayOverflow); - assert(date == Date(-1999, 2, 28)); - date.add!"months"(-14, No.allowDayOverflow); - assert(date == Date(-2001, 12, 28)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1, No.allowDayOverflow); - assert(date == Date(0, 12, 1)); - date.add!"months"(1, No.allowDayOverflow); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.add!"months"(-48, No.allowDayOverflow); - assert(date == Date(0, 1, 1)); - date.add!"months"(48, No.allowDayOverflow); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-49, No.allowDayOverflow); - assert(date == Date(0, 2, 29)); - date.add!"months"(49, No.allowDayOverflow); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.add!"months"(-85, No.allowDayOverflow); - assert(date == Date(-3, 2, 28)); - date.add!"months"(85, No.allowDayOverflow); - assert(date == Date(4, 3, 28)); - } - - { - auto date = Date(-3, 3, 31); - date.add!"months"(85, No.allowDayOverflow).add!"months"(-83, No.allowDayOverflow); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of years or months to this $(LREF Date). A negative - number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF Date) 12 months gets - the exact same $(LREF Date). However, the days can still be affected due to - the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF Date). - allowOverflow = Whether the day should be allowed to overflow, - causing the month to increment. - +/ - ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - @safe unittest - { - import std.typecons : No; - - auto d1 = Date(2010, 1, 1); - d1.roll!"months"(1); - assert(d1 == Date(2010, 2, 1)); - - auto d2 = Date(2010, 1, 1); - d2.roll!"months"(-1); - assert(d2 == Date(2010, 12, 1)); - - auto d3 = Date(1999, 1, 29); - d3.roll!"months"(1); - assert(d3 == Date(1999, 3, 1)); - - auto d4 = Date(1999, 1, 29); - d4.roll!"months"(1, No.allowDayOverflow); - assert(d4 == Date(1999, 2, 28)); - - auto d5 = Date(2000, 2, 29); - d5.roll!"years"(1); - assert(d5 == Date(2001, 3, 1)); - - auto d6 = Date(2000, 2, 29); - d6.roll!"years"(1, No.allowDayOverflow); - assert(d6 == Date(2001, 2, 28)); - } - - @safe unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"years"(3))); - static assert(!__traits(compiles, idate.rolYears(3))); - } - - - //Shares documentation with "years" version. - ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "months") - { - months %= 12; - auto newMonth = _month + months; - - if (months < 0) - { - if (newMonth < 1) - newMonth += 12; - } - else - { - if (newMonth > 12) - newMonth -= 12; - } - - _month = cast(Month) newMonth; - - immutable currMaxDay = maxDay(_year, _month); - immutable overflow = _day - currMaxDay; - - if (overflow > 0) - { - if (allowOverflow == Yes.allowDayOverflow) - { - ++_month; - _day = cast(ubyte) overflow; - } - else - _day = cast(ubyte) currMaxDay; - } - - return this; - } - - //Test roll!"months"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(1999, 5, 1)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(1999, 10, 1)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(1998, 9, 1)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1997, 1, 3)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14); - assert(date == Date(1998, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1998, 1, 3)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14); - assert(date == Date(1999, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(1999, 1, 3)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 7, 1)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1); - assert(date == Date(-1999, 5, 1)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1); - assert(date == Date(-1999, 10, 1)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13); - assert(date == Date(-1998, 10, 1)); - date.roll!"months"(-13); - assert(date == Date(-1998, 9, 1)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14); - assert(date == Date(-1997, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-1997, 1, 3)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2002, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2002, 1, 3)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14); - assert(date == Date(-2001, 3, 3)); - date.roll!"months"(-14); - assert(date == Date(-2001, 1, 3)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(4, 4, 2)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(49); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85); - assert(date == Date(-4, 3, 2)); - date.roll!"months"(85); - assert(date == Date(-4, 4, 2)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85).roll!"months"(-83); - assert(date == Date(-3, 6, 1)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"months"(3))); - static assert(!__traits(compiles, idate.roll!"months"(3))); - } - - //Test roll!"months"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3, No.allowDayOverflow); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-4, No.allowDayOverflow); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6, No.allowDayOverflow); - assert(date == Date(1999, 1, 6)); - date.roll!"months"(-6, No.allowDayOverflow); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27, No.allowDayOverflow); - assert(date == Date(1999, 10, 6)); - date.roll!"months"(-28, No.allowDayOverflow); - assert(date == Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1, No.allowDayOverflow); - assert(date == Date(1999, 4, 30)); - } - - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12, No.allowDayOverflow); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12, No.allowDayOverflow); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 8, 31)); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(1999, 9, 30)); - } - - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13, No.allowDayOverflow); - assert(date == Date(1998, 9, 30)); - date.roll!"months"(-13, No.allowDayOverflow); - assert(date == Date(1998, 8, 30)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13, No.allowDayOverflow); - assert(date == Date(1997, 1, 31)); - date.roll!"months"(-13, No.allowDayOverflow); - assert(date == Date(1997, 12, 31)); - } - - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(1997, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(1997, 12, 28)); - } - - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(1998, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(1998, 12, 28)); - } - - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(1999, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(1999, 12, 28)); - } - - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3, No.allowDayOverflow); - assert(date == Date(-1999, 10, 6)); - date.roll!"months"(-4, No.allowDayOverflow); - assert(date == Date(-1999, 6, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6, No.allowDayOverflow); - assert(date == Date(-1999, 1, 6)); - date.roll!"months"(-6, No.allowDayOverflow); - assert(date == Date(-1999, 7, 6)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27, No.allowDayOverflow); - assert(date == Date(-1999, 4, 6)); - date.roll!"months"(28, No.allowDayOverflow); - assert(date == Date(-1999, 8, 6)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1, No.allowDayOverflow); - assert(date == Date(-1999, 4, 30)); - } - - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12, No.allowDayOverflow); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12, No.allowDayOverflow); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 8, 31)); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(-1999, 9, 30)); - } - - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13, No.allowDayOverflow); - assert(date == Date(-1998, 9, 30)); - date.roll!"months"(-13, No.allowDayOverflow); - assert(date == Date(-1998, 8, 30)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13, No.allowDayOverflow); - assert(date == Date(-1997, 1, 31)); - date.roll!"months"(-13, No.allowDayOverflow); - assert(date == Date(-1997, 12, 31)); - } - - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(-1997, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(-1997, 12, 28)); - } - - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(-2002, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(-2002, 12, 28)); - } - - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14, No.allowDayOverflow); - assert(date == Date(-2001, 2, 28)); - date.roll!"months"(-14, No.allowDayOverflow); - assert(date == Date(-2001, 12, 28)); - } - - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1, No.allowDayOverflow); - assert(date == Date(1, 12, 1)); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(1, 1, 1)); - } - - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48, No.allowDayOverflow); - assert(date == Date(4, 1, 1)); - date.roll!"months"(48, No.allowDayOverflow); - assert(date == Date(4, 1, 1)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49, No.allowDayOverflow); - assert(date == Date(4, 2, 29)); - date.roll!"months"(49, No.allowDayOverflow); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85, No.allowDayOverflow); - assert(date == Date(4, 2, 29)); - date.roll!"months"(85, No.allowDayOverflow); - assert(date == Date(4, 3, 29)); - } - - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1, No.allowDayOverflow); - assert(date == Date(-1, 12, 1)); - date.roll!"months"(1, No.allowDayOverflow); - assert(date == Date(-1, 1, 1)); - } - - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48, No.allowDayOverflow); - assert(date == Date(-4, 1, 1)); - date.roll!"months"(48, No.allowDayOverflow); - assert(date == Date(-4, 1, 1)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49, No.allowDayOverflow); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(49, No.allowDayOverflow); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85, No.allowDayOverflow); - assert(date == Date(-4, 2, 29)); - date.roll!"months"(85, No.allowDayOverflow); - assert(date == Date(-4, 3, 29)); - } - - { - auto date = Date(-3, 3, 31); - date.roll!"months"(85, No.allowDayOverflow).roll!"months"(-83, No.allowDayOverflow); - assert(date == Date(-3, 5, 30)); - } - } - - - /++ - Adds the given number of units to this $(LREF Date). A negative number will - subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF Date) one - year's worth of days gets the exact same $(LREF Date). - - The only accepted units are $(D "days"). - - Params: - units = The units to add. Must be $(D "days"). - days = The number of days to add to this $(LREF Date). - +/ - ref Date roll(string units)(long days) @safe pure nothrow - if (units == "days") - { - immutable limit = maxDay(_year, _month); - days %= limit; - auto newDay = _day + days; - - if (days < 0) - { - if (newDay < 1) - newDay += limit; - } - else if (newDay > limit) - newDay -= limit; - - _day = cast(ubyte) newDay; - return this; - } - - /// - @safe unittest - { - auto d = Date(2010, 1, 1); - d.roll!"days"(1); - assert(d == Date(2010, 1, 2)); - d.roll!"days"(365); - assert(d == Date(2010, 1, 26)); - d.roll!"days"(-32); - assert(d == Date(2010, 1, 25)); - } - - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 2, 6); - date.roll!"days"(365); - assert(date == Date(1999, 2, 7)); - date.roll!"days"(-365); - assert(date == Date(1999, 2, 6)); - date.roll!"days"(366); - assert(date == Date(1999, 2, 8)); - date.roll!"days"(730); - assert(date == Date(1999, 2, 10)); - date.roll!"days"(-1096); - assert(date == Date(1999, 2, 6)); - } - - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date.roll!"days"(1); - assert(date == Date(-1999, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 29)); - date.roll!"days"(1); - assert(date == Date(-2000, 2, 1)); - date.roll!"days"(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date.roll!"days"(1); - assert(date == Date(-1999, 6, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date.roll!"days"(1); - assert(date == Date(-1999, 7, 1)); - date.roll!"days"(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date.roll!"days"(-1); - assert(date == Date(-1999, 1, 31)); - date.roll!"days"(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(9); - assert(date == Date(-1999, 7, 15)); - date.roll!"days"(-11); - assert(date == Date(-1999, 7, 4)); - date.roll!"days"(30); - assert(date == Date(-1999, 7, 3)); - date.roll!"days"(-3); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(365); - assert(date == Date(-1999, 7, 30)); - date.roll!"days"(-365); - assert(date == Date(-1999, 7, 6)); - date.roll!"days"(366); - assert(date == Date(-1999, 7, 31)); - date.roll!"days"(730); - assert(date == Date(-1999, 7, 17)); - date.roll!"days"(-1096); - assert(date == Date(-1999, 7, 6)); - } - - //Test Both - { - auto date = Date(1, 7, 6); - date.roll!"days"(-365); - assert(date == Date(1, 7, 13)); - date.roll!"days"(365); - assert(date == Date(1, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(1, 7, 19)); - date.roll!"days"(730); - assert(date == Date(1, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365); - assert(date == Date(0, 7, 13)); - date.roll!"days"(365); - assert(date == Date(0, 7, 6)); - date.roll!"days"(-731); - assert(date == Date(0, 7, 19)); - date.roll!"days"(730); - assert(date == Date(0, 7, 5)); - } - - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(date == Date(0, 7, 8)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"days"(12))); - static assert(!__traits(compiles, idate.roll!"days"(12))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF Date). - +/ - Date opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - Date retval = this; - immutable days = duration.total!"days"; - mixin("return retval._addDays(" ~ op ~ "days);"); - } - - /// - @safe unittest - { - assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1)); - assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1)); - - assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31)); - assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26)); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(date + dur!"days"(7) == Date(1999, 7, 13)); - assert(date + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(date + dur!"hours"(24) == Date(1999, 7, 7)); - assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(date - dur!"days"(-7) == Date(1999, 7, 13)); - assert(date - dur!"days"(7) == Date(1999, 6, 29)); - - assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(date - dur!"hours"(24) == Date(1999, 7, 5)); - assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - auto duration = dur!"days"(12); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date + duration == Date(1999, 7, 18)); - assert(cdate + duration == Date(1999, 7, 18)); - assert(idate + duration == Date(1999, 7, 18)); - - assert(date - duration == Date(1999, 6, 24)); - assert(cdate - duration == Date(1999, 6, 24)); - assert(idate - duration == Date(1999, 6, 24)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - Date opBinary(string op)(TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - Date retval = this; - immutable days = convert!("hnsecs", "days")(td.hnsecs); - mixin("return retval._addDays(" ~ op ~ "days);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto date = Date(1999, 7, 6); - - assert(date + TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(date + TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - - assert(date - TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(date - TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF Date), as well as assigning the result to this $(LREF Date). - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD +) $(TD Duration) $(TD -->) $(TD Date)) - $(TR $(TD Date) $(TD -) $(TD Duration) $(TD -->) $(TD Date)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF Date). - +/ - ref Date opOpAssign(string op)(Duration duration) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable days = duration.total!"days"; - mixin("return _addDays(" ~ op ~ "days);"); - } - - @safe unittest - { - assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); - - assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); - assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); - assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); - assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); - - assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); - assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); - - { - auto date = Date(0, 1, 31); - (date += dur!"days"(507)) += dur!"days"(-2); - assert(date == Date(1, 6, 19)); - } - - auto duration = dur!"days"(12); - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - date += duration; - static assert(!__traits(compiles, cdate += duration)); - static assert(!__traits(compiles, idate += duration)); - - date -= duration; - static assert(!__traits(compiles, cdate -= duration)); - static assert(!__traits(compiles, idate -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref Date opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable days = convert!("seconds", "days")(td.seconds); - mixin("return _addDays(" ~ op ~ "days);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto date = Date(1999, 7, 6); - date += TickDuration.from!"usecs"(86_400_000_000); - assert(date == Date(1999, 7, 7)); - } - - { - auto date = Date(1999, 7, 6); - date += TickDuration.from!"usecs"(-86_400_000_000); - assert(date == Date(1999, 7, 5)); - } - - { - auto date = Date(1999, 7, 6); - date -= TickDuration.from!"usecs"(-86_400_000_000); - assert(date == Date(1999, 7, 7)); - } - - { - auto date = Date(1999, 7, 6); - date -= TickDuration.from!"usecs"(86_400_000_000); - assert(date == Date(1999, 7, 5)); - } - } - } - - - /++ - Gives the difference between two $(LREF Date)s. - - The legal types of arithmetic for $(LREF Date) using this operator are - - $(BOOKTABLE, - $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in Date rhs) @safe const pure nothrow - if (op == "-") - { - return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); - assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); - assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); - assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); - assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); - assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date - date == Duration.zero); - assert(cdate - date == Duration.zero); - assert(idate - date == Duration.zero); - - assert(date - cdate == Duration.zero); - assert(cdate - cdate == Duration.zero); - assert(idate - cdate == Duration.zero); - - assert(date - idate == Duration.zero); - assert(cdate - idate == Duration.zero); - assert(idate - idate == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF Date)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either $(LREF Date) is is irrelevant. It is the difference in the month - property combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF Date) to subtract from this one. - +/ - int diffMonths(in Date rhs) @safe const pure nothrow - { - immutable yearDiff = _year - rhs._year; - immutable monthDiff = _month - rhs._month; - - return yearDiff * 12 + monthDiff; - } - - /// - @safe unittest - { - assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); - assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); - assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); - assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - - //Test A.D. - assert(date.diffMonths(Date(1998, 6, 5)) == 13); - assert(date.diffMonths(Date(1998, 7, 5)) == 12); - assert(date.diffMonths(Date(1998, 8, 5)) == 11); - assert(date.diffMonths(Date(1998, 9, 5)) == 10); - assert(date.diffMonths(Date(1998, 10, 5)) == 9); - assert(date.diffMonths(Date(1998, 11, 5)) == 8); - assert(date.diffMonths(Date(1998, 12, 5)) == 7); - assert(date.diffMonths(Date(1999, 1, 5)) == 6); - assert(date.diffMonths(Date(1999, 2, 6)) == 5); - assert(date.diffMonths(Date(1999, 3, 6)) == 4); - assert(date.diffMonths(Date(1999, 4, 6)) == 3); - assert(date.diffMonths(Date(1999, 5, 6)) == 2); - assert(date.diffMonths(Date(1999, 6, 6)) == 1); - assert(date.diffMonths(date) == 0); - assert(date.diffMonths(Date(1999, 8, 6)) == -1); - assert(date.diffMonths(Date(1999, 9, 6)) == -2); - assert(date.diffMonths(Date(1999, 10, 6)) == -3); - assert(date.diffMonths(Date(1999, 11, 6)) == -4); - assert(date.diffMonths(Date(1999, 12, 6)) == -5); - assert(date.diffMonths(Date(2000, 1, 6)) == -6); - assert(date.diffMonths(Date(2000, 2, 6)) == -7); - assert(date.diffMonths(Date(2000, 3, 6)) == -8); - assert(date.diffMonths(Date(2000, 4, 6)) == -9); - assert(date.diffMonths(Date(2000, 5, 6)) == -10); - assert(date.diffMonths(Date(2000, 6, 6)) == -11); - assert(date.diffMonths(Date(2000, 7, 6)) == -12); - assert(date.diffMonths(Date(2000, 8, 6)) == -13); - - assert(Date(1998, 6, 5).diffMonths(date) == -13); - assert(Date(1998, 7, 5).diffMonths(date) == -12); - assert(Date(1998, 8, 5).diffMonths(date) == -11); - assert(Date(1998, 9, 5).diffMonths(date) == -10); - assert(Date(1998, 10, 5).diffMonths(date) == -9); - assert(Date(1998, 11, 5).diffMonths(date) == -8); - assert(Date(1998, 12, 5).diffMonths(date) == -7); - assert(Date(1999, 1, 5).diffMonths(date) == -6); - assert(Date(1999, 2, 6).diffMonths(date) == -5); - assert(Date(1999, 3, 6).diffMonths(date) == -4); - assert(Date(1999, 4, 6).diffMonths(date) == -3); - assert(Date(1999, 5, 6).diffMonths(date) == -2); - assert(Date(1999, 6, 6).diffMonths(date) == -1); - assert(Date(1999, 8, 6).diffMonths(date) == 1); - assert(Date(1999, 9, 6).diffMonths(date) == 2); - assert(Date(1999, 10, 6).diffMonths(date) == 3); - assert(Date(1999, 11, 6).diffMonths(date) == 4); - assert(Date(1999, 12, 6).diffMonths(date) == 5); - assert(Date(2000, 1, 6).diffMonths(date) == 6); - assert(Date(2000, 2, 6).diffMonths(date) == 7); - assert(Date(2000, 3, 6).diffMonths(date) == 8); - assert(Date(2000, 4, 6).diffMonths(date) == 9); - assert(Date(2000, 5, 6).diffMonths(date) == 10); - assert(Date(2000, 6, 6).diffMonths(date) == 11); - assert(Date(2000, 7, 6).diffMonths(date) == 12); - assert(Date(2000, 8, 6).diffMonths(date) == 13); - - assert(date.diffMonths(Date(1999, 6, 30)) == 1); - assert(date.diffMonths(Date(1999, 7, 1)) == 0); - assert(date.diffMonths(Date(1999, 7, 6)) == 0); - assert(date.diffMonths(Date(1999, 7, 11)) == 0); - assert(date.diffMonths(Date(1999, 7, 16)) == 0); - assert(date.diffMonths(Date(1999, 7, 21)) == 0); - assert(date.diffMonths(Date(1999, 7, 26)) == 0); - assert(date.diffMonths(Date(1999, 7, 31)) == 0); - assert(date.diffMonths(Date(1999, 8, 1)) == -1); - - assert(date.diffMonths(Date(1990, 6, 30)) == 109); - assert(date.diffMonths(Date(1990, 7, 1)) == 108); - assert(date.diffMonths(Date(1990, 7, 6)) == 108); - assert(date.diffMonths(Date(1990, 7, 11)) == 108); - assert(date.diffMonths(Date(1990, 7, 16)) == 108); - assert(date.diffMonths(Date(1990, 7, 21)) == 108); - assert(date.diffMonths(Date(1990, 7, 26)) == 108); - assert(date.diffMonths(Date(1990, 7, 31)) == 108); - assert(date.diffMonths(Date(1990, 8, 1)) == 107); - - assert(Date(1999, 6, 30).diffMonths(date) == -1); - assert(Date(1999, 7, 1).diffMonths(date) == 0); - assert(Date(1999, 7, 6).diffMonths(date) == 0); - assert(Date(1999, 7, 11).diffMonths(date) == 0); - assert(Date(1999, 7, 16).diffMonths(date) == 0); - assert(Date(1999, 7, 21).diffMonths(date) == 0); - assert(Date(1999, 7, 26).diffMonths(date) == 0); - assert(Date(1999, 7, 31).diffMonths(date) == 0); - assert(Date(1999, 8, 1).diffMonths(date) == 1); - - assert(Date(1990, 6, 30).diffMonths(date) == -109); - assert(Date(1990, 7, 1).diffMonths(date) == -108); - assert(Date(1990, 7, 6).diffMonths(date) == -108); - assert(Date(1990, 7, 11).diffMonths(date) == -108); - assert(Date(1990, 7, 16).diffMonths(date) == -108); - assert(Date(1990, 7, 21).diffMonths(date) == -108); - assert(Date(1990, 7, 26).diffMonths(date) == -108); - assert(Date(1990, 7, 31).diffMonths(date) == -108); - assert(Date(1990, 8, 1).diffMonths(date) == -107); - - //Test B.C. - auto dateBC = Date(-1999, 7, 6); - - assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); - assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); - assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); - assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); - assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); - assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); - assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); - assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); - assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); - assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); - assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); - assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); - assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); - assert(dateBC.diffMonths(dateBC) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); - assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); - assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); - assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); - assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); - assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); - assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); - assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); - assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); - assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); - assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); - assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); - assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); - - assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); - assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); - assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); - assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); - assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); - assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); - assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); - assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); - assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); - assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); - assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); - assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); - assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); - assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); - assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); - assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); - assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); - assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); - assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); - assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); - assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); - assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); - assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); - assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); - assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); - assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); - - assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); - assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); - assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); - assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); - - assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); - assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); - assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); - assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); - - assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); - assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); - assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); - assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); - - assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); - assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); - assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); - assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); - - //Test Both - assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); - assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.diffMonths(date) == 0); - assert(cdate.diffMonths(date) == 0); - assert(idate.diffMonths(date) == 0); - - assert(date.diffMonths(cdate) == 0); - assert(cdate.diffMonths(cdate) == 0); - assert(idate.diffMonths(cdate) == 0); - - assert(date.diffMonths(idate) == 0); - assert(cdate.diffMonths(idate) == 0); - assert(idate.diffMonths(idate) == 0); - } - - - /++ - Whether this $(LREF Date) is in a leap year. - +/ - @property bool isLeapYear() @safe const pure nothrow - { - return yearIsLeapYear(_year); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, date.isLeapYear = true)); - static assert(!__traits(compiles, cdate.isLeapYear = true)); - static assert(!__traits(compiles, idate.isLeapYear = true)); - } - - - /++ - Day of the week this $(LREF Date) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const pure nothrow - { - return getDayOfWeek(dayOfGregorianCal); - } - - @safe unittest - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.dayOfWeek == DayOfWeek.tue); - static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); - assert(idate.dayOfWeek == DayOfWeek.tue); - static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); - } - - - /++ - Day of the year this $(LREF Date) is on. - +/ - @property ushort dayOfYear() @safe const pure nothrow - { - if (_month >= Month.jan && _month <= Month.dec) - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - auto monthIndex = _month - Month.jan; - - return cast(ushort)(lastDay[monthIndex] + _day); - } - assert(0, "Invalid month."); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 1).dayOfYear == 1); - assert(Date(1999, 12, 31).dayOfYear == 365); - assert(Date(2000, 12, 31).dayOfYear == 366); - } - - @safe unittest - { - import std.algorithm.iteration : filter; - import std.range : chain; - - foreach (year; filter!((a){return !yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - foreach (doy; testDaysOfYear) - { - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == - doy.day); - } - } - - foreach (year; filter!((a){return yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - foreach (doy; testDaysOfLeapYear) - { - assert(Date(year, doy.md.month, doy.md.day).dayOfYear == - doy.day); - } - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.dayOfYear == 187); - assert(idate.dayOfYear == 187); - } - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF Date) is on. - - Throws: - $(LREF DateTimeException) if the given day is an invalid day of the - year. - +/ - @property void dayOfYear(int day) @safe pure - { - immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; - - if (day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear) ) - throw new DateTimeException("Invalid day of the year."); - - foreach (i; 1 .. lastDay.length) - { - if (day <= lastDay[i]) - { - _month = cast(Month)(cast(int) Month.jan + i - 1); - _day = cast(ubyte)(day - lastDay[i - 1]); - return; - } - } - assert(0, "Invalid day of the year."); - } - - @safe unittest - { - static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) - { - date.dayOfYear = day; - assert(date.month == expected.month); - assert(date.day == expected.day); - } - - foreach (doy; testDaysOfYear) - { - test(Date(1999, 1, 1), doy.day, doy.md); - test(Date(-1, 1, 1), doy.day, doy.md); - } - - foreach (doy; testDaysOfLeapYear) - { - test(Date(2000, 1, 1), doy.day, doy.md); - test(Date(-4, 1, 1), doy.day, doy.md); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.dayOfYear = 187)); - static assert(!__traits(compiles, idate.dayOfYear = 187)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - +/ - @property int dayOfGregorianCal() @safe const pure nothrow - { - if (isAD) - { - if (_year == 1) - return dayOfYear; - - int years = _year - 1; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - days += years * daysInYear; - - days += dayOfYear; - - return days; - } - else if (_year == 0) - return dayOfYear - daysInLeapYear; - else - { - int years = _year; - auto days = (years / 400) * daysIn400Years; - years %= 400; - - days += (years / 100) * daysIn100Years; - years %= 100; - - days += (years / 4) * daysIn4Years; - years %= 4; - - if (years < 0) - { - days -= daysInLeapYear; - ++years; - - days += years * daysInYear; - - days -= daysInYear - dayOfYear; - } - else - days -= daysInLeapYear - dayOfYear; - - return days; - } - } - - /// - @safe unittest - { - assert(Date(1, 1, 1).dayOfGregorianCal == 1); - assert(Date(1, 12, 31).dayOfGregorianCal == 365); - assert(Date(2, 1, 1).dayOfGregorianCal == 366); - - assert(Date(0, 12, 31).dayOfGregorianCal == 0); - assert(Date(0, 1, 1).dayOfGregorianCal == -365); - assert(Date(-1, 12, 31).dayOfGregorianCal == -366); - - assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); - assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - import std.range : chain; - - foreach (gd; chain(testGregDaysBC, testGregDaysAD)) - assert(gd.date.dayOfGregorianCal == gd.day); - - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.dayOfGregorianCal == 729_941); - assert(cdate.dayOfGregorianCal == 729_941); - assert(idate.dayOfGregorianCal == 729_941); - } - - /++ - The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - - Params: - day = The day of the Gregorian Calendar to set this $(LREF Date) to. - +/ - @property void dayOfGregorianCal(int day) @safe pure nothrow - { - this = Date(day); - } - - /// - @safe unittest - { - auto date = Date.init; - date.dayOfGregorianCal = 1; - assert(date == Date(1, 1, 1)); - - date.dayOfGregorianCal = 365; - assert(date == Date(1, 12, 31)); - - date.dayOfGregorianCal = 366; - assert(date == Date(2, 1, 1)); - - date.dayOfGregorianCal = 0; - assert(date == Date(0, 12, 31)); - - date.dayOfGregorianCal = -365; - assert(date == Date(-0, 1, 1)); - - date.dayOfGregorianCal = -366; - assert(date == Date(-1, 12, 31)); - - date.dayOfGregorianCal = 730_120; - assert(date == Date(2000, 1, 1)); - - date.dayOfGregorianCal = 734_137; - assert(date == Date(2010, 12, 31)); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - date.dayOfGregorianCal = 187; - assert(date.dayOfGregorianCal == 187); - static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF Date) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() @safe const pure nothrow - { - immutable weekday = dayOfWeek; - immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; - immutable week = (dayOfYear - adjustedWeekday + 10) / 7; - - try - { - if (week == 53) - { - switch (Date(_year + 1, 1, 1).dayOfWeek) - { - case DayOfWeek.mon: - case DayOfWeek.tue: - case DayOfWeek.wed: - case DayOfWeek.thu: - return 1; - case DayOfWeek.fri: - case DayOfWeek.sat: - case DayOfWeek.sun: - return 53; - default: - assert(0, "Invalid ISO Week"); - } - } - else if (week > 0) - return cast(ubyte) week; - else - return Date(_year - 1, 12, 31).isoWeek; - } - catch (Exception e) - assert(0, "Date's constructor threw."); - } - - @safe unittest - { - //Test A.D. - assert(Date(2009, 12, 28).isoWeek == 53); - assert(Date(2009, 12, 29).isoWeek == 53); - assert(Date(2009, 12, 30).isoWeek == 53); - assert(Date(2009, 12, 31).isoWeek == 53); - assert(Date(2010, 1, 1).isoWeek == 53); - assert(Date(2010, 1, 2).isoWeek == 53); - assert(Date(2010, 1, 3).isoWeek == 53); - assert(Date(2010, 1, 4).isoWeek == 1); - assert(Date(2010, 1, 5).isoWeek == 1); - assert(Date(2010, 1, 6).isoWeek == 1); - assert(Date(2010, 1, 7).isoWeek == 1); - assert(Date(2010, 1, 8).isoWeek == 1); - assert(Date(2010, 1, 9).isoWeek == 1); - assert(Date(2010, 1, 10).isoWeek == 1); - assert(Date(2010, 1, 11).isoWeek == 2); - assert(Date(2010, 12, 31).isoWeek == 52); - - assert(Date(2004, 12, 26).isoWeek == 52); - assert(Date(2004, 12, 27).isoWeek == 53); - assert(Date(2004, 12, 28).isoWeek == 53); - assert(Date(2004, 12, 29).isoWeek == 53); - assert(Date(2004, 12, 30).isoWeek == 53); - assert(Date(2004, 12, 31).isoWeek == 53); - assert(Date(2005, 1, 1).isoWeek == 53); - assert(Date(2005, 1, 2).isoWeek == 53); - - assert(Date(2005, 12, 31).isoWeek == 52); - assert(Date(2007, 1, 1).isoWeek == 1); - - assert(Date(2007, 12, 30).isoWeek == 52); - assert(Date(2007, 12, 31).isoWeek == 1); - assert(Date(2008, 1, 1).isoWeek == 1); - - assert(Date(2008, 12, 28).isoWeek == 52); - assert(Date(2008, 12, 29).isoWeek == 1); - assert(Date(2008, 12, 30).isoWeek == 1); - assert(Date(2008, 12, 31).isoWeek == 1); - assert(Date(2009, 1, 1).isoWeek == 1); - assert(Date(2009, 1, 2).isoWeek == 1); - assert(Date(2009, 1, 3).isoWeek == 1); - assert(Date(2009, 1, 4).isoWeek == 1); - - //Test B.C. - //The algorithm should work identically for both A.D. and B.C. since - //it doesn't really take the year into account, so B.C. testing - //probably isn't really needed. - assert(Date(0, 12, 31).isoWeek == 52); - assert(Date(0, 1, 4).isoWeek == 1); - assert(Date(0, 1, 1).isoWeek == 52); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.isoWeek == 27); - static assert(!__traits(compiles, cdate.isoWeek = 3)); - assert(idate.isoWeek == 27); - static assert(!__traits(compiles, idate.isoWeek = 3)); - } - - - /++ - $(LREF Date) for the last day in the month that this $(LREF Date) is in. - +/ - @property Date endOfMonth() @safe const pure nothrow - { - try - return Date(_year, _month, maxDay(_year, _month)); - catch (Exception e) - assert(0, "Date's constructor threw."); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); - assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); - } - - @safe unittest - { - //Test A.D. - assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); - assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); - assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); - assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); - assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); - assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); - assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); - assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); - assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); - assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); - assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); - - //Test B.C. - assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); - assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); - assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); - assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); - assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); - assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); - assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); - assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); - assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); - assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); - assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); - assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); - assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); - static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); - } - - - /++ - The last day in the month that this $(LREF Date) is in. - +/ - @property ubyte daysInMonth() @safe const pure nothrow - { - return maxDay(_year, _month); - } - - /// - @safe unittest - { - assert(Date(1999, 1, 6).daysInMonth == 31); - assert(Date(1999, 2, 7).daysInMonth == 28); - assert(Date(2000, 2, 7).daysInMonth == 29); - assert(Date(2000, 6, 4).daysInMonth == 30); - } - - @safe unittest - { - //Test A.D. - assert(Date(1999, 1, 1).daysInMonth == 31); - assert(Date(1999, 2, 1).daysInMonth == 28); - assert(Date(2000, 2, 1).daysInMonth == 29); - assert(Date(1999, 3, 1).daysInMonth == 31); - assert(Date(1999, 4, 1).daysInMonth == 30); - assert(Date(1999, 5, 1).daysInMonth == 31); - assert(Date(1999, 6, 1).daysInMonth == 30); - assert(Date(1999, 7, 1).daysInMonth == 31); - assert(Date(1999, 8, 1).daysInMonth == 31); - assert(Date(1999, 9, 1).daysInMonth == 30); - assert(Date(1999, 10, 1).daysInMonth == 31); - assert(Date(1999, 11, 1).daysInMonth == 30); - assert(Date(1999, 12, 1).daysInMonth == 31); - - //Test B.C. - assert(Date(-1999, 1, 1).daysInMonth == 31); - assert(Date(-1999, 2, 1).daysInMonth == 28); - assert(Date(-2000, 2, 1).daysInMonth == 29); - assert(Date(-1999, 3, 1).daysInMonth == 31); - assert(Date(-1999, 4, 1).daysInMonth == 30); - assert(Date(-1999, 5, 1).daysInMonth == 31); - assert(Date(-1999, 6, 1).daysInMonth == 30); - assert(Date(-1999, 7, 1).daysInMonth == 31); - assert(Date(-1999, 8, 1).daysInMonth == 31); - assert(Date(-1999, 9, 1).daysInMonth == 30); - assert(Date(-1999, 10, 1).daysInMonth == 31); - assert(Date(-1999, 11, 1).daysInMonth == 30); - assert(Date(-1999, 12, 1).daysInMonth == 31); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.daysInMonth = 30)); - static assert(!__traits(compiles, idate.daysInMonth = 30)); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const pure nothrow - { - return _year > 0; - } - - /// - @safe unittest - { - assert(Date(1, 1, 1).isAD); - assert(Date(2010, 12, 31).isAD); - assert(!Date(0, 12, 31).isAD); - assert(!Date(-2010, 1, 1).isAD); - } - - @safe unittest - { - assert(Date(2010, 7, 4).isAD); - assert(Date(1, 1, 1).isAD); - assert(!Date(0, 1, 1).isAD); - assert(!Date(-1, 1, 1).isAD); - assert(!Date(-2010, 7, 4).isAD); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.isAD); - assert(idate.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this $(LREF Date) at noon (since the Julian day changes - at noon). - +/ - @property long julianDay() @safe const pure nothrow - { - return dayOfGregorianCal + 1_721_425; - } - - @safe unittest - { - assert(Date(-4713, 11, 24).julianDay == 0); - assert(Date(0, 12, 31).julianDay == 1_721_425); - assert(Date(1, 1, 1).julianDay == 1_721_426); - assert(Date(1582, 10, 15).julianDay == 2_299_161); - assert(Date(1858, 11, 17).julianDay == 2_400_001); - assert(Date(1982, 1, 4).julianDay == 2_444_974); - assert(Date(1996, 3, 31).julianDay == 2_450_174); - assert(Date(2010, 8, 24).julianDay == 2_455_433); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.julianDay == 2_451_366); - assert(idate.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const pure nothrow - { - return julianDay - 2_400_001; - } - - @safe unittest - { - assert(Date(1858, 11, 17).modJulianDay == 0); - assert(Date(2010, 8, 24).modJulianDay == 55_432); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.modJulianDay == 51_365); - assert(idate.modJulianDay == 51_365); - } - - - /++ - Converts this $(LREF Date) to a string with the format YYYYMMDD. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - { - if (_year >= 0) - { - if (_year < 10_000) - return format("%04d%02d%02d", _year, _month, _day); - else - return format("+%05d%02d%02d", _year, _month, _day); - } - else if (_year > -10_000) - return format("%05d%02d%02d", _year, _month, _day); - else - return format("%06d%02d%02d", _year, _month, _day); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toISOString() == "20100704"); - assert(Date(1998, 12, 25).toISOString() == "19981225"); - assert(Date(0, 1, 5).toISOString() == "00000105"); - assert(Date(-4, 1, 5).toISOString() == "-00040105"); - } - - @safe unittest - { - //Test A.D. - assert(Date(9, 12, 4).toISOString() == "00091204"); - assert(Date(99, 12, 4).toISOString() == "00991204"); - assert(Date(999, 12, 4).toISOString() == "09991204"); - assert(Date(9999, 7, 4).toISOString() == "99990704"); - assert(Date(10000, 10, 20).toISOString() == "+100001020"); - - //Test B.C. - assert(Date(0, 12, 4).toISOString() == "00001204"); - assert(Date(-9, 12, 4).toISOString() == "-00091204"); - assert(Date(-99, 12, 4).toISOString() == "-00991204"); - assert(Date(-999, 12, 4).toISOString() == "-09991204"); - assert(Date(-9999, 7, 4).toISOString() == "-99990704"); - assert(Date(-10000, 10, 20).toISOString() == "-100001020"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toISOString() == "19990706"); - assert(idate.toISOString() == "19990706"); - } - - /++ - Converts this $(LREF Date) to a string with the format YYYY-MM-DD. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - { - if (_year >= 0) - { - if (_year < 10_000) - return format("%04d-%02d-%02d", _year, _month, _day); - else - return format("+%05d-%02d-%02d", _year, _month, _day); - } - else if (_year > -10_000) - return format("%05d-%02d-%02d", _year, _month, _day); - else - return format("%06d-%02d-%02d", _year, _month, _day); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); - assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); - assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); - assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); - } - - @safe unittest - { - //Test A.D. - assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); - assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); - assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); - assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); - assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); - - //Test B.C. - assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); - assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); - assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); - assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); - assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); - assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toISOExtString() == "1999-07-06"); - assert(idate.toISOExtString() == "1999-07-06"); - } - - /++ - Converts this $(LREF Date) to a string with the format YYYY-Mon-DD. - +/ - string toSimpleString() @safe const pure nothrow - { - import std.format : format; - try - { - if (_year >= 0) - { - if (_year < 10_000) - return format("%04d-%s-%02d", _year, monthToString(_month), _day); - else - return format("+%05d-%s-%02d", _year, monthToString(_month), _day); - } - else if (_year > -10_000) - return format("%05d-%s-%02d", _year, monthToString(_month), _day); - else - return format("%06d-%s-%02d", _year, monthToString(_month), _day); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); - assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); - assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); - assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); - } - - @safe unittest - { - //Test A.D. - assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); - assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); - assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); - assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); - assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); - - //Test B.C. - assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); - assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); - assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); - assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); - assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); - assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(cdate.toSimpleString() == "1999-Jul-06"); - assert(idate.toSimpleString() == "1999-Jul-06"); - } - - - /++ - Converts this $(LREF Date) to a string. - +/ - string toString() @safe const pure nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - assert(date.toString()); - assert(cdate.toString()); - assert(idate.toString()); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYYMMDD. Whitespace - is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromISOString(S)(in S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : all, startsWith; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - auto day = dstr[$-2 .. $]; - auto month = dstr[$-4 .. $-2]; - auto year = dstr[0 .. $-4]; - - enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - if (year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid ISO String: %s", isoString))); - } - else - enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - return Date(to!short(year), to!ubyte(month), to!ubyte(day)); - } - - /// - @safe unittest - { - assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); - assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); - assert(Date.fromISOString("00000105") == Date(0, 1, 5)); - assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); - assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromISOString("")); - assertThrown!DateTimeException(Date.fromISOString("990704")); - assertThrown!DateTimeException(Date.fromISOString("0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070")); - assertThrown!DateTimeException(Date.fromISOString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOString("120100704")); - assertThrown!DateTimeException(Date.fromISOString("-0100704")); - assertThrown!DateTimeException(Date.fromISOString("+0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070a")); - assertThrown!DateTimeException(Date.fromISOString("20100a04")); - assertThrown!DateTimeException(Date.fromISOString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); - - assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); - assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); - assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); - assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-MM-DD. Whitespace - is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for - dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromISOExtString(S)(in S isoExtString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : all, startsWith; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length >= 10, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto day = dstr[$-2 .. $]; - auto month = dstr[$-5 .. $-3]; - auto year = dstr[0 .. $-6]; - - enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(day), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(month), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - if (year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - } - else - enforce(all!isDigit(year), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - return Date(to!short(year), to!ubyte(month), to!ubyte(day)); - } - - /// - @safe unittest - { - assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); - assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); - assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); - assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); - assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromISOExtString("")); - assertThrown!DateTimeException(Date.fromISOExtString("990704")); - assertThrown!DateTimeException(Date.fromISOExtString("0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOExtString("120100704")); - assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); - assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOExtString("20100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); - - assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); - assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); - assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); - } - - - /++ - Creates a $(LREF Date) from a string with the format YYYY-Mon-DD. - Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates. - - Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF Date) would not be valid. - +/ - static Date fromSimpleString(S)(in S simpleString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : all, startsWith; - import std.ascii : isDigit; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(simpleString)); - - enforce(dstr.length >= 11, new DateTimeException(format("Invalid string format: %s", simpleString))); - - auto day = dstr[$-2 .. $]; - auto month = monthFromString(to!string(dstr[$-6 .. $-3])); - auto year = dstr[0 .. $-7]; - - enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString))); - - if (year.length > 4) - { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid string format: %s", simpleString))); - } - else - enforce(all!isDigit(year), - new DateTimeException(format("Invalid string format: %s", simpleString))); - - return Date(to!short(year), month, to!ubyte(day)); - } - - /// - @safe unittest - { - assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); - assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); - assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); - assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); - assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); - } - - @safe unittest - { - assertThrown!DateTimeException(Date.fromSimpleString("")); - assertThrown!DateTimeException(Date.fromSimpleString("990704")); - assertThrown!DateTimeException(Date.fromSimpleString("0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); - assertThrown!DateTimeException(Date.fromSimpleString("120100704")); - assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); - assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromSimpleString("20100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); - - assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); - assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); - assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); - } - - - /++ - Returns the $(LREF Date) farthest in the past which is representable by - $(LREF Date). - +/ - @property static Date min() @safe pure nothrow - { - auto date = Date.init; - date._year = short.min; - date._month = Month.jan; - date._day = 1; - - return date; - } - - @safe unittest - { - assert(Date.min.year < 0); - assert(Date.min < Date.max); - } - - - /++ - Returns the $(LREF Date) farthest in the future which is representable by - $(LREF Date). - +/ - @property static Date max() @safe pure nothrow - { - auto date = Date.init; - date._year = short.max; - date._month = Month.dec; - date._day = 31; - - return date; - } - - @safe unittest - { - assert(Date.max.year > 0); - assert(Date.max > Date.min); - } - - -private: - - /+ - Whether the given values form a valid date. - - Params: - year = The year to test. - month = The month of the Gregorian Calendar to test. - day = The day of the month to test. - +/ - static bool _valid(int year, int month, int day) @safe pure nothrow - { - if (!valid!"months"(month)) - return false; - - return valid!"days"(year, month, day); - } - - /+ - Adds the given number of days to this $(LREF Date). A negative number will - subtract. - - The month will be adjusted along with the day if the number of days - added (or subtracted) would overflow (or underflow) the current month. - The year will be adjusted along with the month if the increase (or - decrease) to the month would cause it to overflow (or underflow) the - current year. - - $(D _addDays(numDays)) is effectively equivalent to - $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). - - Params: - days = The number of days to add to this Date. - +/ - ref Date _addDays(long days) return @safe pure nothrow - { - dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); - return this; - } - - @safe unittest - { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date._addDays(1); - assert(date == Date(1999, 3, 1)); - date._addDays(-1); - assert(date == Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date._addDays(1); - assert(date == Date(2000, 2, 29)); - date._addDays(1); - assert(date == Date(2000, 3, 1)); - date._addDays(-1); - assert(date == Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date._addDays(1); - assert(date == Date(1999, 7, 1)); - date._addDays(-1); - assert(date == Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date._addDays(1); - assert(date == Date(1999, 8, 1)); - date._addDays(-1); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 1, 1); - date._addDays(-1); - assert(date == Date(1998, 12, 31)); - date._addDays(1); - assert(date == Date(1999, 1, 1)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(9); - assert(date == Date(1999, 7, 15)); - date._addDays(-11); - assert(date == Date(1999, 7, 4)); - date._addDays(30); - assert(date == Date(1999, 8, 3)); - date._addDays(-3); - assert(date == Date(1999, 7, 31)); - } - - { - auto date = Date(1999, 7, 6); - date._addDays(365); - assert(date == Date(2000, 7, 5)); - date._addDays(-365); - assert(date == Date(1999, 7, 6)); - date._addDays(366); - assert(date == Date(2000, 7, 6)); - date._addDays(730); - assert(date == Date(2002, 7, 6)); - date._addDays(-1096); - assert(date == Date(1999, 7, 6)); - } - - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date._addDays(1); - assert(date == Date(-1999, 3, 1)); - date._addDays(-1); - assert(date == Date(-1999, 2, 28)); - } - - { - auto date = Date(-2000, 2, 28); - date._addDays(1); - assert(date == Date(-2000, 2, 29)); - date._addDays(1); - assert(date == Date(-2000, 3, 1)); - date._addDays(-1); - assert(date == Date(-2000, 2, 29)); - } - - { - auto date = Date(-1999, 6, 30); - date._addDays(1); - assert(date == Date(-1999, 7, 1)); - date._addDays(-1); - assert(date == Date(-1999, 6, 30)); - } - - { - auto date = Date(-1999, 7, 31); - date._addDays(1); - assert(date == Date(-1999, 8, 1)); - date._addDays(-1); - assert(date == Date(-1999, 7, 31)); - } - - { - auto date = Date(-1999, 1, 1); - date._addDays(-1); - assert(date == Date(-2000, 12, 31)); - date._addDays(1); - assert(date == Date(-1999, 1, 1)); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(9); - assert(date == Date(-1999, 7, 15)); - date._addDays(-11); - assert(date == Date(-1999, 7, 4)); - date._addDays(30); - assert(date == Date(-1999, 8, 3)); - date._addDays(-3); - } - - { - auto date = Date(-1999, 7, 6); - date._addDays(365); - assert(date == Date(-1998, 7, 6)); - date._addDays(-365); - assert(date == Date(-1999, 7, 6)); - date._addDays(366); - assert(date == Date(-1998, 7, 7)); - date._addDays(730); - assert(date == Date(-1996, 7, 6)); - date._addDays(-1096); - assert(date == Date(-1999, 7, 6)); - } - - //Test Both - { - auto date = Date(1, 7, 6); - date._addDays(-365); - assert(date == Date(0, 7, 6)); - date._addDays(365); - assert(date == Date(1, 7, 6)); - date._addDays(-731); - assert(date == Date(-1, 7, 6)); - date._addDays(730); - assert(date == Date(1, 7, 5)); - } - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate._addDays(12))); - static assert(!__traits(compiles, idate._addDays(12))); - } - - - @safe pure invariant() - { - import std.format : format; - assert(valid!"months"(_month), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - assert(valid!"days"(_year, _month, _day), - format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); - } - - - short _year = 1; - Month _month = Month.jan; - ubyte _day = 1; -} - - /++ Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object which holds both the date and the time. It is optimized for calendar-based @@ -18821,43 +14500,6 @@ if (!isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) //============================================================================== private: -//============================================================================== -// Section with private enums and constants. -//============================================================================== - -enum daysInYear = 365; // The number of days in a non-leap year. -enum daysInLeapYear = 366; // The numbef or days in a leap year. -enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. -enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. -enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. - -/+ - Array of integers representing the last days of each month in a year. - +/ -immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; - -/+ - Array of integers representing the last days of each month in a leap year. - +/ -immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; - -/+ - Array of the short (three letter) names of each month. - +/ -immutable string[12] _monthNames = ["Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec"]; - - //============================================================================== // Section with private helper functions and templates. //============================================================================== @@ -18948,156 +14590,6 @@ if (validTimeUnits(units) && assert(hnsecs == 2595000000007L); } -/+ - Returns the day of the week for the given day of the Gregorian Calendar. - - Params: - day = The day of the Gregorian Calendar for which to get the day of - the week. - +/ -DayOfWeek getDayOfWeek(int day) @safe pure nothrow -{ - //January 1st, 1 A.D. was a Monday - if (day >= 0) - return cast(DayOfWeek)(day % 7); - else - { - immutable dow = cast(DayOfWeek)((day % 7) + 7); - - if (dow == 7) - return DayOfWeek.sun; - else - return dow; - } -} - -@safe unittest -{ - //Test A.D. - assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); - - //Test B.C. - assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); - assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); - assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); - assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); - assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); - assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); - assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); - assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); -} - - -/+ - Returns the string representation of the given month. - +/ -string monthToString(Month month) @safe pure -{ - import std.format : format; - assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); - return _monthNames[month - Month.jan]; -} - -@safe unittest -{ - assert(monthToString(Month.jan) == "Jan"); - assert(monthToString(Month.feb) == "Feb"); - assert(monthToString(Month.mar) == "Mar"); - assert(monthToString(Month.apr) == "Apr"); - assert(monthToString(Month.may) == "May"); - assert(monthToString(Month.jun) == "Jun"); - assert(monthToString(Month.jul) == "Jul"); - assert(monthToString(Month.aug) == "Aug"); - assert(monthToString(Month.sep) == "Sep"); - assert(monthToString(Month.oct) == "Oct"); - assert(monthToString(Month.nov) == "Nov"); - assert(monthToString(Month.dec) == "Dec"); -} - - -/+ - Returns the Month corresponding to the given string. - - Params: - monthStr = The string representation of the month to get the Month for. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month string. - +/ -Month monthFromString(string monthStr) @safe pure -{ - import std.format : format; - switch (monthStr) - { - case "Jan": - return Month.jan; - case "Feb": - return Month.feb; - case "Mar": - return Month.mar; - case "Apr": - return Month.apr; - case "May": - return Month.may; - case "Jun": - return Month.jun; - case "Jul": - return Month.jul; - case "Aug": - return Month.aug; - case "Sep": - return Month.sep; - case "Oct": - return Month.oct; - case "Nov": - return Month.nov; - case "Dec": - return Month.dec; - default: - throw new DateTimeException(format("Invalid month %s", monthStr)); - } -} - -@safe unittest -{ - import std.stdio : writeln; - import std.traits : EnumMembers; - foreach (badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", - "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) - { - scope(failure) writeln(badStr); - assertThrown!DateTimeException(monthFromString(badStr)); - } - - foreach (month; EnumMembers!Month) - { - scope(failure) writeln(month); - assert(monthFromString(monthToString(month)) == month); - } -} - /+ The time units which are one step smaller than the given units. @@ -19576,7 +15068,7 @@ version(unittest) SysTime[] testSysTimesBC; SysTime[] testSysTimesAD; - //I'd use a Tuple, but I get forward reference errors if I try. + // I'd use a Tuple, but I get forward reference errors if I try. struct GregDay { int day; Date date; } auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), //Start of the Hebrew Calendar GregDay(-735_233, Date(-2012, 1, 1)), diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 2b0a2e3cc1e..e9eb44f37ff 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -11,6 +11,7 @@ module std.datetime.timezone; import core.time; import std.datetime.common; +import std.datetime.date; import std.exception : enforce; import std.range.primitives; import std.traits : isIntegral, isSomeString, Unqual; @@ -36,7 +37,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : Clock, Date, DateTime, stdTimeToUnixTime, SysTime; // temporary +import std.datetime : Clock, DateTime, stdTimeToUnixTime, SysTime; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time From d1471c2699d1d8775713591b37fb9abd9add9407 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 14:43:55 +0200 Subject: [PATCH 100/262] Move DateTime to std.datetime.datetime. --- std/datetime/common.d | 16 +- std/datetime/datetime.d | 3781 +++++++++++++++++++++++++++++++++++++++ std/datetime/interval.d | 19 +- std/datetime/package.d | 3510 ------------------------------------ std/datetime/timezone.d | 3 +- 5 files changed, 3808 insertions(+), 3521 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index ffd1c5be4df..733982c7e75 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -455,13 +455,11 @@ private: @safe unittest { import core.time : Duration; - import std.datetime : Date, DateTime, Interval, SysTime; // temporary - /+ - import std.datetime.date : Date; - import std.datetime.datetime : DateTime; + import std.datetime : SysTime; // temporary + import std.datetime.date; + import std.datetime.datetime; import std.datetime.interval : Interval; - import std.datetime.systime : SysTime; - +/ + //import std.datetime.systime; import std.datetime.timeofday; static assert(isTimePoint!Date); @@ -477,9 +475,9 @@ private: @safe unittest { import core.time; - import std.datetime : Date, DateTime, SysTime; // temporary - //import std.datetime.date; - //import std.datetime.datetime; + import std.datetime : SysTime; // temporary + import std.datetime.date; + import std.datetime.datetime; import std.datetime.interval; //import std.datetime.systime; import std.datetime.timeofday; diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d index 322b8b65175..559b8c48b58 100644 --- a/std/datetime/datetime.d +++ b/std/datetime/datetime.d @@ -8,3 +8,3784 @@ LREF2=$(D $2) +/ module std.datetime.datetime; + +import core.time; +import std.datetime.common; +import std.datetime.date; +import std.datetime.timeofday; +import std.traits : isSomeString, Unqual; + +version(unittest) import std.exception : assertThrown; + + +@safe unittest +{ + initializeTests(); +} + + +/++ + Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object + which holds both the date and the time. It is optimized for calendar-based + operations and has no concept of time zone. For an object which is + optimized for time operations based on the system time, use + $(LREF SysTime). $(LREF SysTime) has a concept of time zone and has much higher + precision (hnsecs). $(D DateTime) is intended primarily for calendar-based + uses rather than precise time operations. + +/ +struct DateTime +{ +public: + + /++ + Params: + date = The date portion of $(LREF DateTime). + tod = The time portion of $(LREF DateTime). + +/ + this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow + { + _date = date; + _tod = tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Params: + year = The year portion of the date. + month = The month portion of the date. + day = The day portion of the date. + hour = The hour portion of the time; + minute = The minute portion of the time; + second = The second portion of the time; + +/ + this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure + { + _date = Date(year, month, day); + _tod = TimeOfDay(hour, minute, second); + } + + @safe unittest + { + { + auto dt = DateTime(1999, 7 ,6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(1999, 7 ,6, 12, 30, 33); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Compares this $(LREF DateTime) with the given $(D DateTime.). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in DateTime rhs) @safe const pure nothrow + { + immutable dateResult = _date.opCmp(rhs._date); + + if (dateResult != 0) + return dateResult; + + return _tod.opCmp(rhs._tod); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); + + assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); + assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); + assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); + + assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); + + assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); + + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + + assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + // Test B.C. + assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + // Test Both + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + assert(dt.opCmp(dt) == 0); + assert(dt.opCmp(cdt) == 0); + assert(dt.opCmp(idt) == 0); + assert(cdt.opCmp(dt) == 0); + assert(cdt.opCmp(cdt) == 0); + assert(cdt.opCmp(idt) == 0); + assert(idt.opCmp(dt) == 0); + assert(idt.opCmp(cdt) == 0); + assert(idt.opCmp(idt) == 0); + } + + + /++ + The date portion of $(LREF DateTime). + +/ + @property Date date() @safe const pure nothrow + { + return _date; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.date == Date.init); + } + + { + auto dt = DateTime(Date(1999, 7, 6)); + assert(dt.date == Date(1999, 7, 6)); + } + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + assert(cdt.date == Date(1999, 7, 6)); + assert(idt.date == Date(1999, 7, 6)); + } + + + /++ + The date portion of $(LREF DateTime). + + Params: + date = The Date to set this $(LREF DateTime)'s date portion to. + +/ + @property void date(in Date date) @safe pure nothrow + { + _date = date; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.date = Date(1999, 7, 6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); + static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); + } + + + /++ + The time portion of $(LREF DateTime). + +/ + @property TimeOfDay timeOfDay() @safe const pure nothrow + { + return _tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.timeOfDay == TimeOfDay.init); + } + + { + auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); + assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); + assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + + /++ + The time portion of $(LREF DateTime). + + Params: + tod = The $(LREF TimeOfDay) to set this $(LREF DateTime)'s time portion + to. + +/ + @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow + { + _tod = tod; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.timeOfDay = TimeOfDay(12, 30, 33); + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay(12, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); + static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const pure nothrow + { + return _date.year; + } + + @safe unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(idt.year == 1999); + assert(idt.year == 1999); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this $(LREF DateTime)'s year to. + + Throws: + $(LREF DateTimeException) if the new year is not a leap year and if the + resulting date would be on February 29th. + +/ + @property void year(int year) @safe pure + { + _date.year = year; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); + } + + @safe unittest + { + static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + { + dt.year = year; + assert(dt == expected); + } + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 1999, + DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 0, + DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + -1999, + DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.year = 7)); + static assert(!__traits(compiles, idt.year = 7)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(LREF DateTimeException) if $(D isAD) is true. + +/ + @property short yearBC() @safe const pure + { + return _date.yearBC; + } + + /// + @safe unittest + { + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); + assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); + assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); + } + + @safe unittest + { + assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF DateTime)'s year to. + + Throws: + $(LREF DateTimeException) if a non-positive value is given. + +/ + @property void yearBC(int year) @safe pure + { + _date.yearBC = year; + } + + /// + @safe unittest + { + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); + dt.yearBC = 1; + assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + + dt.yearBC = 10; + assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); + } + + @safe unittest + { + assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const pure nothrow + { + return _date.month; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); + } + + @safe unittest + { + assert(DateTime.init.month == 1); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.month == 7); + assert(idt.month == 7); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF DateTime)'s month to. + + Throws: + $(LREF DateTimeException) if the given month is not a valid month. + +/ + @property void month(Month month) @safe pure + { + _date.month = month; + } + + @safe unittest + { + static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) + { + dt.month = month; + assert(expected != DateTime.init); + assert(dt == expected); + } + + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.month = 12)); + static assert(!__traits(compiles, idt.month = 12)); + } + + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const pure nothrow + { + return _date.day; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); + } + + @safe unittest + { + import std.format : format; + import std.range : chain; + + static void test(DateTime dateTime, int expected) + { + assert(dateTime.day == expected, format("Value given: %s", dateTime)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + test(DateTime(Date(year, md.month, md.day), tod), md.day); + } + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.day == 6); + assert(idt.day == 6); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF DateTime)'s day to. + + Throws: + $(LREF DateTimeException) if the given day is not a valid day of the + current month. + +/ + @property void day(int day) @safe pure + { + _date.day = day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + + static void testDT(DateTime dt, int day) + { + dt.day = day; + } + + // Test A.D. + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); + + { + auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); + } + + // Test B.C. + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); + + auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.day = 27)); + static assert(!__traits(compiles, idt.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _tod.hour; + } + + @safe unittest + { + assert(DateTime.init.hour == 0); + assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.hour == 12); + assert(idt.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF DateTime)'s hour to. + + Throws: + $(LREF DateTimeException) if the given hour would result in an invalid + $(LREF DateTime). + +/ + @property void hour(int hour) @safe pure + { + _tod.hour = hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); + + auto dt = DateTime.init; + dt.hour = 12; + assert(dt == DateTime(1, 1, 1, 12, 0, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.hour = 27)); + static assert(!__traits(compiles, idt.hour = 27)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _tod.minute; + } + + @safe unittest + { + assert(DateTime.init.minute == 0); + assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.minute == 30); + assert(idt.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF DateTime)'s minute to. + + Throws: + $(LREF DateTimeException) if the given minute would result in an + invalid $(LREF DateTime). + +/ + @property void minute(int minute) @safe pure + { + _tod.minute = minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); + + auto dt = DateTime.init; + dt.minute = 30; + assert(dt == DateTime(1, 1, 1, 0, 30, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.minute = 27)); + static assert(!__traits(compiles, idt.minute = 27)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _tod.second; + } + + @safe unittest + { + assert(DateTime.init.second == 0); + assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.second == 33); + assert(idt.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF DateTime)'s second to. + + Throws: + $(LREF DateTimeException) if the given seconds would result in an + invalid $(LREF DateTime). + +/ + @property void second(int second) @safe pure + { + _tod.second = second; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.second = 60;}()); + + auto dt = DateTime.init; + dt.second = 33; + assert(dt == DateTime(1, 1, 1, 0, 0, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.second = 27)); + static assert(!__traits(compiles, idt.second = 27)); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime add(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.add!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); + dt1.add!"months"(11); + assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); + dt2.add!"months"(-11); + assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); + + auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); + dt3.add!"years"(1); + assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); + dt4.add!"years"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.add!"years"(7).add!"months"(-4); + assert(dt == DateTime(2006, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.add!"years"(4))); + static assert(!__traits(compiles, idt.add!"years"(4))); + static assert(!__traits(compiles, cdt.add!"months"(4))); + static assert(!__traits(compiles, idt.add!"months"(4))); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF DateTime) 12 months + gets the exact same $(LREF DateTime). However, the days can still be + affected due to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.roll!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); + dt1.roll!"months"(1); + assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); + dt2.roll!"months"(-1); + assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); + + auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); + dt3.roll!"months"(1); + assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); + + auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); + dt4.roll!"months"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); + + auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); + dt5.roll!"years"(1); + assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); + dt6.roll!"years"(1, AllowDayOverflow.no); + assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"years"(7).roll!"months"(-4); + assert(dt == DateTime(2007, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"years"(4))); + static assert(!__traits(compiles, idt.roll!"years"(4))); + static assert(!__traits(compiles, cdt.roll!"months"(4))); + static assert(!__traits(compiles, idt.roll!"months"(4))); + } + + + /++ + Adds the given number of units to this $(LREF DateTime). A negative number + will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF DateTime) one + year's worth of days gets the exact same $(LREF DateTime). + + Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), + $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this $(LREF DateTime). + +/ + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "days") + { + _date.roll!"days"(value); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); + dt1.roll!"days"(1); + assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); + dt1.roll!"days"(365); + assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); + dt1.roll!"days"(-32); + assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); + + auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); + dt2.roll!"hours"(1); + assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); + + auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); + dt3.roll!"seconds"(-1); + assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"days"(7).roll!"days"(-4); + assert(dt == DateTime(2000, 1, 3)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); + } + + + // Shares documentation with "days" version. + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "hours" || + units == "minutes" || + units == "seconds") + { + _tod.roll!units(value); + return this; + } + + // Test roll!"hours"(). + @safe unittest + { + static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test Both + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, + DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, + DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"hours"(27).roll!"hours"(-9); + assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"hours"(4))); + static assert(!__traits(compiles, idt.roll!"hours"(4))); + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"minutes"(92).roll!"minutes"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"minutes"(4))); + static assert(!__traits(compiles, idt.roll!"minutes"(4))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"seconds"(92).roll!"seconds"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"seconds"(4))); + static assert(!__traits(compiles, idt.roll!"seconds"(4))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF DateTime). + +/ + DateTime opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == + DateTime(2016, 1, 1, 0, 0, 0)); + + assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == + DateTime(2016, 1, 1, 0, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + + + /++ + Gives the result of adding or subtracting a duration from this + $(LREF DateTime), as well as assigning the result to this $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The duration to add to or subtract from this + $(LREF DateTime). + +/ + ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow + if ((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + import std.format : format; + + DateTime retval = this; + + static if (is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if (is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); + } + + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + (dt += dur!"seconds"(92)) -= dur!"days"(-500); + assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt += duration)); + static assert(!__traits(compiles, idt += duration)); + static assert(!__traits(compiles, cdt -= duration)); + static assert(!__traits(compiles, idt -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + } + + + /++ + Gives the difference between two $(LREF DateTime)s. + + The legal types of arithmetic for $(LREF DateTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow + if (op == "-") + { + immutable dateResult = _date - rhs.date; + immutable todResult = _tod - rhs._tod; + + return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); + } + + @safe unittest + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == + dur!"seconds"(86_400)); + assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == + dur!"seconds"(3600)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(60)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == + dur!"seconds"(-60)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(1)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == + dur!"seconds"(-1)); + + assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); + assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt - dt == Duration.zero); + assert(cdt - dt == Duration.zero); + assert(idt - dt == Duration.zero); + + assert(dt - cdt == Duration.zero); + assert(cdt - cdt == Duration.zero); + assert(idt - cdt == Duration.zero); + + assert(dt - idt == Duration.zero); + assert(cdt - idt == Duration.zero); + assert(idt - idt == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF DateTime)s in months. + + To get the difference in years, subtract the year property + of two $(LREF DateTime)s. To get the difference in days or weeks, + subtract the $(LREF DateTime)s themselves and use the $(REF Duration, core,time) + that results. Because converting between months and smaller + units requires a specific date (which $(REF Duration, core,time)s don't have), + getting the difference in months requires some math using both + the year and month properties, so this is a convenience function for + getting the difference in months. + + Note that the number of days in the months or how far into the month + either date is is irrelevant. It is the difference in the month property + combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF DateTime) to subtract from this one. + +/ + int diffMonths(in DateTime rhs) @safe const pure nothrow + { + return _date.diffMonths(rhs._date); + } + + /// + @safe unittest + { + assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( + DateTime(1999, 1, 31, 23, 59, 59)) == 1); + + assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( + DateTime(1999, 2, 1, 12, 3, 42)) == -1); + + assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( + DateTime(1999, 1, 1, 2, 4, 7)) == 2); + + assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( + DateTime(1999, 3, 31, 0, 30, 58)) == -2); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.diffMonths(dt) == 0); + assert(cdt.diffMonths(dt) == 0); + assert(idt.diffMonths(dt) == 0); + + assert(dt.diffMonths(cdt) == 0); + assert(cdt.diffMonths(cdt) == 0); + assert(idt.diffMonths(cdt) == 0); + + assert(dt.diffMonths(idt) == 0); + assert(cdt.diffMonths(idt) == 0); + assert(idt.diffMonths(idt) == 0); + } + + + /++ + Whether this $(LREF DateTime) is in a leap year. + +/ + @property bool isLeapYear() @safe const pure nothrow + { + return _date.isLeapYear; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(!dt.isLeapYear); + assert(!cdt.isLeapYear); + assert(!idt.isLeapYear); + } + + + /++ + Day of the week this $(LREF DateTime) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const pure nothrow + { + return _date.dayOfWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfWeek == DayOfWeek.tue); + assert(cdt.dayOfWeek == DayOfWeek.tue); + assert(idt.dayOfWeek == DayOfWeek.tue); + } + + + /++ + Day of the year this $(LREF DateTime) is on. + +/ + @property ushort dayOfYear() @safe const pure nothrow + { + return _date.dayOfYear; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); + assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); + assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfYear == 187); + assert(cdt.dayOfYear == 187); + assert(idt.dayOfYear == 187); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF DateTime) is on. + +/ + @property void dayOfYear(int day) @safe pure + { + _date.dayOfYear = day; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt.dayOfYear = 12; + assert(dt.dayOfYear == 12); + static assert(!__traits(compiles, cdt.dayOfYear = 12)); + static assert(!__traits(compiles, idt.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + +/ + @property int dayOfGregorianCal() @safe const pure nothrow + { + return _date.dayOfGregorianCal; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); + assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); + assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); + assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); + assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + + assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.dayOfGregorianCal == 729_941); + assert(idt.dayOfGregorianCal == 729_941); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + Setting this property does not affect the time portion of + $(LREF DateTime). + + Params: + days = The day of the Gregorian Calendar to set this $(LREF DateTime) + to. + +/ + @property void dayOfGregorianCal(int days) @safe pure nothrow + { + _date.dayOfGregorianCal = days; + } + + /// + @safe unittest + { + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); + dt.dayOfGregorianCal = 1; + assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 365; + assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 366; + assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 0; + assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -365; + assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -366; + assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 730_120; + assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 734_137; + assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF DateTime) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) + +/ + @property ubyte isoWeek() @safe const pure nothrow + { + return _date.isoWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.isoWeek == 27); + assert(cdt.isoWeek == 27); + assert(idt.isoWeek == 27); + } + + + /++ + $(LREF DateTime) for the last day in the month that this $(LREF DateTime) is + in. The time portion of endOfMonth is always 23:59:59. + +/ + @property DateTime endOfMonth() @safe const pure nothrow + { + try + return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); + catch (Exception e) + assert(0, "DateTime constructor threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == + DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == + DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == + DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == + DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); + assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); + assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); + assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); + assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); + assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); + assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); + assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); + assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); + assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); + assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); + assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); + + // Test B.C. + assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); + assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); + assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); + assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); + assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); + assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); + assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); + assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); + assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); + assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); + assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); + assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); + assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + } + + + /++ + The last day in the month that this $(LREF DateTime) is in. + +/ + @property ubyte daysInMonth() @safe const pure nothrow + { + return _date.daysInMonth; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.daysInMonth == 31); + assert(idt.daysInMonth == 31); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const pure nothrow + { + return _date.isAD; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); + assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); + assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.isAD); + assert(idt.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF DateTime) at the given time. For example, prior to noon, + 1996-03-31 would be the Julian day number 2_450_173, so this function + returns 2_450_173, while from noon onward, the julian day number would + be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() @safe const pure nothrow + { + if (_tod._hour < 12) + return _date.julianDay - 1; + else + return _date.julianDay; + } + + @safe unittest + { + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); + assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); + + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); + + assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); + assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); + + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); + + assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); + assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); + + assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); + assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.julianDay == 2_451_366); + assert(idt.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any + time on this date (since, the modified Julian day changes at midnight). + +/ + @property long modJulianDay() @safe const pure nothrow + { + return _date.modJulianDay; + } + + @safe unittest + { + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.modJulianDay == 51_365); + assert(idt.modJulianDay == 51_365); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOString(), _tod.toISOString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == + "20100704T070612"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == + "19981225T021500"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == + "00000105T230959"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == + "-00040105T000002"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOString() == "19990706T123033"); + assert(idt.toISOString() == "19990706T123033"); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-MM-DDTHH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == + "1998-12-25T02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == + "-0004-01-05T00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); + assert(idt.toISOExtString() == "1999-07-06T12:30:33"); + } + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-Mon-DD HH:MM:SS. + +/ + string toSimpleString() @safe const pure nothrow + { + import std.format : format; + try + return format("%s %s", _date.toSimpleString(), _tod.toString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == + "1998-Dec-25 02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == + "-0004-Jan-05 00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); + assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); + } + + + /++ + Converts this $(LREF DateTime) to a string. + +/ + string toString() @safe const pure nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.toString()); + assert(cdt.toString()); + assert(idt.toString()); + } + + + + /++ + Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates and times. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO format + or if the resulting $(LREF DateTime) would not be valid. + +/ + static DateTime fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + immutable date = Date.fromISOString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOString("20100704T070612") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOString("19981225T021500") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOString("00000105T230959") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOString("-00040105T000002") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOString(" 20100704T070612 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for dates + and times. + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO + Extended format or if the resulting $(LREF DateTime) would not be + valid. + +/ + static DateTime fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + immutable date = Date.fromISOExtString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOExtString("")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString + formats dates and times. + + Throws: + $(LREF DateTimeException) if the given string is not in the correct + format or if the resulting $(LREF DateTime) would not be valid. + +/ + static DateTime fromSimpleString(S)(in S simpleString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); + auto t = dstr.countUntil(' '); + + enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); + + immutable date = Date.fromSimpleString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); + + assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == + DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Returns the $(LREF DateTime) farthest in the past which is representable by + $(LREF DateTime). + +/ + @property static DateTime min() @safe pure nothrow + out(result) + { + assert(result._date == Date.min); + assert(result._tod == TimeOfDay.min); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.min; + dt._date._month = Month.jan; + dt._date._day = 1; + + return dt; + } + + @safe unittest + { + assert(DateTime.min.year < 0); + assert(DateTime.min < DateTime.max); + } + + + /++ + Returns the $(LREF DateTime) farthest in the future which is representable + by $(LREF DateTime). + +/ + @property static DateTime max() @safe pure nothrow + out(result) + { + assert(result._date == Date.max); + assert(result._tod == TimeOfDay.max); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.max; + dt._date._month = Month.dec; + dt._date._day = 31; + dt._tod._hour = TimeOfDay.maxHour; + dt._tod._minute = TimeOfDay.maxMinute; + dt._tod._second = TimeOfDay.maxSecond; + + return dt; + } + + @safe unittest + { + assert(DateTime.max.year > 0); + assert(DateTime.max > DateTime.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. The + same goes for any larger units. + + Params: + seconds = The number of seconds to add to this $(LREF DateTime). + +/ + ref DateTime _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_tod._hour); + hnsecs += convert!("minutes", "hnsecs")(_tod._minute); + hnsecs += convert!("seconds", "hnsecs")(_tod._second); + + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + { + hnsecs += convert!("days", "hnsecs")(1); + --days; + } + + _date._addDays(days); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _tod._hour = cast(ubyte) newHours; + _tod._minute = cast(ubyte) newMinutes; + _tod._second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); + + testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); + + // Test B.C. + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); + + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); + + // Test Both + testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); + testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); + + testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); + testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); + testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); + testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt._addSeconds(4))); + static assert(!__traits(compiles, idt._addSeconds(4))); + } + + + Date _date; + TimeOfDay _tod; +} + + +version(unittest) +{ + // All of these helper arrays are sorted in ascending order. + auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; + auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct MonthDay + { + Month month; + short day; + + this(int m, short d) + { + month = cast(Month) m; + day = d; + } + } + + MonthDay[] testMonthDays = [MonthDay(1, 1), + MonthDay(1, 2), + MonthDay(3, 17), + MonthDay(7, 4), + MonthDay(10, 27), + MonthDay(12, 30), + MonthDay(12, 31)]; + + auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + + auto testTODs = [TimeOfDay(0, 0, 0), + TimeOfDay(0, 0, 1), + TimeOfDay(0, 1, 0), + TimeOfDay(1, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + auto testHours = [0, 1, 12, 22, 23]; + auto testMinSecs = [0, 1, 30, 58, 59]; + + // Throwing exceptions is incredibly expensive, so we want to use a smaller + // set of values for tests using assertThrown. + auto testTODsThrown = [TimeOfDay(0, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + Date[] testDatesBC; + Date[] testDatesAD; + + DateTime[] testDateTimesBC; + DateTime[] testDateTimesAD; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct GregDay { int day; Date date; } + auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar + GregDay(-735_233, Date(-2012, 1, 1)), + GregDay(-735_202, Date(-2012, 2, 1)), + GregDay(-735_175, Date(-2012, 2, 28)), + GregDay(-735_174, Date(-2012, 2, 29)), + GregDay(-735_173, Date(-2012, 3, 1)), + GregDay(-734_502, Date(-2010, 1, 1)), + GregDay(-734_472, Date(-2010, 1, 31)), + GregDay(-734_471, Date(-2010, 2, 1)), + GregDay(-734_444, Date(-2010, 2, 28)), + GregDay(-734_443, Date(-2010, 3, 1)), + GregDay(-734_413, Date(-2010, 3, 31)), + GregDay(-734_412, Date(-2010, 4, 1)), + GregDay(-734_383, Date(-2010, 4, 30)), + GregDay(-734_382, Date(-2010, 5, 1)), + GregDay(-734_352, Date(-2010, 5, 31)), + GregDay(-734_351, Date(-2010, 6, 1)), + GregDay(-734_322, Date(-2010, 6, 30)), + GregDay(-734_321, Date(-2010, 7, 1)), + GregDay(-734_291, Date(-2010, 7, 31)), + GregDay(-734_290, Date(-2010, 8, 1)), + GregDay(-734_260, Date(-2010, 8, 31)), + GregDay(-734_259, Date(-2010, 9, 1)), + GregDay(-734_230, Date(-2010, 9, 30)), + GregDay(-734_229, Date(-2010, 10, 1)), + GregDay(-734_199, Date(-2010, 10, 31)), + GregDay(-734_198, Date(-2010, 11, 1)), + GregDay(-734_169, Date(-2010, 11, 30)), + GregDay(-734_168, Date(-2010, 12, 1)), + GregDay(-734_139, Date(-2010, 12, 30)), + GregDay(-734_138, Date(-2010, 12, 31)), + GregDay(-731_215, Date(-2001, 1, 1)), + GregDay(-730_850, Date(-2000, 1, 1)), + GregDay(-730_849, Date(-2000, 1, 2)), + GregDay(-730_486, Date(-2000, 12, 30)), + GregDay(-730_485, Date(-2000, 12, 31)), + GregDay(-730_484, Date(-1999, 1, 1)), + GregDay(-694_690, Date(-1901, 1, 1)), + GregDay(-694_325, Date(-1900, 1, 1)), + GregDay(-585_118, Date(-1601, 1, 1)), + GregDay(-584_753, Date(-1600, 1, 1)), + GregDay(-584_388, Date(-1600, 12, 31)), + GregDay(-584_387, Date(-1599, 1, 1)), + GregDay(-365_972, Date(-1001, 1, 1)), + GregDay(-365_607, Date(-1000, 1, 1)), + GregDay(-183_351, Date(-501, 1, 1)), + GregDay(-182_986, Date(-500, 1, 1)), + GregDay(-182_621, Date(-499, 1, 1)), + GregDay(-146_827, Date(-401, 1, 1)), + GregDay(-146_462, Date(-400, 1, 1)), + GregDay(-146_097, Date(-400, 12, 31)), + GregDay(-110_302, Date(-301, 1, 1)), + GregDay(-109_937, Date(-300, 1, 1)), + GregDay(-73_778, Date(-201, 1, 1)), + GregDay(-73_413, Date(-200, 1, 1)), + GregDay(-38_715, Date(-105, 1, 1)), + GregDay(-37_254, Date(-101, 1, 1)), + GregDay(-36_889, Date(-100, 1, 1)), + GregDay(-36_524, Date(-99, 1, 1)), + GregDay(-36_160, Date(-99, 12, 31)), + GregDay(-35_794, Date(-97, 1, 1)), + GregDay(-18_627, Date(-50, 1, 1)), + GregDay(-18_262, Date(-49, 1, 1)), + GregDay(-3652, Date(-9, 1, 1)), + GregDay(-2191, Date(-5, 1, 1)), + GregDay(-1827, Date(-5, 12, 31)), + GregDay(-1826, Date(-4, 1, 1)), + GregDay(-1825, Date(-4, 1, 2)), + GregDay(-1462, Date(-4, 12, 30)), + GregDay(-1461, Date(-4, 12, 31)), + GregDay(-1460, Date(-3, 1, 1)), + GregDay(-1096, Date(-3, 12, 31)), + GregDay(-1095, Date(-2, 1, 1)), + GregDay(-731, Date(-2, 12, 31)), + GregDay(-730, Date(-1, 1, 1)), + GregDay(-367, Date(-1, 12, 30)), + GregDay(-366, Date(-1, 12, 31)), + GregDay(-365, Date(0, 1, 1)), + GregDay(-31, Date(0, 11, 30)), + GregDay(-30, Date(0, 12, 1)), + GregDay(-1, Date(0, 12, 30)), + GregDay(0, Date(0, 12, 31))]; + + auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), + GregDay(2, Date(1, 1, 2)), + GregDay(32, Date(1, 2, 1)), + GregDay(365, Date(1, 12, 31)), + GregDay(366, Date(2, 1, 1)), + GregDay(731, Date(3, 1, 1)), + GregDay(1096, Date(4, 1, 1)), + GregDay(1097, Date(4, 1, 2)), + GregDay(1460, Date(4, 12, 30)), + GregDay(1461, Date(4, 12, 31)), + GregDay(1462, Date(5, 1, 1)), + GregDay(17_898, Date(50, 1, 1)), + GregDay(35_065, Date(97, 1, 1)), + GregDay(36_160, Date(100, 1, 1)), + GregDay(36_525, Date(101, 1, 1)), + GregDay(37_986, Date(105, 1, 1)), + GregDay(72_684, Date(200, 1, 1)), + GregDay(73_049, Date(201, 1, 1)), + GregDay(109_208, Date(300, 1, 1)), + GregDay(109_573, Date(301, 1, 1)), + GregDay(145_732, Date(400, 1, 1)), + GregDay(146_098, Date(401, 1, 1)), + GregDay(182_257, Date(500, 1, 1)), + GregDay(182_622, Date(501, 1, 1)), + GregDay(364_878, Date(1000, 1, 1)), + GregDay(365_243, Date(1001, 1, 1)), + GregDay(584_023, Date(1600, 1, 1)), + GregDay(584_389, Date(1601, 1, 1)), + GregDay(693_596, Date(1900, 1, 1)), + GregDay(693_961, Date(1901, 1, 1)), + GregDay(729_755, Date(1999, 1, 1)), + GregDay(730_120, Date(2000, 1, 1)), + GregDay(730_121, Date(2000, 1, 2)), + GregDay(730_484, Date(2000, 12, 30)), + GregDay(730_485, Date(2000, 12, 31)), + GregDay(730_486, Date(2001, 1, 1)), + GregDay(733_773, Date(2010, 1, 1)), + GregDay(733_774, Date(2010, 1, 2)), + GregDay(733_803, Date(2010, 1, 31)), + GregDay(733_804, Date(2010, 2, 1)), + GregDay(733_831, Date(2010, 2, 28)), + GregDay(733_832, Date(2010, 3, 1)), + GregDay(733_862, Date(2010, 3, 31)), + GregDay(733_863, Date(2010, 4, 1)), + GregDay(733_892, Date(2010, 4, 30)), + GregDay(733_893, Date(2010, 5, 1)), + GregDay(733_923, Date(2010, 5, 31)), + GregDay(733_924, Date(2010, 6, 1)), + GregDay(733_953, Date(2010, 6, 30)), + GregDay(733_954, Date(2010, 7, 1)), + GregDay(733_984, Date(2010, 7, 31)), + GregDay(733_985, Date(2010, 8, 1)), + GregDay(734_015, Date(2010, 8, 31)), + GregDay(734_016, Date(2010, 9, 1)), + GregDay(734_045, Date(2010, 9, 30)), + GregDay(734_046, Date(2010, 10, 1)), + GregDay(734_076, Date(2010, 10, 31)), + GregDay(734_077, Date(2010, 11, 1)), + GregDay(734_106, Date(2010, 11, 30)), + GregDay(734_107, Date(2010, 12, 1)), + GregDay(734_136, Date(2010, 12, 30)), + GregDay(734_137, Date(2010, 12, 31)), + GregDay(734_503, Date(2012, 1, 1)), + GregDay(734_534, Date(2012, 2, 1)), + GregDay(734_561, Date(2012, 2, 28)), + GregDay(734_562, Date(2012, 2, 29)), + GregDay(734_563, Date(2012, 3, 1)), + GregDay(734_858, Date(2012, 12, 21))]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct DayOfYear { int day; MonthDay md; } + auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(3, 1)), + DayOfYear(90, MonthDay(3, 31)), + DayOfYear(91, MonthDay(4, 1)), + DayOfYear(120, MonthDay(4, 30)), + DayOfYear(121, MonthDay(5, 1)), + DayOfYear(151, MonthDay(5, 31)), + DayOfYear(152, MonthDay(6, 1)), + DayOfYear(181, MonthDay(6, 30)), + DayOfYear(182, MonthDay(7, 1)), + DayOfYear(212, MonthDay(7, 31)), + DayOfYear(213, MonthDay(8, 1)), + DayOfYear(243, MonthDay(8, 31)), + DayOfYear(244, MonthDay(9, 1)), + DayOfYear(273, MonthDay(9, 30)), + DayOfYear(274, MonthDay(10, 1)), + DayOfYear(304, MonthDay(10, 31)), + DayOfYear(305, MonthDay(11, 1)), + DayOfYear(334, MonthDay(11, 30)), + DayOfYear(335, MonthDay(12, 1)), + DayOfYear(363, MonthDay(12, 29)), + DayOfYear(364, MonthDay(12, 30)), + DayOfYear(365, MonthDay(12, 31))]; + + auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(2, 29)), + DayOfYear(61, MonthDay(3, 1)), + DayOfYear(91, MonthDay(3, 31)), + DayOfYear(92, MonthDay(4, 1)), + DayOfYear(121, MonthDay(4, 30)), + DayOfYear(122, MonthDay(5, 1)), + DayOfYear(152, MonthDay(5, 31)), + DayOfYear(153, MonthDay(6, 1)), + DayOfYear(182, MonthDay(6, 30)), + DayOfYear(183, MonthDay(7, 1)), + DayOfYear(213, MonthDay(7, 31)), + DayOfYear(214, MonthDay(8, 1)), + DayOfYear(244, MonthDay(8, 31)), + DayOfYear(245, MonthDay(9, 1)), + DayOfYear(274, MonthDay(9, 30)), + DayOfYear(275, MonthDay(10, 1)), + DayOfYear(305, MonthDay(10, 31)), + DayOfYear(306, MonthDay(11, 1)), + DayOfYear(335, MonthDay(11, 30)), + DayOfYear(336, MonthDay(12, 1)), + DayOfYear(364, MonthDay(12, 29)), + DayOfYear(365, MonthDay(12, 30)), + DayOfYear(366, MonthDay(12, 31))]; + + void initializeTests() @safe + { + foreach (year; testYearsBC) + { + foreach (md; testMonthDays) + testDatesBC ~= Date(year, md.month, md.day); + } + + foreach (year; testYearsAD) + { + foreach (md; testMonthDays) + testDatesAD ~= Date(year, md.month, md.day); + } + + foreach (dt; testDatesBC) + { + foreach (tod; testTODs) + testDateTimesBC ~= DateTime(dt, tod); + } + + foreach (dt; testDatesAD) + { + foreach (tod; testTODs) + testDateTimesAD ~= DateTime(dt, tod); + } + } +} diff --git a/std/datetime/interval.d b/std/datetime/interval.d index bf2bfbeae30..24a6bed3443 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -17,7 +17,7 @@ import std.typecons : Flag; version(unittest) import std.exception : assertThrown; -import std.datetime : DateTime, SysTime; // temporary +import std.datetime : SysTime; // temporary /++ @@ -1565,6 +1565,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); @@ -1630,6 +1631,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); @@ -1653,6 +1655,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); @@ -4121,6 +4124,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; PosInfInterval!Date(Date.init); @@ -4154,6 +4158,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); @@ -6335,6 +6340,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; NegInfInterval!Date(Date.init); @@ -6365,6 +6371,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); @@ -7592,6 +7599,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); @@ -7717,6 +7725,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; + import std.datetime.datetime; auto funcFwd = everyMonth!Date(Month.jun); auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); @@ -7821,6 +7830,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; auto funcFwd = everyDuration!Date(dur!"days"(27)); @@ -7945,6 +7955,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; { @@ -8225,6 +8236,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; import std.range.primitives; @@ -8252,6 +8264,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; { @@ -8667,6 +8680,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; import std.range.primitives; @@ -8693,6 +8707,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; { @@ -8950,6 +8965,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; import std.range.primitives; @@ -8975,6 +8991,7 @@ private: @safe unittest { import std.datetime.date; + import std.datetime.datetime; import std.datetime.timeofday; { diff --git a/std/datetime/package.d b/std/datetime/package.d index afc5c1adc9c..be9fdb94172 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -9114,3516 +9114,6 @@ private: } -/++ - Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object - which holds both the date and the time. It is optimized for calendar-based - operations and has no concept of time zone. For an object which is - optimized for time operations based on the system time, use - $(LREF SysTime). $(LREF SysTime) has a concept of time zone and has much higher - precision (hnsecs). $(D DateTime) is intended primarily for calendar-based - uses rather than precise time operations. - +/ -struct DateTime -{ -public: - - /++ - Params: - date = The date portion of $(LREF DateTime). - tod = The time portion of $(LREF DateTime). - +/ - this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow - { - _date = date; - _tod = tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Params: - year = The year portion of the date. - month = The month portion of the date. - day = The day portion of the date. - hour = The hour portion of the time; - minute = The minute portion of the time; - second = The second portion of the time; - +/ - this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure - { - _date = Date(year, month, day); - _tod = TimeOfDay(hour, minute, second); - } - - @safe unittest - { - { - auto dt = DateTime(1999, 7 ,6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(1999, 7 ,6, 12, 30, 33); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Compares this $(LREF DateTime) with the given $(D DateTime.). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in DateTime rhs) @safe const pure nothrow - { - immutable dateResult = _date.opCmp(rhs._date); - - if (dateResult != 0) - return dateResult; - - return _tod.opCmp(rhs._tod); - } - - @safe unittest - { - //Test A.D. - assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); - - assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); - assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); - assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); - - assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); - - assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); - - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - - assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - //Test B.C. - assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - //Test Both - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - assert(dt.opCmp(dt) == 0); - assert(dt.opCmp(cdt) == 0); - assert(dt.opCmp(idt) == 0); - assert(cdt.opCmp(dt) == 0); - assert(cdt.opCmp(cdt) == 0); - assert(cdt.opCmp(idt) == 0); - assert(idt.opCmp(dt) == 0); - assert(idt.opCmp(cdt) == 0); - assert(idt.opCmp(idt) == 0); - } - - - /++ - The date portion of $(LREF DateTime). - +/ - @property Date date() @safe const pure nothrow - { - return _date; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.date == Date.init); - } - - { - auto dt = DateTime(Date(1999, 7, 6)); - assert(dt.date == Date(1999, 7, 6)); - } - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - assert(cdt.date == Date(1999, 7, 6)); - assert(idt.date == Date(1999, 7, 6)); - } - - - /++ - The date portion of $(LREF DateTime). - - Params: - date = The Date to set this $(LREF DateTime)'s date portion to. - +/ - @property void date(in Date date) @safe pure nothrow - { - _date = date; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.date = Date(1999, 7, 6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); - static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); - } - - - /++ - The time portion of $(LREF DateTime). - +/ - @property TimeOfDay timeOfDay() @safe const pure nothrow - { - return _tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.timeOfDay == TimeOfDay.init); - } - - { - auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); - assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); - assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - - /++ - The time portion of $(LREF DateTime). - - Params: - tod = The $(LREF TimeOfDay) to set this $(LREF DateTime)'s time portion - to. - +/ - @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow - { - _tod = tod; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.timeOfDay = TimeOfDay(12, 30, 33); - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay(12, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); - static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const pure nothrow - { - return _date.year; - } - - @safe unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(idt.year == 1999); - assert(idt.year == 1999); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF DateTime)'s year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and if the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - _date.year = year; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); - } - - @safe unittest - { - static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) - { - dt.year = year; - assert(dt == expected); - } - - testDT( - DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 1999, - DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33)) - ); - testDT( - DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 0, - DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)) - ); - testDT( - DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - -1999, - DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33)) - ); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.year = 7)); - static assert(!__traits(compiles, idt.year = 7)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property short yearBC() @safe const pure - { - return _date.yearBC; - } - - /// - @safe unittest - { - assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); - assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); - assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); - } - - @safe unittest - { - assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF DateTime)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe pure - { - _date.yearBC = year; - } - - /// - @safe unittest - { - auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); - dt.yearBC = 1; - assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - - dt.yearBC = 10; - assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); - } - - @safe unittest - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const pure nothrow - { - return _date.month; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); - } - - @safe unittest - { - assert(DateTime.init.month == 1); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.month == 7); - assert(idt.month == 7); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF DateTime)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month. - +/ - @property void month(Month month) @safe pure - { - _date.month = month; - } - - @safe unittest - { - static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) - { - dt.month = month; - assert(expected != DateTime.init); - assert(dt == expected); - } - - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); - - testDT( - DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33)) - ); - testDT( - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)) - ); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.month = 12)); - static assert(!__traits(compiles, idt.month = 12)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const pure nothrow - { - return _date.day; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(DateTime dateTime, int expected) - { - assert(dateTime.day == expected, format("Value given: %s", dateTime)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - test(DateTime(Date(year, md.month, md.day), tod), md.day); - } - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.day == 6); - assert(idt.day == 6); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF DateTime)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe pure - { - _date.day = day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - static void testDT(DateTime dt, int day) - { - dt.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); - - { - auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); - } - - //Test B.C. - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); - - auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.day = 27)); - static assert(!__traits(compiles, idt.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _tod.hour; - } - - @safe unittest - { - assert(DateTime.init.hour == 0); - assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.hour == 12); - assert(idt.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF DateTime)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF DateTime). - +/ - @property void hour(int hour) @safe pure - { - _tod.hour = hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); - - auto dt = DateTime.init; - dt.hour = 12; - assert(dt == DateTime(1, 1, 1, 12, 0, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.hour = 27)); - static assert(!__traits(compiles, idt.hour = 27)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _tod.minute; - } - - @safe unittest - { - assert(DateTime.init.minute == 0); - assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.minute == 30); - assert(idt.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF DateTime)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF DateTime). - +/ - @property void minute(int minute) @safe pure - { - _tod.minute = minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); - - auto dt = DateTime.init; - dt.minute = 30; - assert(dt == DateTime(1, 1, 1, 0, 30, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.minute = 27)); - static assert(!__traits(compiles, idt.minute = 27)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _tod.second; - } - - @safe unittest - { - assert(DateTime.init.second == 0); - assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.second == 33); - assert(idt.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF DateTime)'s second to. - - Throws: - $(LREF DateTimeException) if the given seconds would result in an - invalid $(LREF DateTime). - +/ - @property void second(int second) @safe pure - { - _tod.second = second; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.second = 60;}()); - - auto dt = DateTime.init; - dt.second = 33; - assert(dt == DateTime(1, 1, 1, 0, 0, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.second = 27)); - static assert(!__traits(compiles, idt.second = 27)); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime add(string units) - (long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "years" || - units == "months") - { - _date.add!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - import std.typecons : No; - - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); - dt1.add!"months"(11); - assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); - dt2.add!"months"(-11); - assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - - auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); - dt3.add!"years"(1); - assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); - dt4.add!"years"(1, No.allowDayOverflow); - assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.add!"years"(7).add!"months"(-4); - assert(dt == DateTime(2006, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.add!"years"(4))); - static assert(!__traits(compiles, idt.add!"years"(4))); - static assert(!__traits(compiles, cdt.add!"months"(4))); - static assert(!__traits(compiles, idt.add!"months"(4))); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF DateTime) 12 months - gets the exact same $(LREF DateTime). However, the days can still be - affected due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime roll(string units) - (long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe pure nothrow - if (units == "years" || - units == "months") - { - _date.roll!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - import std.typecons : No; - - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); - dt1.roll!"months"(1); - assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); - dt2.roll!"months"(-1); - assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - - auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); - dt3.roll!"months"(1); - assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - - auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); - dt4.roll!"months"(1, No.allowDayOverflow); - assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - - auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); - dt5.roll!"years"(1); - assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); - dt6.roll!"years"(1, No.allowDayOverflow); - assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"years"(7).roll!"months"(-4); - assert(dt == DateTime(2007, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"years"(4))); - static assert(!__traits(compiles, idt.roll!"years"(4))); - static assert(!__traits(compiles, cdt.roll!"months"(4))); - static assert(!__traits(compiles, idt.roll!"months"(4))); - } - - - /++ - Adds the given number of units to this $(LREF DateTime). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF DateTime) one - year's worth of days gets the exact same $(LREF DateTime). - - Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), - $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF DateTime). - +/ - ref DateTime roll(string units)(long value) @safe pure nothrow - if (units == "days") - { - _date.roll!"days"(value); - return this; - } - - /// - @safe unittest - { - auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); - dt1.roll!"days"(1); - assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); - dt1.roll!"days"(365); - assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); - dt1.roll!"days"(-32); - assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - - auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); - dt2.roll!"hours"(1); - assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - - auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); - dt3.roll!"seconds"(-1); - assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"days"(7).roll!"days"(-4); - assert(dt == DateTime(2000, 1, 3)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } - - - //Shares documentation with "days" version. - ref DateTime roll(string units)(long value) @safe pure nothrow - if (units == "hours" || - units == "minutes" || - units == "seconds") - { - _tod.roll!units(value); - return this; - } - - //Test roll!"hours"(). - @safe unittest - { - static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(1999, - 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(1999, - 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test Both - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, - DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, - DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"hours"(27).roll!"hours"(-9); - assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"hours"(4))); - static assert(!__traits(compiles, idt.roll!"hours"(4))); - } - - //Test roll!"minutes"(). - @safe unittest - { - static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, - 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(0, - 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, - 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, - DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"minutes"(92).roll!"minutes"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"minutes"(4))); - static assert(!__traits(compiles, idt.roll!"minutes"(4))); - } - - //Test roll!"seconds"(). - @safe unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(1999, - 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(1999, - 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(1999, - 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(1999, - 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1999, - 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, - 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(0, - 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, - 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1, - 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"seconds"(92).roll!"seconds"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"seconds"(4))); - static assert(!__traits(compiles, idt.roll!"seconds"(4))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF DateTime). - +/ - DateTime opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == - DateTime(2016, 1, 1, 0, 0, 0)); - - assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == - DateTime(2016, 1, 1, 0, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = td.seconds; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF DateTime), as well as assigning the result to this $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF DateTime). - +/ - ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow - if ((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - DateTime retval = this; - - static if (is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if (is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == DateTime(Date(1999, - 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == DateTime(Date(1999, - 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == DateTime(Date(1999, - 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == DateTime(Date(1999, - 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == DateTime(Date(1999, - 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == DateTime(Date(1999, - 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == DateTime(Date(1999, - 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == DateTime(Date(1999, - 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, - 7, 6), TimeOfDay(12, 30, 26))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - (dt += dur!"seconds"(92)) -= dur!"days"(-500); - assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt += duration)); - static assert(!__traits(compiles, idt += duration)); - static assert(!__traits(compiles, cdt -= duration)); - static assert(!__traits(compiles, idt -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = td.seconds; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt += TickDuration.from!"usecs"(7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt += TickDuration.from!"usecs"(-7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt -= TickDuration.from!"usecs"(-7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt -= TickDuration.from!"usecs"(7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - } - } - - - /++ - Gives the difference between two $(LREF DateTime)s. - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow - if (op == "-") - { - immutable dateResult = _date - rhs.date; - immutable todResult = _tod - rhs._tod; - - return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); - } - - @safe unittest - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == - dur!"seconds"(86_400)); - assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == - dur!"seconds"(3600)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(60)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == - dur!"seconds"(-60)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(1)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == - dur!"seconds"(-1)); - - assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); - assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt - dt == Duration.zero); - assert(cdt - dt == Duration.zero); - assert(idt - dt == Duration.zero); - - assert(dt - cdt == Duration.zero); - assert(cdt - cdt == Duration.zero); - assert(idt - cdt == Duration.zero); - - assert(dt - idt == Duration.zero); - assert(cdt - idt == Duration.zero); - assert(idt - idt == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF DateTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF DateTime) to subtract from this one. - +/ - int diffMonths(in DateTime rhs) @safe const pure nothrow - { - return _date.diffMonths(rhs._date); - } - - /// - @safe unittest - { - assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( - DateTime(1999, 1, 31, 23, 59, 59)) == 1); - - assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( - DateTime(1999, 2, 1, 12, 3, 42)) == -1); - - assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( - DateTime(1999, 1, 1, 2, 4, 7)) == 2); - - assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( - DateTime(1999, 3, 31, 0, 30, 58)) == -2); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.diffMonths(dt) == 0); - assert(cdt.diffMonths(dt) == 0); - assert(idt.diffMonths(dt) == 0); - - assert(dt.diffMonths(cdt) == 0); - assert(cdt.diffMonths(cdt) == 0); - assert(idt.diffMonths(cdt) == 0); - - assert(dt.diffMonths(idt) == 0); - assert(cdt.diffMonths(idt) == 0); - assert(idt.diffMonths(idt) == 0); - } - - - /++ - Whether this $(LREF DateTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const pure nothrow - { - return _date.isLeapYear; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(!dt.isLeapYear); - assert(!cdt.isLeapYear); - assert(!idt.isLeapYear); - } - - - /++ - Day of the week this $(LREF DateTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const pure nothrow - { - return _date.dayOfWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfWeek == DayOfWeek.tue); - assert(cdt.dayOfWeek == DayOfWeek.tue); - assert(idt.dayOfWeek == DayOfWeek.tue); - } - - - /++ - Day of the year this $(LREF DateTime) is on. - +/ - @property ushort dayOfYear() @safe const pure nothrow - { - return _date.dayOfYear; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); - assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); - assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfYear == 187); - assert(cdt.dayOfYear == 187); - assert(idt.dayOfYear == 187); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF DateTime) is on. - +/ - @property void dayOfYear(int day) @safe pure - { - _date.dayOfYear = day; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt.dayOfYear = 12; - assert(dt.dayOfYear == 12); - static assert(!__traits(compiles, cdt.dayOfYear = 12)); - static assert(!__traits(compiles, idt.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - +/ - @property int dayOfGregorianCal() @safe const pure nothrow - { - return _date.dayOfGregorianCal; - } - - /// - @safe unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); - assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); - assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); - assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); - assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); - - assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.dayOfGregorianCal == 729_941); - assert(idt.dayOfGregorianCal == 729_941); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - Setting this property does not affect the time portion of - $(LREF DateTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF DateTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe pure nothrow - { - _date.dayOfGregorianCal = days; - } - - /// - @safe unittest - { - auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); - dt.dayOfGregorianCal = 1; - assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 365; - assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 366; - assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 0; - assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -365; - assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -366; - assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 730_120; - assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 734_137; - assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF DateTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() @safe const pure nothrow - { - return _date.isoWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.isoWeek == 27); - assert(cdt.isoWeek == 27); - assert(idt.isoWeek == 27); - } - - - /++ - $(LREF DateTime) for the last day in the month that this $(LREF DateTime) is - in. The time portion of endOfMonth is always 23:59:59. - +/ - @property DateTime endOfMonth() @safe const pure nothrow - { - try - return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); - catch (Exception e) - assert(0, "DateTime constructor threw."); - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == - DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == - DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == - DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == - DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); - } - - @safe unittest - { - //Test A.D. - assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); - assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); - assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); - assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); - assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); - assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); - assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); - assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); - assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); - assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); - assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); - assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); - - //Test B.C. - assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); - assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); - assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); - assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); - assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); - assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); - assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); - assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); - assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); - assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); - assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); - assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); - assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - } - - - /++ - The last day in the month that this $(LREF DateTime) is in. - +/ - @property ubyte daysInMonth() @safe const pure nothrow - { - return _date.daysInMonth; - } - - /// - @safe unittest - { - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.daysInMonth == 31); - assert(idt.daysInMonth == 31); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const pure nothrow - { - return _date.isAD; - } - - /// - @safe unittest - { - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); - assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); - assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.isAD); - assert(idt.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this - $(LREF DateTime) at the given time. For example, prior to noon, - 1996-03-31 would be the Julian day number 2_450_173, so this function - returns 2_450_173, while from noon onward, the julian day number would - be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const pure nothrow - { - if (_tod._hour < 12) - return _date.julianDay - 1; - else - return _date.julianDay; - } - - @safe unittest - { - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); - assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); - - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); - - assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); - assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); - - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); - - assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); - assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); - - assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); - assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.julianDay == 2_451_366); - assert(idt.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any - time on this date (since, the modified Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const pure nothrow - { - return _date.modJulianDay; - } - - @safe unittest - { - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.modJulianDay == 51_365); - assert(idt.modJulianDay == 51_365); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOString(), _tod.toISOString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == - "19981225T021500"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == - "-00040105T000002"); - } - - @safe unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOString() == "19990706T123033"); - assert(idt.toISOString() == "19990706T123033"); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-MM-DDTHH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == - "1998-12-25T02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == - "-0004-01-05T00:00:02"); - } - - @safe unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); - assert(idt.toISOExtString() == "1999-07-06T12:30:33"); - } - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-Mon-DD HH:MM:SS. - +/ - string toSimpleString() @safe const pure nothrow - { - import std.format : format; - try - return format("%s %s", _date.toSimpleString(), _tod.toString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == - "1998-Dec-25 02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == - "-0004-Jan-05 00:00:02"); - } - - @safe unittest - { - //Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - //Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); - assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); - } - - - /++ - Converts this $(LREF DateTime) to a string. - +/ - string toString() @safe const pure nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.toString()); - assert(cdt.toString()); - assert(idt.toString()); - } - - - - /++ - Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF DateTime) would not be valid. - +/ - static DateTime fromISOString(S)(in S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(isoString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - immutable date = Date.fromISOString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromISOString("20100704T070612") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOString("19981225T021500") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOString("00000105T230959") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOString("-00040105T000002") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOString(" 20100704T070612 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for dates - and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF DateTime) would not be - valid. - +/ - static DateTime fromISOExtString(S)(in S isoExtString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - immutable date = Date.fromISOExtString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOExtString("")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates and times. - - Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF DateTime) would not be valid. - +/ - static DateTime fromSimpleString(S)(in S simpleString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(simpleString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); - auto t = dstr.countUntil(' '); - - enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); - - immutable date = Date.fromSimpleString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); - - assert( - DateTime.fromSimpleString("2010-Dec-22 17:22:01") == DateTime( - Date(2010, 12, 22), TimeOfDay(17, 22, 01)) - ); - assert( - DateTime.fromSimpleString("1999-Jul-06 12:30:33") == DateTime( - Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - assert( - DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == DateTime( - Date(-1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - assert( - DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == DateTime( - Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - assert( - DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == DateTime( - Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - assert( - DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == DateTime( - Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - assert( - DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == DateTime( - Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - ); - } - - - /++ - Returns the $(LREF DateTime) farthest in the past which is representable by - $(LREF DateTime). - +/ - @property static DateTime min() @safe pure nothrow - out(result) - { - assert(result._date == Date.min); - assert(result._tod == TimeOfDay.min); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.min; - dt._date._month = Month.jan; - dt._date._day = 1; - - return dt; - } - - @safe unittest - { - assert(DateTime.min.year < 0); - assert(DateTime.min < DateTime.max); - } - - - /++ - Returns the $(LREF DateTime) farthest in the future which is representable - by $(LREF DateTime). - +/ - @property static DateTime max() @safe pure nothrow - out(result) - { - assert(result._date == Date.max); - assert(result._tod == TimeOfDay.max); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.max; - dt._date._month = Month.dec; - dt._date._day = 31; - dt._tod._hour = TimeOfDay.maxHour; - dt._tod._minute = TimeOfDay.maxMinute; - dt._tod._second = TimeOfDay.maxSecond; - - return dt; - } - - @safe unittest - { - assert(DateTime.max.year > 0); - assert(DateTime.max > DateTime.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. The - same goes for any larger units. - - Params: - seconds = The number of seconds to add to this $(LREF DateTime). - +/ - ref DateTime _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_tod._hour); - hnsecs += convert!("minutes", "hnsecs")(_tod._minute); - hnsecs += convert!("seconds", "hnsecs")(_tod._second); - - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - { - hnsecs += convert!("days", "hnsecs")(1); - --days; - } - - _date._addDays(days); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _tod._hour = cast(ubyte) newHours; - _tod._minute = cast(ubyte) newMinutes; - _tod._second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - //Test A.D. - testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); - - testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); - - //Test B.C. - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); - - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); - - //Test Both - testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); - testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); - - testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); - testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); - testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); - testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt._addSeconds(4))); - static assert(!__traits(compiles, idt._addSeconds(4))); - } - - - Date _date; - TimeOfDay _tod; -} - - //============================================================================== // Section with StopWatch and Benchmark Code. //============================================================================== diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index e9eb44f37ff..f9a7e90ea52 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -12,6 +12,7 @@ module std.datetime.timezone; import core.time; import std.datetime.common; import std.datetime.date; +import std.datetime.datetime; import std.exception : enforce; import std.range.primitives; import std.traits : isIntegral, isSomeString, Unqual; @@ -37,7 +38,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : Clock, DateTime, stdTimeToUnixTime, SysTime; // temporary +import std.datetime : Clock, stdTimeToUnixTime, SysTime; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time From 4c86b696721dedc32c016a1bef6244840221cc88 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 5 May 2017 08:31:14 +0100 Subject: [PATCH 101/262] Show argument kind in getNth exception messages Show argument number for type mismatches, but not for missing arguments. Also remove unnecessary to!int calls. --- std/format.d | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/std/format.d b/std/format.d index 7943f6b344e..7c4c45255b6 100644 --- a/std/format.d +++ b/std/format.d @@ -465,7 +465,7 @@ if (isSomeString!(typeof(fmt))) /// ditto uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) { - import std.conv : text, to; + import std.conv : text; alias FPfmt = void function(Writer, scope const(void)*, const ref FormatSpec!Char) @safe pure nothrow; @@ -501,7 +501,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) if (spec.width == spec.DYNAMIC) { - auto width = to!(typeof(spec.width))(getNthInt(currentArg, args)); + auto width = getNthInt!"integer width"(currentArg, args); if (width < 0) { spec.flDash = true; @@ -515,7 +515,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) // means: get width as a positional parameter auto index = cast(uint) -spec.width; assert(index > 0); - auto width = to!(typeof(spec.width))(getNthInt(index - 1, args)); + auto width = getNthInt!"integer width"(index - 1, args); if (currentArg < index) currentArg = index; if (width < 0) { @@ -527,8 +527,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) if (spec.precision == spec.DYNAMIC) { - auto precision = to!(typeof(spec.precision))( - getNthInt(currentArg, args)); + auto precision = getNthInt!"integer precision"(currentArg, args); if (precision >= 0) spec.precision = precision; // else negative precision is same as no precision else spec.precision = spec.UNSPECIFIED; @@ -539,8 +538,7 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) // means: get precision as a positional parameter auto index = cast(uint) -spec.precision; assert(index > 0); - auto precision = to!(typeof(spec.precision))( - getNthInt(index- 1, args)); + auto precision = getNthInt!"integer precision"(index- 1, args); if (currentArg < index) currentArg = index; if (precision >= 0) spec.precision = precision; // else negative precision is same as no precision @@ -549,14 +547,15 @@ uint formattedWrite(Writer, Char, A...)(Writer w, in Char[] fmt, A args) if (spec.separators == spec.DYNAMIC) { - auto separators = to!(typeof(spec.width))(getNthInt(currentArg, args)); + auto separators = getNthInt!"separator digit width"(currentArg, args); spec.separators = separators; ++currentArg; } if (spec.separatorCharPos == spec.DYNAMIC) { - auto separatorChar = getNth!(isSomeChar,dchar)(currentArg, args); + auto separatorChar = + getNth!("separator character", isSomeChar, dchar)(currentArg, args); spec.separatorChar = separatorChar; ++currentArg; } @@ -4057,12 +4056,12 @@ private void formatNth(Writer, Char, A...)(Writer w, const ref FormatSpec!Char f //------------------------------------------------------------------------------ // Fix for issue 1591 -private int getNthInt(A...)(uint index, A args) +private int getNthInt(string kind, A...)(uint index, A args) { - return getNth!(isIntegral,int)(index, args); + return getNth!(kind, isIntegral,int)(index, args); } -private T getNth(alias Condition, T, A...)(uint index, A args) +private T getNth(string kind, alias Condition, T, A...)(uint index, A args) { import std.conv : text, to; @@ -4077,13 +4076,14 @@ private T getNth(alias Condition, T, A...)(uint index, A args) } else { - throw new FormatException(T.stringof ~ " expected, not " ~ - typeof(args[n]).stringof); + throw new FormatException( + text(kind, " expected, not ", typeof(args[n]).stringof, + " for argument #", index + 1)); } } default: throw new FormatException( - text("Missing ", T.stringof, " for argument #", index + 1)); + text("Missing ", kind, " argument")); } } @@ -4091,21 +4091,21 @@ private T getNth(alias Condition, T, A...)(uint index, A args) { // width/precision assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) - == "int expected, not double"); + == "integer width expected, not double for argument #1"); assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) - == "int expected, not char"); + == "integer precision expected, not char for argument #1"); assert(collectExceptionMsg!FormatException(format("%.*d", 5)) == "Orphan format specifier: %d"); assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) - == "Missing int for argument #2"); + == "Missing integer precision argument"); // separatorCharPos assert(collectExceptionMsg!FormatException(format("%,?d", 5)) - == "dchar expected, not int"); + == "separator character expected, not int for argument #1"); assert(collectExceptionMsg!FormatException(format("%,?d", '?')) == "Orphan format specifier: %d"); - assert(collectExceptionMsg!FormatException(format("%.*,?d", 5)) - == "Missing dchar for argument #2"); + assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5)) + == "Missing separator digit width argument"); } /* ======================== Unit Tests ====================================== */ From 2431287c83bce0fa64f42c86e891ce03bb1fe688 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Sat, 4 Mar 2017 04:53:17 +0100 Subject: [PATCH 102/262] [BOOKTABLES]: Add BOOKTABLE to std.encoding --- std/encoding.d | 85 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/std/encoding.d b/std/encoding.d index 9e27c2eedbe..3acb4199e58 100644 --- a/std/encoding.d +++ b/std/encoding.d @@ -10,33 +10,84 @@ between strings of different type, as well as validation and sanitization. Encodings currently supported are UTF-8, UTF-16, UTF-32, ASCII, ISO-8859-1 (also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250 and WINDOWS-1252. -$(UL -$(LI The type $(D AsciiChar) represents an ASCII character.) -$(LI The type $(D AsciiString) represents an ASCII string.) -$(LI The type $(D Latin1Char) represents an ISO-8859-1 character.) -$(LI The type $(D Latin1String) represents an ISO-8859-1 string.) -$(LI The type $(D Latin2Char) represents an ISO-8859-2 character.) -$(LI The type $(D Latin2String) represents an ISO-8859-2 string.) -$(LI The type $(D Windows1250Char) represents a Windows-1250 character.) -$(LI The type $(D Windows1250String) represents a Windows-1250 string.) -$(LI The type $(D Windows1252Char) represents a Windows-1252 character.) -$(LI The type $(D Windows1252String) represents a Windows-1252 string.)) +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Decode) $(TD + $(LREF codePoints) + $(LREF decode) + $(LREF decodeReverse) + $(LREF safeDecode) +)) +$(TR $(TD Conversion) $(TD + $(LREF codeUnits) + $(LREF sanitize) + $(LREF transcode) +)) +$(TR $(TD Classification) $(TD + $(LREF canEncode) + $(LREF isValid) + $(LREF isValidCodePoint) + $(LREF isValidCodeUnit) +)) +$(TR $(TD BOM) $(TD + $(LREF BOM) + $(LREF BOMSeq) + $(LREF getBOM) + $(LREF utfBOM) +)) +$(TR $(TD Length & Index) $(TD + $(LREF firstSequence) + $(LREF encodedLength) + $(LREF index) + $(LREF lastSequence) + $(LREF validLength) +)) +$(TR $(TD Encoding schemes) $(TD + $(LREF encodingName) + $(LREF EncodingScheme) + $(LREF EncodingSchemeASCII) + $(LREF EncodingSchemeLatin1) + $(LREF EncodingSchemeLatin2) + $(LREF EncodingSchemeUtf16Native) + $(LREF EncodingSchemeUtf32Native) + $(LREF EncodingSchemeUtf8) + $(LREF EncodingSchemeWindows1250) + $(LREF EncodingSchemeWindows1252) +)) +$(TR $(TD Representation) $(TD + $(LREF AsciiChar) + $(LREF AsciiString) + $(LREF Latin1Char) + $(LREF Latin1String) + $(LREF Latin2Char) + $(LREF Latin2String) + $(LREF Windows1250Char) + $(LREF Windows1250String) + $(LREF Windows1252Char) + $(LREF Windows1252String) +)) +$(TR $(TD Exceptions) $(TD + $(LREF INVALID_SEQUENCE) + $(LREF EncodingException) +)) +) For cases where the _encoding is not known at compile-time, but is -known at run-time, we provide the abstract class $(D EncodingScheme) -and its subclasses. To construct a run-time encoder/decoder, one does -e.g. +known at run-time, the abstract class $(LREF EncodingScheme) +and its subclasses is provided. To construct a run-time encoder/decoder, +one does e.g. ---------------------------------------------------- - auto e = EncodingScheme.create("utf-8"); +auto e = EncodingScheme.create("utf-8"); ---------------------------------------------------- -This library supplies $(D EncodingScheme) subclasses for ASCII, +This library supplies $(LREF EncodingScheme) subclasses for ASCII, ISO-8859-1 (also known as LATIN-1), ISO-8859-2 (LATIN-2), WINDOWS-1250, WINDOWS-1252, UTF-8, and (on little-endian architectures) UTF-16LE and UTF-32LE; or (on big-endian architectures) UTF-16BE and UTF-32BE. -This library provides a mechanism whereby other modules may add $(D +This library provides a mechanism whereby other modules may add $(LREF EncodingScheme) subclasses for any other _encoding. Copyright: Copyright Janice Caron 2008 - 2009. From 54671af21779cd40d39e82906f3ac5fb8660ed4b Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Tue, 28 Feb 2017 02:40:36 +0100 Subject: [PATCH 103/262] std.range: Add missing methods to the booktable, disable the quickindex and improve the docs --- std/range/package.d | 68 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index b229efe2ff2..1582754713c 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -3,18 +3,31 @@ /** This module defines the notion of a range. Ranges generalize the concept of arrays, lists, or anything that involves sequential access. This abstraction -enables the same set of algorithms (see $(LINK2 std_algorithm.html, -std.algorithm)) to be used with a vast variety of different concrete types. For -example, a linear search algorithm such as $(LINK2 std_algorithm.html#find, -std.algorithm.find) works not just for arrays, but for linked-lists, input -files, incoming network data, etc. See also Ali Çehreli's -$(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges) for the basics -of working with and creating range-based code. - -For more detailed information about the conceptual aspect of ranges and the -motivation behind them, see Andrei Alexandrescu's article -$(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1, -$(I On Iteration)). +enables the same set of algorithms (see $(MREF std, algorithm)) to be used +with a vast variety of different concrete types. For example, +a linear search algorithm such as $(REF find, std, algorithm, searching) +works not just for arrays, but for linked-lists, input files, +incoming network data, etc. + +Guides: + +There are many articles available that can bolster understanding ranges: + +$(UL + $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on _ranges) + for the basics of working with and creating range-based code.) + $(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges)) + talk at DConf 2015 a vivid introduction from its core constructs to practical advice.) + $(LI The DLang Tour's $(LINK2 http://tour.dlang.org/tour/en/basics/ranges, chapter on ranges) + for an interactive introduction.) + $(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on + component programming with ranges) for a real-world showcase of the influence + of _range-based programming on complex algorithms.) + $(LI Andrei Alexandrescu's article + $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1, + $(I On Iteration)) for conceptual aspect of ranges and the motivation + ) +) Submodules: @@ -32,6 +45,8 @@ polymorphism. The remainder of this module provides a rich set of _range creation and composition templates that let you construct new ranges out of existing ranges: + +$(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE , $(TR $(TD $(LREF chain)) $(TD Concatenates several ranges into a single _range. @@ -54,13 +69,25 @@ $(BOOKTABLE , $(TD Creates the _range that results from discarding the first $(I n) elements from the given _range. )) + $(TR $(TD $(LREF dropBack)) + $(TD Creates the _range that results from discarding the last $(I n) + elements from the given _range. + )) $(TR $(TD $(LREF dropExactly)) $(TD Creates the _range that results from discarding exactly $(I n) of the first elements from the given _range. )) + $(TR $(TD $(LREF dropBackExactly)) + $(TD Creates the _range that results from discarding exactly $(I n) + of the last elements from the given _range. + )) $(TR $(TD $(LREF dropOne)) $(TD Creates the _range that results from discarding - the first elements from the given _range. + the first element from the given _range. + )) + $(TR $(TD $(D $(LREF dropBackOne))) + $(TD Creates the _range that results from discarding + the last element from the given _range. )) $(TR $(TD $(LREF enumerate)) $(TD Iterates a _range with an attached index variable. @@ -73,6 +100,10 @@ $(BOOKTABLE , $(TD Creates a _range that iterates over the first elements of the given ranges. )) + $(TR $(TD $(LREF generate)) + $(TD Creates a _range by successive calls to a given function. This + allows to create ranges as a single delegate. + )) $(TR $(TD $(LREF indexed)) $(TD Creates a _range that offers a view of a given _range as though its elements were reordered according to a given _range of indices. @@ -94,7 +125,7 @@ $(BOOKTABLE , )) $(TR $(TD $(LREF padLeft)) $(TD Pads a _range to a specified length by adding a given element to - the front of the _range. Is lazy if the range has a known length. + the front of the _range. Is lazy if the _range has a known length. )) $(TR $(TD $(LREF padRight)) $(TD Lazily pads a _range to a specified length by adding a given element to @@ -109,6 +140,11 @@ $(BOOKTABLE , $(TD Creates a forward _range whose values are defined by a mathematical recurrence relation. )) + $(TR $(TD $(LREF refRange)) + $(TD Pass a _range by reference. Both the original _range and the RefRange + will always have the exact same elements. + Any operation done on one will affect the other. + )) $(TR $(TD $(LREF repeat)) $(TD Creates a _range that consists of a single element repeated $(I n) times, or an infinite _range repeating that element indefinitely. @@ -166,11 +202,13 @@ $(BOOKTABLE , )) ) +Sortedness: + Ranges whose elements are sorted afford better efficiency with certain operations. For this, the $(LREF assumeSorted) function can be used to construct a $(LREF SortedRange) from a pre-sorted _range. The $(REF sort, std, algorithm, sorting) function also conveniently -returns a $(D SortedRange). $(D SortedRange) objects provide some additional +returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional _range operations that take advantage of the fact that the _range is sorted. Source: $(PHOBOSSRC std/_range/_package.d) From 7093a541d5eeb91270d4de68eda7efe855dbd5e2 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 3 Mar 2017 00:17:10 +0100 Subject: [PATCH 104/262] [BOOKTABLES]: Add BOOKTABLE to std.uni --- std/uni.d | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/std/uni.d b/std/uni.d index 310913c11c1..12f53fe8f78 100644 --- a/std/uni.d +++ b/std/uni.d @@ -4,9 +4,100 @@ $(P The $(D std.uni) module provides an implementation of fundamental Unicode algorithms and data structures. This doesn't include UTF encoding and decoding primitives, - see $(REF decode, std,_utf) and $(REF encode, std,_utf) in std.utf + see $(REF decode, std,_utf) and $(REF encode, std,_utf) in $(MREF std, utf) for this functionality. ) +$(SCRIPT inhibitQuickIndex = 1;) +$(BOOKTABLE, +$(TR $(TH Category) $(TH Functions)) +$(TR $(TD Decode) $(TD + $(LREF byCodePoint) + $(LREF byGrapheme) + $(LREF decodeGrapheme) + $(LREF graphemeStride) +)) +$(TR $(TD Comparison) $(TD + $(LREF icmp) + $(LREF sicmp) +)) +$(TR $(TD Classification) $(TD + $(LREF isAlpha) + $(LREF isAlphaNum) + $(LREF isCodepointSet) + $(LREF isControl) + $(LREF isFormat) + $(LREF isGraphical) + $(LREF isIntegralPair) + $(LREF isMark) + $(LREF isNonCharacter) + $(LREF isNumber) + $(LREF isPrivateUse) + $(LREF isPunctuation) + $(LREF isSpace) + $(LREF isSurrogate) + $(LREF isSurrogateHi) + $(LREF isSurrogateLo) + $(LREF isSymbol) + $(LREF isWhite) +)) +$(TR $(TD Normalization) $(TD + $(LREF NFC) + $(LREF NFD) + $(LREF NFKD) + $(LREF NormalizationForm) + $(LREF normalize) +)) +$(TR $(TD Decompose) $(TD + $(LREF decompose) + $(LREF decomposeHangul) + $(LREF UnicodeDecomposition) +)) +$(TR $(TD Compose) $(TD + $(LREF compose) + $(LREF composeJamo) +)) +$(TR $(TD Sets) $(TD + $(LREF CodepointInterval) + $(LREF CodepointSet) + $(LREF InversionList) + $(LREF unicode) +)) +$(TR $(TD Trie) $(TD + $(LREF codepointSetTrie) + $(LREF CodepointSetTrie) + $(LREF codepointTrie) + $(LREF CodepointTrie) + $(LREF toTrie) + $(LREF toDelegate) +)) +$(TR $(TD Casing) $(TD + $(LREF asCapitalized) + $(LREF asLowerCase) + $(LREF asUpperCase) + $(LREF isLower) + $(LREF isUpper) + $(LREF toLower) + $(LREF toLowerInPlace) + $(LREF toUpper) + $(LREF toUpperInPlace) +)) +$(TR $(TD Utf8Matcher) $(TD + $(LREF isUtfMatcher) + $(LREF MatcherConcept) + $(LREF utfMatcher) +)) +$(TR $(TD Separators) $(TD + $(LREF lineSep) + $(LREF nelSep) + $(LREF paraSep) +)) +$(TR $(TD Building blocks) $(TD + $(LREF allowedIn) + $(LREF combiningClass) + $(LREF Grapheme) +)) +) + $(P All primitives listed operate on Unicode characters and sets of characters. For functions which operate on ASCII characters and ignore Unicode $(CHARACTERS), see $(MREF std, ascii). From 27330e76b72393ebb611176ea5600c94e447f067 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 5 May 2017 16:48:49 +0200 Subject: [PATCH 105/262] issue# 17372: icmp chokes on a lambda that can throw. This removes some inappropriate attributes from a fullCasedCmp. It's templated, and their applicability depends on the template argument, so they should be inferred. And the unit tests for icmp which test the attributes (including @safe) pass without the attributes on fullCasedCmp, so I have no idea why any of them (particularly @trusted) were ever there. --- std/uni.d | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/std/uni.d b/std/uni.d index 310913c11c1..0d81cb966e8 100644 --- a/std/uni.d +++ b/std/uni.d @@ -7027,7 +7027,7 @@ if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1) } /// -@safe @nogc nothrow unittest +@safe @nogc pure nothrow unittest { assert(sicmp("Август", "авгусТ") == 0); // Greek also works as long as there is no 1:M mapping in sight @@ -7053,7 +7053,6 @@ if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1) } private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail) - @trusted pure nothrow { import std.algorithm.searching : skipOver; import std.internal.unicode_tables : fullCaseTable; // generated file @@ -7147,7 +7146,7 @@ if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1) } /// -@safe @nogc nothrow unittest +@safe @nogc pure nothrow unittest { assert(icmp("Rußland", "Russland") == 0); assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0); @@ -7227,6 +7226,15 @@ if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1) }); } +// issue 17372 +@safe pure unittest +{ + import std.algorithm.iteration : joiner, map; + import std.algorithm.sorting : sort; + import std.array : array; + auto a = [["foo", "bar"], ["baz"]].map!(line => line.joiner(" ")).array.sort!((a, b) => icmp(a, b) < 0); +} + // This is package for the moment to be used as a support tool for std.regex // It needs a better API /* From 56c626938252d600f56e64131714120216cdaef9 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 5 May 2017 18:49:08 +0200 Subject: [PATCH 106/262] Split style target for CircleCi --- circle.yml | 4 +++- posix.mak | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index ce9e67f9d5f..72801310c99 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,9 @@ dependencies: test: override: - ./circleci.sh setup-repos - - ./circleci.sh style + - ./circleci.sh style_lint + - ./circleci.sh has_public_example + - ./circleci.sh publictests - ./circleci.sh coverage: parallel: true timeout: 1200 diff --git a/posix.mak b/posix.mak index 33eae909424..766c270e34e 100644 --- a/posix.mak +++ b/posix.mak @@ -499,7 +499,9 @@ $(TOOLS_DIR)/checkwhitespace.d: mv dscanner_makefile_tmp ../dscanner/makefile make -C ../dscanner githash debug -style: ../dscanner/dsc $(LIB) has_public_example publictests +style: has_public_example publictests style_lint + +style_lint: ../dscanner/dsc $(LIB) @echo "Check for trailing whitespace" grep -nr '[[:blank:]]$$' etc std ; test $$? -eq 1 From 27943964f491b731b5c3b893ce95a53222aabc98 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 15:57:47 +0200 Subject: [PATCH 107/262] Move SysTime to std.datetime.systime. --- std/datetime/common.d | 6 +- std/datetime/date.d | 2 +- std/datetime/interval.d | 20 +- std/datetime/package.d | 9469 +-------------------------------------- std/datetime/systime.d | 9317 ++++++++++++++++++++++++++++++++++++++ std/datetime/timezone.d | 3 +- 6 files changed, 9373 insertions(+), 9444 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index 733982c7e75..42ea8b56ea6 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -455,11 +455,10 @@ private: @safe unittest { import core.time : Duration; - import std.datetime : SysTime; // temporary import std.datetime.date; import std.datetime.datetime; import std.datetime.interval : Interval; - //import std.datetime.systime; + import std.datetime.systime; import std.datetime.timeofday; static assert(isTimePoint!Date); @@ -475,11 +474,10 @@ private: @safe unittest { import core.time; - import std.datetime : SysTime; // temporary import std.datetime.date; import std.datetime.datetime; import std.datetime.interval; - //import std.datetime.systime; + import std.datetime.systime; import std.datetime.timeofday; import std.meta : AliasSeq; diff --git a/std/datetime/date.d b/std/datetime/date.d index 68d66841a64..35a8f298666 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -4362,7 +4362,7 @@ DayOfWeek getDayOfWeek(int day) @safe pure nothrow @safe unittest { - import std.datetime : SysTime; // temporary + import std.datetime.systime : SysTime; // Test A.D. assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 24a6bed3443..dc5335ee4c0 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -17,8 +17,6 @@ import std.typecons : Flag; version(unittest) import std.exception : assertThrown; -import std.datetime : SysTime; // temporary - /++ Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s @@ -1566,6 +1564,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); @@ -1632,6 +1631,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); @@ -1656,6 +1656,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); @@ -4125,6 +4126,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; PosInfInterval!Date(Date.init); @@ -4159,6 +4161,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); @@ -6341,6 +6344,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; NegInfInterval!Date(Date.init); @@ -6372,6 +6376,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); @@ -7600,6 +7605,7 @@ if (isTimePoint!TP && { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); @@ -7726,6 +7732,7 @@ if (isTimePoint!TP && { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; auto funcFwd = everyMonth!Date(Month.jun); auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); @@ -7831,6 +7838,7 @@ if (isTimePoint!TP && { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; auto funcFwd = everyDuration!Date(dur!"days"(27)); @@ -7956,6 +7964,7 @@ if (isTimePoint!TP && { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; { @@ -8237,6 +8246,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; import std.range.primitives; @@ -8265,6 +8275,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; { @@ -8681,6 +8692,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; import std.range.primitives; @@ -8708,6 +8720,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; { @@ -8731,7 +8744,7 @@ private: { SysTime stFunc(in SysTime st) { return cast(SysTime) st; } auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); - auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); + auto ir = PosInfIntervalRange!SysTime(posInfInterval, &stFunc); } } @@ -8992,6 +9005,7 @@ private: { import std.datetime.date; import std.datetime.datetime; + import std.datetime.systime; import std.datetime.timeofday; { diff --git a/std/datetime/package.d b/std/datetime/package.d index be9fdb94172..efa23c46095 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -144,11 +144,6 @@ else version(Posix) import core.sys.posix.sys.types; // time_t } -@safe unittest -{ - initializeTests(); -} - //Verify module example. @safe unittest { @@ -380,8740 +375,69 @@ public: ts.tv_nsec / 100 + hnsecsToUnixEpoch; } - } - else static assert(0, "Unsupported OS"); - } - else static assert(0, "Unsupported OS"); - } - - @safe unittest - { - import std.format : format; - import std.math : abs; - import std.meta : AliasSeq; - import std.stdio : writefln; - enum limit = convert!("seconds", "hnsecs")(2); - - auto norm1 = Clock.currStdTime; - auto norm2 = Clock.currStdTime; - assert(norm1 <= norm2, format("%s %s", norm1, norm2)); - assert(abs(norm1 - norm2) <= limit); - - foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) - { - scope(failure) writefln("ClockType.%s", ct); - auto value1 = Clock.currStdTime!ct; - auto value2 = Clock.currStdTime!ct; - assert(value1 <= value2, format("%s %s", value1, value2)); - assert(abs(value1 - value2) <= limit); - } - } - - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use core.time.MonoTime.currTime instead") - static @property TickDuration currSystemTick() @safe nothrow - { - return TickDuration.currSystemTick; - } - - deprecated @safe unittest - { - assert(Clock.currSystemTick.length > 0); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use core.time.MonoTime instead. See currAppTick's documentation for details.") - static @property TickDuration currAppTick() @safe - { - return currSystemTick - TickDuration.appOrigin; - } - - deprecated @safe unittest - { - auto a = Clock.currSystemTick; - auto b = Clock.currAppTick; - assert(a.length); - assert(b.length); - assert(a > b); - } - -private: - - @disable this() {} -} - -//============================================================================== -// Section with time points. -//============================================================================== - -/++ - $(D SysTime) is the type used to get the current time from the - system or doing anything that involves time zones. Unlike - $(LREF DateTime), the time zone is an integral part of $(D SysTime) (though for - local time applications, time zones can be ignored and - it will work, since it defaults to using the local time zone). It holds its - internal time in std time (hnsecs since midnight, January 1st, 1 A.D. UTC), - so it interfaces well with the system time. However, that means that, unlike - $(LREF DateTime), it is not optimized for calendar-based operations, and - getting individual units from it such as years or days is going to involve - conversions and be less efficient. - - For calendar-based operations that don't - care about time zones, then $(LREF DateTime) would be the type to - use. For system time, use $(D SysTime). - - $(LREF2 .Clock.currTime, Clock.currTime) will return the current time as a $(D SysTime). - To convert a $(D SysTime) to a $(LREF Date) or $(LREF DateTime), simply cast - it. To convert a $(LREF Date) or $(LREF DateTime) to a - $(D SysTime), use $(D SysTime)'s constructor, and pass in the - intended time zone with it (or don't pass in a $(LREF2 .TimeZone, TimeZone), and the local - time zone will be used). Be aware, however, that converting from a - $(LREF DateTime) to a $(D SysTime) will not necessarily be 100% accurate due to - DST (one hour of the year doesn't exist and another occurs twice). - To not risk any conversion errors, keep times as - $(D SysTime)s. Aside from DST though, there shouldn't be any conversion - problems. - - For using time zones other than local time or UTC, use - $(LREF PosixTimeZone) on Posix systems (or on Windows, if providing the TZ - Database files), and use $(LREF WindowsTimeZone) on Windows systems. - The time in $(D SysTime) is kept internally in hnsecs from midnight, - January 1st, 1 A.D. UTC. Conversion error cannot happen when changing - the time zone of a $(D SysTime). $(LREF LocalTime) is the $(LREF2 .TimeZone, TimeZone) class - which represents the local time, and $(D UTC) is the $(LREF2 .TimeZone, TimeZone) class - which represents UTC. $(D SysTime) uses $(LREF LocalTime) if no $(LREF2 .TimeZone, TimeZone) - is provided. For more details on time zones, see the documentation for - $(LREF2 .TimeZone, TimeZone), $(LREF PosixTimeZone), and $(LREF WindowsTimeZone). - - $(D SysTime)'s range is from approximately 29,000 B.C. to approximately - 29,000 A.D. - +/ -struct SysTime -{ - import core.stdc.time : tm; - version(Posix) import core.sys.posix.sys.time : timeval; - import std.typecons : Rebindable; - -public: - - /++ - Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of - time zone, tz is used as its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. - +/ - this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow - { - try - this(dateTime, Duration.zero, tz); - catch (Exception e) - assert(0, "SysTime's constructor threw when it shouldn't have."); - } - - @safe unittest - { - import std.format : format; - static void test(DateTime dt, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s", dt)); - } - - test(DateTime.init, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0); - test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L); - test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L); - - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); - test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); - } - - /++ - Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of - time zone, tz is used as its time zone. - fracSecs = The fractional seconds portion of the time. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. - - Throws: - $(LREF DateTimeException) if $(D fracSecs) is negative or if it's - greater than or equal to one second. - +/ - this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - auto nonNullTZ = tz is null ? LocalTime() : tz; - - immutable dateDiff = dateTime.date - Date.init; - immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; - - immutable adjustedTime = dateDiff + todDiff + fracSecs; - immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs"); - - this(standardTime, nonNullTZ); - } - - @safe unittest - { - import std.format : format; - static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected) - { - auto sysTime = SysTime(dt, fracSecs, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s, Given Duration: %s", dt, fracSecs)); - } - - test(DateTime.init, Duration.zero, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L); - test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L); - - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1); - test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999); - test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000); - - assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); - assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); - } - - // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ - deprecated("Please use the overload which takes a Duration instead of a FracSec.") - this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) @safe - { - immutable fracHNSecs = fracSec.hnsecs; - enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - _timezone = tz is null ? LocalTime() : tz; - - try - { - immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; - immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; - - immutable adjustedTime = dateDiff + todDiff + fracHNSecs; - immutable standardTime = _timezone.tzToUTC(adjustedTime); - - this(standardTime, _timezone); - } - catch (Exception e) - assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); - } - - deprecated @safe unittest - { - import std.format : format; - - static void test(DateTime dt, - FracSec fracSec, - immutable TimeZone tz, - long expected) - { - auto sysTime = SysTime(dt, fracSec, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given DateTime: %s, Given FracSec: %s", dt, fracSec)); - } - - test(DateTime.init, FracSec.init, UTC(), 0); - test(DateTime(1, 1, 1, 12, 30, 33), FracSec.init, UTC(), 450_330_000_000L); - test(DateTime(0, 12, 31, 12, 30, 33), FracSec.init, UTC(), -413_670_000_000L); - test(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC(), 10_000L); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC(), -10_000L); - - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC(), -1); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC(), -9_999_999); - test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0), UTC(), -10_000_000); - - assertThrown!DateTimeException(SysTime(DateTime.init, FracSec.from!"hnsecs"(-1), UTC())); - } - - /++ - Params: - date = The $(LREF Date) to use to set this $(LREF SysTime)'s internal std - time. As $(LREF Date) has no concept of time zone, tz is used as - its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF Date) is assumed - to be in the given time zone. - +/ - this(in Date date, immutable TimeZone tz = null) @safe nothrow - { - _timezone = tz is null ? LocalTime() : tz; - - try - { - immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; - immutable standardTime = _timezone.tzToUTC(adjustedTime); - - this(standardTime, _timezone); - } - catch (Exception e) - assert(0, "Date's constructor through when it shouldn't have."); - } - - @safe unittest - { - static void test(Date d, immutable TimeZone tz, long expected) - { - import std.format : format; - auto sysTime = SysTime(d, tz); - assert(sysTime._stdTime == expected); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given Date: %s", d)); - } - - test(Date.init, UTC(), 0); - test(Date(1, 1, 1), UTC(), 0); - test(Date(1, 1, 2), UTC(), 864000000000); - test(Date(0, 12, 31), UTC(), -864000000000); - } - - /++ - Note: - Whereas the other constructors take in the given date/time, assume - that it's in the given time zone, and convert it to hnsecs in UTC - since midnight, January 1st, 1 A.D. UTC - i.e. std time - this - constructor takes a std time, which is specifically already in UTC, - so no conversion takes place. Of course, the various getter - properties and functions will use the given time zone's conversion - function to convert the results to that time zone, but no conversion - of the arguments to this constructor takes place. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. - +/ - this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow - { - _stdTime = stdTime; - _timezone = tz is null ? LocalTime() : tz; - } - - @safe unittest - { - static void test(long stdTime, immutable TimeZone tz) - { - import std.format : format; - auto sysTime = SysTime(stdTime, tz); - assert(sysTime._stdTime == stdTime); - assert(sysTime._timezone is (tz is null ? LocalTime() : tz), - format("Given stdTime: %s", stdTime)); - } - - foreach (stdTime; [-1234567890L, -250, 0, 250, 1235657390L]) - { - foreach (tz; testTZs) - test(stdTime, tz); - } - } - - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - +/ - ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow - { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - - return this; - } - - /++ - Params: - rhs = The $(LREF SysTime) to assign to this one. - +/ - ref SysTime opAssign(SysTime rhs) scope return @safe pure nothrow - { - _stdTime = rhs._stdTime; - _timezone = rhs._timezone; - - return this; - } - - /++ - Checks for equality between this $(LREF SysTime) and the given - $(LREF SysTime). - - Note that the time zone is ignored. Only the internal - std times (which are in UTC) are compared. - +/ - bool opEquals(const SysTime rhs) @safe const pure nothrow - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(const ref SysTime rhs) @safe const pure nothrow - { - return _stdTime == rhs._stdTime; - } - - @safe unittest - { - import std.range : chain; - assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); - assert(SysTime(DateTime.init, UTC()) == SysTime(0)); - assert(SysTime(Date.init, UTC()) == SysTime(0)); - assert(SysTime(0) == SysTime(0)); - - static void test(DateTime dt, - immutable TimeZone tz1, - immutable TimeZone tz2) - { - auto st1 = SysTime(dt); - st1.timezone = tz1; - - auto st2 = SysTime(dt); - st2.timezone = tz2; - - assert(st1 == st2); - } - - foreach (tz1; testTZs) - { - foreach (tz2; testTZs) - { - foreach (dt; chain(testDateTimesBC, testDateTimesAD)) - test(dt, tz1, tz2); - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - assert(st == st); - assert(st == cst); - //assert(st == ist); - assert(cst == st); - assert(cst == cst); - //assert(cst == ist); - //assert(ist == st); - //assert(ist == cst); - //assert(ist == ist); - } - - /++ - Compares this $(LREF SysTime) with the given $(LREF SysTime). - - Time zone is irrelevant when comparing $(LREF SysTime)s. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in SysTime rhs) @safe const pure nothrow - { - if (_stdTime < rhs._stdTime) - return -1; - if (_stdTime > rhs._stdTime) - return 1; - - return 0; - } - - @safe unittest - { - import std.algorithm.iteration : map; - import std.array : array; - import std.range : chain; - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); - assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); - assert(SysTime(0).opCmp(SysTime(0)) == 0); - - static void testEqual(SysTime st, - immutable TimeZone tz1, - immutable TimeZone tz2) - { - auto st1 = st; - st1.timezone = tz1; - - auto st2 = st; - st2.timezone = tz2; - - assert(st1.opCmp(st2) == 0); - } - - auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); - - foreach (st; sts) - foreach (tz1; testTZs) - foreach (tz2; testTZs) - testEqual(st, tz1, tz2); - - static void testCmp(SysTime st1, - immutable TimeZone tz1, - SysTime st2, - immutable TimeZone tz2) - { - st1.timezone = tz1; - st2.timezone = tz2; - assert(st1.opCmp(st2) < 0); - assert(st2.opCmp(st1) > 0); - } - - foreach (si, st1; sts) - foreach (st2; sts[si+1 .. $]) - foreach (tz1; testTZs) - foreach (tz2; testTZs) - testCmp(st1, tz1, st2, tz2); - - auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); - assert(st.opCmp(st) == 0); - assert(st.opCmp(cst) == 0); - //assert(st.opCmp(ist) == 0); - assert(cst.opCmp(st) == 0); - assert(cst.opCmp(cst) == 0); - //assert(cst.opCmp(ist) == 0); - //assert(ist.opCmp(st) == 0); - //assert(ist.opCmp(cst) == 0); - //assert(ist.opCmp(ist) == 0); - } - - /** - * Returns: A hash of the $(LREF SysTime) - */ - size_t toHash() const @nogc pure nothrow @safe - { - static if (is(size_t == ulong)) - { - return _stdTime; - } - else - { - // MurmurHash2 - enum ulong m = 0xc6a4a7935bd1e995UL; - enum ulong n = m * 16; - enum uint r = 47; - - ulong k = _stdTime; - k *= m; - k ^= k >> r; - k *= m; - - ulong h = n; - h ^= k; - h *= m; - - return cast(size_t) h; - } - } - - @safe unittest - { - assert(SysTime(0).toHash == SysTime(0).toHash); - assert(SysTime(DateTime(2000, 1, 1)).toHash == SysTime(DateTime(2000, 1, 1)).toHash); - assert(SysTime(DateTime(2000, 1, 1)).toHash != SysTime(DateTime(2000, 1, 2)).toHash); - - // test that timezones aren't taken into account - assert(SysTime(0, LocalTime()).toHash == SysTime(0, LocalTime()).toHash); - assert(SysTime(0, LocalTime()).toHash == SysTime(0, UTC()).toHash); - assert( - SysTime( - DateTime(2000, 1, 1), LocalTime() - ).toHash == SysTime( - DateTime(2000, 1, 1), LocalTime() - ).toHash - ); - immutable zone = new SimpleTimeZone(dur!"minutes"(60)); - assert( - SysTime( - DateTime(2000, 1, 1, 1), zone - ).toHash == SysTime( - DateTime(2000, 1, 1), UTC() - ).toHash - ); - assert( - SysTime( - DateTime(2000, 1, 1), zone - ).toHash != SysTime( - DateTime(2000, 1, 1), UTC() - ).toHash - ); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const nothrow - { - return (cast(Date) this).year; - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime sysTime, long expected) - { - import std.format : format; - assert(sysTime.year == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 0); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), year); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.year == 1999); - //assert(ist.year == 1999); - } - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF SysTime)'s year to. - - Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. - +/ - @property void year(int year) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.year = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime st, int year, in SysTime expected) - { - st.year = year; - assert(st == expected); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (year; chain(testYearsBC, testYearsAD)) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, year, e); - } - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999, - SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz)); - } - - foreach (tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = 1999); - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.year = 7)); - //static assert(!__traits(compiles, ist.year = 7)); - } - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(LREF DateTimeException) if $(D isAD) is true. - +/ - @property ushort yearBC() @safe const - { - return (cast(Date) this).yearBC; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); - assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); - assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); - } - - @safe unittest - { - import std.exception : assertNotThrown; - import std.format : format; - foreach (st; testSysTimesBC) - { - auto msg = format("SysTime: %s", st); - assertNotThrown!DateTimeException(st.yearBC, msg); - assert(st.yearBC == (st.year * -1) + 1, msg); - } - - foreach (st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) - assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st)); - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.year = 12; - assert(st.year == 12); - static assert(!__traits(compiles, cst.year = 12)); - //static assert(!__traits(compiles, ist.year = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF SysTime)'s year to. - - Throws: - $(LREF DateTimeException) if a non-positive value is given. - +/ - @property void yearBC(int year) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.yearBC = year; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); - st.yearBC = 1; - assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); - - st.yearBC = 10; - assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime st, int year, in SysTime expected) - { - import std.format : format; - st.yearBC = year; - assert(st == expected, format("SysTime: %s", st)); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (year; testYearsBC) - { - auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, (year * -1) + 1, e); - } - } - - foreach (st; [testSysTimesBC[0], testSysTimesBC[$ - 1], - testSysTimesAD[0], testSysTimesAD[$ - 1]]) - { - foreach (year; testYearsBC) - assertThrown!DateTimeException(st.yearBC = year); - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001, - SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000, - SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz)); - } - - foreach (tod; testTODsThrown) - { - auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz); - assertThrown!DateTimeException(st.year = -1999); - } - } - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.yearBC = 12; - assert(st.yearBC == 12); - static assert(!__traits(compiles, cst.yearBC = 12)); - //static assert(!__traits(compiles, ist.yearBC = 12)); - } - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const nothrow - { - return (cast(Date) this).month; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); - } - - @safe unittest - { - import std.range : chain; - static void test(SysTime sysTime, Month expected) - { - import std.format : format; - assert(sysTime.month == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), Month.jan); - test(SysTime(1, UTC()), Month.jan); - test(SysTime(-1, UTC()), Month.dec); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - test(SysTime(dt, fs, tz), md.month); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.month == 7); - //assert(ist.month == 7); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF SysTime)'s month to. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month. - +/ - @property void month(Month month) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.month = month; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - import std.algorithm.iteration : filter; - import std.range : chain; - - static void test(SysTime st, Month month, in SysTime expected) - { - st.month = cast(Month) month; - assert(st == expected); - } - - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - foreach (md; testMonthDays) - { - if (st.day > maxDay(dt.year, md.month)) - continue; - auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - test(st, md.month, e); - } - } - - foreach (fs; testFracSecs) - { - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - foreach (year; filter!((a){return yearIsLeapYear(a);}) - (chain(testYearsBC, testYearsAD))) - { - test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 29), tod), fs, tz)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz), - Month.feb, - SysTime(DateTime(Date(year, 2, 28), tod), fs, tz)); - test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz), - Month.jun, - SysTime(DateTime(Date(year, 6, 30), tod), fs, tz)); - } - } - } - } - - foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach (tz; testTZs) - { - foreach (tod; testTODsThrown) - { - foreach (year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - auto day = yearIsLeapYear(year) ? 30 : 29; - auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz); - assertThrown!DateTimeException(st1.month = Month.feb); - - auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz); - assertThrown!DateTimeException(st2.month = Month.jun); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.month = 12)); - //static assert(!__traits(compiles, ist.month = 12)); - } - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const nothrow - { - return (cast(Date) this).day; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); - assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); - assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); - } - - @safe unittest - { - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - import std.format : format; - assert(sysTime.day == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 1); - test(SysTime(1, UTC()), 1); - test(SysTime(-1, UTC()), 31); - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - { - auto dt = DateTime(Date(year, md.month, md.day), tod); - - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), md.day); - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.day == 6); - //assert(ist.day == 6); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF SysTime)'s day to. - - Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. - +/ - @property void day(int day) @safe - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.day = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - adjTime = newDaysHNSecs + hnsecs; - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - import std.traits : EnumMembers; - - foreach (day; chain(testDays)) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - - if (day > maxDay(dt.year, dt.month)) - continue; - auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.day = day; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - foreach (tz; testTZs) - { - foreach (tod; testTODs) - { - foreach (fs; testFracSecs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz); - - st.day = max; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - } - } - } - - foreach (tz; testTZs) - { - foreach (tod; testTODsThrown) - { - foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) - { - foreach (year; [testYearsBC[$-3], testYearsBC[$-2], - testYearsBC[$-2], testYearsAD[0], - testYearsAD[$-2], testYearsAD[$-1]]) - { - foreach (month; EnumMembers!Month) - { - auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); - immutable max = maxDay(year, month); - - assertThrown!DateTimeException(st.day = max + 1); - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.day = 27)); - //static assert(!__traits(compiles, ist.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - return cast(ubyte) getUnitsFromHNSecs!"hours"(hnsecs); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.hour == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 23); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), hour); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.hour == 12); - //assert(ist.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hours to set this $(LREF SysTime)'s hour to. - - Throws: - $(LREF DateTimeException) if the given hour are not a valid hour of - the day. - +/ - @property void hour(int hour) @safe - { - enforceValid!"hours"(hour); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs += convert!("hours", "hnsecs")(hour); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - foreach (hour; chain(testHours)) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second), - st.fracSecs, - st.timezone); - st.hour = hour; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.hour = -1); - assertThrown!DateTimeException(st.hour = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.hour = 27)); - //static assert(!__traits(compiles, ist.hour = 27)); - } - - - /++ - Minutes past the current hour. - +/ - @property ubyte minute() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - - return cast(ubyte) getUnitsFromHNSecs!"minutes"(hnsecs); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.minute == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), minute); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.minute == 30); - //assert(ist.minute == 30); - } - - - /++ - Minutes past the current hour. - - Params: - minute = The minute to set this $(LREF SysTime)'s minute to. - - Throws: - $(LREF DateTimeException) if the given minute are not a valid minute - of an hour. - +/ - @property void minute(int minute) @safe - { - enforceValid!"minutes"(minute); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - foreach (minute; testMinSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second), - st.fracSecs, - st.timezone); - st.minute = minute; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.minute = -1); - assertThrown!DateTimeException(st.minute = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.minute = 27)); - //static assert(!__traits(compiles, ist.minute = 27)); - } - - - /++ - Seconds past the current minute. - +/ - @property ubyte second() @safe const nothrow - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); - - return cast(ubyte) getUnitsFromHNSecs!"seconds"(hnsecs); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(SysTime sysTime, int expected) - { - assert(sysTime.second == expected, - format("Value given: %s", sysTime)); - } - - test(SysTime(0, UTC()), 0); - test(SysTime(1, UTC()), 0); - test(SysTime(-1, UTC()), 59); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), second); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.second == 33); - //assert(ist.second == 33); - } - - - /++ - Seconds past the current minute. - - Params: - second = The second to set this $(LREF SysTime)'s second to. - - Throws: - $(LREF DateTimeException) if the given second are not a valid second - of a minute. - +/ - @property void second(int second) @safe - { - enforceValid!"seconds"(second); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); - - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - hnsecs += convert!("seconds", "hnsecs")(second); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - foreach (second; testMinSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second), - st.fracSecs, - st.timezone); - st.second = second; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.second = -1); - assertThrown!DateTimeException(st.second = 60); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.seconds = 27)); - //static assert(!__traits(compiles, ist.seconds = 27)); - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - +/ - @property Duration fracSecs() @safe const nothrow - { - auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs)); - } - - /// - @safe unittest - { - auto dt = DateTime(1982, 4, 1, 20, 59, 22); - assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); - assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); - assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567)); - - // SysTime and Duration both have a precision of hnsecs (100 ns), - // so nsecs are going to be truncated. - assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700)); - } - - @safe unittest - { - import std.range : chain; - - assert(SysTime(0, UTC()).fracSecs == Duration.zero); - assert(SysTime(1, UTC()).fracSecs == hnsecs(1)); - assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999)); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); - foreach (fs; testFracSecs) - assert(SysTime(dt, fs, tz).fracSecs == fs); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.fracSecs == Duration.zero); - //assert(ist.fracSecs == Duration.zero); - } - - - /++ - Fractional seconds past the second (i.e. the portion of a - $(LREF SysTime) which is less than a second). - - Params: - fracSecs = The duration to set this $(LREF SysTime)'s fractional - seconds to. - - Throws: - $(LREF DateTimeException) if the given duration is negative or if - it's greater than or equal to one second. - +/ - @property void fracSecs(Duration fracSecs) @safe - { - enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); - - auto oldHNSecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(oldHNSecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = oldHNSecs < 0; - - if (negative) - oldHNSecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs); - immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds); - auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs; - - if (negative) - newHNSecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + newHNSecs; - } - - /// - @safe unittest - { - auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); - assert(st.fracSecs == Duration.zero); - - st.fracSecs = msecs(213); - assert(st.fracSecs == msecs(213)); - - st.fracSecs = hnsecs(1234567); - assert(st.fracSecs == hnsecs(1234567)); - - // SysTime has a precision of hnsecs (100 ns), so nsecs are - // going to be truncated. - st.fracSecs = nsecs(123456789); - assert(st.fracSecs == hnsecs(1234567)); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - foreach (fracSec; testFracSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(dt, fracSec, st.timezone); - st.fracSecs = fracSec; - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.fracSecs = hnsecs(-1)); - assertThrown!DateTimeException(st.fracSecs = seconds(1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.fracSecs = msecs(7))); - //static assert(!__traits(compiles, ist.fracSecs = msecs(7))); - } - - - // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ - deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " - ~"It returns a Duration instead of a FracSec, as FracSec is being deprecated.") - @property FracSec fracSec() @safe const nothrow - { - try - { - auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); - - return FracSec.from!"hnsecs"(cast(int) hnsecs); - } - catch (Exception e) - assert(0, "FracSec.from!\"hnsecs\"() threw."); - } - - deprecated @safe unittest - { - import std.range; - import std.format : format; - - static void test(SysTime sysTime, FracSec expected, size_t line = __LINE__) - { - if (sysTime.fracSec != expected) - throw new AssertError(format("Value given: %s", sysTime.fracSec), __FILE__, line); - } - - test(SysTime(0, UTC()), FracSec.from!"hnsecs"(0)); - test(SysTime(1, UTC()), FracSec.from!"hnsecs"(1)); - test(SysTime(-1, UTC()), FracSec.from!"hnsecs"(9_999_999)); - - foreach (tz; testTZs) - { - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (hour; testHours) - { - foreach (minute; testMinSecs) - { - foreach (second; testMinSecs) - { - auto dt = DateTime(Date(year, md.month, md.day), - TimeOfDay(hour, minute, second)); - - foreach (fs; testFracSecs) - test(SysTime(dt, fs, tz), FracSec.from!"hnsecs"(fs.total!"hnsecs")); - } - } - } - } - } - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.fracSec == FracSec.zero); - //assert(ist.fracSec == FracSec.zero); - } - - - // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ - deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " - ~"It takes a Duration instead of a FracSec, as FracSec is being deprecated.") - @property void fracSec(FracSec fracSec) @safe - { - immutable fracHNSecs = fracSec.hnsecs; - enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); - - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable daysHNSecs = convert!("days", "hnsecs")(days); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - hnsecs = fracHNSecs; - hnsecs += convert!("hours", "hnsecs")(hour); - hnsecs += convert!("minutes", "hnsecs")(minute); - hnsecs += convert!("seconds", "hnsecs")(second); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - adjTime = daysHNSecs + hnsecs; - } - - deprecated @safe unittest - { - import std.range; - import std.format : format; - - foreach (fracSec; testFracSecs) - { - foreach (st; chain(testSysTimesBC, testSysTimesAD)) - { - auto dt = cast(DateTime) st; - auto expected = SysTime(dt, fracSec, st.timezone); - st.fracSec = FracSec.from!"hnsecs"(fracSec.total!"hnsecs"); - assert(st == expected, format("[%s] [%s]", st, expected)); - } - } - - auto st = testSysTimesAD[0]; - assertThrown!DateTimeException(st.fracSec = FracSec.from!"hnsecs"(-1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.fracSec = FracSec.from!"msecs"(7))); - //static assert(!__traits(compiles, ist.fracSec = FracSec.from!"msecs"(7))); - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - +/ - @property long stdTime() @safe const pure nothrow - { - return _stdTime; - } - - @safe unittest - { - assert(SysTime(0).stdTime == 0); - assert(SysTime(1).stdTime == 1); - assert(SysTime(-1).stdTime == -1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330_000_502L); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.stdTime > 0); - //assert(ist.stdTime > 0); - } - - - /++ - The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the - internal representation of $(LREF SysTime). - - Params: - stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. - +/ - @property void stdTime(long stdTime) @safe pure nothrow - { - _stdTime = stdTime; - } - - @safe unittest - { - static void test(long stdTime, in SysTime expected, size_t line = __LINE__) - { - auto st = SysTime(0, UTC()); - st.stdTime = stdTime; - assert(st == expected); - } - - test(0, SysTime(Date(1, 1, 1), UTC())); - test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC())); - test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC())); - test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC())); - test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.stdTime = 27)); - //static assert(!__traits(compiles, ist.stdTime = 27)); - } - - - /++ - The current time zone of this $(LREF SysTime). Its internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. - +/ - @property immutable(TimeZone) timezone() @safe const pure nothrow - { - return _timezone; - } - - - /++ - The current time zone of this $(LREF SysTime). It's internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. - - Params: - timezone = The $(LREF2 .TimeZone, TimeZone) to set this $(LREF SysTime)'s time zone to. - +/ - @property void timezone(immutable TimeZone timezone) @safe pure nothrow - { - if (timezone is null) - _timezone = LocalTime(); - else - _timezone = timezone; - } - - - /++ - Returns whether DST is in effect for this $(LREF SysTime). - +/ - @property bool dstInEffect() @safe const nothrow - { - return _timezone.dstInEffect(_stdTime); - //This function's unit testing is done in the time zone classes. - } - - - /++ - Returns what the offset from UTC is for this $(LREF SysTime). - It includes the DST offset in effect at that time (if any). - +/ - @property Duration utcOffset() @safe const nothrow - { - return _timezone.utcOffsetAt(_stdTime); - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - $(LREF LocalTime) as its time zone. - +/ - SysTime toLocalTime() @safe const pure nothrow - { - return SysTime(_stdTime, LocalTime()); - } - - @safe unittest - { - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone is sysTime.timezone); - assert(sysTime.toLocalTime().timezone !is UTC()); - } - - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz); - assert(sysTime == sysTime.toLocalTime()); - assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone !is UTC()); - assert(sysTime.toLocalTime().timezone !is stz); - } - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - $(D UTC) as its time zone. - +/ - SysTime toUTC() @safe const pure nothrow - { - return SysTime(_stdTime, UTC()); - } - - @safe unittest - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toUTC()); - assert(sysTime._stdTime == sysTime.toUTC()._stdTime); - assert(sysTime.toUTC().timezone is UTC()); - assert(sysTime.toUTC().timezone !is LocalTime()); - assert(sysTime.toUTC().timezone !is sysTime.timezone); - } - - - /++ - Returns a $(LREF SysTime) with the same std time as this one, but with - given time zone as its time zone. - +/ - SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow - { - if (tz is null) - return SysTime(_stdTime, LocalTime()); - else - return SysTime(_stdTime, tz); - } - - @safe unittest - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); - assert(sysTime == sysTime.toOtherTZ(stz)); - assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); - assert(sysTime.toOtherTZ(stz).timezone is stz); - assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); - assert(sysTime.toOtherTZ(stz).timezone !is UTC()); - } - - - /++ - Converts this $(LREF SysTime) to unix time (i.e. seconds from midnight, - January 1st, 1970 in UTC). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - By default, the return type is time_t (which is normally an alias for - int on 32-bit systems and long on 64-bit systems), but if a different - size is required than either int or long can be passed as a template - argument to get the desired size. - - If the return type is int, and the result can't fit in an int, then the - closest value that can be held in 32 bits will be used (so $(D int.max) - if it goes over and $(D int.min) if it goes under). However, no attempt - is made to deal with integer overflow if the return type is long. - - Params: - T = The return type (int or long). It defaults to time_t, which is - normally 32 bits on a 32-bit system and 64 bits on a 64-bit - system. - - Returns: - A signed integer representing the unix time which is equivalent to - this SysTime. - +/ - T toUnixTime(T = time_t)() @safe const pure nothrow - if (is(T == int) || is(T == long)) - { - return stdTimeToUnixTime!T(_stdTime); - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); - - auto pst = new immutable SimpleTimeZone(hours(-8)); - assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800); - - auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()); - assert(utc.toUnixTime() == 1_198_311_285); - - auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst); - assert(ca.toUnixTime() == 1_198_340_085); - } - - @safe unittest - { - import std.meta : AliasSeq; - assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); - foreach (units; AliasSeq!("hnsecs", "usecs", "msecs")) - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); - } - - - /++ - Converts from unix time (i.e. seconds from midnight, January 1st, 1970 - in UTC) to a $(LREF SysTime). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - Params: - unixTime = Seconds from midnight, January 1st, 1970 in UTC. - tz = The time zone for the SysTime that's returned. - +/ - static SysTime fromUnixTime(long unixTime, immutable TimeZone tz = LocalTime()) @safe pure nothrow - { - return SysTime(unixTimeToStdTime(unixTime), tz); - } - - /// - @safe unittest - { - assert(SysTime.fromUnixTime(0) == - SysTime(DateTime(1970, 1, 1), UTC())); - - auto pst = new immutable SimpleTimeZone(hours(-8)); - assert(SysTime.fromUnixTime(28800) == - SysTime(DateTime(1970, 1, 1), pst)); - - auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC()); - assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); - assert(st1.timezone is UTC()); - assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); - - auto st2 = SysTime.fromUnixTime(1_198_311_285, pst); - assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); - assert(st2.timezone is pst); - assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); - } - - @safe unittest - { - assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC())); - assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC())); - assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC())); - - auto st = SysTime.fromUnixTime(0); - auto dt = cast(DateTime) st; - assert(dt <= DateTime(1970, 2, 1) && dt >= DateTime(1969, 12, 31)); - assert(st.timezone is LocalTime()); - - auto aest = new immutable SimpleTimeZone(hours(10)); - assert(SysTime.fromUnixTime(-36000) == SysTime(DateTime(1970, 1, 1), aest)); - } - - - /++ - Returns a $(D timeval) which represents this $(LREF SysTime). - - Note that like all conversions in std.datetime, this is a truncating - conversion. - - If $(D timeval.tv_sec) is int, and the result can't fit in an int, then - the closest value that can be held in 32 bits will be used for - $(D tv_sec). (so $(D int.max) if it goes over and $(D int.min) if it - goes under). - +/ - timeval toTimeVal() @safe const pure nothrow - { - immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))(); - immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); - immutable tv_usec = cast(typeof(timeval.tv_usec))convert!("hnsecs", "usecs")(fracHNSecs); - return timeval(tv_sec, tv_usec); - } - - @safe unittest - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1)); - assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7)); - - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1)); - - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); - } - - - version(StdDdoc) - { - private struct timespec {} - /++ - Returns a $(D timespec) which represents this $(LREF SysTime). - - $(BLUE This function is Posix-Only.) - +/ - timespec toTimeSpec() @safe const pure nothrow; - } - else - version(Posix) - { - timespec toTimeSpec() @safe const pure nothrow - { - immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))(); - immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); - immutable tv_nsec = cast(typeof(timespec.tv_nsec))convert!("hnsecs", "nsecs")(fracHNSecs); - return timespec(tv_sec, tv_nsec); - } - - @safe unittest - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900)); - assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000)); - assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeSpec() == timespec(0, 7000)); - - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeSpec() == timespec(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(1, 900)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(1, 1000)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeSpec() == timespec(1, 7000)); - - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC() - ).toTimeSpec() == timespec(0, -100)); - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC() - ).toTimeSpec() == timespec(0, -1000)); - - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC() - ).toTimeSpec() == timespec(0, -1_000)); - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC() - ).toTimeSpec() == timespec(0, -999_001_000)); - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC() - ).toTimeSpec() == timespec(0, -1_000_000)); - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 59), UTC() - ).toTimeSpec() == timespec(-1, 0)); - assert(SysTime( - DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC() - ).toTimeSpec() == timespec(-1, -999_983_000)); - } - } - - /++ - Returns a $(D tm) which represents this $(LREF SysTime). - +/ - tm toTM() @safe const nothrow - { - auto dateTime = cast(DateTime) this; - tm timeInfo; - - timeInfo.tm_sec = dateTime.second; - timeInfo.tm_min = dateTime.minute; - timeInfo.tm_hour = dateTime.hour; - timeInfo.tm_mday = dateTime.day; - timeInfo.tm_mon = dateTime.month - 1; - timeInfo.tm_year = dateTime.year - 1900; - timeInfo.tm_wday = dateTime.dayOfWeek; - timeInfo.tm_yday = dateTime.dayOfYear - 1; - timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); - - version(Posix) - { - import std.utf : toUTFz; - timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime); - auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName); - timeInfo.tm_zone = zone.toUTFz!(char*)(); - } - - return timeInfo; - } - - @system unittest - { - import std.conv : to; - version(Posix) - { - scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - } - - { - auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); - - assert(timeInfo.tm_sec == 0); - assert(timeInfo.tm_min == 0); - assert(timeInfo.tm_hour == 0); - assert(timeInfo.tm_mday == 1); - assert(timeInfo.tm_mon == 0); - assert(timeInfo.tm_year == 70); - assert(timeInfo.tm_wday == 4); - assert(timeInfo.tm_yday == 0); - - version(Posix) - assert(timeInfo.tm_isdst == 0); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - assert(timeInfo.tm_gmtoff == -8 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PST"); - } - } - - { - auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM(); - - assert(timeInfo.tm_sec == 7); - assert(timeInfo.tm_min == 15); - assert(timeInfo.tm_hour == 12); - assert(timeInfo.tm_mday == 4); - assert(timeInfo.tm_mon == 6); - assert(timeInfo.tm_year == 110); - assert(timeInfo.tm_wday == 0); - assert(timeInfo.tm_yday == 184); - - version(Posix) - assert(timeInfo.tm_isdst == 1); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - assert(timeInfo.tm_gmtoff == -7 * 60 * 60); - assert(to!string(timeInfo.tm_zone) == "PDT"); - } - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe nothrow - if (units == "years" || - units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.add!units(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - - return this; - } - - @safe unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st1.add!"months"(11); - assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st2.add!"months"(-11); - assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); - - auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st3.add!"years"(1); - assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st4.add!"years"(1, No.allowDayOverflow); - assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - //Test add!"years"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(Date(-1999, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3))); - } - - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(Date(1, 3, 1))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(Date(-1, 3, 1))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5).add!"years"(7); - assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"years"(4))); - //static assert(!__traits(compiles, ist.add!"years"(4))); - } - - //Test add!"years"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(7, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(-9, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); - } - - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); - sysTime.add!"years"(-7, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); - sysTime.add!"years"(9, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); - } - - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3))); - } - - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1, 2, 28))); - } - - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1, 2, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"years"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"years"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); - } - - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555))); - } - - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); - sysTime.add!"years"(-5, No.allowDayOverflow).add!"years"(7, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555))); - } - } - - //Test add!"months"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12); - assert(sysTime == SysTime(Date(2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 2))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(2001, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12); - assert(sysTime == SysTime(Date(-2001, 3, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1997, 10, 1))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1995, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-1996, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-2000, 3, 2))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 2))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(Date(-1999, 3, 3))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(Date(-2000, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14); - assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14); - assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49); - assert(sysTime == SysTime(Date(0, 3, 2))); - sysTime.add!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(Date(-3, 3, 3))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85); - assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85).add!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"months"(4))); - //static assert(!__traits(compiles, ist.add!"months"(4))); - } - - //Test add!"months"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 9, 30))); - sysTime.add!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 12, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2001, 2, 28))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2000, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 9, 30))); - sysTime.add!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1995, 2, 28))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2002, 12, 29))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.add!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.add!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.add!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.add!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(0, 2, 29))); - sysTime.add!"months"(49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-3, 2, 28))); - sysTime.add!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 3, 28))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.add!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.add!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9))); - sysTime.add!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9))); - sysTime.add!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.add!"months"(85, No.allowDayOverflow).add!"months"(-83, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of years or months to this $(LREF SysTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF SysTime) 12 months - gets the exact same $(LREF SysTime). However, the days can still be affected - due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF SysTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe nothrow - if (units == "years") - { - return add!"years"(value, allowOverflow); - } - - /// - @safe unittest - { - import std.typecons : No; - - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st1.roll!"months"(1); - assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); - - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st2.roll!"months"(-1); - assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); - - auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st3.roll!"months"(1); - assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); - - auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st4.roll!"months"(1, No.allowDayOverflow); - assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); - - auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st5.roll!"years"(1); - assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - - auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st6.roll!"years"(1, No.allowDayOverflow); - assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.roll!"years"(4); - static assert(!__traits(compiles, cst.roll!"years"(4))); - //static assert(!__traits(compiles, ist.roll!"years"(4))); - } - - - //Shares documentation with "years" overload. - ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = Yes.allowDayOverflow) @safe nothrow - if (units == "months") - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto date = Date(cast(int) days); - date.roll!"months"(value, allowOverflow); - days = date.dayOfGregorianCal - 1; - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - //Test roll!"months"() with Yes.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1998, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1998, 1, 3))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(1999, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(1999, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1999, 10, 1))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1998, 10, 1))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1998, 9, 1))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-1997, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-1997, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2002, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2002, 1, 3))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(Date(-2001, 3, 3))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(Date(-2001, 1, 3))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), hnsecs(5007)); - sysTime.roll!"months"(3); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), hnsecs(5007))); - sysTime.roll!"months"(-4); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), hnsecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14); - assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14); - assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(49); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(Date(-4, 4, 2))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85); - assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85); - assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85).roll!"months"(-83); - assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"months"(4))); - //static assert(!__traits(compiles, ist.roll!"months"(4))); - } - - //Test roll!"months"() with No.allowDayOverflow - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 9, 30))); - sysTime.roll!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1998, 12, 28))); - } - - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1999, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 8, 6))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 4, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1999, 9, 30))); - } - - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1998, 9, 30))); - sysTime.roll!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1998, 8, 30))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 12, 31))); - } - - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1997, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2002, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2002, 12, 28))); - } - - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2001, 2, 28))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-2001, 12, 28))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); - sysTime.roll!"months"(3, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); - sysTime.roll!"months"(-4, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); - } - - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); - sysTime.roll!"months"(14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), hnsecs(422202))); - sysTime.roll!"months"(-14, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-1, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(49, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(Date(-4, 3, 29))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); - sysTime.roll!"months"(-1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); - sysTime.roll!"months"(1, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); - } - - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), msecs(9))); - sysTime.roll!"months"(-85, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); - } - - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); - sysTime.roll!"months"(85, No.allowDayOverflow).roll!"months"(-83, No.allowDayOverflow); - assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); - } - } - - - /++ - Adds the given number of units to this $(LREF SysTime). A negative number - will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF SysTime) one - year's worth of days gets the exact same $(LREF SysTime). - - Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), - $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), and - $(D "hnsecs"). - - Note that when rolling msecs, usecs or hnsecs, they all add up to a - second. So, for example, rolling 1000 msecs is exactly the same as - rolling 100,000 usecs. - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF SysTime). - +/ - ref SysTime roll(string units)(long value) @safe nothrow - if (units == "days") - { - auto hnsecs = adjTime; - auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --gdays; - } - - auto date = Date(cast(int) gdays); - date.roll!"days"(value); - gdays = date.dayOfGregorianCal - 1; - - if (gdays < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++gdays; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - /// - @safe unittest - { - auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); - st1.roll!"days"(1); - assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); - st1.roll!"days"(365); - assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); - st1.roll!"days"(-32); - assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); - - auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); - st2.roll!"hours"(1); - assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); - - auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); - st3.roll!"hours"(-1); - assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); - - auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st4.roll!"minutes"(1); - assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); - - auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st5.roll!"minutes"(-1); - assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); - - auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st6.roll!"seconds"(1); - assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); - - auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st7.roll!"seconds"(-1); - assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); - - auto dt = DateTime(2010, 1, 1, 0, 0, 0); - auto st8 = SysTime(dt); - st8.roll!"msecs"(1); - assert(st8 == SysTime(dt, msecs(1))); - - auto st9 = SysTime(dt); - st9.roll!"msecs"(-1); - assert(st9 == SysTime(dt, msecs(999))); - - auto st10 = SysTime(dt); - st10.roll!"hnsecs"(1); - assert(st10 == SysTime(dt, hnsecs(1))); - - auto st11 = SysTime(dt); - st11.roll!"hnsecs"(-1); - assert(st11 == SysTime(dt, hnsecs(9_999_999))); - } - - @safe unittest - { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1999, 2, 7))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1999, 2, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(1999, 2, 8))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1999, 2, 10))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(1999, 2, 6))); - } - - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), usecs(234578))); - } - - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(-2000, 2, 28)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-2000, 2, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(-1999, 6, 30)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 6, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 6, 30))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 7, 1))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 1, 1)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(Date(-1999, 1, 31))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(Date(-1999, 1, 1))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(Date(-1999, 7, 15))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(Date(-1999, 7, 4))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(Date(-1999, 7, 3))); - sysTime.roll!"days"(-3); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - } - - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(-1999, 7, 30))); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - sysTime.roll!"days"(366); - assert(sysTime == SysTime(Date(-1999, 7, 31))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(-1999, 7, 17))); - sysTime.roll!"days"(-1096); - assert(sysTime == SysTime(Date(-1999, 7, 6))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578))); - } - - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), usecs(234578)); - sysTime.roll!"days"(9); - assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-11); - assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(30); - assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), usecs(234578))); - sysTime.roll!"days"(-3); - } - - //Test Both - { - auto sysTime = SysTime(Date(1, 7, 6)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(Date(1, 7, 13))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(Date(1, 7, 6))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(Date(1, 7, 19))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(Date(1, 7, 5))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"days"(1); - assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"days"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365); - assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(365); - assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(-731); - assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), msecs(22))); - sysTime.roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), msecs(22))); - } - - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); - sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); - assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), msecs(22))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"days"(4))); - //static assert(!__traits(compiles, ist.roll!"days"(4))); - } - - - //Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow - if (units == "hours" || - units == "minutes" || - units == "seconds") - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - dateTime.roll!units(value); - --days; - - hnsecs += convert!("hours", "hnsecs")(dateTime.hour); - hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); - hnsecs += convert!("seconds", "hnsecs")(dateTime.second); - - if (days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - catch (Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - //Test roll!"hours"(). - @safe unittest - { - static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"hours"(hours); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = msecs(45); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); - testST(beforeAD, -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); - testST(beforeAD, -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); - testST(beforeAD, -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); - testST(beforeAD, -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); - testST(beforeAD, -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(beforeAD, -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(beforeAD, -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(beforeAD, -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); - testST(beforeAD, -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - testST(beforeAD, -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); - testST(beforeAD, -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); - testST(beforeAD, -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); - testST(beforeAD, -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); - testST(beforeAD, -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); - testST(beforeAD, -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); - testST(beforeAD, -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); - testST(beforeAD, -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); - testST(beforeAD, -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); - testST(beforeAD, -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); - testST(beforeBC, -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); - testST(beforeBC, -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); - testST(beforeBC, -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); - testST(beforeBC, -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); - testST(beforeBC, -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(beforeBC, -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(beforeBC, -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(beforeBC, -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); - testST(beforeBC, -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - testST(beforeBC, -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); - testST(beforeBC, -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); - testST(beforeBC, -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); - testST(beforeBC, -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); - testST(beforeBC, -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); - testST(beforeBC, -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); - testST(beforeBC, -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); - testST(beforeBC, -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); - testST(beforeBC, -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); - testST(beforeBC, -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), d)); - - testST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), d)); - testST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), d)); - - //Test Both - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"hours"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"hours"(1).roll!"hours"(-67); - assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hours"(4))); - //static assert(!__traits(compiles, ist.roll!"hours"(4))); - } - - //Test roll!"minutes"(). - @safe unittest - { - static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"minutes"(minutes); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = usecs(7203); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), d)); - - testST(beforeAD, 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); - testST(beforeAD, -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), d)); - - testST(beforeAD, -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(beforeAD, -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(beforeAD, -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - testST(beforeAD, -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); - testST(beforeAD, -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); - testST(beforeAD, -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), d)); - - testST(beforeBC, 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); - testST(beforeBC, -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), d)); - - testST(beforeBC, -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(beforeBC, -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(beforeBC, -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - testST(beforeBC, -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); - testST(beforeBC, -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); - testST(beforeBC, -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), d)); - - //Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 52, 33), d), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"minutes"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"minutes"(1).roll!"minutes"(-79); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"minutes"(4))); - //static assert(!__traits(compiles, ist.roll!"minutes"(4))); - } - - //Test roll!"seconds"(). - @safe unittest - { - static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"seconds"(seconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - immutable d = msecs(274); - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), d)); - testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), d)); - testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), d)); - testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), d)); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), d)); - testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), d)); - testST(beforeAD, 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), d)); - testST(beforeAD, 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - - testST(beforeAD, 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(beforeAD, 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), d)); - testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), d)); - testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), d)); - testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), d)); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), d)); - testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), d)); - testST(beforeAD, -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(beforeAD, -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - testST(beforeAD, -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), d)); - testST(beforeAD, -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); - testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); - testST(beforeAD, -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), d)); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), d)); - testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), d)); - testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), d)); - testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), d)); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), d)); - testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), d)); - testST(beforeBC, 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), d)); - testST(beforeBC, 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - - testST(beforeBC, 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(beforeBC, 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), d)); - testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), d)); - testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), d)); - testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), d)); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), d)); - testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), d)); - testST(beforeBC, -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(beforeBC, -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - testST(beforeBC, -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), d)); - testST(beforeBC, -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); - testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); - testST(beforeBC, -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d)); - testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), d)); - - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d)); - testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), d)); - - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d)); - testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), d)); - - //Test Both - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), d)); - testST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), d)); - testST(SysTime(DateTime(1, 1, 1, 13, 30, 50), d), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999)); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), hnsecs(9_999_999))); - sysTime.roll!"seconds"(-1); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - sysTime.roll!"seconds"(1).roll!"seconds"(-102); - assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), hnsecs(9_999_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"seconds"(4))); - //static assert(!__traits(compiles, ist.roll!"seconds"(4))); - } - - - //Shares documentation with "days" version. - ref SysTime roll(string units)(long value) @safe nothrow - if (units == "msecs" || - units == "usecs" || - units == "hnsecs") - { - auto hnsecs = adjTime; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - immutable negative = hnsecs < 0; - - if (negative) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - hnsecs += convert!(units, "hnsecs")(value); - hnsecs %= convert!("seconds", "hnsecs")(1); - - if (hnsecs < 0) - hnsecs += convert!("seconds", "hnsecs")(1); - hnsecs += convert!("seconds", "hnsecs")(seconds); - - if (negative) - hnsecs -= convert!("hours", "hnsecs")(24); - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; - return this; - } - - - //Test roll!"msecs"(). - @safe unittest - { - static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"msecs"(milliseconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(1))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(445))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_989_999))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(5_549_999))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"msecs"(1202).roll!"msecs"(-703); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(4_989_999))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.addMSecs(4))); - //static assert(!__traits(compiles, ist.addMSecs(4))); - } - - //Test roll!"usecs"(). - @safe unittest - { - static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"usecs"(microseconds); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(666_667))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_989))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9))); - testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19))); - testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); - testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); - testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(25_549))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(3_333_329))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(200_269))); - } - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); - assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_499_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"usecs"(4))); - //static assert(!__traits(compiles, ist.roll!"usecs"(4))); - } - - //Test roll!"hnsecs"(). - @safe unittest - { - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - orig.roll!"hnsecs"(hnsecs); - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - auto dtAD = DateTime(1999, 7, 6, 12, 30, 33); - auto beforeAD = SysTime(dtAD, hnsecs(274)); - testST(beforeAD, 0, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 1, SysTime(dtAD, hnsecs(275))); - testST(beforeAD, 2, SysTime(dtAD, hnsecs(276))); - testST(beforeAD, 10, SysTime(dtAD, hnsecs(284))); - testST(beforeAD, 100, SysTime(dtAD, hnsecs(374))); - testST(beforeAD, 725, SysTime(dtAD, hnsecs(999))); - testST(beforeAD, 726, SysTime(dtAD, hnsecs(1000))); - testST(beforeAD, 1000, SysTime(dtAD, hnsecs(1274))); - testST(beforeAD, 1001, SysTime(dtAD, hnsecs(1275))); - testST(beforeAD, 2000, SysTime(dtAD, hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(dtAD, hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(dtAD, hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(dtAD, hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(dtAD, hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(dtAD, hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(dtAD, hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - testST(beforeAD, -1, SysTime(dtAD, hnsecs(273))); - testST(beforeAD, -2, SysTime(dtAD, hnsecs(272))); - testST(beforeAD, -10, SysTime(dtAD, hnsecs(264))); - testST(beforeAD, -100, SysTime(dtAD, hnsecs(174))); - testST(beforeAD, -274, SysTime(dtAD)); - testST(beforeAD, -275, SysTime(dtAD, hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(dtAD, hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(dtAD, hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(dtAD, hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(dtAD, hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(dtAD, hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(dtAD, hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(dtAD, hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(dtAD, hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(dtAD, hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(dtAD, hnsecs(274))); - - //Test B.C. - auto dtBC = DateTime(-1999, 7, 6, 12, 30, 33); - auto beforeBC = SysTime(dtBC, hnsecs(274)); - testST(beforeBC, 0, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 1, SysTime(dtBC, hnsecs(275))); - testST(beforeBC, 2, SysTime(dtBC, hnsecs(276))); - testST(beforeBC, 10, SysTime(dtBC, hnsecs(284))); - testST(beforeBC, 100, SysTime(dtBC, hnsecs(374))); - testST(beforeBC, 725, SysTime(dtBC, hnsecs(999))); - testST(beforeBC, 726, SysTime(dtBC, hnsecs(1000))); - testST(beforeBC, 1000, SysTime(dtBC, hnsecs(1274))); - testST(beforeBC, 1001, SysTime(dtBC, hnsecs(1275))); - testST(beforeBC, 2000, SysTime(dtBC, hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(dtBC, hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(dtBC, hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(dtBC, hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(dtBC, hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(dtBC, hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(dtBC, hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - testST(beforeBC, -1, SysTime(dtBC, hnsecs(273))); - testST(beforeBC, -2, SysTime(dtBC, hnsecs(272))); - testST(beforeBC, -10, SysTime(dtBC, hnsecs(264))); - testST(beforeBC, -100, SysTime(dtBC, hnsecs(174))); - testST(beforeBC, -274, SysTime(dtBC)); - testST(beforeBC, -275, SysTime(dtBC, hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(dtBC, hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(dtBC, hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(dtBC, hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(dtBC, hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(dtBC, hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(dtBC, hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(dtBC, hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(dtBC, hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(dtBC, hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(dtBC, hnsecs(274))); - - //Test Both - auto dtBoth1 = DateTime(1, 1, 1, 0, 0, 0); - auto beforeBoth1 = SysTime(dtBoth1); - testST(beforeBoth1, 1, SysTime(dtBoth1, hnsecs(1))); - testST(beforeBoth1, 0, SysTime(dtBoth1)); - testST(beforeBoth1, -1, SysTime(dtBoth1, hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(dtBoth1, hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(dtBoth1, hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(dtBoth1, hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(dtBoth1, hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(dtBoth1, hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(dtBoth1, hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(dtBoth1, hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_000_000, SysTime(dtBoth1)); - testST(beforeBoth1, -20_888_888, SysTime(dtBoth1, hnsecs(9_111_112))); - - auto dtBoth2 = DateTime(0, 12, 31, 23, 59, 59); - auto beforeBoth2 = SysTime(dtBoth2, hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(dtBoth2, hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(dtBoth2)); - testST(beforeBoth2, 2, SysTime(dtBoth2, hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(dtBoth2, hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(dtBoth2, hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(dtBoth2, hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(dtBoth2, hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(dtBoth2, hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(dtBoth2, hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(dtBoth2, hnsecs(888_887))); - - { - auto st = SysTime(dtBoth2, hnsecs(9_999_999)); - st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); - assert(st == SysTime(dtBoth2, hnsecs(8_221_929))); - } - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); - //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF SysTime). - +/ - SysTime opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - SysTime retval = SysTime(this._stdTime, this._timezone); - immutable hnsecs = duration.total!"hnsecs"; - mixin("retval._stdTime " ~ op ~ "= hnsecs;"); - return retval; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == - SysTime(DateTime(2016, 1, 1, 0, 0, 0))); - - assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + hours(1) == - SysTime(DateTime(2016, 1, 1, 0, 59, 59))); - - assert(SysTime(DateTime(2016, 1, 1, 0, 0, 0)) - seconds(1) == - SysTime(DateTime(2015, 12, 31, 23, 59, 59))); - - assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) == - SysTime(DateTime(2015, 12, 31, 23, 59, 59))); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - - assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); - assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); - assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); - assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); - assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); - assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); - assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); - assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); - - static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - auto result = orig + dur!"hnsecs"(hnsecs); - if (result != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", result, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); - //assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); - assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); - //assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - SysTime opBinary(string op)(TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - SysTime retval = SysTime(this._stdTime, this._timezone); - immutable hnsecs = td.hnsecs; - mixin("retval._stdTime " ~ op ~ "= hnsecs;"); - return retval; - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - - assert(st + TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st + TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - - assert(st - TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - assert(st - TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF SysTime), as well as assigning the result to this - $(LREF SysTime). - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) - $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF SysTime). - +/ - ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable hnsecs = duration.total!"hnsecs"; - mixin("_stdTime " ~ op ~ "= hnsecs;"); - return this; - } - - @safe unittest - { - auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - assert(before - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - assert(before - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - assert(before - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - assert(before - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - assert(before - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - assert(before - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - assert(before - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - assert(before - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - assert(before - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - assert(before - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - assert(before - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); - assert(before - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); - assert(before - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); - assert(before - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); - assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); - assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); - - static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - auto r = orig += dur!"hnsecs"(hnsecs); - if (orig != expected) - throw new AssertError(format("Failed 1. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - if (r != expected) - throw new AssertError(format("Failed 2. actual [%s] != expected [%s]", r, expected), __FILE__, line); - } - - //Test A.D. - auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test B.C. - auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); - testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); - testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); - testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); - testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); - testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); - testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); - testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); - testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); - testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); - testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); - testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); - testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); - testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); - testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); - testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); - testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); - testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); - testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); - testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); - testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); - - testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); - testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); - testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); - testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); - testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); - testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); - testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); - testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); - testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); - testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); - testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); - testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); - testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); - testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); - testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); - testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); - testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); - - //Test Both - auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); - testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); - testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); - testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); - testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); - testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); - testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); - testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); - testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); - - auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); - testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); - testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); - testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); - testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); - testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); - testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); - testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); - testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); - - { - auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); - (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); - assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), hnsecs(51))); - } - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst += duration)); - //static assert(!__traits(compiles, ist += duration)); - static assert(!__traits(compiles, cst -= duration)); - //static assert(!__traits(compiles, ist -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref SysTime opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable hnsecs = td.hnsecs; - mixin("_stdTime " ~ op ~ "= hnsecs;"); - return this; - } - - deprecated @safe unittest - { - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - st += TickDuration.from!"usecs"(7); - assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - } - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - st += TickDuration.from!"usecs"(-7); - assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - } - - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - st -= TickDuration.from!"usecs"(-7); - assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); - } - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); - st -= TickDuration.from!"usecs"(7); - assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); - } - } - } - - - /++ - Gives the difference between two $(LREF SysTime)s. - - The legal types of arithmetic for $(LREF SysTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow - if (op == "-") - { - return dur!"hnsecs"(_stdTime - rhs._stdTime); - } - - @safe unittest - { - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == - dur!"seconds"(86_400)); - assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == - dur!"seconds"(3600)); - assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(60)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == - dur!"seconds"(-60)); - - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == - dur!"seconds"(1)); - assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == - dur!"seconds"(-1)); - - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - assert(SysTime(dt, msecs(532)) - SysTime(dt) == msecs(532)); - assert(SysTime(dt) - SysTime(dt, msecs(532)) == msecs(-532)); - - assert(SysTime(dt, usecs(333_347)) - SysTime(dt) == usecs(333_347)); - assert(SysTime(dt) - SysTime(dt, usecs(333_347)) == usecs(-333_347)); - - assert(SysTime(dt, hnsecs(1_234_567)) - SysTime(dt) == hnsecs(1_234_567)); - assert(SysTime(dt) - SysTime(dt, hnsecs(1_234_567)) == hnsecs(-1_234_567)); - } - - assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); - assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) == - dur!"hnsecs"(1)); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == - dur!"hnsecs"(-1)); - - version(Posix) - immutable tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); - else version(Windows) - immutable tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); - - { - auto dt = DateTime(2011, 1, 13, 8, 17, 2); - auto d = msecs(296); - assert(SysTime(dt, d, tz) - SysTime(dt, d, tz) == Duration.zero); - assert(SysTime(dt, d, tz) - SysTime(dt, d, UTC()) == hours(8)); - assert(SysTime(dt, d, UTC()) - SysTime(dt, d, tz) == hours(-8)); - } - - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st - st == Duration.zero); - assert(cst - st == Duration.zero); - //assert(ist - st == Duration.zero); - - assert(st - cst == Duration.zero); - assert(cst - cst == Duration.zero); - //assert(ist - cst == Duration.zero); - - //assert(st - ist == Duration.zero); - //assert(cst - ist == Duration.zero); - //assert(ist - ist == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF SysTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF SysTime) to subtract from this one. - +/ - int diffMonths(in SysTime rhs) @safe const nothrow - { - return (cast(Date) this).diffMonths(cast(Date) rhs); - } - - /// - @safe unittest - { - assert(SysTime(Date(1999, 2, 1)).diffMonths( - SysTime(Date(1999, 1, 31))) == 1); - - assert(SysTime(Date(1999, 1, 31)).diffMonths( - SysTime(Date(1999, 2, 1))) == -1); - - assert(SysTime(Date(1999, 3, 1)).diffMonths( - SysTime(Date(1999, 1, 1))) == 2); - - assert(SysTime(Date(1999, 1, 1)).diffMonths( - SysTime(Date(1999, 3, 31))) == -2); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.diffMonths(st) == 0); - assert(cst.diffMonths(st) == 0); - //assert(ist.diffMonths(st) == 0); - - assert(st.diffMonths(cst) == 0); - assert(cst.diffMonths(cst) == 0); - //assert(ist.diffMonths(cst) == 0); - - //assert(st.diffMonths(ist) == 0); - //assert(cst.diffMonths(ist) == 0); - //assert(ist.diffMonths(ist) == 0); - } - - - /++ - Whether this $(LREF SysTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const nothrow - { - return (cast(Date) this).isLeapYear; - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(!st.isLeapYear); - assert(!cst.isLeapYear); - //assert(!ist.isLeapYear); - } - - - /++ - Day of the week this $(LREF SysTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const nothrow - { - return getDayOfWeek(dayOfGregorianCal); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.dayOfWeek == DayOfWeek.tue); - assert(cst.dayOfWeek == DayOfWeek.tue); - //assert(ist.dayOfWeek == DayOfWeek.tue); - } - - - /++ - Day of the year this $(LREF SysTime) is on. - +/ - @property ushort dayOfYear() @safe const nothrow - { - return (cast(Date) this).dayOfYear; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); - assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); - assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.dayOfYear == 187); - assert(cst.dayOfYear == 187); - //assert(ist.dayOfYear == 187); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF SysTime) is on. - +/ - @property void dayOfYear(int day) @safe - { - immutable hnsecs = adjTime; - immutable days = convert!("hnsecs", "days")(hnsecs); - immutable theRest = hnsecs - convert!("days", "hnsecs")(days); - - auto date = Date(cast(int) days); - date.dayOfYear = day; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); - - adjTime = newDaysHNSecs + theRest; - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - st.dayOfYear = 12; - assert(st.dayOfYear == 12); - static assert(!__traits(compiles, cst.dayOfYear = 12)); - //static assert(!__traits(compiles, ist.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - +/ - @property int dayOfGregorianCal() @safe const nothrow - { - immutable adjustedTime = adjTime; - - //We have to add one because 0 would be midnight, January 1st, 1 A.D., - //which would be the 1st day of the Gregorian Calendar, not the 0th. So, - //simply casting to days is one day off. - if (adjustedTime > 0) - return cast(int) getUnitsFromHNSecs!"days"(adjustedTime) + 1; - - long hnsecs = adjustedTime; - immutable days = cast(int) splitUnitsFromHNSecs!"days"(hnsecs); - - return hnsecs == 0 ? days + 1 : days; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); - assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); - - assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); - assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 1); - - assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212)).dayOfGregorianCal == 2); - assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 32); - assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 366); - assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 731); - assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1096); - assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1462); - assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 17_898); - assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 35_065); - assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_160); - assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_525); - assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 37_986); - assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 72_684); - assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 73_049); - assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_208); - assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_573); - assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 145_732); - assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 146_098); - assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_257); - assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_622); - assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 364_878); - assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 365_243); - assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_023); - assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_389); - assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_596); - assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_961); - assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), msecs(212)).dayOfGregorianCal == 710_347); - assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 729_755); - assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_486); - - assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_773); - assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_803); - assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_804); - assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_831); - assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_832); - assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_862); - assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_863); - assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_892); - assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_893); - assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_923); - assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_924); - assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_953); - assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_954); - assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_984); - assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_985); - assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_015); - assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_016); - assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_045); - assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_046); - assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_076); - assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_077); - assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_106); - assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_107); - assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_137); - - assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == 734_534); - assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == 734_561); - assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == 734_562); - assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == 734_563); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).dayOfGregorianCal == 0); - - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1); - assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -30); - assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -31); - - assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -366); - assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -367); - assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730); - assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731); - assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1095); - assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1096); - assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1460); - assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1461); - assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1826); - assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1827); - assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -2191); - assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -3652); - - assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_262); - assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_627); - assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -35_794); - assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_160); - assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_524); - assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_889); - assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -37_254); - assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -38_715); - assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_413); - assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_778); - assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -109_937); - assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -110_302); - assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_097); - assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_462); - assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_827); - assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_621); - assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_986); - assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -183_351); - assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_607); - assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_972); - assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_387); - assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_388); - assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_753); - assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -585_118); - assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_325); - assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_690); - assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_484); - assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_485); - assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_850); - assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731_215); - - assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_502); - assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_472); - assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_471); - assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_444); - assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_443); - assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_413); - assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_412); - assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_383); - assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_382); - assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_352); - assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_351); - assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_322); - assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_321); - assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_291); - assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_290); - assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_260); - assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_259); - assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_230); - assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_229); - assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_199); - assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_198); - assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_169); - assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_168); - assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_138); - - assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == -735_202); - assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == -735_175); - assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == -735_174); - assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == -735_173); - - // Start of Hebrew Calendar - assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.dayOfGregorianCal == 729_941); - //assert(ist.dayOfGregorianCal == 729_941); - } - - - //Test that the logic for the day of the Gregorian Calendar is consistent - //between Date and SysTime. - @safe unittest - { - void test(Date date, SysTime st, size_t line = __LINE__) - { - import std.format : format; - - if (date.dayOfGregorianCal != st.dayOfGregorianCal) - { - throw new AssertError(format("Date [%s] SysTime [%s]", date.dayOfGregorianCal, st.dayOfGregorianCal), - __FILE__, line); - } - } - - //Test A.D. - test(Date(1, 1, 1), SysTime(DateTime(1, 1, 1, 0, 0, 0))); - test(Date(1, 1, 2), SysTime(DateTime(1, 1, 2, 0, 0, 0), hnsecs(500))); - test(Date(1, 2, 1), SysTime(DateTime(1, 2, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(2, 1, 1), SysTime(DateTime(2, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(3, 1, 1), SysTime(DateTime(3, 1, 1, 12, 13, 14))); - test(Date(4, 1, 1), SysTime(DateTime(4, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(5, 1, 1), SysTime(DateTime(5, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(50, 1, 1), SysTime(DateTime(50, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(97, 1, 1), SysTime(DateTime(97, 1, 1, 23, 59, 59))); - test(Date(100, 1, 1), SysTime(DateTime(100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(101, 1, 1), SysTime(DateTime(101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(105, 1, 1), SysTime(DateTime(105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(200, 1, 1), SysTime(DateTime(200, 1, 1, 0, 0, 0))); - test(Date(201, 1, 1), SysTime(DateTime(201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(300, 1, 1), SysTime(DateTime(300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(301, 1, 1), SysTime(DateTime(301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(400, 1, 1), SysTime(DateTime(400, 1, 1, 12, 13, 14))); - test(Date(401, 1, 1), SysTime(DateTime(401, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(500, 1, 1), SysTime(DateTime(500, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(501, 1, 1), SysTime(DateTime(501, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(1000, 1, 1), SysTime(DateTime(1000, 1, 1, 23, 59, 59))); - test(Date(1001, 1, 1), SysTime(DateTime(1001, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(1600, 1, 1), SysTime(DateTime(1600, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(1601, 1, 1), SysTime(DateTime(1601, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(1900, 1, 1), SysTime(DateTime(1900, 1, 1, 0, 0, 0))); - test(Date(1901, 1, 1), SysTime(DateTime(1901, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(1945, 11, 12), SysTime(DateTime(1945, 11, 12, 0, 0, 0), hnsecs(50_000))); - test(Date(1999, 1, 1), SysTime(DateTime(1999, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(1999, 7, 6), SysTime(DateTime(1999, 7, 6, 12, 13, 14))); - test(Date(2000, 1, 1), SysTime(DateTime(2000, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(2001, 1, 1), SysTime(DateTime(2001, 1, 1, 12, 13, 14), hnsecs(50_000))); - - test(Date(2010, 1, 1), SysTime(DateTime(2010, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 1, 31), SysTime(DateTime(2010, 1, 31, 23, 0, 0))); - test(Date(2010, 2, 1), SysTime(DateTime(2010, 2, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 2, 28), SysTime(DateTime(2010, 2, 28, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 3, 1), SysTime(DateTime(2010, 3, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 3, 31), SysTime(DateTime(2010, 3, 31, 0, 0, 0))); - test(Date(2010, 4, 1), SysTime(DateTime(2010, 4, 1, 0, 0, 0), hnsecs(500))); - test(Date(2010, 4, 30), SysTime(DateTime(2010, 4, 30, 0, 0, 0), hnsecs(50_000))); - test(Date(2010, 5, 1), SysTime(DateTime(2010, 5, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 5, 31), SysTime(DateTime(2010, 5, 31, 12, 13, 14))); - test(Date(2010, 6, 1), SysTime(DateTime(2010, 6, 1, 12, 13, 14), hnsecs(500))); - test(Date(2010, 6, 30), SysTime(DateTime(2010, 6, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(2010, 7, 1), SysTime(DateTime(2010, 7, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(2010, 7, 31), SysTime(DateTime(2010, 7, 31, 23, 59, 59))); - test(Date(2010, 8, 1), SysTime(DateTime(2010, 8, 1, 23, 59, 59), hnsecs(500))); - test(Date(2010, 8, 31), SysTime(DateTime(2010, 8, 31, 23, 59, 59), hnsecs(50_000))); - test(Date(2010, 9, 1), SysTime(DateTime(2010, 9, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(2010, 9, 30), SysTime(DateTime(2010, 9, 30, 12, 0, 0))); - test(Date(2010, 10, 1), SysTime(DateTime(2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(2010, 10, 31), SysTime(DateTime(2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(2010, 11, 1), SysTime(DateTime(2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(2010, 11, 30), SysTime(DateTime(2010, 11, 30, 0, 59, 0))); - test(Date(2010, 12, 1), SysTime(DateTime(2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(2010, 12, 31), SysTime(DateTime(2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(2012, 2, 1), SysTime(DateTime(2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(2012, 2, 28), SysTime(DateTime(2012, 2, 28, 23, 59, 0))); - test(Date(2012, 2, 29), SysTime(DateTime(2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(2012, 3, 1), SysTime(DateTime(2012, 3, 1, 7, 7, 7), hnsecs(7))); - - //Test B.C. - test(Date(0, 12, 31), SysTime(DateTime(0, 12, 31, 0, 0, 0))); - test(Date(0, 12, 30), SysTime(DateTime(0, 12, 30, 0, 0, 0), hnsecs(500))); - test(Date(0, 12, 1), SysTime(DateTime(0, 12, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(0, 11, 30), SysTime(DateTime(0, 11, 30, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-1, 12, 31), SysTime(DateTime(-1, 12, 31, 12, 13, 14))); - test(Date(-1, 12, 30), SysTime(DateTime(-1, 12, 30, 12, 13, 14), hnsecs(500))); - test(Date(-1, 1, 1), SysTime(DateTime(-1, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-2, 12, 31), SysTime(DateTime(-2, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2, 1, 1), SysTime(DateTime(-2, 1, 1, 23, 59, 59))); - test(Date(-3, 12, 31), SysTime(DateTime(-3, 12, 31, 23, 59, 59), hnsecs(500))); - test(Date(-3, 1, 1), SysTime(DateTime(-3, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-4, 12, 31), SysTime(DateTime(-4, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-4, 1, 1), SysTime(DateTime(-4, 1, 1, 0, 0, 0))); - test(Date(-5, 12, 31), SysTime(DateTime(-5, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-5, 1, 1), SysTime(DateTime(-5, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-9, 1, 1), SysTime(DateTime(-9, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - - test(Date(-49, 1, 1), SysTime(DateTime(-49, 1, 1, 12, 13, 14))); - test(Date(-50, 1, 1), SysTime(DateTime(-50, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-97, 1, 1), SysTime(DateTime(-97, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-99, 12, 31), SysTime(DateTime(-99, 12, 31, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-99, 1, 1), SysTime(DateTime(-99, 1, 1, 23, 59, 59))); - test(Date(-100, 1, 1), SysTime(DateTime(-100, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-101, 1, 1), SysTime(DateTime(-101, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-105, 1, 1), SysTime(DateTime(-105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-200, 1, 1), SysTime(DateTime(-200, 1, 1, 0, 0, 0))); - test(Date(-201, 1, 1), SysTime(DateTime(-201, 1, 1, 0, 0, 0), hnsecs(500))); - test(Date(-300, 1, 1), SysTime(DateTime(-300, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-301, 1, 1), SysTime(DateTime(-301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-400, 12, 31), SysTime(DateTime(-400, 12, 31, 12, 13, 14))); - test(Date(-400, 1, 1), SysTime(DateTime(-400, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-401, 1, 1), SysTime(DateTime(-401, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-499, 1, 1), SysTime(DateTime(-499, 1, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-500, 1, 1), SysTime(DateTime(-500, 1, 1, 23, 59, 59))); - test(Date(-501, 1, 1), SysTime(DateTime(-501, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-1000, 1, 1), SysTime(DateTime(-1000, 1, 1, 23, 59, 59), hnsecs(50_000))); - test(Date(-1001, 1, 1), SysTime(DateTime(-1001, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-1599, 1, 1), SysTime(DateTime(-1599, 1, 1, 0, 0, 0))); - test(Date(-1600, 12, 31), SysTime(DateTime(-1600, 12, 31, 0, 0, 0), hnsecs(500))); - test(Date(-1600, 1, 1), SysTime(DateTime(-1600, 1, 1, 0, 0, 0), hnsecs(50_000))); - test(Date(-1601, 1, 1), SysTime(DateTime(-1601, 1, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-1900, 1, 1), SysTime(DateTime(-1900, 1, 1, 12, 13, 14))); - test(Date(-1901, 1, 1), SysTime(DateTime(-1901, 1, 1, 12, 13, 14), hnsecs(500))); - test(Date(-1999, 1, 1), SysTime(DateTime(-1999, 1, 1, 12, 13, 14), hnsecs(50_000))); - test(Date(-1999, 7, 6), SysTime(DateTime(-1999, 7, 6, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2000, 12, 31), SysTime(DateTime(-2000, 12, 31, 23, 59, 59))); - test(Date(-2000, 1, 1), SysTime(DateTime(-2000, 1, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2001, 1, 1), SysTime(DateTime(-2001, 1, 1, 23, 59, 59), hnsecs(50_000))); - - test(Date(-2010, 1, 1), SysTime(DateTime(-2010, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 1, 31), SysTime(DateTime(-2010, 1, 31, 0, 0, 0))); - test(Date(-2010, 2, 1), SysTime(DateTime(-2010, 2, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 2, 28), SysTime(DateTime(-2010, 2, 28, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 3, 1), SysTime(DateTime(-2010, 3, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 3, 31), SysTime(DateTime(-2010, 3, 31, 12, 13, 14))); - test(Date(-2010, 4, 1), SysTime(DateTime(-2010, 4, 1, 12, 13, 14), hnsecs(500))); - test(Date(-2010, 4, 30), SysTime(DateTime(-2010, 4, 30, 12, 13, 14), hnsecs(50_000))); - test(Date(-2010, 5, 1), SysTime(DateTime(-2010, 5, 1, 12, 13, 14), hnsecs(9_999_999))); - test(Date(-2010, 5, 31), SysTime(DateTime(-2010, 5, 31, 23, 59, 59))); - test(Date(-2010, 6, 1), SysTime(DateTime(-2010, 6, 1, 23, 59, 59), hnsecs(500))); - test(Date(-2010, 6, 30), SysTime(DateTime(-2010, 6, 30, 23, 59, 59), hnsecs(50_000))); - test(Date(-2010, 7, 1), SysTime(DateTime(-2010, 7, 1, 23, 59, 59), hnsecs(9_999_999))); - test(Date(-2010, 7, 31), SysTime(DateTime(-2010, 7, 31, 0, 0, 0))); - test(Date(-2010, 8, 1), SysTime(DateTime(-2010, 8, 1, 0, 0, 0), hnsecs(500))); - test(Date(-2010, 8, 31), SysTime(DateTime(-2010, 8, 31, 0, 0, 0), hnsecs(50_000))); - test(Date(-2010, 9, 1), SysTime(DateTime(-2010, 9, 1, 0, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 9, 30), SysTime(DateTime(-2010, 9, 30, 12, 0, 0))); - test(Date(-2010, 10, 1), SysTime(DateTime(-2010, 10, 1, 0, 12, 0), hnsecs(500))); - test(Date(-2010, 10, 31), SysTime(DateTime(-2010, 10, 31, 0, 0, 12), hnsecs(50_000))); - test(Date(-2010, 11, 1), SysTime(DateTime(-2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); - test(Date(-2010, 11, 30), SysTime(DateTime(-2010, 11, 30, 0, 59, 0))); - test(Date(-2010, 12, 1), SysTime(DateTime(-2010, 12, 1, 0, 0, 59), hnsecs(500))); - test(Date(-2010, 12, 31), SysTime(DateTime(-2010, 12, 31, 0, 59, 59), hnsecs(50_000))); - - test(Date(-2012, 2, 1), SysTime(DateTime(-2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); - test(Date(-2012, 2, 28), SysTime(DateTime(-2012, 2, 28, 23, 59, 0))); - test(Date(-2012, 2, 29), SysTime(DateTime(-2012, 2, 29, 7, 7, 7), hnsecs(7))); - test(Date(-2012, 3, 1), SysTime(DateTime(-2012, 3, 1, 7, 7, 7), hnsecs(7))); - - test(Date(-3760, 9, 7), SysTime(DateTime(-3760, 9, 7, 0, 0, 0))); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - Setting this property does not affect the time portion of $(LREF SysTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF SysTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe nothrow - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - if (--days < 0) - { - hnsecs -= convert!("hours", "hnsecs")(24); - ++days; - } - - immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - - adjTime = newDaysHNSecs + hnsecs; - } - - /// - @safe unittest - { - auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); - st.dayOfGregorianCal = 1; - assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 365; - assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 366; - assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 0; - assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = -365; - assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = -366; - assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 730_120; - assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 734_137; - assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); - } - - @safe unittest - { - void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - orig.dayOfGregorianCal = day; - if (orig != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); - } - - //Test A.D. - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - //Test B.C. - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - //Test Both. - testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); - testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); - testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), hnsecs(9_999_999)), 1, - SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); - - testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); - testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), hnsecs(9_999_999)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), hnsecs(1)), 0, - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); - testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); - - - auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); - - void testST2(int day, in SysTime expected, size_t line = __LINE__) - { - import std.format : format; - - st.dayOfGregorianCal = day; - if (st != expected) - throw new AssertError(format("Failed. actual [%s] != expected [%s]", st, expected), __FILE__, line); - } - - //Test A.D. - testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212))); - testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212))); - testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212))); - testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212))); - testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212))); - testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212))); - testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212))); - testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212))); - testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212))); - testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212))); - testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212))); - testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212))); - testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212))); - testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212))); - testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212))); - testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212))); - testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212))); - testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); - - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); - - //Test B.C. - testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212))); - testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212))); - testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212))); - - testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212))); - testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212))); - testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212))); - testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212))); - testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212))); - testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212))); - testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212))); - testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212))); - testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212))); - testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212))); - testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212))); - testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212))); - testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212))); - testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212))); - testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212))); - testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212))); - testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212))); - testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212))); - testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212))); - testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212))); - testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212))); - testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212))); - testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212))); - testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212))); - testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212))); - testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212))); - testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212))); - - testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), msecs(212))); - testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), msecs(212))); - testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), msecs(212))); - testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), msecs(212))); - testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), msecs(212))); - testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), msecs(212))); - testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), msecs(212))); - testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), msecs(212))); - testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), msecs(212))); - testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), msecs(212))); - testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), msecs(212))); - testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), msecs(212))); - testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), msecs(212))); - testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), msecs(212))); - testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), msecs(212))); - testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), msecs(212))); - testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), msecs(212))); - testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), msecs(212))); - testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), msecs(212))); - testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), msecs(212))); - testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), msecs(212))); - testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), msecs(212))); - testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), msecs(212))); - testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), msecs(212))); - - testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), msecs(212))); - testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), msecs(212))); - testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), msecs(212))); - testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); - //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF SysTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). - +/ - @property ubyte isoWeek() @safe const nothrow - { - return (cast(Date) this).isoWeek; - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.isoWeek == 27); - assert(cst.isoWeek == 27); - //assert(ist.isoWeek == 27); - } - - - /++ - $(LREF SysTime) for the last day in the month that this Date is in. - The time portion of endOfMonth is always 23:59:59.9999999. - +/ - @property SysTime endOfMonth() @safe const nothrow - { - immutable hnsecs = adjTime; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - - auto date = Date(cast(int) days + 1).endOfMonth; - auto newDays = date.dayOfGregorianCal - 1; - long theTimeHNSecs; - - if (newDays < 0) - { - theTimeHNSecs = -1; - ++newDays; - } - else - theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; - - immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); - - auto retval = SysTime(this._stdTime, this._timezone); - retval.adjTime = newDaysHNSecs + theTimeHNSecs; - - return retval; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == - SysTime(DateTime(1999, 1, 31, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), - msecs(24)).endOfMonth == - SysTime(DateTime(1999, 2, 28, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), - usecs(5203)).endOfMonth == - SysTime(DateTime(2000, 2, 29, 23, 59, 59), - hnsecs(9_999_999))); - - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), - hnsecs(12345)).endOfMonth == - SysTime(DateTime(2000, 6, 30, 23, 59, 59), - hnsecs(9_999_999))); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - //Test B.C. - assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 10, 1)).endOfMonth == - SysTime(DateTime(-1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 11, 1)).endOfMonth == - SysTime(DateTime(-1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); - assert(SysTime(Date(-1999, 12, 1)).endOfMonth == - SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - //assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); - } - - - /++ - The last day in the month that this $(LREF SysTime) is in. - +/ - @property ubyte daysInMonth() @safe const nothrow - { - return Date(dayOfGregorianCal).daysInMonth; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - //Test B.C. - assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); - assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); - assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); - assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); - assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.daysInMonth == 31); - //assert(ist.daysInMonth == 31); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const nothrow - { - return adjTime >= 0; - } - - /// - @safe unittest - { - assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); - assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); - } - - @safe unittest - { - assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); - assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.isAD); - //assert(ist.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) - for this $(LREF SysTime) at the given time. For example, - prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so - this function returns 2_450_173, while from noon onward, the Julian - day number would be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const nothrow - { - immutable jd = dayOfGregorianCal + 1_721_425; - - return hour < 12 ? jd - 1 : jd; - } - - @safe unittest - { - assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); - assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); - - assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); - assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); - - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); - assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); - - assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); - assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); - - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); - - assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); - assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); - - assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); - assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.julianDay == 2_451_366); - //assert(ist.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const nothrow - { - return (dayOfGregorianCal + 1_721_425) - 2_400_001; - } - - @safe unittest - { - assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); - assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); - - assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); - assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cst.modJulianDay == 51_365); - //assert(ist.modJulianDay == 51_365); - } - - - /++ - Returns a $(LREF Date) equivalent to this $(LREF SysTime). - +/ - Date opCast(T)() @safe const nothrow - if (is(Unqual!T == Date)) - { - return Date(dayOfGregorianCal); - } - - @safe unittest - { - assert(cast(Date) SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); - assert(cast(Date) SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); - assert(cast(Date) SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); - - assert(cast(Date) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); - assert(cast(Date) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); - assert(cast(Date) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); - - assert(cast(Date) SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); - assert(cast(Date) SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); - assert(cast(Date) SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); - - assert(cast(Date) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); - assert(cast(Date) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); - assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(Date) cst != Date.init); - //assert(cast(Date) ist != Date.init); - } - - - /++ - Returns a $(LREF DateTime) equivalent to this $(LREF SysTime). - +/ - DateTime opCast(T)() @safe const nothrow - if (is(Unqual!T == DateTime)) - { - try - { - auto hnsecs = adjTime; - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second)); - } - catch (Exception e) - assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); - } - - @safe unittest - { - assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(-1, 1, 6, 7, 12, 22)); - assert(cast(DateTime) SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); - assert(cast(DateTime) SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); - - assert(cast(DateTime) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); - assert(cast(DateTime) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); - assert(cast(DateTime) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); - - assert(cast(DateTime) SysTime(DateTime(2011, 1, 13, 8, 17, 2), msecs(296), LocalTime()) == - DateTime(2011, 1, 13, 8, 17, 2)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(DateTime) cst != DateTime.init); - //assert(cast(DateTime) ist != DateTime.init); - } - - - /++ - Returns a $(LREF TimeOfDay) equivalent to this $(LREF SysTime). - +/ - TimeOfDay opCast(T)() @safe const nothrow - if (is(Unqual!T == TimeOfDay)) - { - try - { - auto hnsecs = adjTime; - hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - hnsecs += convert!("hours", "hnsecs")(24); - - immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); - - return TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second); - } - catch (Exception e) - assert(0, "TimeOfDay's constructor threw."); - } - - @safe unittest - { - assert(cast(TimeOfDay) SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - assert(cast(TimeOfDay) SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); - assert(cast(TimeOfDay) SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); - - assert(cast(TimeOfDay) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); - assert(cast(TimeOfDay) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); - assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); - } - - - //Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed. - //This allows assignment from const(SysTime) to SysTime. - //It may be a good idea to keep it though, since casting from a type to itself - //should be allowed, and it doesn't work without this opCast() since opCast() - //has already been defined for other types. - SysTime opCast(T)() @safe const pure nothrow - if (is(Unqual!T == SysTime)) - { - return SysTime(_stdTime, _timezone); - } - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ is time - zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. - If its time zone is $(D UTC), then it is "Z". Otherwise, it is the - offset from UTC (e.g. +0100 or -0700). Note that the offset from UTC - is $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HHMM or -HHMM. - - $(RED Warning: - Previously, toISOString did the same as $(LREF toISOExtString) and - generated +HH:MM or -HH:MM for the time zone when it was not - $(LREF LocalTime) or $(LREF UTC), which is not in conformance with - ISO 9601 for the non-extended string format. This has now been - fixed. However, for now, fromISOString will continue to accept the - extended format for the time zone so that any code which has been - writing out the result of toISOString to read in later will continue - to work.) - +/ - string toISOString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); - - if (_timezone is LocalTime()) - return dateTime.toISOString() ~ fracSecStr; - - if (_timezone is UTC()) - return dateTime.toISOString() ~ fracSecStr ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toISOString(), - fracSecStr, - SimpleTimeZone.toISOExtString(utcOffset)); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toISOString() == - "19981225T021500.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toISOString() == - "-00040105T000002.052092"); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "00091204T000000.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "00991204T050612.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "09991204T134459.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "99990704T235959.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "+100001020T010101.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == - "20121221T121212-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == - "20121221T121212+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOString() == - "00001231T235959.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOString() == "00001231T235959.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOString() == "00001204T000000.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "-00091204T000000.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "-00991204T050612.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "-09991204T134459.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "-99990704T235959.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); - } - - - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - +/ - string toISOExtString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); - - if (_timezone is LocalTime()) - return dateTime.toISOExtString() ~ fracSecStr; - - if (_timezone is UTC()) - return dateTime.toISOExtString() ~ fracSecStr ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toISOExtString(), - fracSecStr, - SimpleTimeZone.toISOExtString(utcOffset)); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toISOExtString() == - "1998-12-25T02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toISOExtString() == - "-0004-01-05T00:00:02.052092"); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() == - "0001-01-01T00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "+10000-10-20T01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == - "2012-12-21T12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == - "2012-12-21T12:12:12+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOExtString() == - "0000-12-31T23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOExtString() == - "0000-12-31T23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOExtString() == "0000-12-04T00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == - "-0999-12-04T13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == - "-9999-07-04T23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == - "-10000-10-20T01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); - } - - /++ - Converts this $(LREF SysTime) to a string with the format - YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ - is the time zone). - - Note that the number of digits in the fractional seconds varies with the - number of fractional seconds. It's a maximum of 7 (which would be - hnsecs), but only has as many as are necessary to hold the correct value - (so no trailing zeroes), and if there are no fractional seconds, then - there is no decimal point. - - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. - - Time zone offsets will be in the form +HH:MM or -HH:MM. - +/ - string toSimpleString() @safe const nothrow - { - import std.format : format; - try - { - immutable adjustedTime = adjTime; - long hnsecs = adjustedTime; - - auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; - - if (hnsecs < 0) - { - hnsecs += convert!("hours", "hnsecs")(24); - --days; - } - - auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); - auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); - auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); - - auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, - cast(int) minute, cast(int) second)); - auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); - - if (_timezone is LocalTime()) - return dateTime.toSimpleString() ~ fracSecStr; - - if (_timezone is UTC()) - return dateTime.toSimpleString() ~ fracSecStr ~ "Z"; - - immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); - - return format("%s%s%s", - dateTime.toSimpleString(), - fracSecStr, - SimpleTimeZone.toISOExtString(utcOffset)); - } - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - msecs(24)).toSimpleString() == - "1998-Dec-25 02:15:00.024"); - - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - hnsecs(520_920)).toSimpleString() == - "-0004-Jan-05 00:00:02.052092"); - } - - @safe unittest - { - //Test A.D. - assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "+10000-Oct-20 01:01:01.050789"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == - "2012-Dec-21 12:12:12-06:00"); - - assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == - "2012-Dec-21 12:12:12+07:00"); - - //Test B.C. - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.9999999Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toSimpleString() == - "0000-Dec-31 23:59:59.0000001Z"); - assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); - - assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); - assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); - assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); - assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == - "-0999-Dec-04 13:44:59.04502"); - assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == - "-9999-Jul-04 23:59:59.0000012"); - assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == - "-10000-Oct-20 01:01:01.050789"); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(cast(TimeOfDay) cst != TimeOfDay.init); - //assert(cast(TimeOfDay) ist != TimeOfDay.init); - } - - - /++ - Converts this $(LREF SysTime) to a string. - +/ - string toString() @safe const nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - assert(st.toString()); - assert(cst.toString()); - //assert(ist.toString()); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds is the time - zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toISOString) except that - trailing zeroes are permitted - including having fractional seconds with - all zeroes. However, a decimal point with nothing following it is - invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. - If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HHMM, and - -HHMM. - - $(RED Warning: - Previously, $(LREF toISOString) did the same as - $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time - zone when it was not $(LREF LocalTime) or $(LREF UTC), which is not - in conformance with ISO 9601 for the non-extended string format. - This has now been fixed. However, for now, fromISOString will - continue to accept the extended format for the time zone so that any - code which has been writing out the result of toISOString to read in - later will continue to work.) - - Params: - isoString = A string formatted in the ISO format for dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - format or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe - if (isSomeString!S) - { - import std.algorithm.searching : startsWith, find; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoString)); - immutable skipFirst = dstr.startsWith('+', '-') != 0; - - auto found = (skipFirst ? dstr[1..$] : dstr).find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if (foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromISOString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - { - try - parsedZone = SimpleTimeZone.fromISOString(zoneStr); - catch (DateTimeException dte) - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - } - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; - } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid ISO String: %s", isoString)); - } - - /// - @safe unittest - { - assert(SysTime.fromISOString("20100704T070612") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("19981225T021500.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromISOString("00000105T230959.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromISOString("-00040105T000002") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromISOString(" 20100704T070612 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("20100704T070612Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromISOString("20100704T070612-0800") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - - assert(SysTime.fromISOString("20100704T070612+0800") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); - } - - @safe unittest - { - import std.format : format; - - foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000", - "20100704T000000.", "20100704T000000.A", "20100704T000000.Z", - "20100704T000000.00000000", "20100704T000000.00000000", - "20100704T000000+", "20100704T000000-", "20100704T000000:", - "20100704T000000-:", "20100704T000000+:", "20100704T000000-1:", - "20100704T000000+1:", "20100704T000000+1:0", - "20100704T000000-12.00", "20100704T000000+12.00", - "20100704T000000-8", "20100704T000000+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "20100704T000000-8:00", "20100704T000000+8:00", - "20100704T000000-08:0", "20100704T000000+08:0", - "20100704T000000-24:00", "20100704T000000+24:00", - "20100704T000000-12:60", "20100704T000000+12:60", - "20100704T000000.0-8:00", "20100704T000000.0+8:00", - "20100704T000000.0-08:0", "20100704T000000.0+08:0", - "20100704T000000.0-24:00", "20100704T000000.0+24:00", - "20100704T000000.0-12:60", "20100704T000000.0+12:60", - "2010-07-0400:00:00", "2010-07-04 00:00:00", - "2010-07-04t00:00:00", "2010-07-04T00:00:00.", - "2010-Jul-0400:00:00", "2010-Jul-04 00:00:00", "2010-Jul-04t00:00:00", - "2010-Jul-04T00:00:00", "2010-Jul-04 00:00:00.", - "2010-12-22T172201", "2010-Dec-22 17:22:01"]) - { - assertThrown!DateTimeException(SysTime.fromISOString(str), format("[%s]", str)); - } - - static void test(string str, SysTime st, size_t line = __LINE__) - { - if (SysTime.fromISOString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - test("19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-19990706T123033", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+019990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("19070707T121212.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("19070707T121212.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("19070707T121212.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - test("19070707T121212.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("19070707T121212.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("19070707T121212.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - test("19070707T121212.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); - test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); - test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); - - test("20101103T065106.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); - test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); - test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); - test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); - test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); - test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); - - // @@@DEPRECATED_2017-07@@@ - // This isn't deprecated per se, but that text will make it so that it - // pops up when deprecations are moved along around July 2017. At that - // time, the notice on the documentation should be removed, and we may - // or may not change the behavior of fromISOString to no longer accept - // ISO extended time zones (the concern being that programs will have - // written out strings somewhere to read in again that they'll still be - // reading in for years to come and may not be able to fix, even if the - // code is fixed). If/when we do change the behavior, these tests will - // start failing and will need to be updated accordingly. - test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); - test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); - test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); - - test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); - test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); - test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); - test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); - test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the - time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toISOExtString) - except that trailing zeroes are permitted - including having fractional - seconds with all zeroes. However, a decimal point with nothing following - it is invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. - If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - isoExtString = A string formatted in the ISO Extended format for - dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO - format or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil, find; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoExtString)); - - auto tIndex = dstr.countUntil('T'); - enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto found = dstr[tIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if (foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromISOExtString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; - } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); - } - - /// - @safe unittest - { - assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12-08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12+08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); - } - - @safe unittest - { - import std.format : format; - - foreach (str; ["", "20100704000000", "20100704 000000", - "20100704t000000", "20100704T000000.", "20100704T000000.0", - "2010-07:0400:00:00", "2010-07-04 00:00:00", - "2010-07-04 00:00:00", "2010-07-04t00:00:00", - "2010-07-04T00:00:00.", "2010-07-04T00:00:00.A", "2010-07-04T00:00:00.Z", - "2010-07-04T00:00:00.00000000", "2010-07-04T00:00:00.00000000", - "2010-07-04T00:00:00+", "2010-07-04T00:00:00-", - "2010-07-04T00:00:00:", "2010-07-04T00:00:00-:", "2010-07-04T00:00:00+:", - "2010-07-04T00:00:00-1:", "2010-07-04T00:00:00+1:", "2010-07-04T00:00:00+1:0", - "2010-07-04T00:00:00-12.00", "2010-07-04T00:00:00+12.00", - "2010-07-04T00:00:00-8", "2010-07-04T00:00:00+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "2010-07-04T00:00:00-8:00", "2010-07-04T00:00:00+8:00", - "2010-07-04T00:00:00-24:00", "2010-07-04T00:00:00+24:00", - "2010-07-04T00:00:00-12:60", "2010-07-04T00:00:00+12:60", - "2010-07-04T00:00:00.0-8:00", "2010-07-04T00:00:00.0+8:00", - "2010-07-04T00:00:00.0-8", "2010-07-04T00:00:00.0+8", - "2010-07-04T00:00:00.0-24:00", "2010-07-04T00:00:00.0+24:00", - "2010-07-04T00:00:00.0-12:60", "2010-07-04T00:00:00.0+12:60", - "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", - "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.0", - "20101222T172201", "2010-Dec-22 17:22:01"]) - { - assertThrown!DateTimeException(SysTime.fromISOExtString(str), format("[%s]", str)); - } - - static void test(string str, SysTime st, size_t line = __LINE__) - { - if (SysTime.fromISOExtString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); - } - - test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - test("1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-1999-07-06T12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+01999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); - test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); - test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); - - test("2010-11-03T06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - test("2010-12-22T17:22:01.23112-01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); - test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); - test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); - test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); - test("2010-12-22T17:22:01.1234567+01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); - test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); - } - - - /++ - Creates a $(LREF SysTime) from a string with the format - YYYY-MM-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the - time zone). Whitespace is stripped from the given string. - - The exact format is exactly as described in $(D toSimpleString) except - that trailing zeroes are permitted - including having fractional seconds - with all zeroes. However, a decimal point with nothing following it is - invalid. - - If there is no time zone in the string, then $(LREF LocalTime) is used. If - the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). - - The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and - -HH:MM. - - Params: - simpleString = A string formatted in the way that - $(D toSimpleString) formats dates and times. - tz = The time zone to convert the given time to (no - conversion occurs if null). - - Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF SysTime) would not be valid. - +/ - static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil, find; - import std.conv : to; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(simpleString)); - - auto spaceIndex = dstr.countUntil(' '); - enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); - - auto found = dstr[spaceIndex + 1 .. $].find('.', 'Z', '+', '-'); - auto dateTimeStr = dstr[0 .. $ - found[0].length]; - - dstring fracSecStr; - dstring zoneStr; - - if (found[1] != 0) - { - if (found[1] == 1) - { - auto foundTZ = found[0].find('Z', '+', '-'); - - if (foundTZ[1] != 0) - { - fracSecStr = found[0][0 .. $ - foundTZ[0].length]; - zoneStr = foundTZ[0]; - } - else - fracSecStr = found[0]; - } - else - zoneStr = found[0]; - } - - try - { - auto dateTime = DateTime.fromSimpleString(dateTimeStr); - auto fracSec = fracSecsFromISOString(fracSecStr); - Rebindable!(immutable TimeZone) parsedZone; - - if (zoneStr.empty) - parsedZone = LocalTime(); - else if (zoneStr == "Z") - parsedZone = UTC(); - else - parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); - - auto retval = SysTime(dateTime, fracSec, parsedZone); - - if (tz !is null) - retval.timezone = tz; - - return retval; + } + else static assert(0, "Unsupported OS"); } - catch (DateTimeException dte) - throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); - } - - /// - @safe unittest - { - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); - - assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); - - assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - - assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(-8)))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+08:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new immutable SimpleTimeZone(hours(8)))); + else static assert(0, "Unsupported OS"); } @safe unittest { import std.format : format; + import std.math : abs; + import std.meta : AliasSeq; + import std.stdio : writefln; + enum limit = convert!("seconds", "hnsecs")(2); - foreach (str; ["", "20100704000000", "20100704 000000", - "20100704t000000", "20100704T000000.", "20100704T000000.0", - "2010-07-0400:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00", - "2010-07-04T00:00:00.", "2010-07-04T00:00:00.0", - "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", "2010-Jul-04T00:00:00", - "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.A", "2010-Jul-04 00:00:00.Z", - "2010-Jul-04 00:00:00.00000000", "2010-Jul-04 00:00:00.00000000", - "2010-Jul-04 00:00:00+", "2010-Jul-04 00:00:00-", - "2010-Jul-04 00:00:00:", "2010-Jul-04 00:00:00-:", - "2010-Jul-04 00:00:00+:", "2010-Jul-04 00:00:00-1:", - "2010-Jul-04 00:00:00+1:", "2010-Jul-04 00:00:00+1:0", - "2010-Jul-04 00:00:00-12.00", "2010-Jul-04 00:00:00+12.00", - "2010-Jul-04 00:00:00-8", "2010-Jul-04 00:00:00+8", - "20100704T000000-800", "20100704T000000+800", - "20100704T000000-080", "20100704T000000+080", - "20100704T000000-2400", "20100704T000000+2400", - "20100704T000000-1260", "20100704T000000+1260", - "20100704T000000.0-800", "20100704T000000.0+800", - "20100704T000000.0-8", "20100704T000000.0+8", - "20100704T000000.0-080", "20100704T000000.0+080", - "20100704T000000.0-2400", "20100704T000000.0+2400", - "20100704T000000.0-1260", "20100704T000000.0+1260", - "2010-Jul-04 00:00:00-8:00", "2010-Jul-04 00:00:00+8:00", - "2010-Jul-04 00:00:00-08:0", "2010-Jul-04 00:00:00+08:0", - "2010-Jul-04 00:00:00-24:00", "2010-Jul-04 00:00:00+24:00", - "2010-Jul-04 00:00:00-12:60", "2010-Jul-04 00:00:00+24:60", - "2010-Jul-04 00:00:00.0-8:00", "2010-Jul-04 00:00:00+8:00", - "2010-Jul-04 00:00:00.0-8", "2010-Jul-04 00:00:00.0+8", - "2010-Jul-04 00:00:00.0-08:0", "2010-Jul-04 00:00:00.0+08:0", - "2010-Jul-04 00:00:00.0-24:00", "2010-Jul-04 00:00:00.0+24:00", - "2010-Jul-04 00:00:00.0-12:60", "2010-Jul-04 00:00:00.0+24:60", - "20101222T172201", "2010-12-22T172201"]) - { - assertThrown!DateTimeException(SysTime.fromSimpleString(str), format("[%s]", str)); - } + auto norm1 = Clock.currStdTime; + auto norm2 = Clock.currStdTime; + assert(norm1 <= norm2, format("%s %s", norm1, norm2)); + assert(abs(norm1 - norm2) <= limit); - static void test(string str, SysTime st, size_t line = __LINE__) + foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) { - if (SysTime.fromSimpleString(str) != st) - throw new AssertError("unittest failure", __FILE__, line); + scope(failure) writefln("ClockType.%s", ct); + auto value1 = Clock.currStdTime!ct; + auto value2 = Clock.currStdTime!ct; + assert(value1 <= value2, format("%s %s", value1, value2)); + assert(abs(value1 - value2) <= limit); } - - test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - test("1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("-1999-Jul-06 12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - test("+01999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test("1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - test(" 1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); - test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); - test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); - - auto west60 = new immutable SimpleTimeZone(hours(-1)); - auto west90 = new immutable SimpleTimeZone(minutes(-90)); - auto west480 = new immutable SimpleTimeZone(hours(-8)); - auto east60 = new immutable SimpleTimeZone(hours(1)); - auto east90 = new immutable SimpleTimeZone(minutes(90)); - auto east480 = new immutable SimpleTimeZone(hours(8)); - - test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); - test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); - test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); - test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); - - test("2010-Nov-03 06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); - test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); - test("2010-Dec-22 17:22:01.23112-01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); - test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); - test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); - test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); - test("2010-Dec-22 17:22:01.1234567+01:00", - SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); - test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); - test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); - test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); } - /++ - Returns the $(LREF SysTime) farthest in the past which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime min() @safe pure nothrow + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime.currTime instead") + static @property TickDuration currSystemTick() @safe nothrow { - return SysTime(long.min, UTC()); + return TickDuration.currSystemTick; } - @safe unittest + deprecated @safe unittest { - assert(SysTime.min.year < 0); - assert(SysTime.min < SysTime.max); + assert(Clock.currSystemTick.length > 0); } - - /++ - Returns the $(LREF SysTime) farthest in the future which is representable - by $(LREF SysTime). - - The $(LREF SysTime) which is returned is in UTC. - +/ - @property static SysTime max() @safe pure nothrow + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime instead. See currAppTick's documentation for details.") + static @property TickDuration currAppTick() @safe { - return SysTime(long.max, UTC()); + return currSystemTick - TickDuration.appOrigin; } - @safe unittest + deprecated @safe unittest { - assert(SysTime.max.year > 0); - assert(SysTime.max > SysTime.min); + auto a = Clock.currSystemTick; + auto b = Clock.currAppTick; + assert(a.length); + assert(b.length); + assert(a > b); } - private: - /+ - Returns $(D stdTime) converted to $(LREF SysTime)'s time zone. - +/ - @property long adjTime() @safe const nothrow - { - return _timezone.utcToTZ(_stdTime); - } - - - /+ - Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. - +/ - @property void adjTime(long adjTime) @safe nothrow - { - _stdTime = _timezone.tzToUTC(adjTime); - } - - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 - /+ - invariant() - { - assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); - } - +/ - - - long _stdTime; - Rebindable!(immutable TimeZone) _timezone; + @disable this() {} } - //============================================================================== // Section with StopWatch and Benchmark Code. //============================================================================== @@ -9520,178 +844,6 @@ ComparingBenchmarkResult comparingBenchmark(alias baseFunc, // Section with public helper functions and templates. //============================================================================== -/++ - Converts from unix time (which uses midnight, January 1st, 1970 UTC as its - epoch and seconds as its units) to "std time" (which uses midnight, - January 1st, 1 A.D. UTC and hnsecs as its units). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO - 8601 and is what $(LREF SysTime) uses internally. However, holding the time - as an integer in hnescs since that epoch technically isn't actually part of - the standard, much as it's based on it, so the name "std time" isn't - particularly good, but there isn't an official name for it. C# uses "ticks" - for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so - it didn't make sense to use the term ticks here. So, for better or worse, - std.datetime uses the term "std time" for this. - - Params: - unixTime = The unix time to convert. - - See_Also: - SysTime.fromUnixTime - +/ -long unixTimeToStdTime(long unixTime) @safe pure nothrow -{ - return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); -} - -/// -@safe unittest -{ - // Midnight, January 1st, 1970 - assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); - assert(SysTime(unixTimeToStdTime(0)) == - SysTime(DateTime(1970, 1, 1), UTC())); - - assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L); - assert(SysTime(unixTimeToStdTime(int.max)) == - SysTime(DateTime(2038, 1, 19, 3, 14, 07), UTC())); - - assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L); - assert(SysTime(unixTimeToStdTime(-127_127)) == - SysTime(DateTime(1969, 12, 30, 12, 41, 13), UTC())); -} - -@safe unittest -{ - // Midnight, January 2nd, 1970 - assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); - // Midnight, December 31st, 1969 - assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); - - assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); - assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); - - foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) - assert(unixTimeToStdTime((dt - DateTime(1970, 1, 1)).total!"seconds") == (dt - DateTime.init).total!"hnsecs"); -} - - -/++ - Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch - and hnsecs as its units) to unix time (which uses midnight, January 1st, - 1970 UTC as its epoch and seconds as its units). - - The C standard does not specify the representation of time_t, so it is - implementation defined. On POSIX systems, unix time is equivalent to - time_t, but that's not necessarily true on other systems (e.g. it is - not true for the Digital Mars C runtime). So, be careful when using unix - time with C functions on non-POSIX systems. - - "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO - 8601 and is what $(LREF SysTime) uses internally. However, holding the time - as an integer in hnescs since that epoch technically isn't actually part of - the standard, much as it's based on it, so the name "std time" isn't - particularly good, but there isn't an official name for it. C# uses "ticks" - for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so - it didn't make sense to use the term ticks here. So, for better or worse, - std.datetime uses the term "std time" for this. - - By default, the return type is time_t (which is normally an alias for - int on 32-bit systems and long on 64-bit systems), but if a different - size is required than either int or long can be passed as a template - argument to get the desired size. - - If the return type is int, and the result can't fit in an int, then the - closest value that can be held in 32 bits will be used (so $(D int.max) - if it goes over and $(D int.min) if it goes under). However, no attempt - is made to deal with integer overflow if the return type is long. - - Params: - T = The return type (int or long). It defaults to time_t, which is - normally 32 bits on a 32-bit system and 64 bits on a 64-bit - system. - stdTime = The std time to convert. - - Returns: - A signed integer representing the unix time which is equivalent to - the given std time. - - See_Also: - SysTime.toUnixTime - +/ -T stdTimeToUnixTime(T = time_t)(long stdTime) @safe pure nothrow -if (is(T == int) || is(T == long)) -{ - immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); - - static assert(is(time_t == int) || is(time_t == long), - "Currently, std.datetime only supports systems where time_t is int or long"); - - static if (is(T == long)) - return unixTime; - else static if (is(T == int)) - { - if (unixTime > int.max) - return int.max; - return unixTime < int.min ? int.min : cast(int) unixTime; - } - else static assert(0, "Bug in template constraint. Only int and long allowed."); -} - -/// -@safe unittest -{ - // Midnight, January 1st, 1970 UTC - assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); - - // 2038-01-19 03:14:07 UTC - assert(stdTimeToUnixTime(642_830_804_470_000_000L) == int.max); -} - -@safe unittest -{ - enum unixEpochAsStdTime = (Date(1970, 1, 1) - Date.init).total!"hnsecs"; - - assert(stdTimeToUnixTime(unixEpochAsStdTime) == 0); //Midnight, January 1st, 1970 - assert(stdTimeToUnixTime(unixEpochAsStdTime + 864_000_000_000L) == 86_400); //Midnight, January 2nd, 1970 - assert(stdTimeToUnixTime(unixEpochAsStdTime - 864_000_000_000L) == -86_400); //Midnight, December 31st, 1969 - - assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); - assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); - - foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) - assert(stdTimeToUnixTime((dt - DateTime.init).total!"hnsecs") == (dt - DateTime(1970, 1, 1)).total!"seconds"); - - enum max = convert!("seconds", "hnsecs")(int.max); - enum min = convert!("seconds", "hnsecs")(int.min); - enum one = convert!("seconds", "hnsecs")(1); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max) == int.max); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max) == int.max); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + one) == int.max + 1L); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + one) == int.max); - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + 9_999_999) == int.max); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + 9_999_999) == int.max); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min) == int.min); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min) == int.min); - - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - one) == int.min - 1L); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - one) == int.min); - assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - 9_999_999) == int.min); - assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - 9_999_999) == int.min); -} - version(StdDdoc) { @@ -11019,68 +2171,6 @@ if (CmpTimeUnits!(units, "months") < 0) } -/+ - This function is used to split out the units without getting the remaining - hnsecs. - - See_Also: - $(LREF splitUnitsFromHNSecs) - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The split out value. - +/ -long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - return convert!("hnsecs", units)(hnsecs); -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 2595000000007L); -} - - -/+ - This function is used to split out the units without getting the units but - just the remaining hnsecs. - - See_Also: - $(LREF splitUnitsFromHNSecs) - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. - - Returns: - The remaining hnsecs. - +/ -long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && - CmpTimeUnits!(units, "months") < 0) -{ - immutable value = convert!("hnsecs", units)(hnsecs); - - return hnsecs - convert!(units, "hnsecs")(value); -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - auto returned = removeUnitsFromHNSecs!"days"(hnsecs); - assert(returned == 3000000007); - assert(hnsecs == 2595000000007L); -} - - /+ The time units which are one step smaller than the given units. +/ @@ -11133,147 +2223,6 @@ if (validTimeUnits(units) && } -/+ - Returns the given hnsecs as an ISO string of fractional seconds. - +/ -static string fracSecsToISOString(int hnsecs) @safe pure nothrow -{ - import std.format : format; - import std.range.primitives : popBack; - assert(hnsecs >= 0); - - try - { - if (hnsecs == 0) - return ""; - - string isoString = format(".%07d", hnsecs); - - while (isoString[$ - 1] == '0') - isoString.popBack(); - - return isoString; - } - catch (Exception e) - assert(0, "format() threw."); -} - -@safe unittest -{ - assert(fracSecsToISOString(0) == ""); - assert(fracSecsToISOString(1) == ".0000001"); - assert(fracSecsToISOString(10) == ".000001"); - assert(fracSecsToISOString(100) == ".00001"); - assert(fracSecsToISOString(1000) == ".0001"); - assert(fracSecsToISOString(10_000) == ".001"); - assert(fracSecsToISOString(100_000) == ".01"); - assert(fracSecsToISOString(1_000_000) == ".1"); - assert(fracSecsToISOString(1_000_001) == ".1000001"); - assert(fracSecsToISOString(1_001_001) == ".1001001"); - assert(fracSecsToISOString(1_071_601) == ".1071601"); - assert(fracSecsToISOString(1_271_641) == ".1271641"); - assert(fracSecsToISOString(9_999_999) == ".9999999"); - assert(fracSecsToISOString(9_999_990) == ".999999"); - assert(fracSecsToISOString(9_999_900) == ".99999"); - assert(fracSecsToISOString(9_999_000) == ".9999"); - assert(fracSecsToISOString(9_990_000) == ".999"); - assert(fracSecsToISOString(9_900_000) == ".99"); - assert(fracSecsToISOString(9_000_000) == ".9"); - assert(fracSecsToISOString(999) == ".0000999"); - assert(fracSecsToISOString(9990) == ".000999"); - assert(fracSecsToISOString(99_900) == ".00999"); - assert(fracSecsToISOString(999_000) == ".0999"); -} - - -/+ - Returns a Duration corresponding to to the given ISO string of - fractional seconds. - +/ -static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure -if (isSomeString!S) -{ - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.string : representation; - - if (isoString.empty) - return Duration.zero; - - auto str = isoString.representation; - - enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); - str.popFront(); - - enforce(!str.empty && str.length <= 7, new DateTimeException("Invalid ISO String")); - enforce(all!isDigit(str), new DateTimeException("Invalid ISO String")); - - dchar[7] fullISOString = void; - foreach (i, ref dchar c; fullISOString) - { - if (i < str.length) - c = str[i]; - else - c = '0'; - } - - return hnsecs(to!int(fullISOString[])); -} - -@safe unittest -{ - static void testFSInvalid(string isoString) - { - fracSecsFromISOString(isoString); - } - - assertThrown!DateTimeException(testFSInvalid(".")); - assertThrown!DateTimeException(testFSInvalid("0.")); - assertThrown!DateTimeException(testFSInvalid("0")); - assertThrown!DateTimeException(testFSInvalid("0000000")); - assertThrown!DateTimeException(testFSInvalid(".00000000")); - assertThrown!DateTimeException(testFSInvalid(".00000001")); - assertThrown!DateTimeException(testFSInvalid("T")); - assertThrown!DateTimeException(testFSInvalid("T.")); - assertThrown!DateTimeException(testFSInvalid(".T")); - - assert(fracSecsFromISOString("") == Duration.zero); - assert(fracSecsFromISOString(".0000001") == hnsecs(1)); - assert(fracSecsFromISOString(".000001") == hnsecs(10)); - assert(fracSecsFromISOString(".00001") == hnsecs(100)); - assert(fracSecsFromISOString(".0001") == hnsecs(1000)); - assert(fracSecsFromISOString(".001") == hnsecs(10_000)); - assert(fracSecsFromISOString(".01") == hnsecs(100_000)); - assert(fracSecsFromISOString(".1") == hnsecs(1_000_000)); - assert(fracSecsFromISOString(".1000001") == hnsecs(1_000_001)); - assert(fracSecsFromISOString(".1001001") == hnsecs(1_001_001)); - assert(fracSecsFromISOString(".1071601") == hnsecs(1_071_601)); - assert(fracSecsFromISOString(".1271641") == hnsecs(1_271_641)); - assert(fracSecsFromISOString(".9999999") == hnsecs(9_999_999)); - assert(fracSecsFromISOString(".9999990") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".999999") == hnsecs(9_999_990)); - assert(fracSecsFromISOString(".9999900") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".99999") == hnsecs(9_999_900)); - assert(fracSecsFromISOString(".9999000") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9999") == hnsecs(9_999_000)); - assert(fracSecsFromISOString(".9990000") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".999") == hnsecs(9_990_000)); - assert(fracSecsFromISOString(".9900000") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9900") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".99") == hnsecs(9_900_000)); - assert(fracSecsFromISOString(".9000000") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".9") == hnsecs(9_000_000)); - assert(fracSecsFromISOString(".0000999") == hnsecs(999)); - assert(fracSecsFromISOString(".0009990") == hnsecs(9990)); - assert(fracSecsFromISOString(".000999") == hnsecs(9990)); - assert(fracSecsFromISOString(".0099900") == hnsecs(99_900)); - assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); - assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); - assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); -} - - /+ Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand side of the given range (it strips comments delimited by $(D '(') and @@ -11498,356 +2447,6 @@ if (isIntegral!T && isSigned!T) // The constraints on R were already covered by } -version(unittest) -{ - //Variables to help in testing. - Duration currLocalDiffFromUTC; - immutable (TimeZone)[] testTZs; - - //All of these helper arrays are sorted in ascending order. - auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; - auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; - - //I'd use a Tuple, but I get forward reference errors if I try. - struct MonthDay - { - Month month; - short day; - - this(int m, short d) - { - month = cast(Month) m; - day = d; - } - } - - MonthDay[] testMonthDays = [MonthDay(1, 1), - MonthDay(1, 2), - MonthDay(3, 17), - MonthDay(7, 4), - MonthDay(10, 27), - MonthDay(12, 30), - MonthDay(12, 31)]; - - auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; - - auto testTODs = [TimeOfDay(0, 0, 0), - TimeOfDay(0, 0, 1), - TimeOfDay(0, 1, 0), - TimeOfDay(1, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - auto testHours = [0, 1, 12, 22, 23]; - auto testMinSecs = [0, 1, 30, 58, 59]; - - //Throwing exceptions is incredibly expensive, so we want to use a smaller - //set of values for tests using assertThrown. - auto testTODsThrown = [TimeOfDay(0, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - Date[] testDatesBC; - Date[] testDatesAD; - - DateTime[] testDateTimesBC; - DateTime[] testDateTimesAD; - - Duration[] testFracSecs; - - SysTime[] testSysTimesBC; - SysTime[] testSysTimesAD; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct GregDay { int day; Date date; } - auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), //Start of the Hebrew Calendar - GregDay(-735_233, Date(-2012, 1, 1)), - GregDay(-735_202, Date(-2012, 2, 1)), - GregDay(-735_175, Date(-2012, 2, 28)), - GregDay(-735_174, Date(-2012, 2, 29)), - GregDay(-735_173, Date(-2012, 3, 1)), - GregDay(-734_502, Date(-2010, 1, 1)), - GregDay(-734_472, Date(-2010, 1, 31)), - GregDay(-734_471, Date(-2010, 2, 1)), - GregDay(-734_444, Date(-2010, 2, 28)), - GregDay(-734_443, Date(-2010, 3, 1)), - GregDay(-734_413, Date(-2010, 3, 31)), - GregDay(-734_412, Date(-2010, 4, 1)), - GregDay(-734_383, Date(-2010, 4, 30)), - GregDay(-734_382, Date(-2010, 5, 1)), - GregDay(-734_352, Date(-2010, 5, 31)), - GregDay(-734_351, Date(-2010, 6, 1)), - GregDay(-734_322, Date(-2010, 6, 30)), - GregDay(-734_321, Date(-2010, 7, 1)), - GregDay(-734_291, Date(-2010, 7, 31)), - GregDay(-734_290, Date(-2010, 8, 1)), - GregDay(-734_260, Date(-2010, 8, 31)), - GregDay(-734_259, Date(-2010, 9, 1)), - GregDay(-734_230, Date(-2010, 9, 30)), - GregDay(-734_229, Date(-2010, 10, 1)), - GregDay(-734_199, Date(-2010, 10, 31)), - GregDay(-734_198, Date(-2010, 11, 1)), - GregDay(-734_169, Date(-2010, 11, 30)), - GregDay(-734_168, Date(-2010, 12, 1)), - GregDay(-734_139, Date(-2010, 12, 30)), - GregDay(-734_138, Date(-2010, 12, 31)), - GregDay(-731_215, Date(-2001, 1, 1)), - GregDay(-730_850, Date(-2000, 1, 1)), - GregDay(-730_849, Date(-2000, 1, 2)), - GregDay(-730_486, Date(-2000, 12, 30)), - GregDay(-730_485, Date(-2000, 12, 31)), - GregDay(-730_484, Date(-1999, 1, 1)), - GregDay(-694_690, Date(-1901, 1, 1)), - GregDay(-694_325, Date(-1900, 1, 1)), - GregDay(-585_118, Date(-1601, 1, 1)), - GregDay(-584_753, Date(-1600, 1, 1)), - GregDay(-584_388, Date(-1600, 12, 31)), - GregDay(-584_387, Date(-1599, 1, 1)), - GregDay(-365_972, Date(-1001, 1, 1)), - GregDay(-365_607, Date(-1000, 1, 1)), - GregDay(-183_351, Date(-501, 1, 1)), - GregDay(-182_986, Date(-500, 1, 1)), - GregDay(-182_621, Date(-499, 1, 1)), - GregDay(-146_827, Date(-401, 1, 1)), - GregDay(-146_462, Date(-400, 1, 1)), - GregDay(-146_097, Date(-400, 12, 31)), - GregDay(-110_302, Date(-301, 1, 1)), - GregDay(-109_937, Date(-300, 1, 1)), - GregDay(-73_778, Date(-201, 1, 1)), - GregDay(-73_413, Date(-200, 1, 1)), - GregDay(-38_715, Date(-105, 1, 1)), - GregDay(-37_254, Date(-101, 1, 1)), - GregDay(-36_889, Date(-100, 1, 1)), - GregDay(-36_524, Date(-99, 1, 1)), - GregDay(-36_160, Date(-99, 12, 31)), - GregDay(-35_794, Date(-97, 1, 1)), - GregDay(-18_627, Date(-50, 1, 1)), - GregDay(-18_262, Date(-49, 1, 1)), - GregDay(-3652, Date(-9, 1, 1)), - GregDay(-2191, Date(-5, 1, 1)), - GregDay(-1827, Date(-5, 12, 31)), - GregDay(-1826, Date(-4, 1, 1)), - GregDay(-1825, Date(-4, 1, 2)), - GregDay(-1462, Date(-4, 12, 30)), - GregDay(-1461, Date(-4, 12, 31)), - GregDay(-1460, Date(-3, 1, 1)), - GregDay(-1096, Date(-3, 12, 31)), - GregDay(-1095, Date(-2, 1, 1)), - GregDay(-731, Date(-2, 12, 31)), - GregDay(-730, Date(-1, 1, 1)), - GregDay(-367, Date(-1, 12, 30)), - GregDay(-366, Date(-1, 12, 31)), - GregDay(-365, Date(0, 1, 1)), - GregDay(-31, Date(0, 11, 30)), - GregDay(-30, Date(0, 12, 1)), - GregDay(-1, Date(0, 12, 30)), - GregDay(0, Date(0, 12, 31))]; - - auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), - GregDay(2, Date(1, 1, 2)), - GregDay(32, Date(1, 2, 1)), - GregDay(365, Date(1, 12, 31)), - GregDay(366, Date(2, 1, 1)), - GregDay(731, Date(3, 1, 1)), - GregDay(1096, Date(4, 1, 1)), - GregDay(1097, Date(4, 1, 2)), - GregDay(1460, Date(4, 12, 30)), - GregDay(1461, Date(4, 12, 31)), - GregDay(1462, Date(5, 1, 1)), - GregDay(17_898, Date(50, 1, 1)), - GregDay(35_065, Date(97, 1, 1)), - GregDay(36_160, Date(100, 1, 1)), - GregDay(36_525, Date(101, 1, 1)), - GregDay(37_986, Date(105, 1, 1)), - GregDay(72_684, Date(200, 1, 1)), - GregDay(73_049, Date(201, 1, 1)), - GregDay(109_208, Date(300, 1, 1)), - GregDay(109_573, Date(301, 1, 1)), - GregDay(145_732, Date(400, 1, 1)), - GregDay(146_098, Date(401, 1, 1)), - GregDay(182_257, Date(500, 1, 1)), - GregDay(182_622, Date(501, 1, 1)), - GregDay(364_878, Date(1000, 1, 1)), - GregDay(365_243, Date(1001, 1, 1)), - GregDay(584_023, Date(1600, 1, 1)), - GregDay(584_389, Date(1601, 1, 1)), - GregDay(693_596, Date(1900, 1, 1)), - GregDay(693_961, Date(1901, 1, 1)), - GregDay(729_755, Date(1999, 1, 1)), - GregDay(730_120, Date(2000, 1, 1)), - GregDay(730_121, Date(2000, 1, 2)), - GregDay(730_484, Date(2000, 12, 30)), - GregDay(730_485, Date(2000, 12, 31)), - GregDay(730_486, Date(2001, 1, 1)), - GregDay(733_773, Date(2010, 1, 1)), - GregDay(733_774, Date(2010, 1, 2)), - GregDay(733_803, Date(2010, 1, 31)), - GregDay(733_804, Date(2010, 2, 1)), - GregDay(733_831, Date(2010, 2, 28)), - GregDay(733_832, Date(2010, 3, 1)), - GregDay(733_862, Date(2010, 3, 31)), - GregDay(733_863, Date(2010, 4, 1)), - GregDay(733_892, Date(2010, 4, 30)), - GregDay(733_893, Date(2010, 5, 1)), - GregDay(733_923, Date(2010, 5, 31)), - GregDay(733_924, Date(2010, 6, 1)), - GregDay(733_953, Date(2010, 6, 30)), - GregDay(733_954, Date(2010, 7, 1)), - GregDay(733_984, Date(2010, 7, 31)), - GregDay(733_985, Date(2010, 8, 1)), - GregDay(734_015, Date(2010, 8, 31)), - GregDay(734_016, Date(2010, 9, 1)), - GregDay(734_045, Date(2010, 9, 30)), - GregDay(734_046, Date(2010, 10, 1)), - GregDay(734_076, Date(2010, 10, 31)), - GregDay(734_077, Date(2010, 11, 1)), - GregDay(734_106, Date(2010, 11, 30)), - GregDay(734_107, Date(2010, 12, 1)), - GregDay(734_136, Date(2010, 12, 30)), - GregDay(734_137, Date(2010, 12, 31)), - GregDay(734_503, Date(2012, 1, 1)), - GregDay(734_534, Date(2012, 2, 1)), - GregDay(734_561, Date(2012, 2, 28)), - GregDay(734_562, Date(2012, 2, 29)), - GregDay(734_563, Date(2012, 3, 1)), - GregDay(734_858, Date(2012, 12, 21))]; - - //I'd use a Tuple, but I get forward reference errors if I try. - struct DayOfYear { int day; MonthDay md; } - auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(3, 1)), - DayOfYear(90, MonthDay(3, 31)), - DayOfYear(91, MonthDay(4, 1)), - DayOfYear(120, MonthDay(4, 30)), - DayOfYear(121, MonthDay(5, 1)), - DayOfYear(151, MonthDay(5, 31)), - DayOfYear(152, MonthDay(6, 1)), - DayOfYear(181, MonthDay(6, 30)), - DayOfYear(182, MonthDay(7, 1)), - DayOfYear(212, MonthDay(7, 31)), - DayOfYear(213, MonthDay(8, 1)), - DayOfYear(243, MonthDay(8, 31)), - DayOfYear(244, MonthDay(9, 1)), - DayOfYear(273, MonthDay(9, 30)), - DayOfYear(274, MonthDay(10, 1)), - DayOfYear(304, MonthDay(10, 31)), - DayOfYear(305, MonthDay(11, 1)), - DayOfYear(334, MonthDay(11, 30)), - DayOfYear(335, MonthDay(12, 1)), - DayOfYear(363, MonthDay(12, 29)), - DayOfYear(364, MonthDay(12, 30)), - DayOfYear(365, MonthDay(12, 31))]; - - auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(2, 29)), - DayOfYear(61, MonthDay(3, 1)), - DayOfYear(91, MonthDay(3, 31)), - DayOfYear(92, MonthDay(4, 1)), - DayOfYear(121, MonthDay(4, 30)), - DayOfYear(122, MonthDay(5, 1)), - DayOfYear(152, MonthDay(5, 31)), - DayOfYear(153, MonthDay(6, 1)), - DayOfYear(182, MonthDay(6, 30)), - DayOfYear(183, MonthDay(7, 1)), - DayOfYear(213, MonthDay(7, 31)), - DayOfYear(214, MonthDay(8, 1)), - DayOfYear(244, MonthDay(8, 31)), - DayOfYear(245, MonthDay(9, 1)), - DayOfYear(274, MonthDay(9, 30)), - DayOfYear(275, MonthDay(10, 1)), - DayOfYear(305, MonthDay(10, 31)), - DayOfYear(306, MonthDay(11, 1)), - DayOfYear(335, MonthDay(11, 30)), - DayOfYear(336, MonthDay(12, 1)), - DayOfYear(364, MonthDay(12, 29)), - DayOfYear(365, MonthDay(12, 30)), - DayOfYear(366, MonthDay(12, 31))]; - - void initializeTests() @safe - { - import std.algorithm.sorting : sort; - import std.typecons : Rebindable; - immutable lt = LocalTime().utcToTZ(0); - currLocalDiffFromUTC = dur!"hnsecs"(lt); - - version(Posix) - { - immutable otherTZ = lt < 0 ? PosixTimeZone.getTimeZone("Australia/Sydney") - : PosixTimeZone.getTimeZone("America/Denver"); - } - else version(Windows) - { - immutable otherTZ = lt < 0 ? WindowsTimeZone.getTimeZone("AUS Eastern Standard Time") - : WindowsTimeZone.getTimeZone("Mountain Standard Time"); - } - - immutable ot = otherTZ.utcToTZ(0); - - auto diffs = [0L, lt, ot]; - auto diffAA = [0L : Rebindable!(immutable TimeZone)(UTC())]; - diffAA[lt] = Rebindable!(immutable TimeZone)(LocalTime()); - diffAA[ot] = Rebindable!(immutable TimeZone)(otherTZ); - - sort(diffs); - testTZs = [diffAA[diffs[0]], diffAA[diffs[1]], diffAA[diffs[2]]]; - - testFracSecs = [Duration.zero, hnsecs(1), hnsecs(5007), hnsecs(9_999_999)]; - - foreach (year; testYearsBC) - { - foreach (md; testMonthDays) - testDatesBC ~= Date(year, md.month, md.day); - } - - foreach (year; testYearsAD) - { - foreach (md; testMonthDays) - testDatesAD ~= Date(year, md.month, md.day); - } - - foreach (dt; testDatesBC) - { - foreach (tod; testTODs) - testDateTimesBC ~= DateTime(dt, tod); - } - - foreach (dt; testDatesAD) - { - foreach (tod; testTODs) - testDateTimesAD ~= DateTime(dt, tod); - } - - foreach (dt; testDateTimesBC) - { - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - testSysTimesBC ~= SysTime(dt, fs, tz); - } - } - - foreach (dt; testDateTimesAD) - { - foreach (tz; testTZs) - { - foreach (fs; testFracSecs) - testSysTimesAD ~= SysTime(dt, fs, tz); - } - } - } -} - - @safe unittest { import std.traits : hasUnsharedAliasing; diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 8b3cdb293b9..2a447ad75a5 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -8,3 +8,9320 @@ LREF2=$(D $2) +/ module std.datetime.systime; + +import core.time; +import std.datetime.common; +import std.datetime.date; +import std.datetime.datetime; +import std.datetime.timeofday; +import std.datetime.timezone; +import std.format : format; +import std.exception : enforce; +import std.traits : isSomeString, Unqual; + +version(Windows) +{ + import core.stdc.time : time_t; + import core.sys.windows.windows; + import core.sys.windows.winsock2; +} +else version(Posix) +{ + import core.sys.posix.signal : timespec; + import core.sys.posix.sys.types : time_t; +} + +version(unittest) +{ + import core.exception : AssertError; + import std.exception : assertThrown; +} + + +@safe unittest +{ + initializeTests(); +} + + +/++ + $(D SysTime) is the type used to get the current time from the + system or doing anything that involves time zones. Unlike + $(LREF DateTime), the time zone is an integral part of $(D SysTime) (though for + local time applications, time zones can be ignored and + it will work, since it defaults to using the local time zone). It holds its + internal time in std time (hnsecs since midnight, January 1st, 1 A.D. UTC), + so it interfaces well with the system time. However, that means that, unlike + $(LREF DateTime), it is not optimized for calendar-based operations, and + getting individual units from it such as years or days is going to involve + conversions and be less efficient. + + For calendar-based operations that don't + care about time zones, then $(LREF DateTime) would be the type to + use. For system time, use $(D SysTime). + + $(LREF2 .Clock.currTime, Clock.currTime) will return the current time as a $(D SysTime). + To convert a $(D SysTime) to a $(LREF Date) or $(LREF DateTime), simply cast + it. To convert a $(LREF Date) or $(LREF DateTime) to a + $(D SysTime), use $(D SysTime)'s constructor, and pass in the + intended time zone with it (or don't pass in a $(LREF2 .TimeZone, TimeZone), and the local + time zone will be used). Be aware, however, that converting from a + $(LREF DateTime) to a $(D SysTime) will not necessarily be 100% accurate due to + DST (one hour of the year doesn't exist and another occurs twice). + To not risk any conversion errors, keep times as + $(D SysTime)s. Aside from DST though, there shouldn't be any conversion + problems. + + For using time zones other than local time or UTC, use + $(LREF PosixTimeZone) on Posix systems (or on Windows, if providing the TZ + Database files), and use $(LREF WindowsTimeZone) on Windows systems. + The time in $(D SysTime) is kept internally in hnsecs from midnight, + January 1st, 1 A.D. UTC. Conversion error cannot happen when changing + the time zone of a $(D SysTime). $(LREF LocalTime) is the $(LREF2 .TimeZone, TimeZone) class + which represents the local time, and $(D UTC) is the $(LREF2 .TimeZone, TimeZone) class + which represents UTC. $(D SysTime) uses $(LREF LocalTime) if no $(LREF2 .TimeZone, TimeZone) + is provided. For more details on time zones, see the documentation for + $(LREF2 .TimeZone, TimeZone), $(LREF PosixTimeZone), and $(LREF WindowsTimeZone). + + $(D SysTime)'s range is from approximately 29,000 B.C. to approximately + 29,000 A.D. + +/ +struct SysTime +{ + import core.stdc.time : tm; + version(Posix) import core.sys.posix.sys.time : timeval; + import std.typecons : Rebindable; + +public: + + /++ + Params: + dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s + internal std time. As $(LREF DateTime) has no concept of + time zone, tz is used as its time zone. + tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, + $(LREF LocalTime) will be used. The given $(LREF DateTime) is + assumed to be in the given time zone. + +/ + this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow + { + try + this(dateTime, Duration.zero, tz); + catch (Exception e) + assert(0, "SysTime's constructor threw when it shouldn't have."); + } + + @safe unittest + { + static void test(DateTime dt, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s", dt)); + } + + test(DateTime.init, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), UTC(), 0); + test(DateTime(1, 1, 1, 0, 0, 1), UTC(), 10_000_000L); + test(DateTime(0, 12, 31, 23, 59, 59), UTC(), -10_000_000L); + + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(-60)), 36_000_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(Duration.zero), 0); + test(DateTime(1, 1, 1, 0, 0, 0), new immutable SimpleTimeZone(dur!"minutes"(60)), -36_000_000_000L); + } + + /++ + Params: + dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s + internal std time. As $(LREF DateTime) has no concept of + time zone, tz is used as its time zone. + fracSecs = The fractional seconds portion of the time. + tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, + $(LREF LocalTime) will be used. The given $(LREF DateTime) is + assumed to be in the given time zone. + + Throws: + $(LREF DateTimeException) if $(D fracSecs) is negative or if it's + greater than or equal to one second. + +/ + this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe + { + enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); + auto nonNullTZ = tz is null ? LocalTime() : tz; + + immutable dateDiff = dateTime.date - Date.init; + immutable todDiff = dateTime.timeOfDay - TimeOfDay.init; + + immutable adjustedTime = dateDiff + todDiff + fracSecs; + immutable standardTime = nonNullTZ.tzToUTC(adjustedTime.total!"hnsecs"); + + this(standardTime, nonNullTZ); + } + + @safe unittest + { + static void test(DateTime dt, Duration fracSecs, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, fracSecs, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), + format("Given DateTime: %s, Given Duration: %s", dt, fracSecs)); + } + + test(DateTime.init, Duration.zero, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), Duration.zero, UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), Duration.zero, UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), msecs(1), UTC(), 10_000L); + test(DateTime(0, 12, 31, 23, 59, 59), msecs(999), UTC(), -10_000L); + + test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC(), -1); + test(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC(), -9_999_999); + test(DateTime(0, 12, 31, 23, 59, 59), Duration.zero, UTC(), -10_000_000); + + assertThrown!DateTimeException(SysTime(DateTime.init, hnsecs(-1), UTC())); + assertThrown!DateTimeException(SysTime(DateTime.init, seconds(1), UTC())); + } + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use the overload which takes a Duration instead of a FracSec.") + this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) @safe + { + immutable fracHNSecs = fracSec.hnsecs; + enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + _timezone = tz is null ? LocalTime() : tz; + + try + { + immutable dateDiff = (dateTime.date - Date(1, 1, 1)).total!"hnsecs"; + immutable todDiff = (dateTime.timeOfDay - TimeOfDay(0, 0, 0)).total!"hnsecs"; + + immutable adjustedTime = dateDiff + todDiff + fracHNSecs; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone); + } + catch (Exception e) + assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); + } + + deprecated @safe unittest + { + static void test(DateTime dt, FracSec fracSec, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(dt, fracSec, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), + format("Given DateTime: %s, Given FracSec: %s", dt, fracSec)); + } + + test(DateTime.init, FracSec.init, UTC(), 0); + test(DateTime(1, 1, 1, 12, 30, 33), FracSec.init, UTC(), 450_330_000_000L); + test(DateTime(0, 12, 31, 12, 30, 33), FracSec.init, UTC(), -413_670_000_000L); + test(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC(), 10_000L); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC(), -10_000L); + + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC(), -1); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC(), -9_999_999); + test(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0), UTC(), -10_000_000); + + assertThrown!DateTimeException(SysTime(DateTime.init, FracSec.from!"hnsecs"(-1), UTC())); + } + + /++ + Params: + date = The $(LREF Date) to use to set this $(LREF SysTime)'s internal std + time. As $(LREF Date) has no concept of time zone, tz is used as + its time zone. + tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, + $(LREF LocalTime) will be used. The given $(LREF Date) is assumed + to be in the given time zone. + +/ + this(in Date date, immutable TimeZone tz = null) @safe nothrow + { + _timezone = tz is null ? LocalTime() : tz; + + try + { + immutable adjustedTime = (date - Date(1, 1, 1)).total!"hnsecs"; + immutable standardTime = _timezone.tzToUTC(adjustedTime); + + this(standardTime, _timezone); + } + catch (Exception e) + assert(0, "Date's constructor through when it shouldn't have."); + } + + @safe unittest + { + static void test(Date d, immutable TimeZone tz, long expected) + { + auto sysTime = SysTime(d, tz); + assert(sysTime._stdTime == expected); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given Date: %s", d)); + } + + test(Date.init, UTC(), 0); + test(Date(1, 1, 1), UTC(), 0); + test(Date(1, 1, 2), UTC(), 864000000000); + test(Date(0, 12, 31), UTC(), -864000000000); + } + + /++ + Note: + Whereas the other constructors take in the given date/time, assume + that it's in the given time zone, and convert it to hnsecs in UTC + since midnight, January 1st, 1 A.D. UTC - i.e. std time - this + constructor takes a std time, which is specifically already in UTC, + so no conversion takes place. Of course, the various getter + properties and functions will use the given time zone's conversion + function to convert the results to that time zone, but no conversion + of the arguments to this constructor takes place. + + Params: + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. + tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, + $(LREF LocalTime) will be used. + +/ + this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow + { + _stdTime = stdTime; + _timezone = tz is null ? LocalTime() : tz; + } + + @safe unittest + { + static void test(long stdTime, immutable TimeZone tz) + { + auto sysTime = SysTime(stdTime, tz); + assert(sysTime._stdTime == stdTime); + assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given stdTime: %s", stdTime)); + } + + foreach (stdTime; [-1234567890L, -250, 0, 250, 1235657390L]) + { + foreach (tz; testTZs) + test(stdTime, tz); + } + } + + /++ + Params: + rhs = The $(LREF SysTime) to assign to this one. + +/ + ref SysTime opAssign(const ref SysTime rhs) return @safe pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone; + return this; + } + + /++ + Params: + rhs = The $(LREF SysTime) to assign to this one. + +/ + ref SysTime opAssign(SysTime rhs) scope return @safe pure nothrow + { + _stdTime = rhs._stdTime; + _timezone = rhs._timezone; + return this; + } + + /++ + Checks for equality between this $(LREF SysTime) and the given + $(LREF SysTime). + + Note that the time zone is ignored. Only the internal + std times (which are in UTC) are compared. + +/ + bool opEquals(const SysTime rhs) @safe const pure nothrow + { + return opEquals(rhs); + } + + /// ditto + bool opEquals(const ref SysTime rhs) @safe const pure nothrow + { + return _stdTime == rhs._stdTime; + } + + @safe unittest + { + import std.range : chain; + + assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); + assert(SysTime(DateTime.init, UTC()) == SysTime(0)); + assert(SysTime(Date.init, UTC()) == SysTime(0)); + assert(SysTime(0) == SysTime(0)); + + static void test(DateTime dt, immutable TimeZone tz1, immutable TimeZone tz2) + { + auto st1 = SysTime(dt); + st1.timezone = tz1; + + auto st2 = SysTime(dt); + st2.timezone = tz2; + + assert(st1 == st2); + } + + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + { + foreach (dt; chain(testDateTimesBC, testDateTimesAD)) + test(dt, tz1, tz2); + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + assert(st == st); + assert(st == cst); + //assert(st == ist); + assert(cst == st); + assert(cst == cst); + //assert(cst == ist); + //assert(ist == st); + //assert(ist == cst); + //assert(ist == ist); + } + + /++ + Compares this $(LREF SysTime) with the given $(LREF SysTime). + + Time zone is irrelevant when comparing $(LREF SysTime)s. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in SysTime rhs) @safe const pure nothrow + { + if (_stdTime < rhs._stdTime) + return -1; + if (_stdTime > rhs._stdTime) + return 1; + return 0; + } + + @safe unittest + { + import std.algorithm.iteration : map; + import std.array : array; + import std.range : chain; + + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(0).opCmp(SysTime(0)) == 0); + + static void testEqual(SysTime st, immutable TimeZone tz1, immutable TimeZone tz2) + { + auto st1 = st; + st1.timezone = tz1; + + auto st2 = st; + st2.timezone = tz2; + + assert(st1.opCmp(st2) == 0); + } + + auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); + + foreach (st; sts) + { + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + testEqual(st, tz1, tz2); + } + } + + static void testCmp(SysTime st1, immutable TimeZone tz1, SysTime st2, immutable TimeZone tz2) + { + st1.timezone = tz1; + st2.timezone = tz2; + assert(st1.opCmp(st2) < 0); + assert(st2.opCmp(st1) > 0); + } + + foreach (si, st1; sts) + { + foreach (st2; sts[si + 1 .. $]) + { + foreach (tz1; testTZs) + { + foreach (tz2; testTZs) + testCmp(st1, tz1, st2, tz2); + } + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 33, 30)); + assert(st.opCmp(st) == 0); + assert(st.opCmp(cst) == 0); + //assert(st.opCmp(ist) == 0); + assert(cst.opCmp(st) == 0); + assert(cst.opCmp(cst) == 0); + //assert(cst.opCmp(ist) == 0); + //assert(ist.opCmp(st) == 0); + //assert(ist.opCmp(cst) == 0); + //assert(ist.opCmp(ist) == 0); + } + + /** + * Returns: A hash of the $(LREF SysTime) + */ + size_t toHash() const @nogc pure nothrow @safe + { + static if (is(size_t == ulong)) + return _stdTime; + else + { + // MurmurHash2 + enum ulong m = 0xc6a4a7935bd1e995UL; + enum ulong n = m * 16; + enum uint r = 47; + + ulong k = _stdTime; + k *= m; + k ^= k >> r; + k *= m; + + ulong h = n; + h ^= k; + h *= m; + + return cast(size_t) h; + } + } + + @safe unittest + { + assert(SysTime(0).toHash == SysTime(0).toHash); + assert(SysTime(DateTime(2000, 1, 1)).toHash == SysTime(DateTime(2000, 1, 1)).toHash); + assert(SysTime(DateTime(2000, 1, 1)).toHash != SysTime(DateTime(2000, 1, 2)).toHash); + + // test that timezones aren't taken into account + assert(SysTime(0, LocalTime()).toHash == SysTime(0, LocalTime()).toHash); + assert(SysTime(0, LocalTime()).toHash == SysTime(0, UTC()).toHash); + assert(SysTime(DateTime(2000, 1, 1), LocalTime()).toHash == SysTime(DateTime(2000, 1, 1), LocalTime()).toHash); + immutable zone = new SimpleTimeZone(dur!"minutes"(60)); + assert(SysTime(DateTime(2000, 1, 1, 1), zone).toHash == SysTime(DateTime(2000, 1, 1), UTC()).toHash); + assert(SysTime(DateTime(2000, 1, 1), zone).toHash != SysTime(DateTime(2000, 1, 1), UTC()).toHash); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const nothrow + { + return (cast(Date) this).year; + } + + @safe unittest + { + import std.range : chain; + static void test(SysTime sysTime, long expected) + { + assert(sysTime.year == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 1); + test(SysTime(1, UTC()), 1); + test(SysTime(-1, UTC()), 0); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), year); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.year == 1999); + //assert(ist.year == 1999); + } + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this $(LREF SysTime)'s year to. + + Throws: + $(LREF DateTimeException) if the new year is not a leap year and the + resulting date would be on February 29th. + +/ + @property void year(int year) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.year = year; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime st, int year, in SysTime expected) + { + st.year = year; + assert(st == expected); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (year; chain(testYearsBC, testYearsAD)) + { + auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, year, e); + } + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + test(SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz), 2000, + SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(2000, 2, 28), tod), fs, tz), 1999, + SysTime(DateTime(Date(1999, 2, 28), tod), fs, tz)); + } + + foreach (tod; testTODsThrown) + { + auto st = SysTime(DateTime(Date(2000, 2, 29), tod), fs, tz); + assertThrown!DateTimeException(st.year = 1999); + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.year = 7)); + //static assert(!__traits(compiles, ist.year = 7)); + } + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(LREF DateTimeException) if $(D isAD) is true. + +/ + @property ushort yearBC() @safe const + { + return (cast(Date) this).yearBC; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); + assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); + assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); + } + + @safe unittest + { + import std.exception : assertNotThrown; + foreach (st; testSysTimesBC) + { + auto msg = format("SysTime: %s", st); + assertNotThrown!DateTimeException(st.yearBC, msg); + assert(st.yearBC == (st.year * -1) + 1, msg); + } + + foreach (st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) + assertThrown!DateTimeException(st.yearBC, format("SysTime: %s", st)); + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.year = 12; + assert(st.year == 12); + static assert(!__traits(compiles, cst.year = 12)); + //static assert(!__traits(compiles, ist.year = 12)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF SysTime)'s year to. + + Throws: + $(LREF DateTimeException) if a non-positive value is given. + +/ + @property void yearBC(int year) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.yearBC = year; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); + st.yearBC = 1; + assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); + + st.yearBC = 10; + assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); + } + + @safe unittest + { + import std.range : chain; + static void test(SysTime st, int year, in SysTime expected) + { + st.yearBC = year; + assert(st == expected, format("SysTime: %s", st)); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (year; testYearsBC) + { + auto e = SysTime(DateTime(year, dt.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, (year * -1) + 1, e); + } + } + + foreach (st; [testSysTimesBC[0], testSysTimesBC[$ - 1], testSysTimesAD[0], testSysTimesAD[$ - 1]]) + { + foreach (year; testYearsBC) + assertThrown!DateTimeException(st.yearBC = year); + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + test(SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz), 2001, + SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(-2000, 2, 28), tod), fs, tz), 2000, + SysTime(DateTime(Date(-1999, 2, 28), tod), fs, tz)); + } + + foreach (tod; testTODsThrown) + { + auto st = SysTime(DateTime(Date(-2000, 2, 29), tod), fs, tz); + assertThrown!DateTimeException(st.year = -1999); + } + } + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.yearBC = 12; + assert(st.yearBC == 12); + static assert(!__traits(compiles, cst.yearBC = 12)); + //static assert(!__traits(compiles, ist.yearBC = 12)); + } + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const nothrow + { + return (cast(Date) this).month; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, Month expected) + { + assert(sysTime.month == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), Month.jan); + test(SysTime(1, UTC()), Month.jan); + test(SysTime(-1, UTC()), Month.dec); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + test(SysTime(dt, fs, tz), md.month); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.month == 7); + //assert(ist.month == 7); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF SysTime)'s month to. + + Throws: + $(LREF DateTimeException) if the given month is not a valid month. + +/ + @property void month(Month month) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.month = month; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + import std.algorithm.iteration : filter; + import std.range : chain; + + static void test(SysTime st, Month month, in SysTime expected) + { + st.month = cast(Month) month; + assert(st == expected); + } + + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + foreach (md; testMonthDays) + { + if (st.day > maxDay(dt.year, md.month)) + continue; + auto e = SysTime(DateTime(dt.year, md.month, dt.day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + test(st, md.month, e); + } + } + + foreach (fs; testFracSecs) + { + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + foreach (year; filter!((a){return yearIsLeapYear(a);}) (chain(testYearsBC, testYearsAD))) + { + test(SysTime(DateTime(Date(year, 1, 29), tod), fs, tz), + Month.feb, + SysTime(DateTime(Date(year, 2, 29), tod), fs, tz)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + test(SysTime(DateTime(Date(year, 1, 28), tod), fs, tz), + Month.feb, + SysTime(DateTime(Date(year, 2, 28), tod), fs, tz)); + test(SysTime(DateTime(Date(year, 7, 30), tod), fs, tz), + Month.jun, + SysTime(DateTime(Date(year, 6, 30), tod), fs, tz)); + } + } + } + } + + foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) + { + foreach (tz; testTZs) + { + foreach (tod; testTODsThrown) + { + foreach (year; [testYearsBC[$-3], testYearsBC[$-2], + testYearsBC[$-2], testYearsAD[0], + testYearsAD[$-2], testYearsAD[$-1]]) + { + auto day = yearIsLeapYear(year) ? 30 : 29; + auto st1 = SysTime(DateTime(Date(year, 1, day), tod), fs, tz); + assertThrown!DateTimeException(st1.month = Month.feb); + + auto st2 = SysTime(DateTime(Date(year, 7, 31), tod), fs, tz); + assertThrown!DateTimeException(st2.month = Month.jun); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.month = 12)); + //static assert(!__traits(compiles, ist.month = 12)); + } + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const nothrow + { + return (cast(Date) this).day; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); + assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); + assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.day == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 1); + test(SysTime(1, UTC()), 1); + test(SysTime(-1, UTC()), 31); + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + { + auto dt = DateTime(Date(year, md.month, md.day), tod); + + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), md.day); + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.day == 6); + //assert(ist.day == 6); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF SysTime)'s day to. + + Throws: + $(LREF DateTimeException) if the given day is not a valid day of the + current month. + +/ + @property void day(int day) @safe + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.day = day; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + adjTime = newDaysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + import std.traits : EnumMembers; + + foreach (day; chain(testDays)) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + + if (day > maxDay(dt.year, dt.month)) + continue; + auto expected = SysTime(DateTime(dt.year, dt.month, day, dt.hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + st.day = day; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + foreach (tz; testTZs) + { + foreach (tod; testTODs) + { + foreach (fs; testFracSecs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (month; EnumMembers!Month) + { + auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); + immutable max = maxDay(year, month); + auto expected = SysTime(DateTime(Date(year, month, max), tod), fs, tz); + + st.day = max; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + } + } + } + + foreach (tz; testTZs) + { + foreach (tod; testTODsThrown) + { + foreach (fs; [testFracSecs[0], testFracSecs[$-1]]) + { + foreach (year; [testYearsBC[$-3], testYearsBC[$-2], + testYearsBC[$-2], testYearsAD[0], + testYearsAD[$-2], testYearsAD[$-1]]) + { + foreach (month; EnumMembers!Month) + { + auto st = SysTime(DateTime(Date(year, month, 1), tod), fs, tz); + immutable max = maxDay(year, month); + + assertThrown!DateTimeException(st.day = max + 1); + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.day = 27)); + //static assert(!__traits(compiles, ist.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + return cast(ubyte) getUnitsFromHNSecs!"hours"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.hour == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 23); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), hour); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.hour == 12); + //assert(ist.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hours to set this $(LREF SysTime)'s hour to. + + Throws: + $(LREF DateTimeException) if the given hour are not a valid hour of + the day. + +/ + @property void hour(int hour) @safe + { + enforceValid!"hours"(hour); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + hnsecs += convert!("hours", "hnsecs")(hour); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (hour; chain(testHours)) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, hour, dt.minute, dt.second), + st.fracSecs, + st.timezone); + st.hour = hour; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.hour = -1); + assertThrown!DateTimeException(st.hour = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.hour = 27)); + //static assert(!__traits(compiles, ist.hour = 27)); + } + + + /++ + Minutes past the current hour. + +/ + @property ubyte minute() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + + return cast(ubyte) getUnitsFromHNSecs!"minutes"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.minute == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 59); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), minute); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.minute == 30); + //assert(ist.minute == 30); + } + + + /++ + Minutes past the current hour. + + Params: + minute = The minute to set this $(LREF SysTime)'s minute to. + + Throws: + $(LREF DateTimeException) if the given minute are not a valid minute + of an hour. + +/ + @property void minute(int minute) @safe + { + enforceValid!"minutes"(minute); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); + + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (minute; testMinSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, minute, dt.second), + st.fracSecs, + st.timezone); + st.minute = minute; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.minute = -1); + assertThrown!DateTimeException(st.minute = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.minute = 27)); + //static assert(!__traits(compiles, ist.minute = 27)); + } + + + /++ + Seconds past the current minute. + +/ + @property ubyte second() @safe const nothrow + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + hnsecs = removeUnitsFromHNSecs!"hours"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"minutes"(hnsecs); + + return cast(ubyte) getUnitsFromHNSecs!"seconds"(hnsecs); + } + + @safe unittest + { + import std.range : chain; + + static void test(SysTime sysTime, int expected) + { + assert(sysTime.second == expected, format("Value given: %s", sysTime)); + } + + test(SysTime(0, UTC()), 0); + test(SysTime(1, UTC()), 0); + test(SysTime(-1, UTC()), 59); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), second); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.second == 33); + //assert(ist.second == 33); + } + + + /++ + Seconds past the current minute. + + Params: + second = The second to set this $(LREF SysTime)'s second to. + + Throws: + $(LREF DateTimeException) if the given second are not a valid second + of a minute. + +/ + @property void second(int second) @safe + { + enforceValid!"seconds"(second); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + hnsecs += convert!("seconds", "hnsecs")(second); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + @safe unittest + { + import std.range : chain; + + foreach (second; testMinSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, second), + st.fracSecs, + st.timezone); + st.second = second; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.second = -1); + assertThrown!DateTimeException(st.second = 60); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.seconds = 27)); + //static assert(!__traits(compiles, ist.seconds = 27)); + } + + + /++ + Fractional seconds past the second (i.e. the portion of a + $(LREF SysTime) which is less than a second). + +/ + @property Duration fracSecs() @safe const nothrow + { + auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + return dur!"hnsecs"(removeUnitsFromHNSecs!"seconds"(hnsecs)); + } + + /// + @safe unittest + { + auto dt = DateTime(1982, 4, 1, 20, 59, 22); + assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); + assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); + assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567)); + + // SysTime and Duration both have a precision of hnsecs (100 ns), + // so nsecs are going to be truncated. + assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700)); + } + + @safe unittest + { + import std.range : chain; + + assert(SysTime(0, UTC()).fracSecs == Duration.zero); + assert(SysTime(1, UTC()).fracSecs == hnsecs(1)); + assert(SysTime(-1, UTC()).fracSecs == hnsecs(9_999_999)); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + assert(SysTime(dt, fs, tz).fracSecs == fs); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.fracSecs == Duration.zero); + //assert(ist.fracSecs == Duration.zero); + } + + + /++ + Fractional seconds past the second (i.e. the portion of a + $(LREF SysTime) which is less than a second). + + Params: + fracSecs = The duration to set this $(LREF SysTime)'s fractional + seconds to. + + Throws: + $(LREF DateTimeException) if the given duration is negative or if + it's greater than or equal to one second. + +/ + @property void fracSecs(Duration fracSecs) @safe + { + enforce(fracSecs >= Duration.zero, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + enforce(fracSecs < seconds(1), new DateTimeException("Fractional seconds must be less than one second.")); + + auto oldHNSecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(oldHNSecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = oldHNSecs < 0; + + if (negative) + oldHNSecs += convert!("hours", "hnsecs")(24); + + immutable seconds = splitUnitsFromHNSecs!"seconds"(oldHNSecs); + immutable secondsHNSecs = convert!("seconds", "hnsecs")(seconds); + auto newHNSecs = fracSecs.total!"hnsecs" + secondsHNSecs; + + if (negative) + newHNSecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + newHNSecs; + } + + /// + @safe unittest + { + auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); + assert(st.fracSecs == Duration.zero); + + st.fracSecs = msecs(213); + assert(st.fracSecs == msecs(213)); + + st.fracSecs = hnsecs(1234567); + assert(st.fracSecs == hnsecs(1234567)); + + // SysTime has a precision of hnsecs (100 ns), so nsecs are + // going to be truncated. + st.fracSecs = nsecs(123456789); + assert(st.fracSecs == hnsecs(1234567)); + } + + @safe unittest + { + import std.range : chain; + + foreach (fracSec; testFracSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(dt, fracSec, st.timezone); + st.fracSecs = fracSec; + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.fracSecs = hnsecs(-1)); + assertThrown!DateTimeException(st.fracSecs = seconds(1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.fracSecs = msecs(7))); + //static assert(!__traits(compiles, ist.fracSecs = msecs(7))); + } + + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " ~ + "It returns a Duration instead of a FracSec, as FracSec is being deprecated.") + @property FracSec fracSec() @safe const nothrow + { + try + { + auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs); + + return FracSec.from!"hnsecs"(cast(int) hnsecs); + } + catch (Exception e) + assert(0, "FracSec.from!\"hnsecs\"() threw."); + } + + deprecated @safe unittest + { + import std.range; + + static void test(SysTime sysTime, FracSec expected, size_t line = __LINE__) + { + if (sysTime.fracSec != expected) + throw new AssertError(format("Value given: %s", sysTime.fracSec), __FILE__, line); + } + + test(SysTime(0, UTC()), FracSec.from!"hnsecs"(0)); + test(SysTime(1, UTC()), FracSec.from!"hnsecs"(1)); + test(SysTime(-1, UTC()), FracSec.from!"hnsecs"(9_999_999)); + + foreach (tz; testTZs) + { + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (hour; testHours) + { + foreach (minute; testMinSecs) + { + foreach (second; testMinSecs) + { + auto dt = DateTime(Date(year, md.month, md.day), TimeOfDay(hour, minute, second)); + foreach (fs; testFracSecs) + test(SysTime(dt, fs, tz), FracSec.from!"hnsecs"(fs.total!"hnsecs")); + } + } + } + } + } + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.fracSec == FracSec.zero); + //assert(ist.fracSec == FracSec.zero); + } + + + // Explicitly undocumented. It will be removed in August 2017. @@@DEPRECATED_2017-08@@@ + deprecated("Please use fracSecs (with an s) rather than fracSec (without an s). " ~ + "It takes a Duration instead of a FracSec, as FracSec is being deprecated.") + @property void fracSec(FracSec fracSec) @safe + { + immutable fracHNSecs = fracSec.hnsecs; + enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); + + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable daysHNSecs = convert!("days", "hnsecs")(days); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + hnsecs = fracHNSecs; + hnsecs += convert!("hours", "hnsecs")(hour); + hnsecs += convert!("minutes", "hnsecs")(minute); + hnsecs += convert!("seconds", "hnsecs")(second); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + adjTime = daysHNSecs + hnsecs; + } + + deprecated @safe unittest + { + import std.range; + + foreach (fracSec; testFracSecs) + { + foreach (st; chain(testSysTimesBC, testSysTimesAD)) + { + auto dt = cast(DateTime) st; + auto expected = SysTime(dt, fracSec, st.timezone); + st.fracSec = FracSec.from!"hnsecs"(fracSec.total!"hnsecs"); + assert(st == expected, format("[%s] [%s]", st, expected)); + } + } + + auto st = testSysTimesAD[0]; + assertThrown!DateTimeException(st.fracSec = FracSec.from!"hnsecs"(-1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.fracSec = FracSec.from!"msecs"(7))); + //static assert(!__traits(compiles, ist.fracSec = FracSec.from!"msecs"(7))); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the + internal representation of $(LREF SysTime). + +/ + @property long stdTime() @safe const pure nothrow + { + return _stdTime; + } + + @safe unittest + { + assert(SysTime(0).stdTime == 0); + assert(SysTime(1).stdTime == 1); + assert(SysTime(-1).stdTime == -1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC()).stdTime == 330_000_502L); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621_355_968_000_000_000L); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.stdTime > 0); + //assert(ist.stdTime > 0); + } + + + /++ + The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the + internal representation of $(LREF SysTime). + + Params: + stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. + +/ + @property void stdTime(long stdTime) @safe pure nothrow + { + _stdTime = stdTime; + } + + @safe unittest + { + static void test(long stdTime, in SysTime expected, size_t line = __LINE__) + { + auto st = SysTime(0, UTC()); + st.stdTime = stdTime; + assert(st == expected); + } + + test(0, SysTime(Date(1, 1, 1), UTC())); + test(1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC())); + test(-1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC())); + test(330_000_502L, SysTime(DateTime(1, 1, 1, 0, 0, 33), hnsecs(502), UTC())); + test(621_355_968_000_000_000L, SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC())); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.stdTime = 27)); + //static assert(!__traits(compiles, ist.stdTime = 27)); + } + + + /++ + The current time zone of this $(LREF SysTime). Its internal time is always + kept in UTC, so there are no conversion issues between time zones due to + DST. Functions which return all or part of the time - such as hours - + adjust the time to this $(LREF SysTime)'s time zone before returning. + +/ + @property immutable(TimeZone) timezone() @safe const pure nothrow + { + return _timezone; + } + + + /++ + The current time zone of this $(LREF SysTime). It's internal time is always + kept in UTC, so there are no conversion issues between time zones due to + DST. Functions which return all or part of the time - such as hours - + adjust the time to this $(LREF SysTime)'s time zone before returning. + + Params: + timezone = The $(LREF2 .TimeZone, TimeZone) to set this $(LREF SysTime)'s time zone to. + +/ + @property void timezone(immutable TimeZone timezone) @safe pure nothrow + { + if (timezone is null) + _timezone = LocalTime(); + else + _timezone = timezone; + } + + + /++ + Returns whether DST is in effect for this $(LREF SysTime). + +/ + @property bool dstInEffect() @safe const nothrow + { + return _timezone.dstInEffect(_stdTime); + // This function's unit testing is done in the time zone classes. + } + + + /++ + Returns what the offset from UTC is for this $(LREF SysTime). + It includes the DST offset in effect at that time (if any). + +/ + @property Duration utcOffset() @safe const nothrow + { + return _timezone.utcOffsetAt(_stdTime); + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + $(LREF LocalTime) as its time zone. + +/ + SysTime toLocalTime() @safe const pure nothrow + { + return SysTime(_stdTime, LocalTime()); + } + + @safe unittest + { + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone is sysTime.timezone); + assert(sysTime.toLocalTime().timezone !is UTC()); + } + + { + auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27), stz); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone !is UTC()); + assert(sysTime.toLocalTime().timezone !is stz); + } + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + $(D UTC) as its time zone. + +/ + SysTime toUTC() @safe const pure nothrow + { + return SysTime(_stdTime, UTC()); + } + + @safe unittest + { + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toUTC()); + assert(sysTime._stdTime == sysTime.toUTC()._stdTime); + assert(sysTime.toUTC().timezone is UTC()); + assert(sysTime.toUTC().timezone !is LocalTime()); + assert(sysTime.toUTC().timezone !is sysTime.timezone); + } + + + /++ + Returns a $(LREF SysTime) with the same std time as this one, but with + given time zone as its time zone. + +/ + SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow + { + if (tz is null) + return SysTime(_stdTime, LocalTime()); + else + return SysTime(_stdTime, tz); + } + + @safe unittest + { + auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), hnsecs(27)); + assert(sysTime == sysTime.toOtherTZ(stz)); + assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); + assert(sysTime.toOtherTZ(stz).timezone is stz); + assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); + assert(sysTime.toOtherTZ(stz).timezone !is UTC()); + } + + + /++ + Converts this $(LREF SysTime) to unix time (i.e. seconds from midnight, + January 1st, 1970 in UTC). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + By default, the return type is time_t (which is normally an alias for + int on 32-bit systems and long on 64-bit systems), but if a different + size is required than either int or long can be passed as a template + argument to get the desired size. + + If the return type is int, and the result can't fit in an int, then the + closest value that can be held in 32 bits will be used (so $(D int.max) + if it goes over and $(D int.min) if it goes under). However, no attempt + is made to deal with integer overflow if the return type is long. + + Params: + T = The return type (int or long). It defaults to time_t, which is + normally 32 bits on a 32-bit system and 64 bits on a 64-bit + system. + + Returns: + A signed integer representing the unix time which is equivalent to + this SysTime. + +/ + T toUnixTime(T = time_t)() @safe const pure nothrow + if (is(T == int) || is(T == long)) + { + return stdTimeToUnixTime!T(_stdTime); + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); + + auto pst = new immutable SimpleTimeZone(hours(-8)); + assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800); + + auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()); + assert(utc.toUnixTime() == 1_198_311_285); + + auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst); + assert(ca.toUnixTime() == 1_198_340_085); + } + + @safe unittest + { + import std.meta : AliasSeq; + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); + foreach (units; AliasSeq!("hnsecs", "usecs", "msecs")) + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), dur!units(1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); + } + + + /++ + Converts from unix time (i.e. seconds from midnight, January 1st, 1970 + in UTC) to a $(LREF SysTime). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + Params: + unixTime = Seconds from midnight, January 1st, 1970 in UTC. + tz = The time zone for the SysTime that's returned. + +/ + static SysTime fromUnixTime(long unixTime, immutable TimeZone tz = LocalTime()) @safe pure nothrow + { + return SysTime(unixTimeToStdTime(unixTime), tz); + } + + /// + @safe unittest + { + assert(SysTime.fromUnixTime(0) == + SysTime(DateTime(1970, 1, 1), UTC())); + + auto pst = new immutable SimpleTimeZone(hours(-8)); + assert(SysTime.fromUnixTime(28800) == + SysTime(DateTime(1970, 1, 1), pst)); + + auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC()); + assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); + assert(st1.timezone is UTC()); + assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); + + auto st2 = SysTime.fromUnixTime(1_198_311_285, pst); + assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC())); + assert(st2.timezone is pst); + assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst)); + } + + @safe unittest + { + assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC())); + assert(SysTime.fromUnixTime(1) == SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC())); + assert(SysTime.fromUnixTime(-1) == SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC())); + + auto st = SysTime.fromUnixTime(0); + auto dt = cast(DateTime) st; + assert(dt <= DateTime(1970, 2, 1) && dt >= DateTime(1969, 12, 31)); + assert(st.timezone is LocalTime()); + + auto aest = new immutable SimpleTimeZone(hours(10)); + assert(SysTime.fromUnixTime(-36000) == SysTime(DateTime(1970, 1, 1), aest)); + } + + + /++ + Returns a $(D timeval) which represents this $(LREF SysTime). + + Note that like all conversions in std.datetime, this is a truncating + conversion. + + If $(D timeval.tv_sec) is int, and the result can't fit in an int, then + the closest value that can be held in 32 bits will be used for + $(D tv_sec). (so $(D int.max) if it goes over and $(D int.min) if it + goes under). + +/ + timeval toTimeVal() @safe const pure nothrow + { + immutable tv_sec = toUnixTime!(typeof(timeval.tv_sec))(); + immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); + immutable tv_usec = cast(typeof(timeval.tv_usec))convert!("hnsecs", "usecs")(fracHNSecs); + return timeval(tv_sec, tv_usec); + } + + @safe unittest + { + assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeVal() == timeval(0, 1)); + assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeVal() == timeval(0, 7)); + + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeVal() == timeval(1, 1)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeVal() == timeval(1, 7)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeVal() == timeval(0, -1)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeVal() == timeval(0, -1)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeVal() == timeval(0, -999_001)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeVal() == timeval(0, -1000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeVal() == timeval(-1, -999_983)); + } + + + version(StdDdoc) + { + private struct timespec {} + /++ + Returns a $(D timespec) which represents this $(LREF SysTime). + + $(BLUE This function is Posix-Only.) + +/ + timespec toTimeSpec() @safe const pure nothrow; + } + else version(Posix) + { + timespec toTimeSpec() @safe const pure nothrow + { + immutable tv_sec = toUnixTime!(typeof(timespec.tv_sec))(); + immutable fracHNSecs = removeUnitsFromHNSecs!"seconds"(_stdTime - 621_355_968_000_000_000L); + immutable tv_nsec = cast(typeof(timespec.tv_nsec))convert!("hnsecs", "nsecs")(fracHNSecs); + return timespec(tv_sec, tv_nsec); + } + + @safe unittest + { + assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeSpec() == timespec(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(0, 900)); + assert(SysTime(DateTime(1970, 1, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(0, 1000)); + assert(SysTime(DateTime(1970, 1, 1), usecs(7), UTC()).toTimeSpec() == timespec(0, 7000)); + + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeSpec() == timespec(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(9), UTC()).toTimeSpec() == timespec(1, 900)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), hnsecs(10), UTC()).toTimeSpec() == timespec(1, 1000)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), usecs(7), UTC()).toTimeSpec() == timespec(1, 7000)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toTimeSpec() == + timespec(0, -100)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), hnsecs(9_999_990), UTC()).toTimeSpec() == + timespec(0, -1000)); + + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999_999), UTC()).toTimeSpec() == + timespec(0, -1_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), usecs(999), UTC()).toTimeSpec() == + timespec(0, -999_001_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), msecs(999), UTC()).toTimeSpec() == + timespec(0, -1_000_000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeSpec() == + timespec(-1, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), usecs(17), UTC()).toTimeSpec() == + timespec(-1, -999_983_000)); + } + } + + /++ + Returns a $(D tm) which represents this $(LREF SysTime). + +/ + tm toTM() @safe const nothrow + { + auto dateTime = cast(DateTime) this; + tm timeInfo; + + timeInfo.tm_sec = dateTime.second; + timeInfo.tm_min = dateTime.minute; + timeInfo.tm_hour = dateTime.hour; + timeInfo.tm_mday = dateTime.day; + timeInfo.tm_mon = dateTime.month - 1; + timeInfo.tm_year = dateTime.year - 1900; + timeInfo.tm_wday = dateTime.dayOfWeek; + timeInfo.tm_yday = dateTime.dayOfYear - 1; + timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); + + version(Posix) + { + import std.utf : toUTFz; + timeInfo.tm_gmtoff = cast(int) convert!("hnsecs", "seconds")(adjTime - _stdTime); + auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName); + timeInfo.tm_zone = zone.toUTFz!(char*)(); + } + + return timeInfo; + } + + @system unittest + { + import std.conv : to; + + version(Posix) + { + scope(exit) clearTZEnvVar(); + setTZEnvVar("America/Los_Angeles"); + } + + { + auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); + + assert(timeInfo.tm_sec == 0); + assert(timeInfo.tm_min == 0); + assert(timeInfo.tm_hour == 0); + assert(timeInfo.tm_mday == 1); + assert(timeInfo.tm_mon == 0); + assert(timeInfo.tm_year == 70); + assert(timeInfo.tm_wday == 4); + assert(timeInfo.tm_yday == 0); + + version(Posix) + assert(timeInfo.tm_isdst == 0); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assert(timeInfo.tm_gmtoff == -8 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PST"); + } + } + + { + auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), hnsecs(15)).toTM(); + + assert(timeInfo.tm_sec == 7); + assert(timeInfo.tm_min == 15); + assert(timeInfo.tm_hour == 12); + assert(timeInfo.tm_mday == 4); + assert(timeInfo.tm_mon == 6); + assert(timeInfo.tm_year == 110); + assert(timeInfo.tm_wday == 0); + assert(timeInfo.tm_yday == 184); + + version(Posix) + assert(timeInfo.tm_isdst == 1); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) + { + assert(timeInfo.tm_gmtoff == -7 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PDT"); + } + } + } + + + /++ + Adds the given number of years or months to this $(LREF SysTime). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF SysTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "years" || units == "months") + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.add!units(value, allowOverflow); + days = date.dayOfGregorianCal - 1; + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + + return this; + } + + @safe unittest + { + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st1.add!"months"(11); + assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); + + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st2.add!"months"(-11); + assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); + + auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st3.add!"years"(1); + assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + + auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st4.add!"years"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); + } + + // Test add!"years"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), usecs(1207))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(-1999, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), hnsecs(3))); + } + + // Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 3, 1))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 3, 1))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5).add!"years"(7); + assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), msecs(555))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"years"(4))); + //static assert(!__traits(compiles, ist.add!"years"(4))); + } + + // Test add!"years"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), usecs(1207))); + } + + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), usecs(1207)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), usecs(1207))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), msecs(234)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), msecs(234))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), msecs(234))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), hnsecs(3))); + } + + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), hnsecs(3)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), hnsecs(3))); + } + + // Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 2, 28))); + } + + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 2, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), usecs(54329))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), usecs(54329))); + } + + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), msecs(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), msecs(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), msecs(555))); + } + } + + // Test add!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 2))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2001, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2001, 3, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1997, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1995, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-1996, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 2))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2000, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49); + assert(sysTime == SysTime(Date(0, 3, 2))); + sysTime.add!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(Date(-3, 3, 3))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), msecs(9))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), msecs(9))); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85).add!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"months"(4))); + //static assert(!__traits(compiles, ist.add!"months"(4))); + } + + // Test add!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 29))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 2, 29))); + sysTime.add!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-3, 2, 28))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 28))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), msecs(9))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), msecs(9))); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); + } + } + + + /++ + Adds the given number of years or months to this $(LREF SysTime). A + negative number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF SysTime) 12 months + gets the exact same $(LREF SysTime). However, the days can still be affected + due to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF SysTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "years") + { + return add!"years"(value, allowOverflow); + } + + /// + @safe unittest + { + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st1.roll!"months"(1); + assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); + + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st2.roll!"months"(-1); + assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); + + auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st3.roll!"months"(1); + assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); + + auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st4.roll!"months"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); + + auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st5.roll!"years"(1); + assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + + auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st6.roll!"years"(1, AllowDayOverflow.no); + assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.roll!"years"(4); + static assert(!__traits(compiles, cst.roll!"years"(4))); + //static assert(!__traits(compiles, ist.roll!"years"(4))); + } + + + // Shares documentation with "years" overload. + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow + if (units == "months") + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto date = Date(cast(int) days); + date.roll!"months"(value, allowOverflow); + days = date.dayOfGregorianCal - 1; + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + // Test roll!"months"() with AllowDayOverflow.yes + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1998, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-1997, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2002, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2002, 1, 3))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2001, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 3))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), hnsecs(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), hnsecs(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), hnsecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85).roll!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), msecs(9))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"months"(4))); + //static assert(!__traits(compiles, ist.roll!"months"(4))); + } + + // Test roll!"months"() with AllowDayOverflow.no + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 28))); + } + + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } + + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } + + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 28))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), usecs(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), usecs(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), usecs(5007))); + } + + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), hnsecs(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), hnsecs(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), hnsecs(422202))); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), hnsecs(17))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), hnsecs(17))); + } + + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), msecs(9))); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), msecs(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), msecs(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), msecs(9))); + } + } + + + /++ + Adds the given number of units to this $(LREF SysTime). A negative number + will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF SysTime) one + year's worth of days gets the exact same $(LREF SysTime). + + Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), + $(D "minutes"), $(D "seconds"), $(D "msecs"), $(D "usecs"), and + $(D "hnsecs"). + + Note that when rolling msecs, usecs or hnsecs, they all add up to a + second. So, for example, rolling 1000 msecs is exactly the same as + rolling 100,000 usecs. + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this $(LREF SysTime). + +/ + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "days") + { + auto hnsecs = adjTime; + auto gdays = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --gdays; + } + + auto date = Date(cast(int) gdays); + date.roll!"days"(value); + gdays = date.dayOfGregorianCal - 1; + + if (gdays < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++gdays; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + /// + @safe unittest + { + auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); + st1.roll!"days"(1); + assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); + st1.roll!"days"(365); + assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); + st1.roll!"days"(-32); + assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); + + auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); + st2.roll!"hours"(1); + assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + + auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); + st3.roll!"hours"(-1); + assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); + + auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st4.roll!"minutes"(1); + assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + + auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st5.roll!"minutes"(-1); + assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + + auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st6.roll!"seconds"(1); + assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + + auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st7.roll!"seconds"(-1); + assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + + auto dt = DateTime(2010, 1, 1, 0, 0, 0); + auto st8 = SysTime(dt); + st8.roll!"msecs"(1); + assert(st8 == SysTime(dt, msecs(1))); + + auto st9 = SysTime(dt); + st9.roll!"msecs"(-1); + assert(st9 == SysTime(dt, msecs(999))); + + auto st10 = SysTime(dt); + st10.roll!"hnsecs"(1); + assert(st10 == SysTime(dt, hnsecs(1))); + + auto st11 = SysTime(dt); + st11.roll!"hnsecs"(-1); + assert(st11 == SysTime(dt, hnsecs(9_999_999))); + } + + @safe unittest + { + // Test A.D. + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } + + { + auto sysTime = SysTime(Date(1999, 2, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 2, 7))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 2, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 2, 8))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 2, 10))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 2, 6))); + } + + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), usecs(234578))); + } + + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), usecs(234578))); + } + + // Test B.C. + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } + + { + auto sysTime = SysTime(Date(-2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } + + { + auto sysTime = SysTime(Date(-1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 1, 1))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(-1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(-1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(-1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } + + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(-1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(-1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), usecs(234578))); + } + + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), usecs(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), usecs(234578))); + sysTime.roll!"days"(-3); + } + + // Test Both + { + auto sysTime = SysTime(Date(1, 7, 6)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1, 7, 13))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(Date(1, 7, 19))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1, 7, 5))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), msecs(22))); + } + + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), msecs(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), msecs(22))); + } + + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), msecs(22)); + sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), msecs(22))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"days"(4))); + //static assert(!__traits(compiles, ist.roll!"days"(4))); + } + + + // Shares documentation with "days" version. + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "hours" || units == "minutes" || units == "seconds") + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + dateTime.roll!units(value); + --days; + + hnsecs += convert!("hours", "hnsecs")(dateTime.hour); + hnsecs += convert!("minutes", "hnsecs")(dateTime.minute); + hnsecs += convert!("seconds", "hnsecs")(dateTime.second); + + if (days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + catch (Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + // Test roll!"hours"(). + @safe unittest + { + static void testST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = msecs(45); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); + testST(beforeAD, 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); + testST(beforeAD, 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); + testST(beforeAD, 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + testST(beforeAD, 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + testST(beforeAD, 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(beforeAD, 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(beforeAD, 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(beforeAD, 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); + testST(beforeAD, 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + testST(beforeAD, 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); + testST(beforeAD, 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); + testST(beforeAD, 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); + testST(beforeAD, 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); + testST(beforeAD, 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); + testST(beforeAD, 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), d)); + testST(beforeAD, -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), d)); + testST(beforeAD, -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), d)); + testST(beforeAD, -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), d)); + testST(beforeAD, -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), d)); + testST(beforeAD, -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(beforeAD, -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(beforeAD, -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(beforeAD, -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), d)); + testST(beforeAD, -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + testST(beforeAD, -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), d)); + testST(beforeAD, -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), d)); + testST(beforeAD, -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), d)); + testST(beforeAD, -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), d)); + testST(beforeAD, -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), d)); + testST(beforeAD, -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), d)); + testST(beforeAD, -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), d)); + testST(beforeAD, -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), d)); + testST(beforeAD, -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), d)); + testST(beforeAD, -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), d)); + + testST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); + testST(beforeBC, 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); + testST(beforeBC, 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); + testST(beforeBC, 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + testST(beforeBC, 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + testST(beforeBC, 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(beforeBC, 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(beforeBC, 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(beforeBC, 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); + testST(beforeBC, 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + testST(beforeBC, 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); + testST(beforeBC, 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); + testST(beforeBC, 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); + testST(beforeBC, 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); + testST(beforeBC, 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); + testST(beforeBC, 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), d)); + testST(beforeBC, -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), d)); + testST(beforeBC, -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), d)); + testST(beforeBC, -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), d)); + testST(beforeBC, -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), d)); + testST(beforeBC, -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(beforeBC, -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(beforeBC, -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(beforeBC, -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), d)); + testST(beforeBC, -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + testST(beforeBC, -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), d)); + testST(beforeBC, -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), d)); + testST(beforeBC, -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), d)); + testST(beforeBC, -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), d)); + testST(beforeBC, -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), d)); + testST(beforeBC, -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), d)); + testST(beforeBC, -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), d)); + testST(beforeBC, -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), d)); + testST(beforeBC, -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), d)); + testST(beforeBC, -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), d), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), d), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), d), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), d)); + testST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), d), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), d), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), d)); + + testST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), d), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), d)); + testST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), d), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), d)); + + // Test Both + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"hours"(1).roll!"hours"(-67); + assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hours"(4))); + //static assert(!__traits(compiles, ist.roll!"hours"(4))); + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = usecs(7203); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), d)); + + testST(beforeAD, 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), d)); + testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), d)); + testST(beforeAD, -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), d)); + + testST(beforeAD, -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(beforeAD, -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(beforeAD, -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + testST(beforeAD, -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), d)); + testST(beforeAD, -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), d)); + testST(beforeAD, -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), d)); + + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), d)); + + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), d)); + + testST(beforeBC, 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), d)); + testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), d)); + testST(beforeBC, -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), d)); + + testST(beforeBC, -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(beforeBC, -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(beforeBC, -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + testST(beforeBC, -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), d)); + testST(beforeBC, -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), d)); + testST(beforeBC, -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), d)); + + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), d)); + + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), d)); + + // Test Both + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); + testST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 52, 33), d), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), hnsecs(9_999_999))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), hnsecs(9_999_999))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"minutes"(1).roll!"minutes"(-79); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"minutes"(4))); + //static assert(!__traits(compiles, ist.roll!"minutes"(4))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + immutable d = msecs(274); + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), d); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), d)); + testST(beforeAD, 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), d)); + testST(beforeAD, 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), d)); + testST(beforeAD, 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), d)); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), d)); + testST(beforeAD, 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), d)); + testST(beforeAD, 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), d)); + testST(beforeAD, 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + + testST(beforeAD, 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); + testST(beforeAD, 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), d)); + testST(beforeAD, -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), d)); + testST(beforeAD, -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), d)); + testST(beforeAD, -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), d)); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), d)); + testST(beforeAD, -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), d)); + testST(beforeAD, -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(beforeAD, -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + testST(beforeAD, -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), d)); + testST(beforeAD, -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), d)); + testST(beforeAD, -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), d)); + testST(beforeAD, -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), d)); + + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), d)); + + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), d)); + testST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), d)); + + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), d)); + testST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), d)); + + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), d)); + testST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), d)); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), d)); + testST(beforeBC, 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), d)); + testST(beforeBC, 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), d)); + testST(beforeBC, 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), d)); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), d)); + testST(beforeBC, 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), d)); + testST(beforeBC, 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), d)); + testST(beforeBC, 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + + testST(beforeBC, 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); + testST(beforeBC, 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), d)); + testST(beforeBC, -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), d)); + testST(beforeBC, -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), d)); + testST(beforeBC, -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), d)); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), d)); + testST(beforeBC, -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), d)); + testST(beforeBC, -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(beforeBC, -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + testST(beforeBC, -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), d)); + testST(beforeBC, -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), d)); + testST(beforeBC, -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), d)); + testST(beforeBC, -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d)); + testST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), d), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), d)); + + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d)); + testST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), d), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), d)); + + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d)); + testST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), d), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), d)); + + // Test Both + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), d)); + testST(SysTime(DateTime(0, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), d)); + + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), d), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), d)); + testST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), d), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 33), d), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + testST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), d), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), d)); + testST(SysTime(DateTime(1, 1, 1, 13, 30, 50), d), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), d)); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), hnsecs(9_999_999))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), hnsecs(9_999_999))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + sysTime.roll!"seconds"(1).roll!"seconds"(-102); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), hnsecs(9_999_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"seconds"(4))); + //static assert(!__traits(compiles, ist.roll!"seconds"(4))); + } + + + // Shares documentation with "days" version. + ref SysTime roll(string units)(long value) @safe nothrow + if (units == "msecs" || units == "usecs" || units == "hnsecs") + { + auto hnsecs = adjTime; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + immutable negative = hnsecs < 0; + + if (negative) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + hnsecs += convert!(units, "hnsecs")(value); + hnsecs %= convert!("seconds", "hnsecs")(1); + + if (hnsecs < 0) + hnsecs += convert!("seconds", "hnsecs")(1); + hnsecs += convert!("seconds", "hnsecs")(seconds); + + if (negative) + hnsecs -= convert!("hours", "hnsecs")(24); + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + adjTime = newDaysHNSecs + hnsecs; + return this; + } + + + // Test roll!"msecs"(). + @safe unittest + { + static void testST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"msecs"(milliseconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(1))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(999))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(1))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), msecs(999))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(999))); + testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(998))); + testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), msecs(445))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_989_999))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); + testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); + testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(5_549_999))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"msecs"(1202).roll!"msecs"(-703); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(4_989_999))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.addMSecs(4))); + //static assert(!__traits(compiles, ist.addMSecs(4))); + } + + // Test roll!"usecs"(). + @safe unittest + { + static void testST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"usecs"(microseconds); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), usecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), usecs(666_667))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_989))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9))); + testST(beforeBoth2, 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19))); + testST(beforeBoth2, 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9999))); + testST(beforeBoth2, 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(19_999))); + testST(beforeBoth2, 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(25_549))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(3_333_329))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"usecs"(9_020_027); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(200_269))); + } + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_499_929))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"usecs"(4))); + //static assert(!__traits(compiles, ist.roll!"usecs"(4))); + } + + // Test roll!"hnsecs"(). + @safe unittest + { + static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + orig.roll!"hnsecs"(hnsecs); + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + auto dtAD = DateTime(1999, 7, 6, 12, 30, 33); + auto beforeAD = SysTime(dtAD, hnsecs(274)); + testST(beforeAD, 0, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 1, SysTime(dtAD, hnsecs(275))); + testST(beforeAD, 2, SysTime(dtAD, hnsecs(276))); + testST(beforeAD, 10, SysTime(dtAD, hnsecs(284))); + testST(beforeAD, 100, SysTime(dtAD, hnsecs(374))); + testST(beforeAD, 725, SysTime(dtAD, hnsecs(999))); + testST(beforeAD, 726, SysTime(dtAD, hnsecs(1000))); + testST(beforeAD, 1000, SysTime(dtAD, hnsecs(1274))); + testST(beforeAD, 1001, SysTime(dtAD, hnsecs(1275))); + testST(beforeAD, 2000, SysTime(dtAD, hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(dtAD, hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(dtAD, hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(dtAD, hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(dtAD, hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(dtAD, hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(dtAD, hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(dtAD, hnsecs(274))); + + testST(beforeAD, -1, SysTime(dtAD, hnsecs(273))); + testST(beforeAD, -2, SysTime(dtAD, hnsecs(272))); + testST(beforeAD, -10, SysTime(dtAD, hnsecs(264))); + testST(beforeAD, -100, SysTime(dtAD, hnsecs(174))); + testST(beforeAD, -274, SysTime(dtAD)); + testST(beforeAD, -275, SysTime(dtAD, hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(dtAD, hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(dtAD, hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(dtAD, hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(dtAD, hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(dtAD, hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(dtAD, hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(dtAD, hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(dtAD, hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(dtAD, hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(dtAD, hnsecs(274))); + + // Test B.C. + auto dtBC = DateTime(-1999, 7, 6, 12, 30, 33); + auto beforeBC = SysTime(dtBC, hnsecs(274)); + testST(beforeBC, 0, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 1, SysTime(dtBC, hnsecs(275))); + testST(beforeBC, 2, SysTime(dtBC, hnsecs(276))); + testST(beforeBC, 10, SysTime(dtBC, hnsecs(284))); + testST(beforeBC, 100, SysTime(dtBC, hnsecs(374))); + testST(beforeBC, 725, SysTime(dtBC, hnsecs(999))); + testST(beforeBC, 726, SysTime(dtBC, hnsecs(1000))); + testST(beforeBC, 1000, SysTime(dtBC, hnsecs(1274))); + testST(beforeBC, 1001, SysTime(dtBC, hnsecs(1275))); + testST(beforeBC, 2000, SysTime(dtBC, hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(dtBC, hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(dtBC, hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(dtBC, hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(dtBC, hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(dtBC, hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(dtBC, hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(dtBC, hnsecs(274))); + + testST(beforeBC, -1, SysTime(dtBC, hnsecs(273))); + testST(beforeBC, -2, SysTime(dtBC, hnsecs(272))); + testST(beforeBC, -10, SysTime(dtBC, hnsecs(264))); + testST(beforeBC, -100, SysTime(dtBC, hnsecs(174))); + testST(beforeBC, -274, SysTime(dtBC)); + testST(beforeBC, -275, SysTime(dtBC, hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(dtBC, hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(dtBC, hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(dtBC, hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(dtBC, hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(dtBC, hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(dtBC, hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(dtBC, hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(dtBC, hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(dtBC, hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(dtBC, hnsecs(274))); + + // Test Both + auto dtBoth1 = DateTime(1, 1, 1, 0, 0, 0); + auto beforeBoth1 = SysTime(dtBoth1); + testST(beforeBoth1, 1, SysTime(dtBoth1, hnsecs(1))); + testST(beforeBoth1, 0, SysTime(dtBoth1)); + testST(beforeBoth1, -1, SysTime(dtBoth1, hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(dtBoth1, hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(dtBoth1, hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(dtBoth1, hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(dtBoth1, hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(dtBoth1, hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(dtBoth1, hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(dtBoth1, hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(dtBoth1)); + testST(beforeBoth1, -20_000_000, SysTime(dtBoth1)); + testST(beforeBoth1, -20_888_888, SysTime(dtBoth1, hnsecs(9_111_112))); + + auto dtBoth2 = DateTime(0, 12, 31, 23, 59, 59); + auto beforeBoth2 = SysTime(dtBoth2, hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(dtBoth2, hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(dtBoth2)); + testST(beforeBoth2, 2, SysTime(dtBoth2, hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(dtBoth2, hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(dtBoth2, hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(dtBoth2, hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(dtBoth2, hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(dtBoth2, hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(dtBoth2, hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(dtBoth2, hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(dtBoth2, hnsecs(888_887))); + + { + auto st = SysTime(dtBoth2, hnsecs(9_999_999)); + st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); + assert(st == SysTime(dtBoth2, hnsecs(8_221_929))); + } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); + //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF SysTime). + + The legal types of arithmetic for $(LREF SysTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF SysTime). + +/ + SysTime opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + SysTime retval = SysTime(this._stdTime, this._timezone); + immutable hnsecs = duration.total!"hnsecs"; + mixin("retval._stdTime " ~ op ~ "= hnsecs;"); + return retval; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == + SysTime(DateTime(2016, 1, 1, 0, 0, 0))); + + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + hours(1) == + SysTime(DateTime(2016, 1, 1, 0, 59, 59))); + + assert(SysTime(DateTime(2016, 1, 1, 0, 0, 0)) - seconds(1) == + SysTime(DateTime(2015, 12, 31, 23, 59, 59))); + + assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) == + SysTime(DateTime(2015, 12, 31, 23, 59, 59))); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + + assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); + assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); + assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); + assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); + assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); + assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); + assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); + assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); + assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); + + assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), hnsecs(2_345_678))); + assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), hnsecs(2_345_678))); + assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), hnsecs(2_345_678))); + assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), hnsecs(2_345_678))); + assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), hnsecs(2_345_678))); + assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_415_678))); + assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_275_678))); + assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_685))); + assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_671))); + + static void testST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + auto result = orig + dur!"hnsecs"(hnsecs); + if (result != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", result, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); + testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); + //assert(ist + duration == SysTime(DateTime(1999, 7, 6, 12, 30, 45))); + assert(cst - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + //assert(ist - duration == SysTime(DateTime(1999, 7, 6, 12, 30, 21))); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + SysTime opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + SysTime retval = SysTime(this._stdTime, this._timezone); + immutable hnsecs = td.hnsecs; + mixin("retval._stdTime " ~ op ~ "= hnsecs;"); + return retval; + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + + assert(st + TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st + TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + + assert(st - TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + assert(st - TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) from + this $(LREF SysTime), as well as assigning the result to this + $(LREF SysTime). + + The legal types of arithmetic for $(LREF SysTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD +) $(TD Duration) $(TD -->) $(TD SysTime)) + $(TR $(TD SysTime) $(TD -) $(TD Duration) $(TD -->) $(TD SysTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF SysTime). + +/ + ref SysTime opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable hnsecs = duration.total!"hnsecs"; + mixin("_stdTime " ~ op ~ "= hnsecs;"); + return this; + } + + @safe unittest + { + auto before = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(before + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(before + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(before + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(before + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(before + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(before + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(before + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(before + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(before + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(before + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(before + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); + assert(before + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); + assert(before + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); + assert(before + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); + assert(before + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); + assert(before + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); + + assert(before - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(before - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(before - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(before - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(before - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(before - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(before - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(before - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(before - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(before - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(before - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), msecs(7))); + assert(before - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), msecs(993))); + assert(before - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), usecs(7))); + assert(before - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), usecs(999_993))); + assert(before - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(7))); + assert(before - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_993))); + + static void testST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + auto r = orig += dur!"hnsecs"(hnsecs); + if (orig != expected) + throw new AssertError(format("Failed 1. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + if (r != expected) + throw new AssertError(format("Failed 2. actual [%s] != expected [%s]", r, expected), __FILE__, line); + } + + // Test A.D. + auto beforeAD = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeAD, 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeAD, 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeAD, 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeAD, 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeAD, 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeAD, 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeAD, 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeAD, 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeAD, 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeAD, 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeAD, 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeAD, 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeAD, 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeAD, 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeAD, 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeAD, 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeAD, 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeAD, 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeAD, 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeAD, 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeAD, -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeAD, -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeAD, -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeAD, -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeAD, -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + testST(beforeAD, -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeAD, -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeAD, -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeAD, -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeAD, -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeAD, -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeAD, -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeAD, -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeAD, -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeAD, -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeAD, -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeAD, -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeAD, -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test B.C. + auto beforeBC = SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274)); + testST(beforeBC, 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(274))); + testST(beforeBC, 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(275))); + testST(beforeBC, 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(276))); + testST(beforeBC, 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(284))); + testST(beforeBC, 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(374))); + testST(beforeBC, 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(999))); + testST(beforeBC, 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1000))); + testST(beforeBC, 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1274))); + testST(beforeBC, 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1275))); + testST(beforeBC, 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(2274))); + testST(beforeBC, 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(26_999))); + testST(beforeBC, 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_000))); + testST(beforeBC, 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(27_001))); + testST(beforeBC, 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_766_999))); + testST(beforeBC, 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_767_000))); + testST(beforeBC, 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(1_000_274))); + testST(beforeBC, 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), hnsecs(274))); + testST(beforeBC, 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), hnsecs(274))); + testST(beforeBC, 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), hnsecs(274))); + testST(beforeBC, 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), hnsecs(274))); + + testST(beforeBC, -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(273))); + testST(beforeBC, -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(272))); + testST(beforeBC, -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(264))); + testST(beforeBC, -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), hnsecs(174))); + testST(beforeBC, -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + testST(beforeBC, -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_999))); + testST(beforeBC, -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_274))); + testST(beforeBC, -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_999_273))); + testST(beforeBC, -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_998_274))); + testST(beforeBC, -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_967_000))); + testST(beforeBC, -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_966_999))); + testST(beforeBC, -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_167_000))); + testST(beforeBC, -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(8_166_999))); + testST(beforeBC, -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), hnsecs(9_000_274))); + testST(beforeBC, -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), hnsecs(274))); + testST(beforeBC, -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), hnsecs(274))); + testST(beforeBC, -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), hnsecs(274))); + testST(beforeBC, -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), hnsecs(274))); + + // Test Both + auto beforeBoth1 = SysTime(DateTime(1, 1, 1, 0, 0, 0)); + testST(beforeBoth1, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth1, 0, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth1, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth1, -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth1, -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_000))); + testST(beforeBoth1, -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_998_000))); + testST(beforeBoth1, -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_997_445))); + testST(beforeBoth1, -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_000_000))); + testST(beforeBoth1, -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(8_000_000))); + testST(beforeBoth1, -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(7_666_667))); + testST(beforeBoth1, -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + testST(beforeBoth1, -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58))); + testST(beforeBoth1, -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), hnsecs(9_111_112))); + + auto beforeBoth2 = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + testST(beforeBoth2, -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998))); + testST(beforeBoth2, 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(beforeBoth2, 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(beforeBoth2, 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(beforeBoth2, 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999))); + testST(beforeBoth2, 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1999))); + testST(beforeBoth2, 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2554))); + testST(beforeBoth2, 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(999_999))); + testST(beforeBoth2, 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1_999_999))); + testST(beforeBoth2, 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(2_333_332))); + testST(beforeBoth2, 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + testST(beforeBoth2, 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), hnsecs(9_999_999))); + testST(beforeBoth2, 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), hnsecs(888_887))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)); + (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); + assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), hnsecs(51))); + } + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst += duration)); + //static assert(!__traits(compiles, ist += duration)); + static assert(!__traits(compiles, cst -= duration)); + //static assert(!__traits(compiles, ist -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref SysTime opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable hnsecs = td.hnsecs; + mixin("_stdTime " ~ op ~ "= hnsecs;"); + return this; + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st += TickDuration.from!"usecs"(7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + } + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st += TickDuration.from!"usecs"(-7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st -= TickDuration.from!"usecs"(-7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_748))); + } + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_678)); + st -= TickDuration.from!"usecs"(7); + assert(st == SysTime(DateTime(1999, 7, 6, 12, 30, 33), hnsecs(2_345_608))); + } + } + } + + + /++ + Gives the difference between two $(LREF SysTime)s. + + The legal types of arithmetic for $(LREF SysTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow + if (op == "-") + { + return dur!"hnsecs"(_stdTime - rhs._stdTime); + } + + @safe unittest + { + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == + dur!"seconds"(86_400)); + assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == + dur!"seconds"(3600)); + assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(60)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == + dur!"seconds"(-60)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(1)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == + dur!"seconds"(-1)); + + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + assert(SysTime(dt, msecs(532)) - SysTime(dt) == msecs(532)); + assert(SysTime(dt) - SysTime(dt, msecs(532)) == msecs(-532)); + + assert(SysTime(dt, usecs(333_347)) - SysTime(dt) == usecs(333_347)); + assert(SysTime(dt) - SysTime(dt, usecs(333_347)) == usecs(-333_347)); + + assert(SysTime(dt, hnsecs(1_234_567)) - SysTime(dt) == hnsecs(1_234_567)); + assert(SysTime(dt) - SysTime(dt, hnsecs(1_234_567)) == hnsecs(-1_234_567)); + } + + assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); + assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) == + dur!"hnsecs"(1)); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == + dur!"hnsecs"(-1)); + + version(Posix) + immutable tz = PosixTimeZone.getTimeZone("America/Los_Angeles"); + else version(Windows) + immutable tz = WindowsTimeZone.getTimeZone("Pacific Standard Time"); + + { + auto dt = DateTime(2011, 1, 13, 8, 17, 2); + auto d = msecs(296); + assert(SysTime(dt, d, tz) - SysTime(dt, d, tz) == Duration.zero); + assert(SysTime(dt, d, tz) - SysTime(dt, d, UTC()) == hours(8)); + assert(SysTime(dt, d, UTC()) - SysTime(dt, d, tz) == hours(-8)); + } + + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st - st == Duration.zero); + assert(cst - st == Duration.zero); + //assert(ist - st == Duration.zero); + + assert(st - cst == Duration.zero); + assert(cst - cst == Duration.zero); + //assert(ist - cst == Duration.zero); + + //assert(st - ist == Duration.zero); + //assert(cst - ist == Duration.zero); + //assert(ist - ist == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF SysTime)s in months. + + To get the difference in years, subtract the year property + of two $(LREF SysTime)s. To get the difference in days or weeks, + subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) + that results. Because converting between months and smaller + units requires a specific date (which $(REF Duration, core,time)s don't have), + getting the difference in months requires some math using both + the year and month properties, so this is a convenience function for + getting the difference in months. + + Note that the number of days in the months or how far into the month + either date is is irrelevant. It is the difference in the month property + combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF SysTime) to subtract from this one. + +/ + int diffMonths(in SysTime rhs) @safe const nothrow + { + return (cast(Date) this).diffMonths(cast(Date) rhs); + } + + /// + @safe unittest + { + assert(SysTime(Date(1999, 2, 1)).diffMonths( + SysTime(Date(1999, 1, 31))) == 1); + + assert(SysTime(Date(1999, 1, 31)).diffMonths( + SysTime(Date(1999, 2, 1))) == -1); + + assert(SysTime(Date(1999, 3, 1)).diffMonths( + SysTime(Date(1999, 1, 1))) == 2); + + assert(SysTime(Date(1999, 1, 1)).diffMonths( + SysTime(Date(1999, 3, 31))) == -2); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.diffMonths(st) == 0); + assert(cst.diffMonths(st) == 0); + //assert(ist.diffMonths(st) == 0); + + assert(st.diffMonths(cst) == 0); + assert(cst.diffMonths(cst) == 0); + //assert(ist.diffMonths(cst) == 0); + + //assert(st.diffMonths(ist) == 0); + //assert(cst.diffMonths(ist) == 0); + //assert(ist.diffMonths(ist) == 0); + } + + + /++ + Whether this $(LREF SysTime) is in a leap year. + +/ + @property bool isLeapYear() @safe const nothrow + { + return (cast(Date) this).isLeapYear; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(!st.isLeapYear); + assert(!cst.isLeapYear); + //assert(!ist.isLeapYear); + } + + + /++ + Day of the week this $(LREF SysTime) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const nothrow + { + return getDayOfWeek(dayOfGregorianCal); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.dayOfWeek == DayOfWeek.tue); + assert(cst.dayOfWeek == DayOfWeek.tue); + //assert(ist.dayOfWeek == DayOfWeek.tue); + } + + + /++ + Day of the year this $(LREF SysTime) is on. + +/ + @property ushort dayOfYear() @safe const nothrow + { + return (cast(Date) this).dayOfYear; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); + assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); + assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.dayOfYear == 187); + assert(cst.dayOfYear == 187); + //assert(ist.dayOfYear == 187); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF SysTime) is on. + +/ + @property void dayOfYear(int day) @safe + { + immutable hnsecs = adjTime; + immutable days = convert!("hnsecs", "days")(hnsecs); + immutable theRest = hnsecs - convert!("days", "hnsecs")(days); + + auto date = Date(cast(int) days); + date.dayOfYear = day; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(date.dayOfGregorianCal - 1); + + adjTime = newDaysHNSecs + theRest; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + st.dayOfYear = 12; + assert(st.dayOfYear == 12); + static assert(!__traits(compiles, cst.dayOfYear = 12)); + //static assert(!__traits(compiles, ist.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. + +/ + @property int dayOfGregorianCal() @safe const nothrow + { + immutable adjustedTime = adjTime; + + // We have to add one because 0 would be midnight, January 1st, 1 A.D., + // which would be the 1st day of the Gregorian Calendar, not the 0th. So, + // simply casting to days is one day off. + if (adjustedTime > 0) + return cast(int) getUnitsFromHNSecs!"days"(adjustedTime) + 1; + + long hnsecs = adjustedTime; + immutable days = cast(int) splitUnitsFromHNSecs!"days"(hnsecs); + + return hnsecs == 0 ? days + 1 : days; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); + assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); + + assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); + assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 1); + + assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212)).dayOfGregorianCal == 2); + assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 32); + assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 366); + assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 731); + assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1096); + assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 1462); + assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 17_898); + assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 35_065); + assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_160); + assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 36_525); + assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 37_986); + assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 72_684); + assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 73_049); + assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_208); + assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 109_573); + assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 145_732); + assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 146_098); + assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_257); + assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 182_622); + assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 364_878); + assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 365_243); + assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_023); + assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 584_389); + assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_596); + assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 693_961); + assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), msecs(212)).dayOfGregorianCal == 710_347); + assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 729_755); + assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == 730_486); + + assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_773); + assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_803); + assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_804); + assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_831); + assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_832); + assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_862); + assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_863); + assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_892); + assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_893); + assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_923); + assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_924); + assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_953); + assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_954); + assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_984); + assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 733_985); + assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_015); + assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_016); + assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_045); + assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_046); + assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_076); + assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_077); + assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_106); + assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_107); + assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == 734_137); + + assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == 734_534); + assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == 734_561); + assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == 734_562); + assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == 734_563); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), hnsecs(1)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).dayOfGregorianCal == 0); + + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_999)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), hnsecs(9_999_998)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1); + assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -30); + assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -31); + + assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212)).dayOfGregorianCal == -367); + assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730); + assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731); + assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1095); + assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1096); + assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1460); + assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1461); + assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1826); + assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -1827); + assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -2191); + assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -3652); + + assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_262); + assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -18_627); + assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -35_794); + assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_160); + assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_524); + assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -36_889); + assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -37_254); + assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -38_715); + assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_413); + assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -73_778); + assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -109_937); + assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -110_302); + assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_097); + assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_462); + assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -146_827); + assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_621); + assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -182_986); + assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -183_351); + assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_607); + assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -365_972); + assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_387); + assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_388); + assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -584_753); + assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -585_118); + assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_325); + assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -694_690); + assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_484); + assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_485); + assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -730_850); + assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212)).dayOfGregorianCal == -731_215); + + assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_502); + assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_472); + assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_471); + assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_444); + assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_443); + assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_413); + assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_412); + assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_383); + assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_382); + assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_352); + assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_351); + assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_322); + assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_321); + assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_291); + assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_290); + assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_260); + assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_259); + assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_230); + assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_229); + assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_199); + assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_198); + assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_169); + assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_168); + assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), msecs(999)).dayOfGregorianCal == -734_138); + + assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0)).dayOfGregorianCal == -735_202); + assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0)).dayOfGregorianCal == -735_175); + assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0)).dayOfGregorianCal == -735_174); + assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0)).dayOfGregorianCal == -735_173); + + // Start of Hebrew Calendar + assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0)).dayOfGregorianCal == -1_373_427); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.dayOfGregorianCal == 729_941); + //assert(ist.dayOfGregorianCal == 729_941); + } + + + // Test that the logic for the day of the Gregorian Calendar is consistent + // between Date and SysTime. + @safe unittest + { + void test(Date date, SysTime st, size_t line = __LINE__) + { + if (date.dayOfGregorianCal != st.dayOfGregorianCal) + { + throw new AssertError(format("Date [%s] SysTime [%s]", date.dayOfGregorianCal, st.dayOfGregorianCal), + __FILE__, line); + } + } + + // Test A.D. + test(Date(1, 1, 1), SysTime(DateTime(1, 1, 1, 0, 0, 0))); + test(Date(1, 1, 2), SysTime(DateTime(1, 1, 2, 0, 0, 0), hnsecs(500))); + test(Date(1, 2, 1), SysTime(DateTime(1, 2, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(2, 1, 1), SysTime(DateTime(2, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(3, 1, 1), SysTime(DateTime(3, 1, 1, 12, 13, 14))); + test(Date(4, 1, 1), SysTime(DateTime(4, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(5, 1, 1), SysTime(DateTime(5, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(50, 1, 1), SysTime(DateTime(50, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(97, 1, 1), SysTime(DateTime(97, 1, 1, 23, 59, 59))); + test(Date(100, 1, 1), SysTime(DateTime(100, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(101, 1, 1), SysTime(DateTime(101, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(105, 1, 1), SysTime(DateTime(105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(200, 1, 1), SysTime(DateTime(200, 1, 1, 0, 0, 0))); + test(Date(201, 1, 1), SysTime(DateTime(201, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(300, 1, 1), SysTime(DateTime(300, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(301, 1, 1), SysTime(DateTime(301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(400, 1, 1), SysTime(DateTime(400, 1, 1, 12, 13, 14))); + test(Date(401, 1, 1), SysTime(DateTime(401, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(500, 1, 1), SysTime(DateTime(500, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(501, 1, 1), SysTime(DateTime(501, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(1000, 1, 1), SysTime(DateTime(1000, 1, 1, 23, 59, 59))); + test(Date(1001, 1, 1), SysTime(DateTime(1001, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(1600, 1, 1), SysTime(DateTime(1600, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(1601, 1, 1), SysTime(DateTime(1601, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(1900, 1, 1), SysTime(DateTime(1900, 1, 1, 0, 0, 0))); + test(Date(1901, 1, 1), SysTime(DateTime(1901, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(1945, 11, 12), SysTime(DateTime(1945, 11, 12, 0, 0, 0), hnsecs(50_000))); + test(Date(1999, 1, 1), SysTime(DateTime(1999, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(1999, 7, 6), SysTime(DateTime(1999, 7, 6, 12, 13, 14))); + test(Date(2000, 1, 1), SysTime(DateTime(2000, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(2001, 1, 1), SysTime(DateTime(2001, 1, 1, 12, 13, 14), hnsecs(50_000))); + + test(Date(2010, 1, 1), SysTime(DateTime(2010, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(2010, 1, 31), SysTime(DateTime(2010, 1, 31, 23, 0, 0))); + test(Date(2010, 2, 1), SysTime(DateTime(2010, 2, 1, 23, 59, 59), hnsecs(500))); + test(Date(2010, 2, 28), SysTime(DateTime(2010, 2, 28, 23, 59, 59), hnsecs(50_000))); + test(Date(2010, 3, 1), SysTime(DateTime(2010, 3, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(2010, 3, 31), SysTime(DateTime(2010, 3, 31, 0, 0, 0))); + test(Date(2010, 4, 1), SysTime(DateTime(2010, 4, 1, 0, 0, 0), hnsecs(500))); + test(Date(2010, 4, 30), SysTime(DateTime(2010, 4, 30, 0, 0, 0), hnsecs(50_000))); + test(Date(2010, 5, 1), SysTime(DateTime(2010, 5, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(2010, 5, 31), SysTime(DateTime(2010, 5, 31, 12, 13, 14))); + test(Date(2010, 6, 1), SysTime(DateTime(2010, 6, 1, 12, 13, 14), hnsecs(500))); + test(Date(2010, 6, 30), SysTime(DateTime(2010, 6, 30, 12, 13, 14), hnsecs(50_000))); + test(Date(2010, 7, 1), SysTime(DateTime(2010, 7, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(2010, 7, 31), SysTime(DateTime(2010, 7, 31, 23, 59, 59))); + test(Date(2010, 8, 1), SysTime(DateTime(2010, 8, 1, 23, 59, 59), hnsecs(500))); + test(Date(2010, 8, 31), SysTime(DateTime(2010, 8, 31, 23, 59, 59), hnsecs(50_000))); + test(Date(2010, 9, 1), SysTime(DateTime(2010, 9, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(2010, 9, 30), SysTime(DateTime(2010, 9, 30, 12, 0, 0))); + test(Date(2010, 10, 1), SysTime(DateTime(2010, 10, 1, 0, 12, 0), hnsecs(500))); + test(Date(2010, 10, 31), SysTime(DateTime(2010, 10, 31, 0, 0, 12), hnsecs(50_000))); + test(Date(2010, 11, 1), SysTime(DateTime(2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); + test(Date(2010, 11, 30), SysTime(DateTime(2010, 11, 30, 0, 59, 0))); + test(Date(2010, 12, 1), SysTime(DateTime(2010, 12, 1, 0, 0, 59), hnsecs(500))); + test(Date(2010, 12, 31), SysTime(DateTime(2010, 12, 31, 0, 59, 59), hnsecs(50_000))); + + test(Date(2012, 2, 1), SysTime(DateTime(2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); + test(Date(2012, 2, 28), SysTime(DateTime(2012, 2, 28, 23, 59, 0))); + test(Date(2012, 2, 29), SysTime(DateTime(2012, 2, 29, 7, 7, 7), hnsecs(7))); + test(Date(2012, 3, 1), SysTime(DateTime(2012, 3, 1, 7, 7, 7), hnsecs(7))); + + // Test B.C. + test(Date(0, 12, 31), SysTime(DateTime(0, 12, 31, 0, 0, 0))); + test(Date(0, 12, 30), SysTime(DateTime(0, 12, 30, 0, 0, 0), hnsecs(500))); + test(Date(0, 12, 1), SysTime(DateTime(0, 12, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(0, 11, 30), SysTime(DateTime(0, 11, 30, 0, 0, 0), hnsecs(9_999_999))); + + test(Date(-1, 12, 31), SysTime(DateTime(-1, 12, 31, 12, 13, 14))); + test(Date(-1, 12, 30), SysTime(DateTime(-1, 12, 30, 12, 13, 14), hnsecs(500))); + test(Date(-1, 1, 1), SysTime(DateTime(-1, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-2, 12, 31), SysTime(DateTime(-2, 12, 31, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2, 1, 1), SysTime(DateTime(-2, 1, 1, 23, 59, 59))); + test(Date(-3, 12, 31), SysTime(DateTime(-3, 12, 31, 23, 59, 59), hnsecs(500))); + test(Date(-3, 1, 1), SysTime(DateTime(-3, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-4, 12, 31), SysTime(DateTime(-4, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-4, 1, 1), SysTime(DateTime(-4, 1, 1, 0, 0, 0))); + test(Date(-5, 12, 31), SysTime(DateTime(-5, 12, 31, 0, 0, 0), hnsecs(500))); + test(Date(-5, 1, 1), SysTime(DateTime(-5, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-9, 1, 1), SysTime(DateTime(-9, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + + test(Date(-49, 1, 1), SysTime(DateTime(-49, 1, 1, 12, 13, 14))); + test(Date(-50, 1, 1), SysTime(DateTime(-50, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-97, 1, 1), SysTime(DateTime(-97, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-99, 12, 31), SysTime(DateTime(-99, 12, 31, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-99, 1, 1), SysTime(DateTime(-99, 1, 1, 23, 59, 59))); + test(Date(-100, 1, 1), SysTime(DateTime(-100, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-101, 1, 1), SysTime(DateTime(-101, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-105, 1, 1), SysTime(DateTime(-105, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-200, 1, 1), SysTime(DateTime(-200, 1, 1, 0, 0, 0))); + test(Date(-201, 1, 1), SysTime(DateTime(-201, 1, 1, 0, 0, 0), hnsecs(500))); + test(Date(-300, 1, 1), SysTime(DateTime(-300, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-301, 1, 1), SysTime(DateTime(-301, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-400, 12, 31), SysTime(DateTime(-400, 12, 31, 12, 13, 14))); + test(Date(-400, 1, 1), SysTime(DateTime(-400, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-401, 1, 1), SysTime(DateTime(-401, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-499, 1, 1), SysTime(DateTime(-499, 1, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-500, 1, 1), SysTime(DateTime(-500, 1, 1, 23, 59, 59))); + test(Date(-501, 1, 1), SysTime(DateTime(-501, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-1000, 1, 1), SysTime(DateTime(-1000, 1, 1, 23, 59, 59), hnsecs(50_000))); + test(Date(-1001, 1, 1), SysTime(DateTime(-1001, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-1599, 1, 1), SysTime(DateTime(-1599, 1, 1, 0, 0, 0))); + test(Date(-1600, 12, 31), SysTime(DateTime(-1600, 12, 31, 0, 0, 0), hnsecs(500))); + test(Date(-1600, 1, 1), SysTime(DateTime(-1600, 1, 1, 0, 0, 0), hnsecs(50_000))); + test(Date(-1601, 1, 1), SysTime(DateTime(-1601, 1, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-1900, 1, 1), SysTime(DateTime(-1900, 1, 1, 12, 13, 14))); + test(Date(-1901, 1, 1), SysTime(DateTime(-1901, 1, 1, 12, 13, 14), hnsecs(500))); + test(Date(-1999, 1, 1), SysTime(DateTime(-1999, 1, 1, 12, 13, 14), hnsecs(50_000))); + test(Date(-1999, 7, 6), SysTime(DateTime(-1999, 7, 6, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2000, 12, 31), SysTime(DateTime(-2000, 12, 31, 23, 59, 59))); + test(Date(-2000, 1, 1), SysTime(DateTime(-2000, 1, 1, 23, 59, 59), hnsecs(500))); + test(Date(-2001, 1, 1), SysTime(DateTime(-2001, 1, 1, 23, 59, 59), hnsecs(50_000))); + + test(Date(-2010, 1, 1), SysTime(DateTime(-2010, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-2010, 1, 31), SysTime(DateTime(-2010, 1, 31, 0, 0, 0))); + test(Date(-2010, 2, 1), SysTime(DateTime(-2010, 2, 1, 0, 0, 0), hnsecs(500))); + test(Date(-2010, 2, 28), SysTime(DateTime(-2010, 2, 28, 0, 0, 0), hnsecs(50_000))); + test(Date(-2010, 3, 1), SysTime(DateTime(-2010, 3, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 3, 31), SysTime(DateTime(-2010, 3, 31, 12, 13, 14))); + test(Date(-2010, 4, 1), SysTime(DateTime(-2010, 4, 1, 12, 13, 14), hnsecs(500))); + test(Date(-2010, 4, 30), SysTime(DateTime(-2010, 4, 30, 12, 13, 14), hnsecs(50_000))); + test(Date(-2010, 5, 1), SysTime(DateTime(-2010, 5, 1, 12, 13, 14), hnsecs(9_999_999))); + test(Date(-2010, 5, 31), SysTime(DateTime(-2010, 5, 31, 23, 59, 59))); + test(Date(-2010, 6, 1), SysTime(DateTime(-2010, 6, 1, 23, 59, 59), hnsecs(500))); + test(Date(-2010, 6, 30), SysTime(DateTime(-2010, 6, 30, 23, 59, 59), hnsecs(50_000))); + test(Date(-2010, 7, 1), SysTime(DateTime(-2010, 7, 1, 23, 59, 59), hnsecs(9_999_999))); + test(Date(-2010, 7, 31), SysTime(DateTime(-2010, 7, 31, 0, 0, 0))); + test(Date(-2010, 8, 1), SysTime(DateTime(-2010, 8, 1, 0, 0, 0), hnsecs(500))); + test(Date(-2010, 8, 31), SysTime(DateTime(-2010, 8, 31, 0, 0, 0), hnsecs(50_000))); + test(Date(-2010, 9, 1), SysTime(DateTime(-2010, 9, 1, 0, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 9, 30), SysTime(DateTime(-2010, 9, 30, 12, 0, 0))); + test(Date(-2010, 10, 1), SysTime(DateTime(-2010, 10, 1, 0, 12, 0), hnsecs(500))); + test(Date(-2010, 10, 31), SysTime(DateTime(-2010, 10, 31, 0, 0, 12), hnsecs(50_000))); + test(Date(-2010, 11, 1), SysTime(DateTime(-2010, 11, 1, 23, 0, 0), hnsecs(9_999_999))); + test(Date(-2010, 11, 30), SysTime(DateTime(-2010, 11, 30, 0, 59, 0))); + test(Date(-2010, 12, 1), SysTime(DateTime(-2010, 12, 1, 0, 0, 59), hnsecs(500))); + test(Date(-2010, 12, 31), SysTime(DateTime(-2010, 12, 31, 0, 59, 59), hnsecs(50_000))); + + test(Date(-2012, 2, 1), SysTime(DateTime(-2012, 2, 1, 23, 0, 59), hnsecs(9_999_999))); + test(Date(-2012, 2, 28), SysTime(DateTime(-2012, 2, 28, 23, 59, 0))); + test(Date(-2012, 2, 29), SysTime(DateTime(-2012, 2, 29, 7, 7, 7), hnsecs(7))); + test(Date(-2012, 3, 1), SysTime(DateTime(-2012, 3, 1, 7, 7, 7), hnsecs(7))); + + test(Date(-3760, 9, 7), SysTime(DateTime(-3760, 9, 7, 0, 0, 0))); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. + Setting this property does not affect the time portion of $(LREF SysTime). + + Params: + days = The day of the Gregorian Calendar to set this $(LREF SysTime) + to. + +/ + @property void dayOfGregorianCal(int days) @safe nothrow + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + if (--days < 0) + { + hnsecs -= convert!("hours", "hnsecs")(24); + ++days; + } + + immutable newDaysHNSecs = convert!("days", "hnsecs")(days); + + adjTime = newDaysHNSecs + hnsecs; + } + + /// + @safe unittest + { + auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); + st.dayOfGregorianCal = 1; + assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 365; + assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 366; + assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 0; + assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = -365; + assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = -366; + assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); + + st.dayOfGregorianCal = 730_120; + assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); + + st.dayOfGregorianCal = 734_137; + assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); + } + + @safe unittest + { + void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) + { + orig.dayOfGregorianCal = day; + if (orig != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", orig, expected), __FILE__, line); + } + + // Test A.D. + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 1, + SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + + // Test B.C. + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(9_999_999)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), hnsecs(1)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + + // Test Both. + testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0))); + testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), hnsecs(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1))); + testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), hnsecs(9_999_999)), 1, + SysTime(DateTime(1, 1, 1, 23, 59, 59), hnsecs(9_999_999))); + + testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0))); + testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), hnsecs(9_999_999)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), hnsecs(1)), 0, + SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1))); + testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59))); + + + auto st = SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212)); + + void testST2(int day, in SysTime expected, size_t line = __LINE__) + { + st.dayOfGregorianCal = day; + if (st != expected) + throw new AssertError(format("Failed. actual [%s] != expected [%s]", st, expected), __FILE__, line); + } + + // Test A.D. + testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), msecs(212))); + testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), msecs(212))); + testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), msecs(212))); + testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), msecs(212))); + testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), msecs(212))); + testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), msecs(212))); + testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), msecs(212))); + testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), msecs(212))); + testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), msecs(212))); + testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), msecs(212))); + testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), msecs(212))); + testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), msecs(212))); + testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), msecs(212))); + testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), msecs(212))); + testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), msecs(212))); + testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), msecs(212))); + testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), msecs(212))); + testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), msecs(212))); + testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), msecs(212))); + testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), msecs(212))); + testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), msecs(212))); + testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), msecs(212))); + testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), msecs(212))); + testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), msecs(212))); + testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), msecs(212))); + testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), msecs(212))); + testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), msecs(212))); + testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), msecs(212))); + testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), msecs(212))); + + testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), msecs(212))); + testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), msecs(212))); + testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), msecs(212))); + testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), msecs(212))); + testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), msecs(212))); + testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), msecs(212))); + testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), msecs(212))); + testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), msecs(212))); + testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), msecs(212))); + testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), msecs(212))); + testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), msecs(212))); + testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), msecs(212))); + testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), msecs(212))); + testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), msecs(212))); + testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), msecs(212))); + testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), msecs(212))); + testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), msecs(212))); + testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), msecs(212))); + testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), msecs(212))); + testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), msecs(212))); + testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), msecs(212))); + testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), msecs(212))); + testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), msecs(212))); + testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), msecs(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), msecs(212))); + + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), msecs(212))); + + // Test B.C. + testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), msecs(212))); + testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), msecs(212))); + testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), msecs(212))); + + testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), msecs(212))); + testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), msecs(212))); + testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), msecs(212))); + testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), msecs(212))); + testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), msecs(212))); + testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), msecs(212))); + testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), msecs(212))); + testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), msecs(212))); + + testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), msecs(212))); + testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), msecs(212))); + testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), msecs(212))); + testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), msecs(212))); + testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), msecs(212))); + testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), msecs(212))); + testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), msecs(212))); + testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), msecs(212))); + testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), msecs(212))); + testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), msecs(212))); + testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), msecs(212))); + testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), msecs(212))); + testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), msecs(212))); + testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), msecs(212))); + testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), msecs(212))); + testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), msecs(212))); + testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), msecs(212))); + testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), msecs(212))); + testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), msecs(212))); + testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), msecs(212))); + testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), msecs(212))); + testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), msecs(212))); + testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), msecs(212))); + testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), msecs(212))); + testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), msecs(212))); + testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), msecs(212))); + testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), msecs(212))); + testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), msecs(212))); + testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), msecs(212))); + testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), msecs(212))); + + testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), msecs(212))); + testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), msecs(212))); + testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), msecs(212))); + testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), msecs(212))); + testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), msecs(212))); + testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), msecs(212))); + testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), msecs(212))); + testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), msecs(212))); + testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), msecs(212))); + testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), msecs(212))); + testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), msecs(212))); + testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), msecs(212))); + testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), msecs(212))); + testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), msecs(212))); + testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), msecs(212))); + testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), msecs(212))); + testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), msecs(212))); + testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), msecs(212))); + testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), msecs(212))); + testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), msecs(212))); + testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), msecs(212))); + testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), msecs(212))); + testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), msecs(212))); + testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), msecs(212))); + + testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), msecs(212))); + testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), msecs(212))); + testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), msecs(212))); + testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), msecs(212))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); + //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF SysTime) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). + +/ + @property ubyte isoWeek() @safe const nothrow + { + return (cast(Date) this).isoWeek; + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.isoWeek == 27); + assert(cst.isoWeek == 27); + //assert(ist.isoWeek == 27); + } + + + /++ + $(LREF SysTime) for the last day in the month that this Date is in. + The time portion of endOfMonth is always 23:59:59.9999999. + +/ + @property SysTime endOfMonth() @safe const nothrow + { + immutable hnsecs = adjTime; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + + auto date = Date(cast(int) days + 1).endOfMonth; + auto newDays = date.dayOfGregorianCal - 1; + long theTimeHNSecs; + + if (newDays < 0) + { + theTimeHNSecs = -1; + ++newDays; + } + else + theTimeHNSecs = convert!("days", "hnsecs")(1) - 1; + + immutable newDaysHNSecs = convert!("days", "hnsecs")(newDays); + + auto retval = SysTime(this._stdTime, this._timezone); + retval.adjTime = newDaysHNSecs + theTimeHNSecs; + + return retval; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == + SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), msecs(24)).endOfMonth == + SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), usecs(5203)).endOfMonth == + SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), hnsecs(12345)).endOfMonth == + SysTime(DateTime(2000, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + + // Test B.C. + assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 10, 1)).endOfMonth == + SysTime(DateTime(-1999, 10, 31, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 11, 1)).endOfMonth == + SysTime(DateTime(-1999, 11, 30, 23, 59, 59), hnsecs(9_999_999))); + assert(SysTime(Date(-1999, 12, 1)).endOfMonth == + SysTime(DateTime(-1999, 12, 31, 23, 59, 59), hnsecs(9_999_999))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + //assert(ist.endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), hnsecs(9_999_999))); + } + + + /++ + The last day in the month that this $(LREF SysTime) is in. + +/ + @property ubyte daysInMonth() @safe const nothrow + { + return Date(dayOfGregorianCal).daysInMonth; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); + + // Test B.C. + assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.daysInMonth == 31); + //assert(ist.daysInMonth == 31); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const nothrow + { + return adjTime >= 0; + } + + /// + @safe unittest + { + assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); + assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); + } + + @safe unittest + { + assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); + assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.isAD); + //assert(ist.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) + for this $(LREF SysTime) at the given time. For example, + prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so + this function returns 2_450_173, while from noon onward, the Julian + day number would be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() @safe const nothrow + { + immutable jd = dayOfGregorianCal + 1_721_425; + return hour < 12 ? jd - 1 : jd; + } + + @safe unittest + { + assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); + assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); + + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); + assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); + assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); + + assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); + assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); + + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); + + assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); + assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); + + assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); + assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); + + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.julianDay == 2_451_366); + //assert(ist.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified + Julian day changes at midnight). + +/ + @property long modJulianDay() @safe const nothrow + { + return dayOfGregorianCal + 1_721_425 - 2_400_001; + } + + @safe unittest + { + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); + + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cst.modJulianDay == 51_365); + //assert(ist.modJulianDay == 51_365); + } + + + /++ + Returns a $(LREF Date) equivalent to this $(LREF SysTime). + +/ + Date opCast(T)() @safe const nothrow + if (is(Unqual!T == Date)) + { + return Date(dayOfGregorianCal); + } + + @safe unittest + { + assert(cast(Date) SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); + assert(cast(Date) SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); + assert(cast(Date) SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); + + assert(cast(Date) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); + assert(cast(Date) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); + assert(cast(Date) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); + + assert(cast(Date) SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); + assert(cast(Date) SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); + assert(cast(Date) SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); + + assert(cast(Date) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); + assert(cast(Date) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); + assert(cast(Date) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(Date) cst != Date.init); + //assert(cast(Date) ist != Date.init); + } + + + /++ + Returns a $(LREF DateTime) equivalent to this $(LREF SysTime). + +/ + DateTime opCast(T)() @safe const nothrow + if (is(Unqual!T == DateTime)) + { + try + { + auto hnsecs = adjTime; + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second)); + } + catch (Exception e) + assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); + } + + @safe unittest + { + assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(DateTime(1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); + + assert(cast(DateTime) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); + + assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(DateTime(-1, 1, 6, 7, 12, 22), msecs(22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime) SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime) SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); + + assert(cast(DateTime) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); + + assert(cast(DateTime) SysTime(DateTime(2011, 1, 13, 8, 17, 2), msecs(296), LocalTime()) == + DateTime(2011, 1, 13, 8, 17, 2)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(DateTime) cst != DateTime.init); + //assert(cast(DateTime) ist != DateTime.init); + } + + + /++ + Returns a $(LREF TimeOfDay) equivalent to this $(LREF SysTime). + +/ + TimeOfDay opCast(T)() @safe const nothrow + if (is(Unqual!T == TimeOfDay)) + { + try + { + auto hnsecs = adjTime; + hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + hnsecs += convert!("hours", "hnsecs")(24); + + immutable hour = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable second = getUnitsFromHNSecs!"seconds"(hnsecs); + + return TimeOfDay(cast(int) hour, cast(int) minute, cast(int) second); + } + catch (Exception e) + assert(0, "TimeOfDay's constructor threw."); + } + + @safe unittest + { + assert(cast(TimeOfDay) SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); + + assert(cast(TimeOfDay) SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay) SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay) SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); + + assert(cast(TimeOfDay) SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay) SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); + + assert(cast(TimeOfDay) SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay) SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay) SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + // Temporary hack until bug http://d.puremagic.com/issues/show_bug.cgi?id=4867 is fixed. + // This allows assignment from const(SysTime) to SysTime. + // It may be a good idea to keep it though, since casting from a type to itself + // should be allowed, and it doesn't work without this opCast() since opCast() + // has already been defined for other types. + SysTime opCast(T)() @safe const pure nothrow + if (is(Unqual!T == SysTime)) + { + return SysTime(_stdTime, _timezone); + } + + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds and TZ is time + zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. + If its time zone is $(D UTC), then it is "Z". Otherwise, it is the + offset from UTC (e.g. +0100 or -0700). Note that the offset from UTC + is $(I not) enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HHMM or -HHMM. + + $(RED Warning: + Previously, toISOString did the same as $(LREF toISOExtString) and + generated +HH:MM or -HH:MM for the time zone when it was not + $(LREF LocalTime) or $(LREF UTC), which is not in conformance with + ISO 9601 for the non-extended string format. This has now been + fixed. However, for now, fromISOString will continue to accept the + extended format for the time zone so that any code which has been + writing out the result of toISOString to read in later will continue + to work.) + +/ + string toISOString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toISOString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toISOString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toISOString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == + "20100704T070612"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOString() == + "19981225T021500.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == + "00000105T230959"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOString() == + "-00040105T000002.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOString() == "00010101T000000.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "00091204T000000.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "00991204T050612.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "09991204T134459.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "99990704T235959.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "+100001020T010101.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == + "20121221T121212-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == + "20121221T121212+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOString() == + "00001231T235959.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOString() == "00001231T235959.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOString() == "00001204T000000.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOString() == "-00091204T000000.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOString() == "-00991204T050612.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOString() == "-09991204T134459.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOString() == "-99990704T235959.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOString() == "-100001020T010101.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ + is the time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If + its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset + from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is + $(I not) enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + +/ + string toISOExtString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toISOExtString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toISOExtString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toISOExtString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOExtString() == + "1998-12-25T02:15:00.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString() == + "-0004-01-05T00:00:02.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toISOExtString() == + "0001-01-01T00:00:00.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == + "+10000-10-20T01:01:01.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == + "2012-12-21T12:12:12-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == + "2012-12-21T12:12:12+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toISOExtString() == + "0000-12-31T23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toISOExtString() == + "0000-12-31T23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toISOExtString() == "0000-12-04T00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toISOExtString() == + "-0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toISOExtString() == + "-9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toISOExtString() == + "-10000-10-20T01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + /++ + Converts this $(LREF SysTime) to a string with the format + YYYY-Mon-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds and TZ + is the time zone). + + Note that the number of digits in the fractional seconds varies with the + number of fractional seconds. It's a maximum of 7 (which would be + hnsecs), but only has as many as are necessary to hold the correct value + (so no trailing zeroes), and if there are no fractional seconds, then + there is no decimal point. + + If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If + its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset + from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is + $(I not) enough to uniquely identify the time zone. + + Time zone offsets will be in the form +HH:MM or -HH:MM. + +/ + string toSimpleString() @safe const nothrow + { + try + { + immutable adjustedTime = adjTime; + long hnsecs = adjustedTime; + + auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; + + if (hnsecs < 0) + { + hnsecs += convert!("hours", "hnsecs")(24); + --days; + } + + auto hour = splitUnitsFromHNSecs!"hours"(hnsecs); + auto minute = splitUnitsFromHNSecs!"minutes"(hnsecs); + auto second = splitUnitsFromHNSecs!"seconds"(hnsecs); + + auto dateTime = DateTime(Date(cast(int) days), TimeOfDay(cast(int) hour, + cast(int) minute, cast(int) second)); + auto fracSecStr = fracSecsToISOString(cast(int) hnsecs); + + if (_timezone is LocalTime()) + return dateTime.toSimpleString() ~ fracSecStr; + + if (_timezone is UTC()) + return dateTime.toSimpleString() ~ fracSecStr ~ "Z"; + + immutable utcOffset = dur!"hnsecs"(adjustedTime - stdTime); + + return format("%s%s%s", + dateTime.toSimpleString(), + fracSecStr, + SimpleTimeZone.toISOExtString(utcOffset)); + } + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); + + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toSimpleString() == + "1998-Dec-25 02:15:00.024"); + + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); + + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toSimpleString() == + "-0004-Jan-05 00:00:02.052092"); + } + + @safe unittest + { + // Test A.D. + assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), hnsecs(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == + "0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == + "9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == + "+10000-Oct-20 01:01:01.050789"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == + "2012-Dec-21 12:12:12-06:00"); + + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == + "2012-Dec-21 12:12:12+07:00"); + + // Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(9_999_999), UTC()).toSimpleString() == + "0000-Dec-31 23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), hnsecs(1), UTC()).toSimpleString() == + "0000-Dec-31 23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), msecs(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), msecs(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), msecs(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), usecs(45020)).toSimpleString() == + "-0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), hnsecs(12)).toSimpleString() == + "-9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), hnsecs(507890)).toSimpleString() == + "-10000-Oct-20 01:01:01.050789"); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(cast(TimeOfDay) cst != TimeOfDay.init); + //assert(cast(TimeOfDay) ist != TimeOfDay.init); + } + + + /++ + Converts this $(LREF SysTime) to a string. + +/ + string toString() @safe const nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + assert(st.toString()); + assert(cst.toString()); + //assert(ist.toString()); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYYMMDDTHHMMSS.FFFFFFFTZ (where F is fractional seconds is the time + zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toISOString) except that + trailing zeroes are permitted - including having fractional seconds with + all zeroes. However, a decimal point with nothing following it is + invalid. + + If there is no time zone in the string, then $(LREF LocalTime) is used. + If the time zone is "Z", then $(D UTC) is used. Otherwise, a + $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is + used. To get the returned $(LREF SysTime) to be a particular time + zone, pass in that time zone and the $(LREF SysTime) to be returned + will be converted to that time zone (though it will still be read in as + whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HHMM, and + -HHMM. + + $(RED Warning: + Previously, $(LREF toISOString) did the same as + $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time + zone when it was not $(LREF LocalTime) or $(LREF UTC), which is not + in conformance with ISO 9601 for the non-extended string format. + This has now been fixed. However, for now, fromISOString will + continue to accept the extended format for the time zone so that any + code which has been writing out the result of toISOString to read in + later will continue to work.) + + Params: + isoString = A string formatted in the ISO format for dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO + format or if the resulting $(LREF SysTime) would not be valid. + +/ + static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe + if (isSomeString!S) + { + import std.algorithm.searching : startsWith, find; + import std.conv : to; + import std.range.primitives; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + immutable skipFirst = dstr.startsWith('+', '-') != 0; + + auto found = (skipFirst ? dstr[1..$] : dstr).find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + { + try + parsedZone = SimpleTimeZone.fromISOString(zoneStr); + catch (DateTimeException dte) + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + } + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid ISO String: %s", isoString)); + } + + /// + @safe unittest + { + assert(SysTime.fromISOString("20100704T070612") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("19981225T021500.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromISOString("00000105T230959.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromISOString("-00040105T000002") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromISOString(" 20100704T070612 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("20100704T070612Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromISOString("20100704T070612-0800") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + + assert(SysTime.fromISOString("20100704T070612+0800") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000", + "20100704T000000.", "20100704T000000.A", "20100704T000000.Z", + "20100704T000000.00000000", "20100704T000000.00000000", + "20100704T000000+", "20100704T000000-", "20100704T000000:", + "20100704T000000-:", "20100704T000000+:", "20100704T000000-1:", + "20100704T000000+1:", "20100704T000000+1:0", + "20100704T000000-12.00", "20100704T000000+12.00", + "20100704T000000-8", "20100704T000000+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "20100704T000000-8:00", "20100704T000000+8:00", + "20100704T000000-08:0", "20100704T000000+08:0", + "20100704T000000-24:00", "20100704T000000+24:00", + "20100704T000000-12:60", "20100704T000000+12:60", + "20100704T000000.0-8:00", "20100704T000000.0+8:00", + "20100704T000000.0-08:0", "20100704T000000.0+08:0", + "20100704T000000.0-24:00", "20100704T000000.0+24:00", + "20100704T000000.0-12:60", "20100704T000000.0+12:60", + "2010-07-0400:00:00", "2010-07-04 00:00:00", + "2010-07-04t00:00:00", "2010-07-04T00:00:00.", + "2010-Jul-0400:00:00", "2010-Jul-04 00:00:00", "2010-Jul-04t00:00:00", + "2010-Jul-04T00:00:00", "2010-Jul-04 00:00:00.", + "2010-12-22T172201", "2010-Dec-22 17:22:01"]) + { + assertThrown!DateTimeException(SysTime.fromISOString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromISOString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("20101222T172201", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-19990706T123033", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+019990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 19990706T123033", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 19990706T123033 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("19070707T121212.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("19070707T121212.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("19070707T121212.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("19070707T121212.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("19070707T121212.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("19070707T121212.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("19070707T121212.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("20101222T172201Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("20101222T172201-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("20101222T172201-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("20101222T172201+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("20101103T065106.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("20101222T172201.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("20101222T172201.23112-0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("20101222T172201.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("20101222T172201.1-0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("20101222T172201.55-0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("20101222T172201.1234567+0100", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("20101222T172201.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201.0000000+0130", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201.45+0800", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + + // @@@DEPRECATED_2017-07@@@ + // This isn't deprecated per se, but that text will make it so that it + // pops up when deprecations are moved along around July 2017. At that + // time, the notice on the documentation should be removed, and we may + // or may not change the behavior of fromISOString to no longer accept + // ISO extended time zones (the concern being that programs will have + // written out strings somewhere to read in again that they'll still be + // reading in for years to come and may not be able to fix, even if the + // code is fixed). If/when we do change the behavior, these tests will + // start failing and will need to be updated accordingly. + test("20101222T172201-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("20101222T172201-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("20101222T172201-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("20101222T172201+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("20101222T172201+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("20101222T172201.23112-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("20101222T172201.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("20101222T172201.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("20101222T172201.1234567+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("20101222T172201.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("20101222T172201.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYY-MM-DDTHH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the + time zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toISOExtString) + except that trailing zeroes are permitted - including having fractional + seconds with all zeroes. However, a decimal point with nothing following + it is invalid. + + If there is no time zone in the string, then $(LREF LocalTime) is used. + If the time zone is "Z", then $(D UTC) is used. Otherwise, a + $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is + used. To get the returned $(LREF SysTime) to be a particular time + zone, pass in that time zone and the $(LREF SysTime) to be returned + will be converted to that time zone (though it will still be read in as + whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + isoExtString = A string formatted in the ISO Extended format for + dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO + format or if the resulting $(LREF SysTime) would not be valid. + +/ + static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil, find; + import std.conv : to; + import std.range.primitives; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + auto tIndex = dstr.countUntil('T'); + enforce(tIndex != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto found = dstr[tIndex + 1 .. $].find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromISOExtString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); + } + + /// + @safe unittest + { + assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12-08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + assert(SysTime.fromISOExtString("2010-07-04T07:06:12+08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", + "20100704t000000", "20100704T000000.", "20100704T000000.0", + "2010-07:0400:00:00", "2010-07-04 00:00:00", + "2010-07-04 00:00:00", "2010-07-04t00:00:00", + "2010-07-04T00:00:00.", "2010-07-04T00:00:00.A", "2010-07-04T00:00:00.Z", + "2010-07-04T00:00:00.00000000", "2010-07-04T00:00:00.00000000", + "2010-07-04T00:00:00+", "2010-07-04T00:00:00-", + "2010-07-04T00:00:00:", "2010-07-04T00:00:00-:", "2010-07-04T00:00:00+:", + "2010-07-04T00:00:00-1:", "2010-07-04T00:00:00+1:", "2010-07-04T00:00:00+1:0", + "2010-07-04T00:00:00-12.00", "2010-07-04T00:00:00+12.00", + "2010-07-04T00:00:00-8", "2010-07-04T00:00:00+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "2010-07-04T00:00:00-8:00", "2010-07-04T00:00:00+8:00", + "2010-07-04T00:00:00-24:00", "2010-07-04T00:00:00+24:00", + "2010-07-04T00:00:00-12:60", "2010-07-04T00:00:00+12:60", + "2010-07-04T00:00:00.0-8:00", "2010-07-04T00:00:00.0+8:00", + "2010-07-04T00:00:00.0-8", "2010-07-04T00:00:00.0+8", + "2010-07-04T00:00:00.0-24:00", "2010-07-04T00:00:00.0+24:00", + "2010-07-04T00:00:00.0-12:60", "2010-07-04T00:00:00.0+12:60", + "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", + "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.0", + "20101222T172201", "2010-Dec-22 17:22:01"]) + { + assertThrown!DateTimeException(SysTime.fromISOExtString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromISOExtString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("2010-12-22T17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-1999-07-06T12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+01999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-07-06T12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-07-06T12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("1907-07-07T12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("2010-12-22T17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("2010-12-22T17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-12-22T17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-12-22T17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("2010-12-22T17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("2010-12-22T17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-12-22T17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("2010-11-03T06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("2010-12-22T17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("2010-12-22T17:22:01.23112-01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("2010-12-22T17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("2010-12-22T17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("2010-12-22T17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("2010-12-22T17:22:01.1234567+01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("2010-12-22T17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-12-22T17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-12-22T17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Creates a $(LREF SysTime) from a string with the format + YYYY-MM-DD HH:MM:SS.FFFFFFFTZ (where F is fractional seconds is the + time zone). Whitespace is stripped from the given string. + + The exact format is exactly as described in $(D toSimpleString) except + that trailing zeroes are permitted - including having fractional seconds + with all zeroes. However, a decimal point with nothing following it is + invalid. + + If there is no time zone in the string, then $(LREF LocalTime) is used. If + the time zone is "Z", then $(D UTC) is used. Otherwise, a + $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is + used. To get the returned $(LREF SysTime) to be a particular time + zone, pass in that time zone and the $(LREF SysTime) to be returned + will be converted to that time zone (though it will still be read in as + whatever time zone is in its string). + + The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and + -HH:MM. + + Params: + simpleString = A string formatted in the way that + $(D toSimpleString) formats dates and times. + tz = The time zone to convert the given time to (no + conversion occurs if null). + + Throws: + $(LREF DateTimeException) if the given string is not in the ISO format + or if the resulting $(LREF SysTime) would not be valid. + +/ + static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil, find; + import std.conv : to; + import std.range.primitives; + import std.string : strip; + + auto dstr = to!dstring(strip(simpleString)); + + auto spaceIndex = dstr.countUntil(' '); + enforce(spaceIndex != -1, new DateTimeException(format("Invalid Simple String: %s", simpleString))); + + auto found = dstr[spaceIndex + 1 .. $].find('.', 'Z', '+', '-'); + auto dateTimeStr = dstr[0 .. $ - found[0].length]; + + dstring fracSecStr; + dstring zoneStr; + + if (found[1] != 0) + { + if (found[1] == 1) + { + auto foundTZ = found[0].find('Z', '+', '-'); + + if (foundTZ[1] != 0) + { + fracSecStr = found[0][0 .. $ - foundTZ[0].length]; + zoneStr = foundTZ[0]; + } + else + fracSecStr = found[0]; + } + else + zoneStr = found[0]; + } + + try + { + auto dateTime = DateTime.fromSimpleString(dateTimeStr); + auto fracSec = fracSecsFromISOString(fracSecStr); + Rebindable!(immutable TimeZone) parsedZone; + + if (zoneStr.empty) + parsedZone = LocalTime(); + else if (zoneStr == "Z") + parsedZone = UTC(); + else + parsedZone = SimpleTimeZone.fromISOExtString(zoneStr); + + auto retval = SysTime(dateTime, fracSec, parsedZone); + + if (tz !is null) + retval.timezone = tz; + + return retval; + } + catch (DateTimeException dte) + throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); + } + + /// + @safe unittest + { + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7))); + + assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + + assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + + assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(-8)))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+08:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(hours(8)))); + } + + @safe unittest + { + foreach (str; ["", "20100704000000", "20100704 000000", + "20100704t000000", "20100704T000000.", "20100704T000000.0", + "2010-07-0400:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00", + "2010-07-04T00:00:00.", "2010-07-04T00:00:00.0", + "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", "2010-Jul-04T00:00:00", + "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.A", "2010-Jul-04 00:00:00.Z", + "2010-Jul-04 00:00:00.00000000", "2010-Jul-04 00:00:00.00000000", + "2010-Jul-04 00:00:00+", "2010-Jul-04 00:00:00-", + "2010-Jul-04 00:00:00:", "2010-Jul-04 00:00:00-:", + "2010-Jul-04 00:00:00+:", "2010-Jul-04 00:00:00-1:", + "2010-Jul-04 00:00:00+1:", "2010-Jul-04 00:00:00+1:0", + "2010-Jul-04 00:00:00-12.00", "2010-Jul-04 00:00:00+12.00", + "2010-Jul-04 00:00:00-8", "2010-Jul-04 00:00:00+8", + "20100704T000000-800", "20100704T000000+800", + "20100704T000000-080", "20100704T000000+080", + "20100704T000000-2400", "20100704T000000+2400", + "20100704T000000-1260", "20100704T000000+1260", + "20100704T000000.0-800", "20100704T000000.0+800", + "20100704T000000.0-8", "20100704T000000.0+8", + "20100704T000000.0-080", "20100704T000000.0+080", + "20100704T000000.0-2400", "20100704T000000.0+2400", + "20100704T000000.0-1260", "20100704T000000.0+1260", + "2010-Jul-04 00:00:00-8:00", "2010-Jul-04 00:00:00+8:00", + "2010-Jul-04 00:00:00-08:0", "2010-Jul-04 00:00:00+08:0", + "2010-Jul-04 00:00:00-24:00", "2010-Jul-04 00:00:00+24:00", + "2010-Jul-04 00:00:00-12:60", "2010-Jul-04 00:00:00+24:60", + "2010-Jul-04 00:00:00.0-8:00", "2010-Jul-04 00:00:00+8:00", + "2010-Jul-04 00:00:00.0-8", "2010-Jul-04 00:00:00.0+8", + "2010-Jul-04 00:00:00.0-08:0", "2010-Jul-04 00:00:00.0+08:0", + "2010-Jul-04 00:00:00.0-24:00", "2010-Jul-04 00:00:00.0+24:00", + "2010-Jul-04 00:00:00.0-12:60", "2010-Jul-04 00:00:00.0+24:60", + "20101222T172201", "2010-12-22T172201"]) + { + assertThrown!DateTimeException(SysTime.fromSimpleString(str), format("[%s]", str)); + } + + static void test(string str, SysTime st, size_t line = __LINE__) + { + if (SysTime.fromSimpleString(str) != st) + throw new AssertError("unittest failure", __FILE__, line); + } + + test("2010-Dec-22 17:22:01", SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + test("1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("-1999-Jul-06 12:30:33", SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + test("+01999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test("1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-Jul-06 12:30:33", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + test(" 1999-Jul-06 12:30:33 ", SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); + test("1907-Jul-07 12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + test("1907-Jul-07 12:12:12.0010000", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); + + auto west60 = new immutable SimpleTimeZone(hours(-1)); + auto west90 = new immutable SimpleTimeZone(minutes(-90)); + auto west480 = new immutable SimpleTimeZone(hours(-8)); + auto east60 = new immutable SimpleTimeZone(hours(1)); + auto east90 = new immutable SimpleTimeZone(minutes(90)); + auto east480 = new immutable SimpleTimeZone(hours(8)); + + test("2010-Dec-22 17:22:01Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + test("2010-Dec-22 17:22:01-01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-Dec-22 17:22:01-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west60)); + test("2010-Dec-22 17:22:01-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west90)); + test("2010-Dec-22 17:22:01-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), west480)); + test("2010-Dec-22 17:22:01+01:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-Dec-22 17:22:01+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east480)); + + test("2010-Nov-03 06:51:06.57159Z", SysTime(DateTime(2010, 11, 3, 6, 51, 6), hnsecs(5715900), UTC())); + test("2010-Dec-22 17:22:01.23412Z", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_341_200), UTC())); + test("2010-Dec-22 17:22:01.23112-01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(2_311_200), west60)); + test("2010-Dec-22 17:22:01.45-01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), west60)); + test("2010-Dec-22 17:22:01.1-01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_000_000), west90)); + test("2010-Dec-22 17:22:01.55-08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(5_500_000), west480)); + test("2010-Dec-22 17:22:01.1234567+01:00", + SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(1_234_567), east60)); + test("2010-Dec-22 17:22:01.0+01", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east60)); + test("2010-Dec-22 17:22:01.0000000+01:30", SysTime(DateTime(2010, 12, 22, 17, 22, 01), east90)); + test("2010-Dec-22 17:22:01.45+08:00", SysTime(DateTime(2010, 12, 22, 17, 22, 01), hnsecs(4_500_000), east480)); + } + + + /++ + Returns the $(LREF SysTime) farthest in the past which is representable + by $(LREF SysTime). + + The $(LREF SysTime) which is returned is in UTC. + +/ + @property static SysTime min() @safe pure nothrow + { + return SysTime(long.min, UTC()); + } + + @safe unittest + { + assert(SysTime.min.year < 0); + assert(SysTime.min < SysTime.max); + } + + + /++ + Returns the $(LREF SysTime) farthest in the future which is representable + by $(LREF SysTime). + + The $(LREF SysTime) which is returned is in UTC. + +/ + @property static SysTime max() @safe pure nothrow + { + return SysTime(long.max, UTC()); + } + + @safe unittest + { + assert(SysTime.max.year > 0); + assert(SysTime.max > SysTime.min); + } + + +private: + + /+ + Returns $(D stdTime) converted to $(LREF SysTime)'s time zone. + +/ + @property long adjTime() @safe const nothrow + { + return _timezone.utcToTZ(_stdTime); + } + + + /+ + Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. + +/ + @property void adjTime(long adjTime) @safe nothrow + { + _stdTime = _timezone.tzToUTC(adjTime); + } + + + // Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5058 + /+ + invariant() + { + assert(_timezone !is null, "Invariant Failure: timezone is null. Were you foolish enough to use " ~ + "SysTime.init? (since timezone for SysTime.init can't be set at compile time)."); + } + +/ + + + long _stdTime; + Rebindable!(immutable TimeZone) _timezone; +} + + +/++ + Converts from unix time (which uses midnight, January 1st, 1970 UTC as its + epoch and seconds as its units) to "std time" (which uses midnight, + January 1st, 1 A.D. UTC and hnsecs as its units). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO + 8601 and is what $(LREF SysTime) uses internally. However, holding the time + as an integer in hnescs since that epoch technically isn't actually part of + the standard, much as it's based on it, so the name "std time" isn't + particularly good, but there isn't an official name for it. C# uses "ticks" + for the same thing, but they aren't actually clock ticks, and the term + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so + it didn't make sense to use the term ticks here. So, for better or worse, + std.datetime uses the term "std time" for this. + + Params: + unixTime = The unix time to convert. + + See_Also: + SysTime.fromUnixTime + +/ +long unixTimeToStdTime(long unixTime) @safe pure nothrow +{ + return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); +} + +/// +@safe unittest +{ + // Midnight, January 1st, 1970 + assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); + assert(SysTime(unixTimeToStdTime(0)) == + SysTime(DateTime(1970, 1, 1), UTC())); + + assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L); + assert(SysTime(unixTimeToStdTime(int.max)) == + SysTime(DateTime(2038, 1, 19, 3, 14, 07), UTC())); + + assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L); + assert(SysTime(unixTimeToStdTime(-127_127)) == + SysTime(DateTime(1969, 12, 30, 12, 41, 13), UTC())); +} + +@safe unittest +{ + // Midnight, January 2nd, 1970 + assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); + // Midnight, December 31st, 1969 + assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); + + assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); + assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); + + foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) + assert(unixTimeToStdTime((dt - DateTime(1970, 1, 1)).total!"seconds") == (dt - DateTime.init).total!"hnsecs"); +} + + +/++ + Converts std time (which uses midnight, January 1st, 1 A.D. UTC as its epoch + and hnsecs as its units) to unix time (which uses midnight, January 1st, + 1970 UTC as its epoch and seconds as its units). + + The C standard does not specify the representation of time_t, so it is + implementation defined. On POSIX systems, unix time is equivalent to + time_t, but that's not necessarily true on other systems (e.g. it is + not true for the Digital Mars C runtime). So, be careful when using unix + time with C functions on non-POSIX systems. + + "std time"'s epoch is based on the Proleptic Gregorian Calendar per ISO + 8601 and is what $(LREF SysTime) uses internally. However, holding the time + as an integer in hnescs since that epoch technically isn't actually part of + the standard, much as it's based on it, so the name "std time" isn't + particularly good, but there isn't an official name for it. C# uses "ticks" + for the same thing, but they aren't actually clock ticks, and the term + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so + it didn't make sense to use the term ticks here. So, for better or worse, + std.datetime uses the term "std time" for this. + + By default, the return type is time_t (which is normally an alias for + int on 32-bit systems and long on 64-bit systems), but if a different + size is required than either int or long can be passed as a template + argument to get the desired size. + + If the return type is int, and the result can't fit in an int, then the + closest value that can be held in 32 bits will be used (so $(D int.max) + if it goes over and $(D int.min) if it goes under). However, no attempt + is made to deal with integer overflow if the return type is long. + + Params: + T = The return type (int or long). It defaults to time_t, which is + normally 32 bits on a 32-bit system and 64 bits on a 64-bit + system. + stdTime = The std time to convert. + + Returns: + A signed integer representing the unix time which is equivalent to + the given std time. + + See_Also: + SysTime.toUnixTime + +/ +T stdTimeToUnixTime(T = time_t)(long stdTime) @safe pure nothrow +if (is(T == int) || is(T == long)) +{ + immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); + + static assert(is(time_t == int) || is(time_t == long), + "Currently, std.datetime only supports systems where time_t is int or long"); + + static if (is(T == long)) + return unixTime; + else static if (is(T == int)) + { + if (unixTime > int.max) + return int.max; + return unixTime < int.min ? int.min : cast(int) unixTime; + } + else + static assert(0, "Bug in template constraint. Only int and long allowed."); +} + +/// +@safe unittest +{ + // Midnight, January 1st, 1970 UTC + assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); + + // 2038-01-19 03:14:07 UTC + assert(stdTimeToUnixTime(642_830_804_470_000_000L) == int.max); +} + +@safe unittest +{ + enum unixEpochAsStdTime = (Date(1970, 1, 1) - Date.init).total!"hnsecs"; + + assert(stdTimeToUnixTime(unixEpochAsStdTime) == 0); // Midnight, January 1st, 1970 + assert(stdTimeToUnixTime(unixEpochAsStdTime + 864_000_000_000L) == 86_400); // Midnight, January 2nd, 1970 + assert(stdTimeToUnixTime(unixEpochAsStdTime - 864_000_000_000L) == -86_400); // Midnight, December 31st, 1969 + + assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); + assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); + + foreach (dt; [DateTime(2010, 11, 1, 19, 5, 22), DateTime(1952, 7, 6, 2, 17, 9)]) + assert(stdTimeToUnixTime((dt - DateTime.init).total!"hnsecs") == (dt - DateTime(1970, 1, 1)).total!"seconds"); + + enum max = convert!("seconds", "hnsecs")(int.max); + enum min = convert!("seconds", "hnsecs")(int.min); + enum one = convert!("seconds", "hnsecs")(1); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max) == int.max); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max) == int.max); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + one) == int.max + 1L); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + one) == int.max); + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + max + 9_999_999) == int.max); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + max + 9_999_999) == int.max); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min) == int.min); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min) == int.min); + + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - one) == int.min - 1L); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - one) == int.min); + assert(stdTimeToUnixTime!long(unixEpochAsStdTime + min - 9_999_999) == int.min); + assert(stdTimeToUnixTime!int(unixEpochAsStdTime + min - 9_999_999) == int.min); +} + + +private: + +/+ + Returns the given hnsecs as an ISO string of fractional seconds. + +/ +static string fracSecsToISOString(int hnsecs) @safe pure nothrow +{ + import std.range.primitives : popBack; + assert(hnsecs >= 0); + + try + { + if (hnsecs == 0) + return ""; + + string isoString = format(".%07d", hnsecs); + + while (isoString[$ - 1] == '0') + isoString.popBack(); + + return isoString; + } + catch (Exception e) + assert(0, "format() threw."); +} + +@safe unittest +{ + assert(fracSecsToISOString(0) == ""); + assert(fracSecsToISOString(1) == ".0000001"); + assert(fracSecsToISOString(10) == ".000001"); + assert(fracSecsToISOString(100) == ".00001"); + assert(fracSecsToISOString(1000) == ".0001"); + assert(fracSecsToISOString(10_000) == ".001"); + assert(fracSecsToISOString(100_000) == ".01"); + assert(fracSecsToISOString(1_000_000) == ".1"); + assert(fracSecsToISOString(1_000_001) == ".1000001"); + assert(fracSecsToISOString(1_001_001) == ".1001001"); + assert(fracSecsToISOString(1_071_601) == ".1071601"); + assert(fracSecsToISOString(1_271_641) == ".1271641"); + assert(fracSecsToISOString(9_999_999) == ".9999999"); + assert(fracSecsToISOString(9_999_990) == ".999999"); + assert(fracSecsToISOString(9_999_900) == ".99999"); + assert(fracSecsToISOString(9_999_000) == ".9999"); + assert(fracSecsToISOString(9_990_000) == ".999"); + assert(fracSecsToISOString(9_900_000) == ".99"); + assert(fracSecsToISOString(9_000_000) == ".9"); + assert(fracSecsToISOString(999) == ".0000999"); + assert(fracSecsToISOString(9990) == ".000999"); + assert(fracSecsToISOString(99_900) == ".00999"); + assert(fracSecsToISOString(999_000) == ".0999"); +} + + +/+ + Returns a Duration corresponding to to the given ISO string of + fractional seconds. + +/ +static Duration fracSecsFromISOString(S)(in S isoString) @trusted pure +if (isSomeString!S) +{ + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.range.primitives; + import std.string : representation; + + if (isoString.empty) + return Duration.zero; + + auto str = isoString.representation; + + enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); + str.popFront(); + + enforce(!str.empty && str.length <= 7, new DateTimeException("Invalid ISO String")); + enforce(all!isDigit(str), new DateTimeException("Invalid ISO String")); + + dchar[7] fullISOString = void; + foreach (i, ref dchar c; fullISOString) + { + if (i < str.length) + c = str[i]; + else + c = '0'; + } + + return hnsecs(to!int(fullISOString[])); +} + +@safe unittest +{ + static void testFSInvalid(string isoString) + { + fracSecsFromISOString(isoString); + } + + assertThrown!DateTimeException(testFSInvalid(".")); + assertThrown!DateTimeException(testFSInvalid("0.")); + assertThrown!DateTimeException(testFSInvalid("0")); + assertThrown!DateTimeException(testFSInvalid("0000000")); + assertThrown!DateTimeException(testFSInvalid(".00000000")); + assertThrown!DateTimeException(testFSInvalid(".00000001")); + assertThrown!DateTimeException(testFSInvalid("T")); + assertThrown!DateTimeException(testFSInvalid("T.")); + assertThrown!DateTimeException(testFSInvalid(".T")); + + assert(fracSecsFromISOString("") == Duration.zero); + assert(fracSecsFromISOString(".0000001") == hnsecs(1)); + assert(fracSecsFromISOString(".000001") == hnsecs(10)); + assert(fracSecsFromISOString(".00001") == hnsecs(100)); + assert(fracSecsFromISOString(".0001") == hnsecs(1000)); + assert(fracSecsFromISOString(".001") == hnsecs(10_000)); + assert(fracSecsFromISOString(".01") == hnsecs(100_000)); + assert(fracSecsFromISOString(".1") == hnsecs(1_000_000)); + assert(fracSecsFromISOString(".1000001") == hnsecs(1_000_001)); + assert(fracSecsFromISOString(".1001001") == hnsecs(1_001_001)); + assert(fracSecsFromISOString(".1071601") == hnsecs(1_071_601)); + assert(fracSecsFromISOString(".1271641") == hnsecs(1_271_641)); + assert(fracSecsFromISOString(".9999999") == hnsecs(9_999_999)); + assert(fracSecsFromISOString(".9999990") == hnsecs(9_999_990)); + assert(fracSecsFromISOString(".999999") == hnsecs(9_999_990)); + assert(fracSecsFromISOString(".9999900") == hnsecs(9_999_900)); + assert(fracSecsFromISOString(".99999") == hnsecs(9_999_900)); + assert(fracSecsFromISOString(".9999000") == hnsecs(9_999_000)); + assert(fracSecsFromISOString(".9999") == hnsecs(9_999_000)); + assert(fracSecsFromISOString(".9990000") == hnsecs(9_990_000)); + assert(fracSecsFromISOString(".999") == hnsecs(9_990_000)); + assert(fracSecsFromISOString(".9900000") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".9900") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".99") == hnsecs(9_900_000)); + assert(fracSecsFromISOString(".9000000") == hnsecs(9_000_000)); + assert(fracSecsFromISOString(".9") == hnsecs(9_000_000)); + assert(fracSecsFromISOString(".0000999") == hnsecs(999)); + assert(fracSecsFromISOString(".0009990") == hnsecs(9990)); + assert(fracSecsFromISOString(".000999") == hnsecs(9990)); + assert(fracSecsFromISOString(".0099900") == hnsecs(99_900)); + assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); + assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); + assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); +} + + +/+ + This function is used to split out the units without getting the remaining + hnsecs. + + See_Also: + $(LREF splitUnitsFromHNSecs) + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The split out value. + +/ +long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + return convert!("hnsecs", units)(hnsecs); +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 2595000000007L); +} + + +/+ + This function is used to split out the units without getting the units but + just the remaining hnsecs. + + See_Also: + $(LREF splitUnitsFromHNSecs) + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. + + Returns: + The remaining hnsecs. + +/ +long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && + CmpTimeUnits!(units, "months") < 0) +{ + immutable value = convert!("hnsecs", units)(hnsecs); + return hnsecs - convert!(units, "hnsecs")(value); +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + auto returned = removeUnitsFromHNSecs!"days"(hnsecs); + assert(returned == 3000000007); + assert(hnsecs == 2595000000007L); +} + + +version(unittest) +{ + // Variables to help in testing. + Duration currLocalDiffFromUTC; + immutable (TimeZone)[] testTZs; + + // All of these helper arrays are sorted in ascending order. + auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; + auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct MonthDay + { + Month month; + short day; + + this(int m, short d) + { + month = cast(Month) m; + day = d; + } + } + + MonthDay[] testMonthDays = [MonthDay(1, 1), + MonthDay(1, 2), + MonthDay(3, 17), + MonthDay(7, 4), + MonthDay(10, 27), + MonthDay(12, 30), + MonthDay(12, 31)]; + + auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + + auto testTODs = [TimeOfDay(0, 0, 0), + TimeOfDay(0, 0, 1), + TimeOfDay(0, 1, 0), + TimeOfDay(1, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + auto testHours = [0, 1, 12, 22, 23]; + auto testMinSecs = [0, 1, 30, 58, 59]; + + // Throwing exceptions is incredibly expensive, so we want to use a smaller + // set of values for tests using assertThrown. + auto testTODsThrown = [TimeOfDay(0, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + Date[] testDatesBC; + Date[] testDatesAD; + + DateTime[] testDateTimesBC; + DateTime[] testDateTimesAD; + + Duration[] testFracSecs; + + SysTime[] testSysTimesBC; + SysTime[] testSysTimesAD; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct GregDay { int day; Date date; } + auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar + GregDay(-735_233, Date(-2012, 1, 1)), + GregDay(-735_202, Date(-2012, 2, 1)), + GregDay(-735_175, Date(-2012, 2, 28)), + GregDay(-735_174, Date(-2012, 2, 29)), + GregDay(-735_173, Date(-2012, 3, 1)), + GregDay(-734_502, Date(-2010, 1, 1)), + GregDay(-734_472, Date(-2010, 1, 31)), + GregDay(-734_471, Date(-2010, 2, 1)), + GregDay(-734_444, Date(-2010, 2, 28)), + GregDay(-734_443, Date(-2010, 3, 1)), + GregDay(-734_413, Date(-2010, 3, 31)), + GregDay(-734_412, Date(-2010, 4, 1)), + GregDay(-734_383, Date(-2010, 4, 30)), + GregDay(-734_382, Date(-2010, 5, 1)), + GregDay(-734_352, Date(-2010, 5, 31)), + GregDay(-734_351, Date(-2010, 6, 1)), + GregDay(-734_322, Date(-2010, 6, 30)), + GregDay(-734_321, Date(-2010, 7, 1)), + GregDay(-734_291, Date(-2010, 7, 31)), + GregDay(-734_290, Date(-2010, 8, 1)), + GregDay(-734_260, Date(-2010, 8, 31)), + GregDay(-734_259, Date(-2010, 9, 1)), + GregDay(-734_230, Date(-2010, 9, 30)), + GregDay(-734_229, Date(-2010, 10, 1)), + GregDay(-734_199, Date(-2010, 10, 31)), + GregDay(-734_198, Date(-2010, 11, 1)), + GregDay(-734_169, Date(-2010, 11, 30)), + GregDay(-734_168, Date(-2010, 12, 1)), + GregDay(-734_139, Date(-2010, 12, 30)), + GregDay(-734_138, Date(-2010, 12, 31)), + GregDay(-731_215, Date(-2001, 1, 1)), + GregDay(-730_850, Date(-2000, 1, 1)), + GregDay(-730_849, Date(-2000, 1, 2)), + GregDay(-730_486, Date(-2000, 12, 30)), + GregDay(-730_485, Date(-2000, 12, 31)), + GregDay(-730_484, Date(-1999, 1, 1)), + GregDay(-694_690, Date(-1901, 1, 1)), + GregDay(-694_325, Date(-1900, 1, 1)), + GregDay(-585_118, Date(-1601, 1, 1)), + GregDay(-584_753, Date(-1600, 1, 1)), + GregDay(-584_388, Date(-1600, 12, 31)), + GregDay(-584_387, Date(-1599, 1, 1)), + GregDay(-365_972, Date(-1001, 1, 1)), + GregDay(-365_607, Date(-1000, 1, 1)), + GregDay(-183_351, Date(-501, 1, 1)), + GregDay(-182_986, Date(-500, 1, 1)), + GregDay(-182_621, Date(-499, 1, 1)), + GregDay(-146_827, Date(-401, 1, 1)), + GregDay(-146_462, Date(-400, 1, 1)), + GregDay(-146_097, Date(-400, 12, 31)), + GregDay(-110_302, Date(-301, 1, 1)), + GregDay(-109_937, Date(-300, 1, 1)), + GregDay(-73_778, Date(-201, 1, 1)), + GregDay(-73_413, Date(-200, 1, 1)), + GregDay(-38_715, Date(-105, 1, 1)), + GregDay(-37_254, Date(-101, 1, 1)), + GregDay(-36_889, Date(-100, 1, 1)), + GregDay(-36_524, Date(-99, 1, 1)), + GregDay(-36_160, Date(-99, 12, 31)), + GregDay(-35_794, Date(-97, 1, 1)), + GregDay(-18_627, Date(-50, 1, 1)), + GregDay(-18_262, Date(-49, 1, 1)), + GregDay(-3652, Date(-9, 1, 1)), + GregDay(-2191, Date(-5, 1, 1)), + GregDay(-1827, Date(-5, 12, 31)), + GregDay(-1826, Date(-4, 1, 1)), + GregDay(-1825, Date(-4, 1, 2)), + GregDay(-1462, Date(-4, 12, 30)), + GregDay(-1461, Date(-4, 12, 31)), + GregDay(-1460, Date(-3, 1, 1)), + GregDay(-1096, Date(-3, 12, 31)), + GregDay(-1095, Date(-2, 1, 1)), + GregDay(-731, Date(-2, 12, 31)), + GregDay(-730, Date(-1, 1, 1)), + GregDay(-367, Date(-1, 12, 30)), + GregDay(-366, Date(-1, 12, 31)), + GregDay(-365, Date(0, 1, 1)), + GregDay(-31, Date(0, 11, 30)), + GregDay(-30, Date(0, 12, 1)), + GregDay(-1, Date(0, 12, 30)), + GregDay(0, Date(0, 12, 31))]; + + auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), + GregDay(2, Date(1, 1, 2)), + GregDay(32, Date(1, 2, 1)), + GregDay(365, Date(1, 12, 31)), + GregDay(366, Date(2, 1, 1)), + GregDay(731, Date(3, 1, 1)), + GregDay(1096, Date(4, 1, 1)), + GregDay(1097, Date(4, 1, 2)), + GregDay(1460, Date(4, 12, 30)), + GregDay(1461, Date(4, 12, 31)), + GregDay(1462, Date(5, 1, 1)), + GregDay(17_898, Date(50, 1, 1)), + GregDay(35_065, Date(97, 1, 1)), + GregDay(36_160, Date(100, 1, 1)), + GregDay(36_525, Date(101, 1, 1)), + GregDay(37_986, Date(105, 1, 1)), + GregDay(72_684, Date(200, 1, 1)), + GregDay(73_049, Date(201, 1, 1)), + GregDay(109_208, Date(300, 1, 1)), + GregDay(109_573, Date(301, 1, 1)), + GregDay(145_732, Date(400, 1, 1)), + GregDay(146_098, Date(401, 1, 1)), + GregDay(182_257, Date(500, 1, 1)), + GregDay(182_622, Date(501, 1, 1)), + GregDay(364_878, Date(1000, 1, 1)), + GregDay(365_243, Date(1001, 1, 1)), + GregDay(584_023, Date(1600, 1, 1)), + GregDay(584_389, Date(1601, 1, 1)), + GregDay(693_596, Date(1900, 1, 1)), + GregDay(693_961, Date(1901, 1, 1)), + GregDay(729_755, Date(1999, 1, 1)), + GregDay(730_120, Date(2000, 1, 1)), + GregDay(730_121, Date(2000, 1, 2)), + GregDay(730_484, Date(2000, 12, 30)), + GregDay(730_485, Date(2000, 12, 31)), + GregDay(730_486, Date(2001, 1, 1)), + GregDay(733_773, Date(2010, 1, 1)), + GregDay(733_774, Date(2010, 1, 2)), + GregDay(733_803, Date(2010, 1, 31)), + GregDay(733_804, Date(2010, 2, 1)), + GregDay(733_831, Date(2010, 2, 28)), + GregDay(733_832, Date(2010, 3, 1)), + GregDay(733_862, Date(2010, 3, 31)), + GregDay(733_863, Date(2010, 4, 1)), + GregDay(733_892, Date(2010, 4, 30)), + GregDay(733_893, Date(2010, 5, 1)), + GregDay(733_923, Date(2010, 5, 31)), + GregDay(733_924, Date(2010, 6, 1)), + GregDay(733_953, Date(2010, 6, 30)), + GregDay(733_954, Date(2010, 7, 1)), + GregDay(733_984, Date(2010, 7, 31)), + GregDay(733_985, Date(2010, 8, 1)), + GregDay(734_015, Date(2010, 8, 31)), + GregDay(734_016, Date(2010, 9, 1)), + GregDay(734_045, Date(2010, 9, 30)), + GregDay(734_046, Date(2010, 10, 1)), + GregDay(734_076, Date(2010, 10, 31)), + GregDay(734_077, Date(2010, 11, 1)), + GregDay(734_106, Date(2010, 11, 30)), + GregDay(734_107, Date(2010, 12, 1)), + GregDay(734_136, Date(2010, 12, 30)), + GregDay(734_137, Date(2010, 12, 31)), + GregDay(734_503, Date(2012, 1, 1)), + GregDay(734_534, Date(2012, 2, 1)), + GregDay(734_561, Date(2012, 2, 28)), + GregDay(734_562, Date(2012, 2, 29)), + GregDay(734_563, Date(2012, 3, 1)), + GregDay(734_858, Date(2012, 12, 21))]; + + // I'd use a Tuple, but I get forward reference errors if I try. + struct DayOfYear { int day; MonthDay md; } + auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(3, 1)), + DayOfYear(90, MonthDay(3, 31)), + DayOfYear(91, MonthDay(4, 1)), + DayOfYear(120, MonthDay(4, 30)), + DayOfYear(121, MonthDay(5, 1)), + DayOfYear(151, MonthDay(5, 31)), + DayOfYear(152, MonthDay(6, 1)), + DayOfYear(181, MonthDay(6, 30)), + DayOfYear(182, MonthDay(7, 1)), + DayOfYear(212, MonthDay(7, 31)), + DayOfYear(213, MonthDay(8, 1)), + DayOfYear(243, MonthDay(8, 31)), + DayOfYear(244, MonthDay(9, 1)), + DayOfYear(273, MonthDay(9, 30)), + DayOfYear(274, MonthDay(10, 1)), + DayOfYear(304, MonthDay(10, 31)), + DayOfYear(305, MonthDay(11, 1)), + DayOfYear(334, MonthDay(11, 30)), + DayOfYear(335, MonthDay(12, 1)), + DayOfYear(363, MonthDay(12, 29)), + DayOfYear(364, MonthDay(12, 30)), + DayOfYear(365, MonthDay(12, 31))]; + + auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), + DayOfYear(2, MonthDay(1, 2)), + DayOfYear(3, MonthDay(1, 3)), + DayOfYear(31, MonthDay(1, 31)), + DayOfYear(32, MonthDay(2, 1)), + DayOfYear(59, MonthDay(2, 28)), + DayOfYear(60, MonthDay(2, 29)), + DayOfYear(61, MonthDay(3, 1)), + DayOfYear(91, MonthDay(3, 31)), + DayOfYear(92, MonthDay(4, 1)), + DayOfYear(121, MonthDay(4, 30)), + DayOfYear(122, MonthDay(5, 1)), + DayOfYear(152, MonthDay(5, 31)), + DayOfYear(153, MonthDay(6, 1)), + DayOfYear(182, MonthDay(6, 30)), + DayOfYear(183, MonthDay(7, 1)), + DayOfYear(213, MonthDay(7, 31)), + DayOfYear(214, MonthDay(8, 1)), + DayOfYear(244, MonthDay(8, 31)), + DayOfYear(245, MonthDay(9, 1)), + DayOfYear(274, MonthDay(9, 30)), + DayOfYear(275, MonthDay(10, 1)), + DayOfYear(305, MonthDay(10, 31)), + DayOfYear(306, MonthDay(11, 1)), + DayOfYear(335, MonthDay(11, 30)), + DayOfYear(336, MonthDay(12, 1)), + DayOfYear(364, MonthDay(12, 29)), + DayOfYear(365, MonthDay(12, 30)), + DayOfYear(366, MonthDay(12, 31))]; + + void initializeTests() @safe + { + import std.algorithm.sorting : sort; + import std.typecons : Rebindable; + immutable lt = LocalTime().utcToTZ(0); + currLocalDiffFromUTC = dur!"hnsecs"(lt); + + version(Posix) + { + immutable otherTZ = lt < 0 ? PosixTimeZone.getTimeZone("Australia/Sydney") + : PosixTimeZone.getTimeZone("America/Denver"); + } + else version(Windows) + { + immutable otherTZ = lt < 0 ? WindowsTimeZone.getTimeZone("AUS Eastern Standard Time") + : WindowsTimeZone.getTimeZone("Mountain Standard Time"); + } + + immutable ot = otherTZ.utcToTZ(0); + + auto diffs = [0L, lt, ot]; + auto diffAA = [0L : Rebindable!(immutable TimeZone)(UTC())]; + diffAA[lt] = Rebindable!(immutable TimeZone)(LocalTime()); + diffAA[ot] = Rebindable!(immutable TimeZone)(otherTZ); + + sort(diffs); + testTZs = [diffAA[diffs[0]], diffAA[diffs[1]], diffAA[diffs[2]]]; + + testFracSecs = [Duration.zero, hnsecs(1), hnsecs(5007), hnsecs(9_999_999)]; + + foreach (year; testYearsBC) + { + foreach (md; testMonthDays) + testDatesBC ~= Date(year, md.month, md.day); + } + + foreach (year; testYearsAD) + { + foreach (md; testMonthDays) + testDatesAD ~= Date(year, md.month, md.day); + } + + foreach (dt; testDatesBC) + { + foreach (tod; testTODs) + testDateTimesBC ~= DateTime(dt, tod); + } + + foreach (dt; testDatesAD) + { + foreach (tod; testTODs) + testDateTimesAD ~= DateTime(dt, tod); + } + + foreach (dt; testDateTimesBC) + { + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + testSysTimesBC ~= SysTime(dt, fs, tz); + } + } + + foreach (dt; testDateTimesAD) + { + foreach (tz; testTZs) + { + foreach (fs; testFracSecs) + testSysTimesAD ~= SysTime(dt, fs, tz); + } + } + } +} diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index f9a7e90ea52..5c7893f2f3d 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -13,6 +13,7 @@ import core.time; import std.datetime.common; import std.datetime.date; import std.datetime.datetime; +import std.datetime.systime; import std.exception : enforce; import std.range.primitives; import std.traits : isIntegral, isSomeString, Unqual; @@ -38,7 +39,7 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : Clock, stdTimeToUnixTime, SysTime; // temporary +import std.datetime : Clock; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time From 10611d68b137374170b32f551809c54c5938721a Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 17:46:20 +0200 Subject: [PATCH 108/262] Move Clock to std.datetime.systime. --- std/datetime/package.d | 258 ---------------------------------------- std/datetime/systime.d | 255 +++++++++++++++++++++++++++++++++++++++ std/datetime/timezone.d | 1 - 3 files changed, 255 insertions(+), 259 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index efa23c46095..6b262ec3c31 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -180,264 +180,6 @@ else version(Posix) alias AutoStart = Flag!"autoStart"; -//============================================================================== -// Section with other types. -//============================================================================== - -/++ - Effectively a namespace to make it clear that the methods it contains are - getting the time from the system clock. It cannot be instantiated. - +/ -final class Clock -{ -public: - - /++ - Returns the current time in the given time zone. - - Params: - clockType = The $(REF ClockType, core,time) indicates which system - clock to use to get the current time. Very few programs - need to use anything other than the default. - tz = The time zone for the SysTime that's returned. - - Throws: - $(LREF DateTimeException) if it fails to get the time. - +/ - static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe - { - return SysTime(currStdTime!clockType, tz); - } - - @safe unittest - { - import std.format : format; - import std.stdio : writefln; - assert(currTime().timezone is LocalTime()); - assert(currTime(UTC()).timezone is UTC()); - - // core.stdc.time.time does not always use unix time on Windows systems. - // In particular, dmc does not use unix time. If we can guarantee that - // the MS runtime uses unix time, then we may be able run this test - // then, but for now, we're just not going to run this test on Windows. - version(Posix) - { - static import core.stdc.time; - static import std.math; - immutable unixTimeD = currTime().toUnixTime(); - immutable unixTimeC = core.stdc.time.time(null); - assert(std.math.abs(unixTimeC - unixTimeD) <= 2); - } - - auto norm1 = Clock.currTime; - auto norm2 = Clock.currTime(UTC()); - assert(norm1 <= norm2, format("%s %s", norm1, norm2)); - assert(abs(norm1 - norm2) <= seconds(2)); - - import std.meta : AliasSeq; - foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) - { - scope(failure) writefln("ClockType.%s", ct); - auto value1 = Clock.currTime!ct; - auto value2 = Clock.currTime!ct(UTC()); - assert(value1 <= value2, format("%s %s", value1, value2)); - assert(abs(value1 - value2) <= seconds(2)); - } - } - - - /++ - Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the - current time. - - Params: - clockType = The $(REF ClockType, core,time) indicates which system - clock to use to get the current time. Very few programs - need to use anything other than the default. - - Throws: - $(LREF DateTimeException) if it fails to get the time. - +/ - static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted - { - static if (clockType != ClockType.coarse && - clockType != ClockType.normal && - clockType != ClockType.precise && - clockType != ClockType.second) - { - import std.format : format; - static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType)); - } - - version(Windows) - { - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - immutable result = FILETIMEToStdTime(&fileTime); - static if (clockType == ClockType.second) - { - // Ideally, this would use core.std.time.time, but the C runtime - // has to be using unix time for that to work, and that's not - // guaranteed on Windows. Digital Mars does not use unix time. - // MS may or may not. If it does, then this can be made to use - // core.stdc.time for MS, but for now, we'll leave it like this. - return convert!("seconds", "hnsecs")(convert!("hnsecs", "seconds")(result)); - } - else - return result; - } - else version(Posix) - { - static import core.stdc.time; - enum hnsecsToUnixEpoch = unixTimeToStdTime(0); - - version(OSX) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.posix.sys.time : gettimeofday, timeval; - timeval tv; - if (gettimeofday(&tv, null) != 0) - throw new TimeException("Call to gettimeofday() failed"); - return convert!("seconds", "hnsecs")(tv.tv_sec) + - convert!("usecs", "hnsecs")(tv.tv_usec) + - hnsecsToUnixEpoch; - } - } - else version(linux) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.linux.time : CLOCK_REALTIME_COARSE; - import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else version(FreeBSD) - { - import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME, - CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; - else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; - else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - else version(NetBSD) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.posix.sys.time : gettimeofday, timeval; - timeval tv; - if (gettimeofday(&tv, null) != 0) - throw new TimeException("Call to gettimeofday() failed"); - return convert!("seconds", "hnsecs")(tv.tv_sec) + - convert!("usecs", "hnsecs")(tv.tv_usec) + - hnsecsToUnixEpoch; - } - } - else version(Solaris) - { - static if (clockType == ClockType.second) - return unixTimeToStdTime(core.stdc.time.time(null)); - else - { - import core.sys.solaris.time : CLOCK_REALTIME; - static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; - else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; - else static assert(0, "Previous static if is wrong."); - timespec ts; - if (clock_gettime(clockArg, &ts) != 0) - throw new TimeException("Call to clock_gettime() failed"); - return convert!("seconds", "hnsecs")(ts.tv_sec) + - ts.tv_nsec / 100 + - hnsecsToUnixEpoch; - } - } - else static assert(0, "Unsupported OS"); - } - else static assert(0, "Unsupported OS"); - } - - @safe unittest - { - import std.format : format; - import std.math : abs; - import std.meta : AliasSeq; - import std.stdio : writefln; - enum limit = convert!("seconds", "hnsecs")(2); - - auto norm1 = Clock.currStdTime; - auto norm2 = Clock.currStdTime; - assert(norm1 <= norm2, format("%s %s", norm1, norm2)); - assert(abs(norm1 - norm2) <= limit); - - foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) - { - scope(failure) writefln("ClockType.%s", ct); - auto value1 = Clock.currStdTime!ct; - auto value2 = Clock.currStdTime!ct; - assert(value1 <= value2, format("%s %s", value1, value2)); - assert(abs(value1 - value2) <= limit); - } - } - - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use core.time.MonoTime.currTime instead") - static @property TickDuration currSystemTick() @safe nothrow - { - return TickDuration.currSystemTick; - } - - deprecated @safe unittest - { - assert(Clock.currSystemTick.length > 0); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use core.time.MonoTime instead. See currAppTick's documentation for details.") - static @property TickDuration currAppTick() @safe - { - return currSystemTick - TickDuration.appOrigin; - } - - deprecated @safe unittest - { - auto a = Clock.currSystemTick; - auto b = Clock.currAppTick; - assert(a.length); - assert(b.length); - assert(a > b); - } - -private: - - @disable this() {} -} - //============================================================================== // Section with StopWatch and Benchmark Code. //============================================================================== diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 2a447ad75a5..c5a14724d69 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -44,6 +44,261 @@ version(unittest) } +/++ + Effectively a namespace to make it clear that the methods it contains are + getting the time from the system clock. It cannot be instantiated. + +/ +final class Clock +{ +public: + + /++ + Returns the current time in the given time zone. + + Params: + clockType = The $(REF ClockType, core,time) indicates which system + clock to use to get the current time. Very few programs + need to use anything other than the default. + tz = The time zone for the SysTime that's returned. + + Throws: + $(LREF DateTimeException) if it fails to get the time. + +/ + static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe + { + return SysTime(currStdTime!clockType, tz); + } + + @safe unittest + { + import std.format : format; + import std.stdio : writefln; + assert(currTime().timezone is LocalTime()); + assert(currTime(UTC()).timezone is UTC()); + + // core.stdc.time.time does not always use unix time on Windows systems. + // In particular, dmc does not use unix time. If we can guarantee that + // the MS runtime uses unix time, then we may be able run this test + // then, but for now, we're just not going to run this test on Windows. + version(Posix) + { + static import core.stdc.time; + static import std.math; + immutable unixTimeD = currTime().toUnixTime(); + immutable unixTimeC = core.stdc.time.time(null); + assert(std.math.abs(unixTimeC - unixTimeD) <= 2); + } + + auto norm1 = Clock.currTime; + auto norm2 = Clock.currTime(UTC()); + assert(norm1 <= norm2, format("%s %s", norm1, norm2)); + assert(abs(norm1 - norm2) <= seconds(2)); + + import std.meta : AliasSeq; + foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) + { + scope(failure) writefln("ClockType.%s", ct); + auto value1 = Clock.currTime!ct; + auto value2 = Clock.currTime!ct(UTC()); + assert(value1 <= value2, format("%s %s", value1, value2)); + assert(abs(value1 - value2) <= seconds(2)); + } + } + + + /++ + Returns the number of hnsecs since midnight, January 1st, 1 A.D. for the + current time. + + Params: + clockType = The $(REF ClockType, core,time) indicates which system + clock to use to get the current time. Very few programs + need to use anything other than the default. + + Throws: + $(LREF DateTimeException) if it fails to get the time. + +/ + static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted + { + static if (clockType != ClockType.coarse && + clockType != ClockType.normal && + clockType != ClockType.precise && + clockType != ClockType.second) + { + import std.format : format; + static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType)); + } + + version(Windows) + { + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + immutable result = FILETIMEToStdTime(&fileTime); + static if (clockType == ClockType.second) + { + // Ideally, this would use core.std.time.time, but the C runtime + // has to be using unix time for that to work, and that's not + // guaranteed on Windows. Digital Mars does not use unix time. + // MS may or may not. If it does, then this can be made to use + // core.stdc.time for MS, but for now, we'll leave it like this. + return convert!("seconds", "hnsecs")(convert!("hnsecs", "seconds")(result)); + } + else + return result; + } + else version(Posix) + { + static import core.stdc.time; + enum hnsecsToUnixEpoch = unixTimeToStdTime(0); + + version(OSX) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.posix.sys.time : gettimeofday, timeval; + timeval tv; + if (gettimeofday(&tv, null) != 0) + throw new TimeException("Call to gettimeofday() failed"); + return convert!("seconds", "hnsecs")(tv.tv_sec) + + convert!("usecs", "hnsecs")(tv.tv_usec) + + hnsecsToUnixEpoch; + } + } + else version(linux) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.linux.time : CLOCK_REALTIME_COARSE; + import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_COARSE; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } + else version(FreeBSD) + { + import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME, + CLOCK_REALTIME_FAST, CLOCK_REALTIME_PRECISE, CLOCK_SECOND; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME_FAST; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME_PRECISE; + else static if (clockType == ClockType.second) alias clockArg = CLOCK_SECOND; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + else version(NetBSD) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.posix.sys.time : gettimeofday, timeval; + timeval tv; + if (gettimeofday(&tv, null) != 0) + throw new TimeException("Call to gettimeofday() failed"); + return convert!("seconds", "hnsecs")(tv.tv_sec) + + convert!("usecs", "hnsecs")(tv.tv_usec) + + hnsecsToUnixEpoch; + } + } + else version(Solaris) + { + static if (clockType == ClockType.second) + return unixTimeToStdTime(core.stdc.time.time(null)); + else + { + import core.sys.solaris.time : CLOCK_REALTIME; + static if (clockType == ClockType.coarse) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.normal) alias clockArg = CLOCK_REALTIME; + else static if (clockType == ClockType.precise) alias clockArg = CLOCK_REALTIME; + else static assert(0, "Previous static if is wrong."); + timespec ts; + if (clock_gettime(clockArg, &ts) != 0) + throw new TimeException("Call to clock_gettime() failed"); + return convert!("seconds", "hnsecs")(ts.tv_sec) + + ts.tv_nsec / 100 + + hnsecsToUnixEpoch; + } + } + else static assert(0, "Unsupported OS"); + } + else static assert(0, "Unsupported OS"); + } + + @safe unittest + { + import std.format : format; + import std.math : abs; + import std.meta : AliasSeq; + import std.stdio : writefln; + enum limit = convert!("seconds", "hnsecs")(2); + + auto norm1 = Clock.currStdTime; + auto norm2 = Clock.currStdTime; + assert(norm1 <= norm2, format("%s %s", norm1, norm2)); + assert(abs(norm1 - norm2) <= limit); + + foreach (ct; AliasSeq!(ClockType.coarse, ClockType.precise, ClockType.second)) + { + scope(failure) writefln("ClockType.%s", ct); + auto value1 = Clock.currStdTime!ct; + auto value2 = Clock.currStdTime!ct; + assert(value1 <= value2, format("%s %s", value1, value2)); + assert(abs(value1 - value2) <= limit); + } + } + + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime.currTime instead") + static @property TickDuration currSystemTick() @safe nothrow + { + return TickDuration.currSystemTick; + } + + deprecated @safe unittest + { + assert(Clock.currSystemTick.length > 0); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use core.time.MonoTime instead. See currAppTick's documentation for details.") + static @property TickDuration currAppTick() @safe + { + return currSystemTick - TickDuration.appOrigin; + } + + deprecated @safe unittest + { + auto a = Clock.currSystemTick; + auto b = Clock.currAppTick; + assert(a.length); + assert(b.length); + assert(a > b); + } + +private: + + @disable this() {} +} + + /++ $(D SysTime) is the type used to get the current time from the system or doing anything that involves time zones. Unlike diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 5c7893f2f3d..65c8ddeb392 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -39,7 +39,6 @@ else version(Posix) version(unittest) import std.exception : assertThrown; -import std.datetime : Clock; // temporary /++ Represents a time zone. It is used with $(LREF SysTime) to indicate the time From 4e5389663116c30b31b884b8843674bf331e29ed Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 17:51:14 +0200 Subject: [PATCH 109/262] Move stray SysTime-related functions to std.datetime.systime. --- std/datetime/package.d | 1380 ---------------------------------------- std/datetime/systime.d | 1371 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 1365 insertions(+), 1386 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index 6b262ec3c31..da7f06a7012 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -582,1162 +582,6 @@ ComparingBenchmarkResult comparingBenchmark(alias baseFunc, } -//============================================================================== -// Section with public helper functions and templates. -//============================================================================== - - -version(StdDdoc) -{ - version(Windows) {} - else - { - alias SYSTEMTIME = void*; - alias FILETIME = void*; - } - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D SYSTEMTIME) struct to a $(LREF SysTime). - - Params: - st = The $(D SYSTEMTIME) struct to convert. - tz = The time zone that the time in the $(D SYSTEMTIME) struct is - assumed to be (if the $(D SYSTEMTIME) was supplied by a Windows - system call, the $(D SYSTEMTIME) will either be in local time - or UTC, depending on the call). - - Throws: - $(LREF DateTimeException) if the given $(D SYSTEMTIME) will not fit in - a $(LREF SysTime), which is highly unlikely to happen given that - $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) - is in 30,827 A.D. - +/ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a $(D SYSTEMTIME) struct. - - The $(D SYSTEMTIME) which is returned will be set using the given - $(LREF SysTime)'s time zone, so to get the $(D SYSTEMTIME) in - UTC, set the $(LREF SysTime)'s time zone to UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is - prior to 1601 A.D. - +/ - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D FILETIME) struct to the number of hnsecs since midnight, - January 1st, 1 A.D. - - Params: - ft = The $(D FILETIME) struct to convert. - - Throws: - $(LREF DateTimeException) if the given $(D FILETIME) cannot be - represented as the return value. - +/ - long FILETIMEToStdTime(scope const FILETIME* ft) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(D FILETIME) struct to a $(LREF SysTime). - - Params: - ft = The $(D FILETIME) struct to convert. - tz = The time zone that the $(LREF SysTime) will be in ($(D FILETIME)s - are in UTC). - - Throws: - $(LREF DateTimeException) if the given $(D FILETIME) will not fit in a - $(LREF SysTime). - +/ - SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a - $(D FILETIME) struct. - - Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - - Throws: - $(LREF DateTimeException) if the given value will not fit in a - $(D FILETIME). - +/ - FILETIME stdTimeToFILETIME(long stdTime) @safe; - - - /++ - $(BLUE This function is Windows-Only.) - - Converts a $(LREF SysTime) to a $(D FILETIME) struct. - - $(D FILETIME)s are always in UTC. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D FILETIME). - +/ - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; -} -else version(Windows) -{ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe - { - const max = SysTime.max; - - static void throwLaterThanMax() - { - throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); - } - - if (st.wYear > max.year) - throwLaterThanMax(); - else if (st.wYear == max.year) - { - if (st.wMonth > max.month) - throwLaterThanMax(); - else if (st.wMonth == max.month) - { - if (st.wDay > max.day) - throwLaterThanMax(); - else if (st.wDay == max.day) - { - if (st.wHour > max.hour) - throwLaterThanMax(); - else if (st.wHour == max.hour) - { - if (st.wMinute > max.minute) - throwLaterThanMax(); - else if (st.wMinute == max.minute) - { - if (st.wSecond > max.second) - throwLaterThanMax(); - else if (st.wSecond == max.second) - { - if (st.wMilliseconds > max.fracSecs.total!"msecs") - throwLaterThanMax(); - } - } - } - } - } - } - - auto dt = DateTime(st.wYear, st.wMonth, st.wDay, - st.wHour, st.wMinute, st.wSecond); - - return SysTime(dt, msecs(st.wMilliseconds), tz); - } - - @system unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - auto converted = SYSTEMTIMEToSysTime(&st, UTC()); - - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - } - - - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe - { - immutable dt = cast(DateTime) sysTime; - - if (dt.year < 1601) - throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); - - SYSTEMTIME st; - - st.wYear = dt.year; - st.wMonth = dt.month; - st.wDayOfWeek = dt.dayOfWeek; - st.wDay = dt.day; - st.wHour = dt.hour; - st.wMinute = dt.minute; - st.wSecond = dt.second; - st.wMilliseconds = cast(ushort) sysTime.fracSecs.total!"msecs"; - - return st; - } - - @system unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); - - SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); - - assert(st.wYear == result.wYear); - assert(st.wMonth == result.wMonth); - assert(st.wDayOfWeek == result.wDayOfWeek); - assert(st.wDay == result.wDay); - assert(st.wHour == result.wHour); - assert(st.wMinute == result.wMinute); - assert(st.wSecond == result.wSecond); - assert(st.wMilliseconds == result.wMilliseconds); - } - - private enum hnsecsFrom1601 = 504_911_232_000_000_000L; - - long FILETIMEToStdTime(scope const FILETIME* ft) @safe - { - ULARGE_INTEGER ul; - ul.HighPart = ft.dwHighDateTime; - ul.LowPart = ft.dwLowDateTime; - ulong tempHNSecs = ul.QuadPart; - - if (tempHNSecs > long.max - hnsecsFrom1601) - throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); - - return cast(long) tempHNSecs + hnsecsFrom1601; - } - - SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe - { - auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); - sysTime.timezone = tz; - - return sysTime; - } - - @system unittest - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - - auto converted = FILETIMEToSysTime(&ft); - - assert(abs((converted - sysTime)) <= dur!"seconds"(2)); - } - - - FILETIME stdTimeToFILETIME(long stdTime) @safe - { - if (stdTime < hnsecsFrom1601) - throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); - - ULARGE_INTEGER ul; - ul.QuadPart = cast(ulong) stdTime - hnsecsFrom1601; - - FILETIME ft; - ft.dwHighDateTime = ul.HighPart; - ft.dwLowDateTime = ul.LowPart; - - return ft; - } - - FILETIME SysTimeToFILETIME(SysTime sysTime) @safe - { - return stdTimeToFILETIME(sysTime.stdTime); - } - - @system unittest - { - SYSTEMTIME st = void; - GetSystemTime(&st); - - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - auto sysTime = FILETIMEToSysTime(&ft, UTC()); - - FILETIME result = SysTimeToFILETIME(sysTime); - - assert(ft.dwLowDateTime == result.dwLowDateTime); - assert(ft.dwHighDateTime == result.dwHighDateTime); - } -} - - -/++ - Type representing the DOS file date/time format. - +/ -alias DosFileTime = uint; - -/++ - Converts from DOS file date/time to $(LREF SysTime). - - Params: - dft = The DOS file time to convert. - tz = The time zone which the DOS file time is assumed to be in. - - Throws: - $(LREF DateTimeException) if the $(D DosFileTime) is invalid. - +/ -SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe -{ - uint dt = cast(uint) dft; - - if (dt == 0) - throw new DateTimeException("Invalid DosFileTime."); - - int year = ((dt >> 25) & 0x7F) + 1980; - int month = ((dt >> 21) & 0x0F); // 1 .. 12 - int dayOfMonth = ((dt >> 16) & 0x1F); // 1 .. 31 - int hour = (dt >> 11) & 0x1F; // 0 .. 23 - int minute = (dt >> 5) & 0x3F; // 0 .. 59 - int second = (dt << 1) & 0x3E; // 0 .. 58 (in 2 second increments) - - try - return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); - catch (DateTimeException dte) - throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); -} - -@safe unittest -{ - assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == - SysTime(DateTime(1980, 1, 1, 0, 0, 0))); - - assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == - SysTime(DateTime(2107, 12, 31, 23, 59, 58))); - - assert(DosFileTimeToSysTime(0x3E3F8456) == - SysTime(DateTime(2011, 1, 31, 16, 34, 44))); -} - - -/++ - Converts from $(LREF SysTime) to DOS file date/time. - - Params: - sysTime = The $(LREF SysTime) to convert. - - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to - a $(D DosFileTime). - +/ -DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe -{ - auto dateTime = cast(DateTime) sysTime; - - if (dateTime.year < 1980) - throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); - - if (dateTime.year > 2107) - throw new DateTimeException("DOS File Times cannot hold dates past 2107."); - - uint retval = 0; - retval = (dateTime.year - 1980) << 25; - retval |= (dateTime.month & 0x0F) << 21; - retval |= (dateTime.day & 0x1F) << 16; - retval |= (dateTime.hour & 0x1F) << 11; - retval |= (dateTime.minute & 0x3F) << 5; - retval |= (dateTime.second >> 1) & 0x1F; - - return cast(DosFileTime) retval; -} - -@safe unittest -{ - assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == - 0b00000000001000010000000000000000); - - assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == - 0b11111111100111111011111101111101); - - assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == - 0x3E3F8456); -} - - -/++ - The given array of $(D char) or random-access range of $(D char) or - $(D ubyte) is expected to be in the format specified in - $(HTTP tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the - grammar rule $(I date-time). It is the date-time format commonly used in - internet messages such as e-mail and HTTP. The corresponding - $(LREF SysTime) will be returned. - - RFC 822 was the original spec (hence the function's name), whereas RFC 5322 - is the current spec. - - The day of the week is ignored beyond verifying that it's a valid day of the - week, as the day of the week can be inferred from the date. It is not - checked whether the given day of the week matches the actual day of the week - of the given date (though it is technically invalid per the spec if the - day of the week doesn't match the actual day of the week of the given date). - - If the time zone is $(D "-0000") (or considered to be equivalent to - $(D "-0000") by section 4.3 of the spec), a $(LREF SimpleTimeZone) with a - utc offset of $(D 0) is used rather than $(LREF UTC), whereas $(D "+0000") - uses $(LREF UTC). - - Note that because $(LREF SysTime) does not currently support having a second - value of 60 (as is sometimes done for leap seconds), if the date-time value - does have a value of 60 for the seconds, it is treated as 59. - - The one area in which this function violates RFC 5322 is that it accepts - $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the - HTTP spec requires it. - - Throws: - $(LREF DateTimeException) if the given string doesn't follow the grammar - for a date-time field or if the resulting $(LREF SysTime) is invalid. - +/ -SysTime parseRFC822DateTime()(in char[] value) @safe -{ - import std.string : representation; - return parseRFC822DateTime(value.representation); -} - -/++ Ditto +/ -SysTime parseRFC822DateTime(R)(R value) @safe -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) -{ - import std.algorithm.searching : find, all; - import std.ascii : isDigit, isAlpha, isPrintable; - import std.conv : to; - import std.functional : not; - import std.range.primitives : ElementEncodingType; - import std.string : capitalize, format; - import std.traits : EnumMembers, isArray; - import std.typecons : Rebindable; - - void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) - { - value = _stripCFWS(valueBefore); - if (value.length < minLen) - throw new DateTimeException("date-time value too short", __FILE__, line); - } - stripAndCheckLen(value, "7Dec1200:00A".length); - - static if (isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) - { - static string sliceAsString(R str) @trusted - { - return cast(string) str; - } - } - else - { - char[4] temp; - char[] sliceAsString(R str) @trusted - { - size_t i = 0; - foreach (c; str) - temp[i++] = cast(char) c; - return temp[0 .. str.length]; - } - } - - // day-of-week - if (isAlpha(value[0])) - { - auto dowStr = sliceAsString(value[0 .. 3]); - switch (dowStr) - { - foreach (dow; EnumMembers!DayOfWeek) - { - enum dowC = capitalize(to!string(dow)); - case dowC: - goto afterDoW; - } - default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); - } -afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); - if (value[0] != ',') - throw new DateTimeException("day-of-week missing comma"); - stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); - } - - // day - immutable digits = isDigit(value[1]) ? 2 : 1; - immutable day = _convDigits!short(value[0 .. digits]); - if (day == -1) - throw new DateTimeException("Invalid day"); - stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); - - // month - Month month; - { - auto monStr = sliceAsString(value[0 .. 3]); - switch (monStr) - { - foreach (mon; EnumMembers!Month) - { - enum monC = capitalize(to!string(mon)); - case monC: - { - month = mon; - goto afterMon; - } - } - default: throw new DateTimeException(format("Invalid month: %s", monStr)); - } -afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); - } - - // year - auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); - size_t yearLen = value.length - found.length; - if (found.length == 0) - throw new DateTimeException("Invalid year"); - if (found[0] == ':') - yearLen -= 2; - auto year = _convDigits!short(value[0 .. yearLen]); - if (year < 1900) - { - if (year == -1) - throw new DateTimeException("Invalid year"); - if (yearLen < 4) - { - if (yearLen == 3) - year += 1900; - else if (yearLen == 2) - year += year < 50 ? 2000 : 1900; - else - throw new DateTimeException("Invalid year. Too few digits."); - } - else - throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); - } - stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); - - // hour - immutable hour = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], ":00A".length); - if (value[0] != ':') - throw new DateTimeException("Invalid hour"); - stripAndCheckLen(value[1 .. value.length], "00A".length); - - // minute - immutable minute = _convDigits!short(value[0 .. 2]); - stripAndCheckLen(value[2 .. value.length], "A".length); - - // second - short second; - if (value[0] == ':') - { - stripAndCheckLen(value[1 .. value.length], "00A".length); - second = _convDigits!short(value[0 .. 2]); - // this is just if/until SysTime is sorted out to fully support leap seconds - if (second == 60) - second = 59; - stripAndCheckLen(value[2 .. value.length], "A".length); - } - - immutable(TimeZone) parseTZ(int sign) - { - if (value.length < 5) - throw new DateTimeException("Invalid timezone"); - immutable zoneHours = _convDigits!short(value[1 .. 3]); - immutable zoneMinutes = _convDigits!short(value[3 .. 5]); - if (zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) - throw new DateTimeException("Invalid timezone"); - value = value[5 .. value.length]; - immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; - if (utcOffset == Duration.zero) - { - return sign == 1 ? cast(immutable(TimeZone))UTC() - : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); - } - return new immutable(SimpleTimeZone)(utcOffset); - } - - // zone - Rebindable!(immutable TimeZone) tz; - if (value[0] == '-') - tz = parseTZ(-1); - else if (value[0] == '+') - tz = parseTZ(1); - else - { - // obs-zone - immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; - switch (sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) - { - case "UT": case "GMT": tz = UTC(); break; - case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; - case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; - case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; - case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; - case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; - case "J": case "j": throw new DateTimeException("Invalid timezone"); - default: - { - if (all!(std.ascii.isAlpha)(value[0 .. tzLen])) - { - tz = new immutable SimpleTimeZone(Duration.zero); - break; - } - throw new DateTimeException("Invalid timezone"); - } - } - value = value[tzLen .. value.length]; - } - - // This is kind of arbitrary. Technically, nothing but CFWS is legal past - // the end of the timezone, but we don't want to be picky about that in a - // function that's just parsing rather than validating. So, the idea here is - // that if the next character is printable (and not part of CFWS), then it - // might be part of the timezone and thus affect what the timezone was - // supposed to be, so we'll throw, but otherwise, we'll just ignore it. - if (!value.empty && isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') - throw new DateTimeException("Invalid timezone"); - - try - return SysTime(DateTime(year, month, day, hour, minute, second), tz); - catch (DateTimeException dte) - throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); -} - -/// -@safe unittest -{ - import std.exception : assertThrown; - - auto tz = new immutable SimpleTimeZone(hours(-8)); - assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == - SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); - - assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == - SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); - - auto badStr = "29 Feb 2001 12:17:16 +0200"; - assertThrown!DateTimeException(parseRFC822DateTime(badStr)); -} - -version(unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) -{ - import std.format : format; - auto value = cr(str); - auto result = parseRFC822DateTime(value); - if (result != expected) - throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); -} - -version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__) -{ - try - parseRFC822DateTime(cr(str)); - catch (DateTimeException) - return; - throw new AssertError("No DateTimeException was thrown", __FILE__, line); -} - -@system unittest -{ - import std.algorithm.iteration : filter, map; - import std.algorithm.searching : canFind; - import std.array : array; - import std.ascii : letters; - import std.format : format; - import std.meta : AliasSeq; - import std.range : chain, iota, take; - import std.stdio : writefln, writeln; - import std.string : representation; - - static struct Rand3Letters - { - enum empty = false; - @property auto front() { return _mon; } - void popFront() - { - import std.exception : assumeUnique; - import std.random : rndGen; - _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); - } - string _mon; - static auto start() { Rand3Letters retval; retval.popFront(); return retval; } - } - - foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, - function(string a){return cast(ubyte[]) a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - alias test = testParse822!cr; - alias testBad = testBadParse822!cr; - - immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); - immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); - immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); - immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); - - test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); - test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); - - test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); - test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); - test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); - - test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); - test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - - auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); - auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); - test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); - test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); - - test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); - test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - - auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); - auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); - test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); - test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - - test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); - test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - - // dst and std times are switched in the Southern Hemisphere which is why the - // time zone names and DateTime variables don't match. - auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); - auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); - test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); - test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - - test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - - foreach (int i, mon; _monthNames) - { - test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); - test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); - } - - import std.uni : toLower, toUpper; - foreach (mon; chain(_monthNames[].map!(a => toLower(a))(), - _monthNames[].map!(a => toUpper(a))(), - ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", - "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", - "Nom", "Nav", "Dem", "Dac"], - Rand3Letters.start().filter!(a => !_monthNames[].canFind(a)).take(20))) - { - scope(failure) writefln("Month: %s", mon); - testBad(format("17 %s 2012 00:05:02 +0000", mon)); - testBad(format("17 %s 2012 00:05 +0000", mon)); - } - - immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - - { - auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); - int day = 11; - - foreach (int i, dow; daysOfWeekNames) - { - auto curr = start + dur!"days"(i); - test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); - test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); - - // Whether the day of the week matches the date is ignored. - test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); - test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); - } - } - - foreach (dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), - daysOfWeekNames[].map!(a => toUpper(a))(), - ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", - "Fro", "Fai", "San", "Sut"], - Rand3Letters.start().filter!(a => !daysOfWeekNames[].canFind(a)).take(20))) - { - scope(failure) writefln("Day of Week: %s", dow); - testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); - testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); - } - - testBad("31 Dec 1899 23:59:59 +0000"); - test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); - test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(Duration.zero))); - test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), - new immutable SimpleTimeZone(dur!"hours"(-7)))); - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach (i; 1900 .. 2102) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - st1.year = 9998; - st2.year = 9998; - foreach (i; 9998 .. 11_002) - { - test(format("1 Jan %05d 00:00 +0000", i), st1); - test(format("1 Jan %05d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - testBad("12 Feb 1907 23:17:09 0000"); - testBad("12 Feb 1907 23:17:09 +000"); - testBad("12 Feb 1907 23:17:09 -000"); - testBad("12 Feb 1907 23:17:09 +00000"); - testBad("12 Feb 1907 23:17:09 -00000"); - testBad("12 Feb 1907 23:17:09 +A"); - testBad("12 Feb 1907 23:17:09 +PST"); - testBad("12 Feb 1907 23:17:09 -A"); - testBad("12 Feb 1907 23:17:09 -PST"); - - // test trailing stuff that gets ignored - { - foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c), SysTime(std1, UTC())); - test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c), SysTime(std1, UTC())); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c)); - testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c)); - testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c)); - } - } - - testBad("32 Jan 2012 12:13:14 -0800"); - testBad("31 Jan 2012 24:13:14 -0800"); - testBad("31 Jan 2012 12:60:14 -0800"); - testBad("31 Jan 2012 12:13:61 -0800"); - testBad("31 Jan 2012 12:13:14 -0860"); - test("31 Jan 2012 12:13:14 -0859", - SysTime(DateTime(2012, 1, 31, 12, 13, 14), - new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); - - // leap-seconds - test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); - - // FWS - test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); - test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); - test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); - - auto str = "01 Jan 2012 12:13:14 -0800 "; - test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); - foreach (i; 0 .. str.length) - { - auto currStr = str.dup; - currStr[i] = 'x'; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string) currStr); - } - foreach (i; 2 .. str.length) - { - auto currStr = str[0 .. $ - i]; - scope(failure) writefln("failed: %s", currStr); - testBad(cast(string) currStr); - testBad((cast(string) currStr) ~ " "); - } - }(); -} - -// Obsolete Format per section 4.3 of RFC 5322. -@system unittest -{ - import std.algorithm.iteration : filter, map; - import std.ascii : letters; - import std.exception : collectExceptionMsg; - import std.format : format; - import std.meta : AliasSeq; - import std.range : chain, iota; - import std.stdio : writefln, writeln; - import std.string : representation; - - auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); - auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); - auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); - auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); - auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); - auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); - auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); - auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); - - foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, - function(string a){return cast(ubyte[]) a;}, - function(string a){return a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - alias test = testParse822!cr; - { - auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", - " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; - - foreach (i, cfws; list) - { - scope(failure) writefln("i: %s", i); - - test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - - test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); - test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - - test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); - test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - - test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); - test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); - } - } - - // test years of 1, 2, and 3 digits. - { - auto st1 = SysTime(Date(2000, 1, 1), UTC()); - auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach (i; 0 .. 50) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1950, 1, 1), UTC()); - auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); - foreach (i; 50 .. 100) - { - test(format("1 Jan %02d 00:00 GMT", i), st1); - test(format("1 Jan %02d 00:00 -1200", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - { - auto st1 = SysTime(Date(1900, 1, 1), UTC()); - auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); - foreach (i; 0 .. 1000) - { - test(format("1 Jan %03d 00:00 GMT", i), st1); - test(format("1 Jan %03d 00:00 -1100", i), st2); - st1.add!"years"(1); - st2.add!"years"(1); - } - } - - foreach (i; 0 .. 10) - { - auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); - auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - assertThrown!DateTimeException(parseRFC822DateTime(str1)); - } - - // test time zones - { - auto dt = DateTime(1982, 05, 03, 12, 22, 04); - test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); - test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); - test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); - test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); - test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); - test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - - auto badTZ = new immutable SimpleTimeZone(Duration.zero); - foreach (dchar c; filter!(a => a != 'j' && a != 'J')(letters)) - { - scope(failure) writefln("c: %s", c); - test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); - test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); - } - - foreach (dchar c; ['j', 'J']) - { - scope(failure) writefln("c: %s", c); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); - assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); - } - - foreach (string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) - { - scope(failure) writefln("s: %s", s); - test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); - } - - // test trailing stuff that gets ignored - { - foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) - { - scope(failure) writefln("c: %d", c); - test(format("21Dec1213:14:15+0000%c", cast(char) c), std1); - test(format("21Dec1213:14:15+0000%c ", cast(char) c), std1); - test(format("21Dec1213:14:15+0000%chello", cast(char) c), std1); - } - } - - // test trailing stuff that doesn't get ignored - { - foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) - { - scope(failure) writefln("c: %d", c); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char) c)))); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char) c)))); - assertThrown!DateTimeException( - parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char) c)))); - } - } - } - - // test that the checks for minimum length work correctly and avoid - // any RangeErrors. - test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), - new immutable SimpleTimeZone(Duration.zero))); - - auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); - foreach (str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) - { - foreach (i; 0 .. str.length) - { - auto value = str[0 .. $ - i]; - scope(failure) writeln(value); - assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); - } - } - }(); -} - - /++ Function for starting to a stop watch time when the function is called and stopping it when its return value goes out of scope and is destroyed. @@ -1965,230 +809,6 @@ if (validTimeUnits(units) && } -/+ - Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand - side of the given range (it strips comments delimited by $(D '(') and - $(D ')') as well as folding whitespace). - - It is assumed that the given range contains the value of a header field and - no terminating CRLF for the line (though the CRLF for folding whitespace is - of course expected and stripped) and thus that the only case of CR or LF is - in folding whitespace. - - If a comment does not terminate correctly (e.g. mismatched parens) or if the - the FWS is malformed, then the range will be empty when stripCWFS is done. - However, only minimal validation of the content is done (e.g. quoted pairs - within a comment aren't validated beyond \$LPAREN or \$RPAREN, because - they're inside a comment, and thus their value doesn't matter anyway). It's - only when the content does not conform to the grammar rules for FWS and thus - literally cannot be parsed that content is considered invalid, and an empty - range is returned. - - Note that _stripCFWS is eager, not lazy. It does not create a new range. - Rather, it pops off the CFWS from the range and returns it. - +/ -R _stripCFWS(R)(R range) -if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && - (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) -{ - immutable e = range.length; - outer: for (size_t i = 0; i < e; ) - { - switch (range[i]) - { - case ' ': case '\t': - { - ++i; - break; - } - case '\r': - { - if (i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) - { - i += 3; - break; - } - break outer; - } - case '\n': - { - if (i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) - { - i += 2; - break; - } - break outer; - } - case '(': - { - ++i; - size_t commentLevel = 1; - while (i < e) - { - if (range[i] == '(') - ++commentLevel; - else if (range[i] == ')') - { - ++i; - if (--commentLevel == 0) - continue outer; - continue; - } - else if (range[i] == '\\') - { - if (++i == e) - break outer; - } - ++i; - } - break outer; - } - default: return range[i .. e]; - } - } - return range[e .. e]; -} - -@system unittest -{ - import std.algorithm.comparison : equal; - import std.algorithm.iteration : map; - import std.meta : AliasSeq; - import std.stdio : writeln; - import std.string : representation; - - foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;}, - function(string a){return map!(b => cast(char) b)(a.representation);})) - (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 - scope(failure) writeln(typeof(cr).stringof); - - assert(_stripCFWS(cr("")).empty); - assert(_stripCFWS(cr("\r")).empty); - assert(_stripCFWS(cr("\r\n")).empty); - assert(_stripCFWS(cr("\r\n ")).empty); - assert(_stripCFWS(cr(" \t\r\n")).empty); - assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); - assert(_stripCFWS(cr(" \t\r\nhello")).empty); - assert(_stripCFWS(cr(" \t\r\n\v")).empty); - assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); - assert(_stripCFWS(cr("()")).empty); - assert(_stripCFWS(cr("(hello world)")).empty); - assert(_stripCFWS(cr("(hello world)(hello world)")).empty); - assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); - assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); - assert(_stripCFWS(cr(" ")).empty); - assert(_stripCFWS(cr("\t\t\t")).empty); - assert(_stripCFWS(cr("\t \r\n\r \n")).empty); - assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); - assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); - assert(_stripCFWS(cr("(((((")).empty); - assert(_stripCFWS(cr("(((()))")).empty); - assert(_stripCFWS(cr("(((())))")).empty); - assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); - assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); - assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); - assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); - assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); - assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); - assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); - assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); - assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); - assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); - - assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); - - assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); - assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); - - assert(_stripCFWS(cr("(hello)(hello)")).empty); - assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); - assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); - assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); - assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); - }(); -} - -// This is so that we don't have to worry about std.conv.to throwing. It also -// doesn't have to worry about quite as many cases as std.conv.to, since it -// doesn't have to worry about a sign on the value or about whether it fits. -T _convDigits(T, R)(R str) -if (isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. -{ - import std.ascii : isDigit; - - assert(!str.empty); - T num = 0; - foreach (i; 0 .. str.length) - { - if (i != 0) - num *= 10; - if (!isDigit(str[i])) - return -1; - num += str[i] - '0'; - } - return num; -} - -@safe unittest -{ - import std.conv : to; - import std.range : chain, iota; - import std.stdio : writeln; - foreach (i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) - { - scope(failure) writeln(i); - assert(_convDigits!int(to!string(i)) == i); - } - foreach (str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) - { - scope(failure) writeln(str); - assert(_convDigits!int(str) == -1); - } -} - - @safe unittest { import std.traits : hasUnsharedAliasing; diff --git a/std/datetime/systime.d b/std/datetime/systime.d index c5a14724d69..a2bc2971294 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -17,7 +17,8 @@ import std.datetime.timeofday; import std.datetime.timezone; import std.format : format; import std.exception : enforce; -import std.traits : isSomeString, Unqual; +import std.range.primitives; +import std.traits : isIntegral, isSigned, isSomeString, Unqual; version(Windows) { @@ -8157,7 +8158,6 @@ public: { import std.algorithm.searching : startsWith, find; import std.conv : to; - import std.range.primitives; import std.string : strip; auto dstr = to!dstring(strip(isoString)); @@ -8394,7 +8394,6 @@ public: { import std.algorithm.searching : countUntil, find; import std.conv : to; - import std.range.primitives; import std.string : strip; auto dstr = to!dstring(strip(isoExtString)); @@ -8605,7 +8604,6 @@ public: { import std.algorithm.searching : countUntil, find; import std.conv : to; - import std.range.primitives; import std.string : strip; auto dstr = to!dstring(strip(simpleString)); @@ -9028,6 +9026,1145 @@ if (is(T == int) || is(T == long)) } +version(StdDdoc) +{ + version(Windows) + {} + else + { + alias SYSTEMTIME = void*; + alias FILETIME = void*; + } + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D SYSTEMTIME) struct to a $(LREF SysTime). + + Params: + st = The $(D SYSTEMTIME) struct to convert. + tz = The time zone that the time in the $(D SYSTEMTIME) struct is + assumed to be (if the $(D SYSTEMTIME) was supplied by a Windows + system call, the $(D SYSTEMTIME) will either be in local time + or UTC, depending on the call). + + Throws: + $(LREF DateTimeException) if the given $(D SYSTEMTIME) will not fit in + a $(LREF SysTime), which is highly unlikely to happen given that + $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) + is in 30,827 A.D. + +/ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(LREF SysTime) to a $(D SYSTEMTIME) struct. + + The $(D SYSTEMTIME) which is returned will be set using the given + $(LREF SysTime)'s time zone, so to get the $(D SYSTEMTIME) in + UTC, set the $(LREF SysTime)'s time zone to UTC. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a + $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is + prior to 1601 A.D. + +/ + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D FILETIME) struct to the number of hnsecs since midnight, + January 1st, 1 A.D. + + Params: + ft = The $(D FILETIME) struct to convert. + + Throws: + $(LREF DateTimeException) if the given $(D FILETIME) cannot be + represented as the return value. + +/ + long FILETIMEToStdTime(scope const FILETIME* ft) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(D FILETIME) struct to a $(LREF SysTime). + + Params: + ft = The $(D FILETIME) struct to convert. + tz = The time zone that the $(LREF SysTime) will be in ($(D FILETIME)s + are in UTC). + + Throws: + $(LREF DateTimeException) if the given $(D FILETIME) will not fit in a + $(LREF SysTime). + +/ + SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a number of hnsecs since midnight, January 1st, 1 A.D. to a + $(D FILETIME) struct. + + Params: + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. + + Throws: + $(LREF DateTimeException) if the given value will not fit in a + $(D FILETIME). + +/ + FILETIME stdTimeToFILETIME(long stdTime) @safe; + + + /++ + $(BLUE This function is Windows-Only.) + + Converts a $(LREF SysTime) to a $(D FILETIME) struct. + + $(D FILETIME)s are always in UTC. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a + $(D FILETIME). + +/ + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; +} +else version(Windows) +{ + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe + { + const max = SysTime.max; + + static void throwLaterThanMax() + { + throw new DateTimeException("The given SYSTEMTIME is for a date greater than SysTime.max."); + } + + if (st.wYear > max.year) + throwLaterThanMax(); + else if (st.wYear == max.year) + { + if (st.wMonth > max.month) + throwLaterThanMax(); + else if (st.wMonth == max.month) + { + if (st.wDay > max.day) + throwLaterThanMax(); + else if (st.wDay == max.day) + { + if (st.wHour > max.hour) + throwLaterThanMax(); + else if (st.wHour == max.hour) + { + if (st.wMinute > max.minute) + throwLaterThanMax(); + else if (st.wMinute == max.minute) + { + if (st.wSecond > max.second) + throwLaterThanMax(); + else if (st.wSecond == max.second) + { + if (st.wMilliseconds > max.fracSecs.total!"msecs") + throwLaterThanMax(); + } + } + } + } + } + } + + auto dt = DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + + return SysTime(dt, msecs(st.wMilliseconds), tz); + } + + @system unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + auto converted = SYSTEMTIMEToSysTime(&st, UTC()); + + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + } + + + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe + { + immutable dt = cast(DateTime) sysTime; + + if (dt.year < 1601) + throw new DateTimeException("SYSTEMTIME cannot hold dates prior to the year 1601."); + + SYSTEMTIME st; + + st.wYear = dt.year; + st.wMonth = dt.month; + st.wDayOfWeek = dt.dayOfWeek; + st.wDay = dt.day; + st.wHour = dt.hour; + st.wMinute = dt.minute; + st.wSecond = dt.second; + st.wMilliseconds = cast(ushort) sysTime.fracSecs.total!"msecs"; + + return st; + } + + @system unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + + SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); + + assert(st.wYear == result.wYear); + assert(st.wMonth == result.wMonth); + assert(st.wDayOfWeek == result.wDayOfWeek); + assert(st.wDay == result.wDay); + assert(st.wHour == result.wHour); + assert(st.wMinute == result.wMinute); + assert(st.wSecond == result.wSecond); + assert(st.wMilliseconds == result.wMilliseconds); + } + + private enum hnsecsFrom1601 = 504_911_232_000_000_000L; + + long FILETIMEToStdTime(scope const FILETIME* ft) @safe + { + ULARGE_INTEGER ul; + ul.HighPart = ft.dwHighDateTime; + ul.LowPart = ft.dwLowDateTime; + ulong tempHNSecs = ul.QuadPart; + + if (tempHNSecs > long.max - hnsecsFrom1601) + throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); + + return cast(long) tempHNSecs + hnsecsFrom1601; + } + + SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe + { + auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); + sysTime.timezone = tz; + return sysTime; + } + + @system unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + + auto converted = FILETIMEToSysTime(&ft); + + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + } + + + FILETIME stdTimeToFILETIME(long stdTime) @safe + { + if (stdTime < hnsecsFrom1601) + throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); + + ULARGE_INTEGER ul; + ul.QuadPart = cast(ulong) stdTime - hnsecsFrom1601; + + FILETIME ft; + ft.dwHighDateTime = ul.HighPart; + ft.dwLowDateTime = ul.LowPart; + + return ft; + } + + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe + { + return stdTimeToFILETIME(sysTime.stdTime); + } + + @system unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + auto sysTime = FILETIMEToSysTime(&ft, UTC()); + + FILETIME result = SysTimeToFILETIME(sysTime); + + assert(ft.dwLowDateTime == result.dwLowDateTime); + assert(ft.dwHighDateTime == result.dwHighDateTime); + } +} + + +/++ + Type representing the DOS file date/time format. + +/ +alias DosFileTime = uint; + +/++ + Converts from DOS file date/time to $(LREF SysTime). + + Params: + dft = The DOS file time to convert. + tz = The time zone which the DOS file time is assumed to be in. + + Throws: + $(LREF DateTimeException) if the $(D DosFileTime) is invalid. + +/ +SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe +{ + uint dt = cast(uint) dft; + + if (dt == 0) + throw new DateTimeException("Invalid DosFileTime."); + + int year = ((dt >> 25) & 0x7F) + 1980; + int month = ((dt >> 21) & 0x0F); // 1 .. 12 + int dayOfMonth = ((dt >> 16) & 0x1F); // 1 .. 31 + int hour = (dt >> 11) & 0x1F; // 0 .. 23 + int minute = (dt >> 5) & 0x3F; // 0 .. 59 + int second = (dt << 1) & 0x3E; // 0 .. 58 (in 2 second increments) + + try + return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); + catch (DateTimeException dte) + throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); +} + +@safe unittest +{ + assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == SysTime(DateTime(1980, 1, 1, 0, 0, 0))); + assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == SysTime(DateTime(2107, 12, 31, 23, 59, 58))); + assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44))); +} + + +/++ + Converts from $(LREF SysTime) to DOS file date/time. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to + a $(D DosFileTime). + +/ +DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe +{ + auto dateTime = cast(DateTime) sysTime; + + if (dateTime.year < 1980) + throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); + + if (dateTime.year > 2107) + throw new DateTimeException("DOS File Times cannot hold dates past 2107."); + + uint retval = 0; + retval = (dateTime.year - 1980) << 25; + retval |= (dateTime.month & 0x0F) << 21; + retval |= (dateTime.day & 0x1F) << 16; + retval |= (dateTime.hour & 0x1F) << 11; + retval |= (dateTime.minute & 0x3F) << 5; + retval |= (dateTime.second >> 1) & 0x1F; + + return cast(DosFileTime) retval; +} + +@safe unittest +{ + assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == 0b00000000001000010000000000000000); + assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == 0b11111111100111111011111101111101); + assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456); +} + + +/++ + The given array of $(D char) or random-access range of $(D char) or + $(D ubyte) is expected to be in the format specified in + $(HTTP tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the + grammar rule $(I date-time). It is the date-time format commonly used in + internet messages such as e-mail and HTTP. The corresponding + $(LREF SysTime) will be returned. + + RFC 822 was the original spec (hence the function's name), whereas RFC 5322 + is the current spec. + + The day of the week is ignored beyond verifying that it's a valid day of the + week, as the day of the week can be inferred from the date. It is not + checked whether the given day of the week matches the actual day of the week + of the given date (though it is technically invalid per the spec if the + day of the week doesn't match the actual day of the week of the given date). + + If the time zone is $(D "-0000") (or considered to be equivalent to + $(D "-0000") by section 4.3 of the spec), a $(LREF SimpleTimeZone) with a + utc offset of $(D 0) is used rather than $(LREF UTC), whereas $(D "+0000") + uses $(LREF UTC). + + Note that because $(LREF SysTime) does not currently support having a second + value of 60 (as is sometimes done for leap seconds), if the date-time value + does have a value of 60 for the seconds, it is treated as 59. + + The one area in which this function violates RFC 5322 is that it accepts + $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the + HTTP spec requires it. + + Throws: + $(LREF DateTimeException) if the given string doesn't follow the grammar + for a date-time field or if the resulting $(LREF SysTime) is invalid. + +/ +SysTime parseRFC822DateTime()(in char[] value) @safe +{ + import std.string : representation; + return parseRFC822DateTime(value.representation); +} + +/++ Ditto +/ +SysTime parseRFC822DateTime(R)(R value) @safe +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + import std.algorithm.searching : find, all; + import std.ascii : isDigit, isAlpha, isPrintable; + import std.conv : to; + import std.functional : not; + import std.string : capitalize, format; + import std.traits : EnumMembers, isArray; + import std.typecons : Rebindable; + + void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) + { + value = _stripCFWS(valueBefore); + if (value.length < minLen) + throw new DateTimeException("date-time value too short", __FILE__, line); + } + stripAndCheckLen(value, "7Dec1200:00A".length); + + static if (isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) + { + static string sliceAsString(R str) @trusted + { + return cast(string) str; + } + } + else + { + char[4] temp; + char[] sliceAsString(R str) @trusted + { + size_t i = 0; + foreach (c; str) + temp[i++] = cast(char) c; + return temp[0 .. str.length]; + } + } + + // day-of-week + if (isAlpha(value[0])) + { + auto dowStr = sliceAsString(value[0 .. 3]); + switch (dowStr) + { + foreach (dow; EnumMembers!DayOfWeek) + { + enum dowC = capitalize(to!string(dow)); + case dowC: + goto afterDoW; + } + default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); + } +afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); + if (value[0] != ',') + throw new DateTimeException("day-of-week missing comma"); + stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); + } + + // day + immutable digits = isDigit(value[1]) ? 2 : 1; + immutable day = _convDigits!short(value[0 .. digits]); + if (day == -1) + throw new DateTimeException("Invalid day"); + stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); + + // month + Month month; + { + auto monStr = sliceAsString(value[0 .. 3]); + switch (monStr) + { + foreach (mon; EnumMembers!Month) + { + enum monC = capitalize(to!string(mon)); + case monC: + { + month = mon; + goto afterMon; + } + } + default: throw new DateTimeException(format("Invalid month: %s", monStr)); + } +afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); + } + + // year + auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); + size_t yearLen = value.length - found.length; + if (found.length == 0) + throw new DateTimeException("Invalid year"); + if (found[0] == ':') + yearLen -= 2; + auto year = _convDigits!short(value[0 .. yearLen]); + if (year < 1900) + { + if (year == -1) + throw new DateTimeException("Invalid year"); + if (yearLen < 4) + { + if (yearLen == 3) + year += 1900; + else if (yearLen == 2) + year += year < 50 ? 2000 : 1900; + else + throw new DateTimeException("Invalid year. Too few digits."); + } + else + throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); + } + stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); + + // hour + immutable hour = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], ":00A".length); + if (value[0] != ':') + throw new DateTimeException("Invalid hour"); + stripAndCheckLen(value[1 .. value.length], "00A".length); + + // minute + immutable minute = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], "A".length); + + // second + short second; + if (value[0] == ':') + { + stripAndCheckLen(value[1 .. value.length], "00A".length); + second = _convDigits!short(value[0 .. 2]); + // this is just if/until SysTime is sorted out to fully support leap seconds + if (second == 60) + second = 59; + stripAndCheckLen(value[2 .. value.length], "A".length); + } + + immutable(TimeZone) parseTZ(int sign) + { + if (value.length < 5) + throw new DateTimeException("Invalid timezone"); + immutable zoneHours = _convDigits!short(value[1 .. 3]); + immutable zoneMinutes = _convDigits!short(value[3 .. 5]); + if (zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) + throw new DateTimeException("Invalid timezone"); + value = value[5 .. value.length]; + immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; + if (utcOffset == Duration.zero) + { + return sign == 1 ? cast(immutable(TimeZone))UTC() + : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); + } + return new immutable(SimpleTimeZone)(utcOffset); + } + + // zone + Rebindable!(immutable TimeZone) tz; + if (value[0] == '-') + tz = parseTZ(-1); + else if (value[0] == '+') + tz = parseTZ(1); + else + { + // obs-zone + immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; + switch (sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) + { + case "UT": case "GMT": tz = UTC(); break; + case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; + case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; + case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "J": case "j": throw new DateTimeException("Invalid timezone"); + default: + { + if (all!(std.ascii.isAlpha)(value[0 .. tzLen])) + { + tz = new immutable SimpleTimeZone(Duration.zero); + break; + } + throw new DateTimeException("Invalid timezone"); + } + } + value = value[tzLen .. value.length]; + } + + // This is kind of arbitrary. Technically, nothing but CFWS is legal past + // the end of the timezone, but we don't want to be picky about that in a + // function that's just parsing rather than validating. So, the idea here is + // that if the next character is printable (and not part of CFWS), then it + // might be part of the timezone and thus affect what the timezone was + // supposed to be, so we'll throw, but otherwise, we'll just ignore it. + if (!value.empty && isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') + throw new DateTimeException("Invalid timezone"); + + try + return SysTime(DateTime(year, month, day, hour, minute, second), tz); + catch (DateTimeException dte) + throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); +} + +/// +@safe unittest +{ + import std.exception : assertThrown; + + auto tz = new immutable SimpleTimeZone(hours(-8)); + assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == + SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); + + assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == + SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); + + auto badStr = "29 Feb 2001 12:17:16 +0200"; + assertThrown!DateTimeException(parseRFC822DateTime(badStr)); +} + +version(unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) +{ + import std.format : format; + auto value = cr(str); + auto result = parseRFC822DateTime(value); + if (result != expected) + throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); +} + +version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__) +{ + try + parseRFC822DateTime(cr(str)); + catch (DateTimeException) + return; + throw new AssertError("No DateTimeException was thrown", __FILE__, line); +} + +@system unittest +{ + import std.algorithm.iteration : filter, map; + import std.algorithm.searching : canFind; + import std.array : array; + import std.ascii : letters; + import std.format : format; + import std.meta : AliasSeq; + import std.range : chain, iota, take; + import std.stdio : writefln, writeln; + import std.string : representation; + + static struct Rand3Letters + { + enum empty = false; + @property auto front() { return _mon; } + void popFront() + { + import std.exception : assumeUnique; + import std.random : rndGen; + _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); + } + string _mon; + static auto start() { Rand3Letters retval; retval.popFront(); return retval; } + } + + foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, + function(string a){return cast(ubyte[]) a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + alias test = testParse822!cr; + alias testBad = testBadParse822!cr; + + immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); + immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); + immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); + immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); + + test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + + test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + + test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + + test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + + auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); + test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + + test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + + test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + + auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); + auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); + test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); + test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); + + test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + + test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + + // dst and std times are switched in the Southern Hemisphere which is why the + // time zone names and DateTime variables don't match. + auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); + auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); + test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); + test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); + + test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + + test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + + foreach (int i, mon; _monthNames) + { + test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); + test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); + } + + import std.uni : toLower, toUpper; + foreach (mon; chain(_monthNames[].map!(a => toLower(a))(), + _monthNames[].map!(a => toUpper(a))(), + ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", + "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", + "Nom", "Nav", "Dem", "Dac"], + Rand3Letters.start().filter!(a => !_monthNames[].canFind(a)).take(20))) + { + scope(failure) writefln("Month: %s", mon); + testBad(format("17 %s 2012 00:05:02 +0000", mon)); + testBad(format("17 %s 2012 00:05 +0000", mon)); + } + + immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + + { + auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); + int day = 11; + + foreach (int i, dow; daysOfWeekNames) + { + auto curr = start + dur!"days"(i); + test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); + test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); + + // Whether the day of the week matches the date is ignored. + test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); + test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); + } + } + + foreach (dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), + daysOfWeekNames[].map!(a => toUpper(a))(), + ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", + "Fro", "Fai", "San", "Sut"], + Rand3Letters.start().filter!(a => !daysOfWeekNames[].canFind(a)).take(20))) + { + scope(failure) writefln("Day of Week: %s", dow); + testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); + testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); + } + + testBad("31 Dec 1899 23:59:59 +0000"); + test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); + test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(Duration.zero))); + test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(dur!"hours"(-7)))); + + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach (i; 1900 .. 2102) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + st1.year = 9998; + st2.year = 9998; + foreach (i; 9998 .. 11_002) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + testBad("12 Feb 1907 23:17:09 0000"); + testBad("12 Feb 1907 23:17:09 +000"); + testBad("12 Feb 1907 23:17:09 -000"); + testBad("12 Feb 1907 23:17:09 +00000"); + testBad("12 Feb 1907 23:17:09 -00000"); + testBad("12 Feb 1907 23:17:09 +A"); + testBad("12 Feb 1907 23:17:09 +PST"); + testBad("12 Feb 1907 23:17:09 -A"); + testBad("12 Feb 1907 23:17:09 -PST"); + + // test trailing stuff that gets ignored + { + foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c), SysTime(std1, UTC())); + } + } + + // test trailing stuff that doesn't get ignored + { + foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char) c)); + testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char) c)); + testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char) c)); + } + } + + testBad("32 Jan 2012 12:13:14 -0800"); + testBad("31 Jan 2012 24:13:14 -0800"); + testBad("31 Jan 2012 12:60:14 -0800"); + testBad("31 Jan 2012 12:13:61 -0800"); + testBad("31 Jan 2012 12:13:14 -0860"); + test("31 Jan 2012 12:13:14 -0859", + SysTime(DateTime(2012, 1, 31, 12, 13, 14), + new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); + + // leap-seconds + test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); + + // FWS + test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); + + auto str = "01 Jan 2012 12:13:14 -0800 "; + test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); + foreach (i; 0 .. str.length) + { + auto currStr = str.dup; + currStr[i] = 'x'; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string) currStr); + } + foreach (i; 2 .. str.length) + { + auto currStr = str[0 .. $ - i]; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string) currStr); + testBad((cast(string) currStr) ~ " "); + } + }(); +} + +// Obsolete Format per section 4.3 of RFC 5322. +@system unittest +{ + import std.algorithm.iteration : filter, map; + import std.ascii : letters; + import std.exception : collectExceptionMsg; + import std.format : format; + import std.meta : AliasSeq; + import std.range : chain, iota; + import std.stdio : writefln, writeln; + import std.string : representation; + + auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); + auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); + auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); + auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); + auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); + auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); + auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); + auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); + + foreach (cr; AliasSeq!(function(string a){return cast(char[]) a;}, + function(string a){return cast(ubyte[]) a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + alias test = testParse822!cr; + { + auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", + " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; + + foreach (i, cfws; list) + { + scope(failure) writefln("i: %s", i); + + test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); + + test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); + + test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); + test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); + + test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + + test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + } + } + + // test years of 1, 2, and 3 digits. + { + auto st1 = SysTime(Date(2000, 1, 1), UTC()); + auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach (i; 0 .. 50) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + { + auto st1 = SysTime(Date(1950, 1, 1), UTC()); + auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach (i; 50 .. 100) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach (i; 0 .. 1000) + { + test(format("1 Jan %03d 00:00 GMT", i), st1); + test(format("1 Jan %03d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } + + foreach (i; 0 .. 10) + { + auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); + auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + } + + // test time zones + { + auto dt = DateTime(1982, 05, 03, 12, 22, 04); + test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); + test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); + test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); + test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); + + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + foreach (dchar c; filter!(a => a != 'j' && a != 'J')(letters)) + { + scope(failure) writefln("c: %s", c); + test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); + test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); + } + + foreach (dchar c; ['j', 'J']) + { + scope(failure) writefln("c: %s", c); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); + } + + foreach (string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) + { + scope(failure) writefln("s: %s", s); + test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); + } + + // test trailing stuff that gets ignored + { + foreach (c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21Dec1213:14:15+0000%c", cast(char) c), std1); + test(format("21Dec1213:14:15+0000%c ", cast(char) c), std1); + test(format("21Dec1213:14:15+0000%chello", cast(char) c), std1); + } + } + + // test trailing stuff that doesn't get ignored + { + foreach (c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char) c)))); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char) c)))); + assertThrown!DateTimeException( + parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char) c)))); + } + } + } + + // test that the checks for minimum length work correctly and avoid + // any RangeErrors. + test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + + auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); + foreach (str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) + { + foreach (i; 0 .. str.length) + { + auto value = str[0 .. $ - i]; + scope(failure) writeln(value); + assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); + } + } + }(); +} + + private: /+ @@ -9035,7 +10172,6 @@ private: +/ static string fracSecsToISOString(int hnsecs) @safe pure nothrow { - import std.range.primitives : popBack; assert(hnsecs >= 0); try @@ -9092,7 +10228,6 @@ if (isSomeString!S) import std.algorithm.searching : all; import std.ascii : isDigit; import std.conv : to; - import std.range.primitives; import std.string : representation; if (isoString.empty) @@ -9232,6 +10367,230 @@ if (validTimeUnits(units) && } +/+ + Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand + side of the given range (it strips comments delimited by $(D '(') and + $(D ')') as well as folding whitespace). + + It is assumed that the given range contains the value of a header field and + no terminating CRLF for the line (though the CRLF for folding whitespace is + of course expected and stripped) and thus that the only case of CR or LF is + in folding whitespace. + + If a comment does not terminate correctly (e.g. mismatched parens) or if the + the FWS is malformed, then the range will be empty when stripCWFS is done. + However, only minimal validation of the content is done (e.g. quoted pairs + within a comment aren't validated beyond \$LPAREN or \$RPAREN, because + they're inside a comment, and thus their value doesn't matter anyway). It's + only when the content does not conform to the grammar rules for FWS and thus + literally cannot be parsed that content is considered invalid, and an empty + range is returned. + + Note that _stripCFWS is eager, not lazy. It does not create a new range. + Rather, it pops off the CFWS from the range and returns it. + +/ +R _stripCFWS(R)(R range) +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + immutable e = range.length; + outer: for (size_t i = 0; i < e; ) + { + switch (range[i]) + { + case ' ': case '\t': + { + ++i; + break; + } + case '\r': + { + if (i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) + { + i += 3; + break; + } + break outer; + } + case '\n': + { + if (i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) + { + i += 2; + break; + } + break outer; + } + case '(': + { + ++i; + size_t commentLevel = 1; + while (i < e) + { + if (range[i] == '(') + ++commentLevel; + else if (range[i] == ')') + { + ++i; + if (--commentLevel == 0) + continue outer; + continue; + } + else if (range[i] == '\\') + { + if (++i == e) + break outer; + } + ++i; + } + break outer; + } + default: return range[i .. e]; + } + } + return range[e .. e]; +} + +@system unittest +{ + import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.meta : AliasSeq; + import std.stdio : writeln; + import std.string : representation; + + foreach (cr; AliasSeq!(function(string a){return cast(ubyte[]) a;}, + function(string a){return map!(b => cast(char) b)(a.representation);})) + (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 + scope(failure) writeln(typeof(cr).stringof); + + assert(_stripCFWS(cr("")).empty); + assert(_stripCFWS(cr("\r")).empty); + assert(_stripCFWS(cr("\r\n")).empty); + assert(_stripCFWS(cr("\r\n ")).empty); + assert(_stripCFWS(cr(" \t\r\n")).empty); + assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); + assert(_stripCFWS(cr(" \t\r\nhello")).empty); + assert(_stripCFWS(cr(" \t\r\n\v")).empty); + assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); + assert(_stripCFWS(cr("()")).empty); + assert(_stripCFWS(cr("(hello world)")).empty); + assert(_stripCFWS(cr("(hello world)(hello world)")).empty); + assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); + assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); + assert(_stripCFWS(cr(" ")).empty); + assert(_stripCFWS(cr("\t\t\t")).empty); + assert(_stripCFWS(cr("\t \r\n\r \n")).empty); + assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); + assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); + assert(_stripCFWS(cr("(((((")).empty); + assert(_stripCFWS(cr("(((()))")).empty); + assert(_stripCFWS(cr("(((())))")).empty); + assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); + assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); + assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); + assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); + assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); + assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); + assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); + assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); + assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + + assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); + assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); + assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); + }(); +} + +// This is so that we don't have to worry about std.conv.to throwing. It also +// doesn't have to worry about quite as many cases as std.conv.to, since it +// doesn't have to worry about a sign on the value or about whether it fits. +T _convDigits(T, R)(R str) +if (isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. +{ + import std.ascii : isDigit; + + assert(!str.empty); + T num = 0; + foreach (i; 0 .. str.length) + { + if (i != 0) + num *= 10; + if (!isDigit(str[i])) + return -1; + num += str[i] - '0'; + } + return num; +} + +@safe unittest +{ + import std.conv : to; + import std.range : chain, iota; + import std.stdio : writeln; + foreach (i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) + { + scope(failure) writeln(i); + assert(_convDigits!int(to!string(i)) == i); + } + foreach (str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) + { + scope(failure) writeln(str); + assert(_convDigits!int(str) == -1); + } +} + + version(unittest) { // Variables to help in testing. From 609a7c9f84d8198581d364b57a1b90d86fdd5521 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 18:16:34 +0200 Subject: [PATCH 110/262] Remove some unused private functions. --- std/datetime/package.d | 146 +++++------------------------------------ 1 file changed, 17 insertions(+), 129 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index da7f06a7012..44ab9ed2d67 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -114,35 +114,11 @@ public import std.datetime.systime; public import std.datetime.timeofday; public import std.datetime.timezone; - -import core.exception; // AssertError - +import core.exception : AssertError; import std.typecons : Flag, Yes, No; -import std.exception; // assertThrown, enforce -import std.range.primitives; // back, ElementType, empty, front, hasLength, - // hasSlicing, isRandomAccessRange, popFront -import std.traits; // isIntegral, isSafe, isSigned, isSomeString, Unqual -// FIXME -import std.functional; //: unaryFun; - -version(Windows) -{ - import core.stdc.time; // time_t - import core.sys.windows.windows; - import core.sys.windows.winsock2; - import std.windows.registry; - - // Uncomment and run unittests to print missing Windows TZ translations. - // Please subscribe to Microsoft Daylight Saving Time & Time Zone Blog - // (https://blogs.technet.microsoft.com/dst2007/) if you feel responsible - // for updating the translations. - // version = UpdateWindowsTZTranslations; -} -else version(Posix) -{ - import core.sys.posix.signal : timespec; - import core.sys.posix.sys.types; // time_t -} +import std.functional : unaryFun; +import std.traits; + //Verify module example. @safe unittest @@ -162,11 +138,23 @@ else version(Posix) dur!"days"(-26)); } +@safe unittest +{ + import std.traits : hasUnsharedAliasing; + /* Issue 6642 */ + static assert(!hasUnsharedAliasing!Date); + static assert(!hasUnsharedAliasing!TimeOfDay); + static assert(!hasUnsharedAliasing!DateTime); + static assert(!hasUnsharedAliasing!SysTime); +} + //============================================================================== -// Section with public enums and constants. +// Everything after here will be deprecated after we have replacements which +// use MonoTime and Duration. //============================================================================== + /++ Used by StopWatch to indicate whether it should start immediately upon construction. @@ -180,10 +168,6 @@ else version(Posix) alias AutoStart = Flag!"autoStart"; -//============================================================================== -// Section with StopWatch and Benchmark Code. -//============================================================================== - /++ $(D StopWatch) measures time as precisely as possible. @@ -722,99 +706,3 @@ if (!isSafe!((){StopWatch sw; unaryFun!func(sw.peek());})) auto trustResult = measureTime!((a){trustFunc();})(); auto sysResult = measureTime!((a){sysFunc();})(); } - -//============================================================================== -// Private Section. -//============================================================================== -private: - -//============================================================================== -// Section with private helper functions and templates. -//============================================================================== - -/+ - Template to help with converting between time units. - +/ -template hnsecsPer(string units) -if (CmpTimeUnits!(units, "months") < 0) -{ - static if (units == "hnsecs") - enum hnsecsPer = 1L; - else static if (units == "usecs") - enum hnsecsPer = 10L; - else static if (units == "msecs") - enum hnsecsPer = 1000 * hnsecsPer!"usecs"; - else static if (units == "seconds") - enum hnsecsPer = 1000 * hnsecsPer!"msecs"; - else static if (units == "minutes") - enum hnsecsPer = 60 * hnsecsPer!"seconds"; - else static if (units == "hours") - enum hnsecsPer = 60 * hnsecsPer!"minutes"; - else static if (units == "days") - enum hnsecsPer = 24 * hnsecsPer!"hours"; - else static if (units == "weeks") - enum hnsecsPer = 7 * hnsecsPer!"days"; -} - - -/+ - The time units which are one step smaller than the given units. - +/ -template nextSmallerTimeUnits(string units) -if (validTimeUnits(units) && - timeStrings.front != units) -{ - import std.algorithm.searching : countUntil; - enum nextSmallerTimeUnits = timeStrings[countUntil(timeStrings, units) - 1]; -} - -@safe unittest -{ - assert(nextSmallerTimeUnits!"years" == "months"); - assert(nextSmallerTimeUnits!"months" == "weeks"); - assert(nextSmallerTimeUnits!"weeks" == "days"); - assert(nextSmallerTimeUnits!"days" == "hours"); - assert(nextSmallerTimeUnits!"hours" == "minutes"); - assert(nextSmallerTimeUnits!"minutes" == "seconds"); - assert(nextSmallerTimeUnits!"seconds" == "msecs"); - assert(nextSmallerTimeUnits!"msecs" == "usecs"); - assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); - static assert(!__traits(compiles, nextSmallerTimeUnits!"hnsecs")); -} - - -/+ - The time units which are one step larger than the given units. - +/ -template nextLargerTimeUnits(string units) -if (validTimeUnits(units) && - timeStrings.back != units) -{ - import std.algorithm.searching : countUntil; - enum nextLargerTimeUnits = timeStrings[countUntil(timeStrings, units) + 1]; -} - -@safe unittest -{ - assert(nextLargerTimeUnits!"hnsecs" == "usecs"); - assert(nextLargerTimeUnits!"usecs" == "msecs"); - assert(nextLargerTimeUnits!"msecs" == "seconds"); - assert(nextLargerTimeUnits!"seconds" == "minutes"); - assert(nextLargerTimeUnits!"minutes" == "hours"); - assert(nextLargerTimeUnits!"hours" == "days"); - assert(nextLargerTimeUnits!"days" == "weeks"); - assert(nextLargerTimeUnits!"weeks" == "months"); - assert(nextLargerTimeUnits!"months" == "years"); - static assert(!__traits(compiles, nextLargerTimeUnits!"years")); -} - - -@safe unittest -{ - import std.traits : hasUnsharedAliasing; - /* Issue 6642 */ - static assert(!hasUnsharedAliasing!Date); - static assert(!hasUnsharedAliasing!TimeOfDay); - static assert(!hasUnsharedAliasing!DateTime); - static assert(!hasUnsharedAliasing!SysTime); -} From 6e2f88a8063d6fbf330e9df60f8722fca4045b68 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Thu, 4 May 2017 22:36:59 +0200 Subject: [PATCH 111/262] Fix links in std.datetime. --- std/datetime/common.d | 10 +- std/datetime/date.d | 104 +++++----- std/datetime/datetime.d | 107 ++++++----- std/datetime/interval.d | 369 ++++++++++++++++++++---------------- std/datetime/package.d | 62 +++--- std/datetime/systime.d | 398 +++++++++++++++++++++------------------ std/datetime/timeofday.d | 49 ++--- std/datetime/timezone.d | 59 +++--- 8 files changed, 630 insertions(+), 528 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index 42ea8b56ea6..750de5edd95 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_common.d) +/ module std.datetime.common; @@ -14,9 +12,9 @@ import std.typecons : Flag; /++ - Exception type used by std.datetime. It's an alias to $(REF TimeException, core,time). - Either can be caught without concern about which - module it came from. + Exception type used by std.datetime. It's an alias to + $(REF TimeException, core,time). Either can be caught without concern about + which module it came from. +/ alias DateTimeException = TimeException; diff --git a/std/datetime/date.d b/std/datetime/date.d index 35a8f298666..a48585b7ae0 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_date.d) +/ module std.datetime.date; @@ -24,10 +22,9 @@ version(unittest) import std.exception : assertThrown; /++ Represents a date in the - $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic Gregorian Calendar) - ranging from - 32,768 B.C. to 32,767 A.D. Positive years are A.D. Non-positive years are - B.C. + $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic + Gregorian Calendar) ranging from 32,768 B.C. to 32,767 A.D. Positive years + are A.D. Non-positive years are B.C. Year, month, and day are kept separately internally so that $(D Date) is optimized for calendar-based operations. @@ -35,8 +32,8 @@ version(unittest) import std.exception : assertThrown; $(D Date) uses the Proleptic Gregorian Calendar, so it assumes the Gregorian leap year calculations for its entire length. As per $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601), it treats 1 B.C. as - year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. as - a positive integer with 1 B.C. being the year prior to 1 A.D. + year 0, i.e. 1 B.C. is 0, 2 B.C. is -1, etc. Use $(LREF yearBC) to use B.C. + as a positive integer with 1 B.C. being the year prior to 1 A.D. Year 0 is a leap year. +/ @@ -46,7 +43,8 @@ public: /++ Throws: - $(LREF DateTimeException) if the resulting $(LREF Date) would not be valid. + $(REF std,datetime,common,DateTimeException) if the resulting + $(LREF Date) would not be valid. Params: year = Year of the Gregorian Calendar. Positive values are A.D. @@ -415,8 +413,8 @@ public: year = The year to set this Date's year to. Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. + $(REF std,datetime,common,DateTimeException) if the new year is not + a leap year and the resulting date would be on February 29th. +/ @property void year(int year) @safe pure { @@ -462,7 +460,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(LREF DateTimeException) if $(D isAD) is true. + $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. +/ @property ushort yearBC() @safe const pure { @@ -501,7 +499,8 @@ public: year = The year B.C. to set this $(LREF Date)'s year to. Throws: - $(LREF DateTimeException) if a non-positive value is given. + $(REF std,datetime,common,DateTimeException) if a non-positive value + is given. +/ @property void yearBC(int year) @safe pure { @@ -570,8 +569,9 @@ public: month = The month to set this $(LREF Date)'s month to. Throws: - $(LREF DateTimeException) if the given month is not a valid month or if - the current day would not be valid in the given month. + $(REF std,datetime,common,DateTimeException) if the given month is + not a valid month or if the current day would not be valid in the + given month. +/ @property void month(Month month) @safe pure { @@ -649,8 +649,8 @@ public: day = The day of the month to set this $(LREF Date)'s day to. Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. + $(REF std,datetime,common,DateTimeException) if the given day is not + a valid day of the current month. +/ @property void day(int day) @safe pure { @@ -747,8 +747,8 @@ public: /++ - Adds the given number of years or months to this $(LREF Date). A negative - number will subtract. + Adds the given number of years or months to this $(LREF Date). A + negative number will subtract. Note that if day overflow is allowed, and the date with the adjusted year/month overflows the number of days in the new month, then the month @@ -1532,8 +1532,8 @@ public: The difference between rolling and adding is that rolling does not affect larger units. Rolling a $(LREF Date) 12 months gets - the exact same $(LREF Date). However, the days can still be affected due to - the differing number of days in each month. + the exact same $(LREF Date). However, the days can still be affected due + to the differing number of days in each month. Because there are no units larger than years, there is no difference between adding and rolling years. @@ -2170,8 +2170,8 @@ public: /++ - Adds the given number of units to this $(LREF Date). A negative number will - subtract. + Adds the given number of units to this $(LREF Date). A negative number + will subtract. The difference between rolling and adding is that rolling does not affect larger units. For instance, rolling a $(LREF Date) one @@ -2407,7 +2407,8 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from + Gives the result of adding or subtracting a $(REF Duration, core,time) + from The legal types of arithmetic for $(LREF Date) using this operator are @@ -2518,8 +2519,9 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF Date), as well as assigning the result to this $(LREF Date). + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF Date), as well as assigning the result to this + $(LREF Date). The legal types of arithmetic for $(LREF Date) using this operator are @@ -2684,13 +2686,13 @@ public: Returns the difference between the two $(LREF Date)s in months. To get the difference in years, subtract the year property - of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. + of two $(LREF Date)s. To get the difference in days or weeks, + subtract the $(LREF Date)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. Note that the number of days in the months or how far into the month either $(LREF Date) is is irrelevant. It is the difference in the month @@ -3028,8 +3030,8 @@ public: $(LREF Date) is on. Throws: - $(LREF DateTimeException) if the given day is an invalid day of the - year. + $(REF std,datetime,common,DateTimeException) if the given day is an + invalid day of the year. +/ @property void dayOfYear(int day) @safe pure { @@ -3710,8 +3712,9 @@ public: isoString = A string formatted in the ISO format for dates. Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF Date) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF Date) would not be + valid. +/ static Date fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) @@ -3825,16 +3828,17 @@ public: /++ - Creates a $(LREF Date) from a string with the format YYYY-MM-DD. Whitespace - is stripped from the given string. + Creates a $(LREF Date) from a string with the format YYYY-MM-DD. + Whitespace is stripped from the given string. Params: isoExtString = A string formatted in the ISO Extended format for dates. Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF Date) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO Extended format or if the resulting $(LREF Date) + would not be valid. +/ static Date fromISOExtString(S)(in S isoExtString) @safe pure if (isSomeString!(S)) @@ -3961,8 +3965,9 @@ public: formats dates. Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF Date) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the correct format or if the resulting $(LREF Date) would not + be valid. +/ static Date fromSimpleString(S)(in S simpleString) @safe pure if (isSomeString!(S)) @@ -4099,8 +4104,8 @@ public: /++ - Returns the $(LREF Date) farthest in the future which is representable by - $(LREF Date). + Returns the $(LREF Date) farthest in the future which is representable + by $(LREF Date). +/ @property static Date max() @safe pure nothrow { @@ -4140,8 +4145,8 @@ private: package: /+ - Adds the given number of days to this $(LREF Date). A negative number will - subtract. + Adds the given number of days to this $(LREF Date). A negative number + will subtract. The month will be adjusted along with the day if the number of days added (or subtracted) would overflow (or underflow) the current month. @@ -4454,7 +4459,8 @@ string monthToString(Month month) @safe pure monthStr = The string representation of the month to get the Month for. Throws: - $(LREF DateTimeException) if the given month is not a valid month string. + $(REF std,datetime,common,DateTimeException) if the given month is not a + valid month string. +/ Month monthFromString(string monthStr) @safe pure { diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d index 559b8c48b58..20e25f6e69a 100644 --- a/std/datetime/datetime.d +++ b/std/datetime/datetime.d @@ -3,7 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) + Source: $(PHOBOSSRC std/datetime/_datetime.d) Macros: LREF2=$(D $2) +/ @@ -25,13 +25,14 @@ version(unittest) import std.exception : assertThrown; /++ - Combines the $(LREF Date) and $(LREF TimeOfDay) structs to give an object - which holds both the date and the time. It is optimized for calendar-based - operations and has no concept of time zone. For an object which is - optimized for time operations based on the system time, use - $(LREF SysTime). $(LREF SysTime) has a concept of time zone and has much higher - precision (hnsecs). $(D DateTime) is intended primarily for calendar-based - uses rather than precise time operations. + Combines the $(REF std,datetime,date,Date) and + $(REF std,datetime,timeofday,TimeOfDay) structs to give an object which holds + both the date and the time. It is optimized for calendar-based operations and + has no concept of time zone. For an object which is optimized for time + operations based on the system time, use $(REF std,datetime,systime,SysTime). + $(REF std,datetime,systime,SysTime) has a concept of time zone and has much + higher precision (hnsecs). $(D DateTime) is intended primarily for + calendar-based uses rather than precise time operations. +/ struct DateTime { @@ -401,8 +402,8 @@ public: The time portion of $(LREF DateTime). Params: - tod = The $(LREF TimeOfDay) to set this $(LREF DateTime)'s time portion - to. + tod = The $(REF std,datetime,timeofday,TimeOfDay) to set this + $(LREF DateTime)'s time portion to. +/ @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow { @@ -453,8 +454,8 @@ public: year = The year to set this $(LREF DateTime)'s year to. Throws: - $(LREF DateTimeException) if the new year is not a leap year and if the - resulting date would be on February 29th. + $(REF std,datetime,common,DateTimeException) if the new year is not + a leap year and if the resulting date would be on February 29th. +/ @property void year(int year) @safe pure { @@ -498,7 +499,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(LREF DateTimeException) if $(D isAD) is true. + $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. +/ @property short yearBC() @safe const pure { @@ -534,7 +535,8 @@ public: year = The year B.C. to set this $(LREF DateTime)'s year to. Throws: - $(LREF DateTimeException) if a non-positive value is given. + $(REF std,datetime,common,DateTimeException) if a non-positive value + is given. +/ @property void yearBC(int year) @safe pure { @@ -602,7 +604,8 @@ public: month = The month to set this $(LREF DateTime)'s month to. Throws: - $(LREF DateTimeException) if the given month is not a valid month. + $(REF std,datetime,common,DateTimeException) if the given month is + not a valid month. +/ @property void month(Month month) @safe pure { @@ -684,8 +687,8 @@ public: day = The day of the month to set this $(LREF DateTime)'s day to. Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. + $(REF std,datetime,common,DateTimeException) if the given day is not + a valid day of the current month. +/ @property void day(int day) @safe pure { @@ -805,8 +808,8 @@ public: hour = The hour of the day to set this $(LREF DateTime)'s hour to. Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF DateTime). + $(REF std,datetime,common,DateTimeException) if the given hour would + result in an invalid $(LREF DateTime). +/ @property void hour(int hour) @safe pure { @@ -855,8 +858,8 @@ public: minute = The minute to set this $(LREF DateTime)'s minute to. Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF DateTime). + $(REF std,datetime,common,DateTimeException) if the given minute + would result in an invalid $(LREF DateTime). +/ @property void minute(int minute) @safe pure { @@ -905,8 +908,8 @@ public: second = The second to set this $(LREF DateTime)'s second to. Throws: - $(LREF DateTimeException) if the given seconds would result in an - invalid $(LREF DateTime). + $(REF std,datetime,common,DateTimeException) if the given seconds + would result in an invalid $(LREF DateTime). +/ @property void second(int second) @safe pure { @@ -1061,8 +1064,8 @@ public: /++ - Adds the given number of units to this $(LREF DateTime). A negative number - will subtract. + Adds the given number of units to this $(LREF DateTime). A negative + number will subtract. The difference between rolling and adding is that rolling does not affect larger units. For instance, rolling a $(LREF DateTime) one @@ -1073,7 +1076,8 @@ public: Params: units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF DateTime). + value = The number of $(D_PARAM units) to add to this + $(LREF DateTime). +/ ref DateTime roll(string units)(long value) @safe pure nothrow if (units == "days") @@ -1988,8 +1992,8 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF DateTime). + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF DateTime). The legal types of arithmetic for $(LREF DateTime) using this operator are @@ -2105,9 +2109,11 @@ public: /++ Gives the result of adding or subtracting a duration from this - $(LREF DateTime), as well as assigning the result to this $(LREF DateTime). + $(LREF DateTime), as well as assigning the result to this + $(LREF DateTime). - The legal types of arithmetic for $(LREF DateTime) using this operator are + The legal types of arithmetic for $(LREF DateTime) using this operator + are $(BOOKTABLE, $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) @@ -2339,12 +2345,12 @@ public: To get the difference in years, subtract the year property of two $(LREF DateTime)s. To get the difference in days or weeks, - subtract the $(LREF DateTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. + subtract the $(LREF DateTime)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. Note that the number of days in the months or how far into the month either date is is irrelevant. It is the difference in the month property @@ -2591,8 +2597,9 @@ public: /++ - $(LREF DateTime) for the last day in the month that this $(LREF DateTime) is - in. The time portion of endOfMonth is always 23:59:59. + $(LREF DateTime) for the last day in the month that this + $(LREF DateTime) is in. The time portion of endOfMonth is always + 23:59:59. +/ @property DateTime endOfMonth() @safe const pure nothrow { @@ -2966,8 +2973,9 @@ public: isoString = A string formatted in the ISO format for dates and times. Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF DateTime) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF DateTime) would not + be valid. +/ static DateTime fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) @@ -3054,9 +3062,9 @@ public: and times. Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF DateTime) would not be - valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO Extended format or if the resulting $(LREF DateTime) + would not be valid. +/ static DateTime fromISOExtString(S)(in S isoExtString) @safe pure if (isSomeString!(S)) @@ -3142,8 +3150,9 @@ public: formats dates and times. Throws: - $(LREF DateTimeException) if the given string is not in the correct - format or if the resulting $(LREF DateTime) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the correct format or if the resulting $(LREF DateTime) + would not be valid. +/ static DateTime fromSimpleString(S)(in S simpleString) @safe pure if (isSomeString!(S)) @@ -3225,8 +3234,8 @@ public: /++ - Returns the $(LREF DateTime) farthest in the past which is representable by - $(LREF DateTime). + Returns the $(LREF DateTime) farthest in the past which is representable + by $(LREF DateTime). +/ @property static DateTime min() @safe pure nothrow out(result) @@ -3252,8 +3261,8 @@ public: /++ - Returns the $(LREF DateTime) farthest in the future which is representable - by $(LREF DateTime). + Returns the $(LREF DateTime) farthest in the future which is + representable by $(LREF DateTime). +/ @property static DateTime max() @safe pure nothrow out(result) diff --git a/std/datetime/interval.d b/std/datetime/interval.d index dc5335ee4c0..7eda15fe8b6 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_interval.d) +/ module std.datetime.interval; @@ -19,9 +17,10 @@ version(unittest) import std.exception : assertThrown; /++ - Indicates a direction in time. One example of its use is $(LREF2 .Interval, Interval)'s - $(LREF expand, expand) function which uses it to indicate whether the interval should - be expanded backwards (into the past), forwards (into the future), or both. + Indicates a direction in time. One example of its use is $(LREF Interval)'s + $(LREF expand) function which uses it to indicate whether the interval + should be expanded backwards (into the past), forwards (into the future), or + both. +/ enum Direction { @@ -100,7 +99,8 @@ public: interval. Throws: - $(LREF DateTimeException) if $(D_PARAM end) is before $(D_PARAM begin). + $(REF std,datetime,common,DateTimeException) if $(D_PARAM end) is + before $(D_PARAM begin). Example: -------------------- @@ -123,8 +123,8 @@ public: duration = The duration from the starting point to the end point. Throws: - $(LREF DateTimeException) if the resulting $(D end) is before - $(D begin). + $(REF std,datetime,common,DateTimeException) if the resulting + $(D end) is before $(D begin). Example: -------------------- @@ -144,7 +144,7 @@ public: /++ Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. + rhs = The $(LREF Interval) to assign to this one. +/ ref Interval opAssign(const ref Interval rhs) pure nothrow { @@ -156,7 +156,7 @@ public: /++ Params: - rhs = The $(LREF2 .Interval, Interval) to assign to this one. + rhs = The $(LREF Interval) to assign to this one. +/ ref Interval opAssign(Interval rhs) pure nothrow { @@ -188,7 +188,8 @@ public: timePoint = The time point to set $(D begin) to. Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if the resulting + interval would be invalid. +/ @property void begin(TP timePoint) pure { @@ -220,7 +221,8 @@ public: timePoint = The time point to set end to. Throws: - $(LREF DateTimeException) if the resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if the resulting + interval would be invalid. +/ @property void end(TP timePoint) pure { @@ -267,7 +269,8 @@ public: timePoint = The time point to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -294,7 +297,8 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -329,7 +333,8 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -355,7 +360,8 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -378,7 +384,8 @@ public: it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -407,7 +414,8 @@ public: interval = The interval to check for against this interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -437,7 +445,8 @@ public: interval = The interval to check for against this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -466,7 +475,8 @@ public: interval = The interval to check for against this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -489,7 +499,8 @@ public: it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -518,7 +529,8 @@ public: interval = The interval to check against this interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -551,7 +563,8 @@ public: interval = The interval to check against this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -574,7 +587,8 @@ public: interval = The interval to check against this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -596,7 +610,8 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -625,7 +640,8 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -650,7 +666,8 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -675,8 +692,8 @@ public: interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - either interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect or if either interval is empty. Example: -------------------- @@ -710,8 +727,8 @@ public: interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect or if this interval is empty. Example: -------------------- @@ -742,8 +759,8 @@ public: interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - this interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect or if this interval is empty. Example: -------------------- @@ -775,7 +792,8 @@ public: interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -805,7 +823,8 @@ public: interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -831,7 +850,8 @@ public: interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -856,8 +876,8 @@ public: interval = The interval to merge with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if either interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect and are not adjacent or if either interval is empty. Example: -------------------- @@ -891,8 +911,8 @@ public: interval = The interval to merge with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if this interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect and are not adjacent or if this interval is empty. Example: -------------------- @@ -923,8 +943,8 @@ public: interval = The interval to merge with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are not - adjacent or if this interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect and are not adjacent or if this interval is empty. Example: -------------------- @@ -957,7 +977,8 @@ public: interval = The interval to create a span together with this interval. Throws: - $(LREF DateTimeException) if either interval is empty. + $(REF std,datetime,common,DateTimeException) if either interval is + empty. Example: -------------------- @@ -991,7 +1012,8 @@ public: interval = The interval to create a span together with this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -1020,7 +1042,8 @@ public: interval = The interval to create a span together with this interval. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Example: -------------------- @@ -1050,8 +1073,8 @@ public: duration = The duration to shift the interval by. Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. + $(REF std,datetime,common,DateTimeException) this interval is empty + or if the resulting interval would be invalid. Example: -------------------- @@ -1100,8 +1123,8 @@ public: to increment. Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if this interval is + empty or if the resulting interval would be invalid. Example: -------------------- @@ -1147,8 +1170,8 @@ public: dir = The direction in time to expand the interval. Throws: - $(LREF DateTimeException) this interval is empty or if the resulting - interval would be invalid. + $(REF std,datetime,common,DateTimeException) this interval is empty + or if the resulting interval would be invalid. Example: -------------------- @@ -1225,8 +1248,8 @@ public: dir = The direction in time to expand the interval. Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if this interval is + empty or if the resulting interval would be invalid. Example: -------------------- @@ -1311,10 +1334,10 @@ public: $(D_PARAM func) would generate). If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or beyond the $(D end) - of the interval. + current $(D front) of the range, then a + $(REF std,datetime,common,DateTimeException) will be thrown. The range + will be empty and iteration complete when $(D_PARAM func) generates a + time point equal to or beyond the $(D end) of the interval. There are helper functions in this module which generate common delegates to pass to $(D fwdRange). Their documentation starts with @@ -1327,7 +1350,8 @@ public: before returning it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Warning: $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) @@ -1404,10 +1428,10 @@ public: $(D_PARAM func) would generate). If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. The range will be empty and iteration complete when - $(D_PARAM func) generates a time point equal to or less than the - $(D begin) of the interval. + the current $(D front) of the range, then a + $(REF std,datetime,common,DateTimeException) will be thrown. The range + will be empty and iteration complete when $(D_PARAM func) generates a + time point equal to or less than the $(D begin) of the interval. There are helper functions in this module which generate common delegates to pass to $(D bwdRange). Their documentation starts with @@ -1420,7 +1444,8 @@ public: before returning it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Warning: $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) @@ -1527,7 +1552,8 @@ private: /+ Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. +/ void _enforceNotEmpty(size_t line = __LINE__) const pure { @@ -3215,7 +3241,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); interval = The interval to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -3312,7 +3339,8 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); interval = The interval to check for against this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -3404,7 +3432,8 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); interval = The interval to check against this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -3479,7 +3508,8 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( interval = The interval to check for intersection with this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -3554,8 +3584,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect or if the given interval is empty. Example: -------------------- @@ -3611,7 +3641,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect. Example: -------------------- @@ -3643,7 +3674,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -3715,8 +3747,9 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( interval = The interval to merge with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect and are not adjacent or if the given interval is + empty. Note: There is no overload for $(D merge) which takes a @@ -3785,7 +3818,8 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Note: There is no overload for $(D span) which takes a @@ -3880,9 +3914,9 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); { /++ Shifts the $(D begin) of this interval forward or backwards in time - by the given number of years and/or months (a positive number of years - and months shifts the interval forward; a negative number shifts it - backward). It adds the years the given years and months to + by the given number of years and/or months (a positive number of + years and months shifts the interval forward; a negative number + shifts it backward). It adds the years the given years and months to $(D begin). It effectively calls $(D add!"years"()) and then $(D add!"months"()) on $(D begin) with the given number of years and months. @@ -3894,8 +3928,8 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); on $(D begin), causing its month to increment. Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if this interval is + empty or if the resulting interval would be invalid. Example: -------------------- @@ -3962,8 +3996,8 @@ assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); on $(D begin), causing its month to increment. Throws: - $(LREF DateTimeException) if this interval is empty or if the - resulting interval would be invalid. + $(REF std,datetime,common,DateTimeException) if this interval is + empty or if the resulting interval would be invalid. Example: -------------------- @@ -4002,8 +4036,8 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); $(D_PARAM func) would generate). If $(D_PARAM func) ever generates a time point less than or equal to the - current $(D front) of the range, then a $(LREF DateTimeException) will be - thrown. + current $(D front) of the range, then a + $(REF std,datetime,common,DateTimeException) will be thrown. There are helper functions in this module which generate common delegates to pass to $(D fwdRange). Their documentation starts with @@ -4016,7 +4050,8 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); before returning it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Warning: $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) @@ -5421,7 +5456,8 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); interval = The interval to check for inclusion in this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -5512,7 +5548,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); interval = The interval to check for against this interval. Throws: - $(LREF DateTimeException) if the given interval is empty + $(REF std,datetime,common,DateTimeException) if the given interval + is empty Example: -------------------- @@ -5616,7 +5653,8 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); interval = The interval to check against this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -5694,7 +5732,8 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( interval = The interval to check for intersection with this interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -5768,8 +5807,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect or if - the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect or if the given interval is empty. Example: -------------------- @@ -5802,7 +5841,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( interval = The interval to intersect with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect. Example: -------------------- @@ -5857,7 +5897,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Example: -------------------- @@ -5935,8 +5976,8 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( interval = The interval to merge with this interval. Throws: - $(LREF DateTimeException) if the two intervals do not intersect and are - not adjacent or if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the two intervals do + not intersect and are not adjacent or if the given interval is empty. Note: There is no overload for $(D merge) which takes a @@ -6005,7 +6046,8 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( interval. Throws: - $(LREF DateTimeException) if the given interval is empty. + $(REF std,datetime,common,DateTimeException) if the given interval + is empty. Note: There is no overload for $(D span) which takes a @@ -6113,8 +6155,8 @@ assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); on $(D end), causing its month to increment. Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. + $(REF std,datetime,common,DateTimeException) if empty is true or + if the resulting interval would be invalid. Example: -------------------- @@ -6181,8 +6223,8 @@ assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); on $(D end), causing their month to increment. Throws: - $(LREF DateTimeException) if empty is true or if the resulting - interval would be invalid. + $(REF std,datetime,common,DateTimeException) if empty is true or + if the resulting interval would be invalid. Example: -------------------- @@ -6221,8 +6263,8 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); $(D_PARAM func) would generate). If $(D_PARAM func) ever generates a time point greater than or equal to - the current $(D front) of the range, then a $(LREF DateTimeException) will - be thrown. + the current $(D front) of the range, then a + $(REF std,datetime,common,DateTimeException) will be thrown. There are helper functions in this module which generate common delegates to pass to $(D bwdRange). Their documentation starts with @@ -6235,7 +6277,8 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); before returning it. Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. Warning: $(D_PARAM func) must be logically pure. Ideally, $(D_PARAM func) @@ -7868,9 +7911,9 @@ if (isTimePoint!TP && number of years, month, and duration later. The difference between this version of $(D everyDuration) and the version - which just takes a $(REF Duration, core,time) is that this one also takes the number of - years and months (along with an $(D AllowDayOverflow) to indicate whether - adding years and months should allow the days to overflow). + which just takes a $(REF Duration, core,time) is that this one also takes + the number of years and months (along with an $(D AllowDayOverflow) to + indicate whether adding years and months should allow the days to overflow). Note that if iterating forward, $(D add!"years"()) is called on the given time point, then $(D add!"months"()), and finally the duration is added @@ -8009,28 +8052,30 @@ if (isTimePoint!TP && /++ - A range over an $(LREF2 .Interval, Interval). + A range over an $(LREF Interval). - $(D IntervalRange) is only ever constructed by $(LREF2 .Interval, Interval). However, when + $(D IntervalRange) is only ever constructed by $(LREF Interval). However, when it is constructed, it is given a function, $(D func), which is used to generate the time points which are iterated over. $(D func) takes a time point and returns a time point of the same type. For instance, to iterate over all of the days in - the interval $(D Interval!Date), pass a function to $(LREF2 .Interval, Interval)'s $(D fwdRange) - where that function took a $(LREF Date) and returned a $(LREF Date) which was one - day later. That function would then be used by $(D IntervalRange)'s - $(D popFront) to iterate over the $(LREF Date)s in the interval. + the interval $(D Interval!Date), pass a function to $(LREF Interval)'s + $(D fwdRange) where that function took a $(REF std,datetime,date,Date) and + returned a $(REF std,datetime,date,Date) which was one day later. That + function would then be used by $(D IntervalRange)'s $(D popFront) to iterate + over the $(REF std,datetime,date,Date)s in the interval. If $(D dir == Direction.fwd), then a range iterates forward in time, whereas if $(D dir == Direction.bwd), then it iterates backwards in time. So, if $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must generate a time point going in the proper direction of iteration, or a - $(LREF DateTimeException) will be thrown. So, to iterate forward in - time, the time point that $(D func) generates must be later in time than the - one passed to it. If it's either identical or earlier in time, then a - $(LREF DateTimeException) will be thrown. To iterate backwards, then - the generated time point must be before the time point which was passed in. + $(REF std,datetime,common,DateTimeException) will be thrown. So, to iterate + forward in time, the time point that $(D func) generates must be later in + time than the one passed to it. If it's either identical or earlier in time, + then a $(REF std,datetime,common,DateTimeException) will be thrown. To + iterate backwards, then the generated time point must be before the time + point which was passed in. If the generated time point is ever passed the edge of the range in the proper direction, then the edge of that range will be used instead. So, if @@ -8043,10 +8088,10 @@ if (isTimePoint!TP && it and its $(D end) is excluded from it, if $(D dir == Direction.bwd), then $(D begin) is treated as excluded and $(D end) is treated as included. This allows for the same behavior in both directions. This works because none of - $(LREF2 .Interval, Interval)'s functions which care about whether $(D begin) or $(D end) is - included or excluded are ever called by $(D IntervalRange). $(D interval) + $(LREF Interval)'s functions which care about whether $(D begin) or $(D end) + is included or excluded are ever called by $(D IntervalRange). $(D interval) returns a normal interval, regardless of whether $(D dir == Direction.fwd) - or if $(D dir == Direction.bwd), so any $(LREF2 .Interval, Interval) functions which are + or if $(D dir == Direction.bwd), so any $(LREF Interval) functions which are called on it which care about whether $(D begin) or $(D end) are included or excluded will treat $(D begin) as included and $(D end) as excluded. +/ @@ -8087,7 +8132,7 @@ public: The first time point in the range. Throws: - $(LREF DateTimeException) if the range is empty. + $(REF std,datetime,common,DateTimeException) if the range is empty. +/ @property TP front() const pure { @@ -8110,10 +8155,10 @@ public: than the interval's $(D begin), then $(D front) is set to $(D begin). Throws: - $(LREF DateTimeException) if the range is empty or if the generated - time point is in the wrong direction (i.e. if iterating - forward and the generated time point is before $(D front), or if - iterating backwards and the generated time point is after + $(REF std,datetime,common,DateTimeException) if the range is empty + or if the generated time point is in the wrong direction (i.e. if + iterating forward and the generated time point is before $(D front), + or if iterating backwards and the generated time point is after $(D front)). +/ void popFront() @@ -8198,7 +8243,8 @@ private: /+ Throws: - $(LREF DateTimeException) if this interval is empty. + $(REF std,datetime,common,DateTimeException) if this interval is + empty. +/ void _enforceNotEmpty(size_t line = __LINE__) const pure { @@ -8209,8 +8255,8 @@ private: /+ Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. + $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const { @@ -8553,20 +8599,22 @@ private: is used to generate the time points which are iterated over. $(D func) takes a time point and returns a time point of the same type. For instance, to iterate - over all of the days in the interval $(D PosInfInterval!Date), pass a function to - $(D PosInfInterval)'s $(D fwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day later. That function would then be - used by $(D PosInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. + over all of the days in the interval $(D PosInfInterval!Date), pass a + function to $(D PosInfInterval)'s $(D fwdRange) where that function took a + $(REF std,datetime,date,Date) and returned a $(REF std,datetime,date,Date) + which was one day later. That function would then be used by + $(D PosInfIntervalRange)'s $(D popFront) to iterate over the + $(REF std,datetime,date,Date)s in the interval - though obviously, since the + range is infinite, use a function such as $(D std.range.take) with it rather + than iterating over $(I all) of the dates. As the interval goes to positive infinity, the range is always iterated over forwards, never backwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be later in time - than the one passed to it. If it's either identical or earlier in time, then - a $(LREF DateTimeException) will be thrown. + the proper direction of iteration, or a + $(REF std,datetime,common,DateTimeException) will be thrown. So, the time + points that $(D func) generates must be later in time than the one passed to + it. If it's either identical or earlier in time, then a + $(REF std,datetime,common,DateTimeException) will be thrown. +/ struct PosInfIntervalRange(TP) if (isTimePoint!TP) @@ -8612,8 +8660,8 @@ public: time point in the range. Throws: - $(LREF DateTimeException) if the generated time point is less than - $(D front). + $(REF std,datetime,common,DateTimeException) if the generated time + point is less than $(D front). +/ void popFront() { @@ -8667,8 +8715,8 @@ private: /+ Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. + $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const { @@ -8829,21 +8877,22 @@ private: However, when it is constructed, it is given a function, $(D func), which is used to generate the time points which are iterated over. $(D func) takes a time point and returns a time point of the same type. For - instance, to iterate - over all of the days in the interval $(D NegInfInterval!Date), pass a function to - $(D NegInfInterval)'s $(D bwdRange) where that function took a $(LREF Date) and - returned a $(LREF Date) which was one day earlier. That function would then be - used by $(D NegInfIntervalRange)'s $(D popFront) to iterate over the - $(LREF Date)s in the interval - though obviously, since the range is infinite, - use a function such as $(D std.range.take) with it rather than - iterating over $(I all) of the dates. + instance, to iterate over all of the days in the interval + $(D NegInfInterval!Date), pass a function to $(D NegInfInterval)'s + $(D bwdRange) where that function took a $(REF std,datetime,date,Date) and + returned a $(REF std,datetime,date,Date) which was one day earlier. That + function would then be used by $(D NegInfIntervalRange)'s $(D popFront) to + iterate over the $(REF std,datetime,date,Date)s in the interval - though + obviously, since the range is infinite, use a function such as + $(D std.range.take) with it rather than iterating over $(I all) of the dates. As the interval goes to negative infinity, the range is always iterated over backwards, never forwards. $(D func) must generate a time point going in - the proper direction of iteration, or a $(LREF DateTimeException) will be - thrown. So, the time points that $(D func) generates must be earlier in time - than the one passed to it. If it's either identical or later in time, then a - $(LREF DateTimeException) will be thrown. + the proper direction of iteration, or a + $(REF std,datetime,common,DateTimeException) will be thrown. So, the time + points that $(D func) generates must be earlier in time than the one passed + to it. If it's either identical or later in time, then a + $(REF std,datetime,common,DateTimeException) will be thrown. Also note that while normally the $(D end) of an interval is excluded from it, $(D NegInfIntervalRange) treats it as if it were included. This allows @@ -8899,8 +8948,8 @@ public: time point in the range. Throws: - $(LREF DateTimeException) if the generated time point is greater than - $(D front). + $(REF std,datetime,common,DateTimeException) if the generated time + point is greater than $(D front). +/ void popFront() { @@ -8954,8 +9003,8 @@ private: /+ Throws: - $(LREF DateTimeException) if $(D_PARAM newTP) is in the wrong - direction. + $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const { diff --git a/std/datetime/package.d b/std/datetime/package.d index 44ab9ed2d67..b6044a0eeff 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -1,15 +1,19 @@ -//Written in the D programming language +// Written in the D programming language /++ Module containing Date/Time functionality. This module provides: $(UL - $(LI Types to represent points in time: $(LREF SysTime), $(LREF Date), - $(LREF TimeOfDay), and $(LREF2 .DateTime, DateTime).) + $(LI Types to represent points in time: + $(REF std,datetime,systime,SysTime), + $(REF std,datetime,date,Date), + $(REF std,datetime,timeofday,TimeOfDay), + $(REF std,datetime,datetime,DateTime).) $(LI Types to represent intervals of time.) $(LI Types to represent ranges over intervals of time.) - $(LI Types to represent time zones (used by $(LREF SysTime)).) + $(LI Types to represent time zones (used by + $(REF std,datetime,systime,SysTime)).) $(LI A platform-independent, high precision stopwatch type: $(LREF StopWatch)) $(LI Benchmarking functions.) @@ -45,21 +49,22 @@ be done on a series of time points. The types that the typical user is most likely to be interested in are - $(LREF Date) (if they want dates but don't care about time), $(LREF DateTime) - (if they want dates and times but don't care about time zones), $(LREF SysTime) - (if they want the date and time from the OS and/or do care about time - zones), and StopWatch (a platform-independent, high precision stop watch). - $(LREF Date) and $(LREF DateTime) are optimized for calendar-based operations, - while $(LREF SysTime) is designed for dealing with time from the OS. Check out - their specific documentation for more details. - - To get the current time, use $(LREF2 .Clock.currTime, Clock.currTime). - It will return the current - time as a $(LREF SysTime). To print it, $(D toString) is - sufficient, but if using $(D toISOString), $(D toISOExtString), or - $(D toSimpleString), use the corresponding $(D fromISOString), - $(D fromISOExtString), or $(D fromSimpleString) to create a - $(LREF SysTime) from the string. + $(REF std,datetime,date,Date) (if they want dates but don't care about + time), $(REF std,datetime,datetime,DateTime) (if they want dates and times + but don't care about time zones), $(REF std,datetime,systime,SysTime) (if + they want the date and time from the OS and/or do care about time zones), + and StopWatch (a platform-independent, high precision stop watch). + $(REF std,datetime,date,Date) and $(REF std,datetime,datetime,DateTime) are + optimized for calendar-based operations, while + $(REF std,datetime,systime,SysTime) is designed for dealing with time from + the OS. Check out their specific documentation for more details. + + To get the current time, use $(REF std,datetime,systime,Clock.currTime). + It will return the current time as a $(REF std,datetime,systime,SysTime). To + print it, $(D toString) is sufficient, but if using $(D toISOString), + $(D toISOExtString), or $(D toSimpleString), use the corresponding + $(D fromISOString), $(D fromISOExtString), or $(D fromSimpleString) to + create a $(REF std,datetime,systime,SysTime) from the string. -------------------- auto currentTime = Clock.currTime(); @@ -82,11 +87,12 @@ auto restoredTime = SysTime.fromISOExtString(timeString); weren't). Note: - $(LREF DateTimeException) is an alias for $(REF TimeException, core,time), - so you don't need to worry about core.time functions and std.datetime - functions throwing different exception types (except in the rare case - that they throw something other than $(REF TimeException, core,time) or - $(LREF DateTimeException)). + $(REF std,datetime,common,DateTimeException) is an alias for + $(REF TimeException, core,time), so you don't need to worry about + core.time functions and std.datetime functions throwing different + exception types (except in the rare case that they throw something other + than $(REF TimeException, core,time) or + $(REF std,datetime,common,DateTimeException)). See_Also: $(DDLINK intro-to-_datetime, Introduction to std.datetime, @@ -99,9 +105,7 @@ auto restoredTime = SysTime.fromISOExtString(timeString); License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis and Kato Shoichi - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/_datetime/package.d) +/ module std.datetime; @@ -120,7 +124,7 @@ import std.functional : unaryFun; import std.traits; -//Verify module example. +// Verify module example. @safe unittest { auto currentTime = Clock.currTime(); @@ -128,7 +132,7 @@ import std.traits; auto restoredTime = SysTime.fromISOExtString(timeString); } -//Verify Examples for core.time.Duration which couldn't be in core.time. +// Verify Examples for core.time.Duration which couldn't be in core.time. @safe unittest { assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == diff --git a/std/datetime/systime.d b/std/datetime/systime.d index a2bc2971294..5ff56e023ac 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_systime.d) +/ module std.datetime.systime; @@ -63,7 +61,8 @@ public: tz = The time zone for the SysTime that's returned. Throws: - $(LREF DateTimeException) if it fails to get the time. + $(REF std,datetime,common,DateTimeException) if it fails to get the + time. +/ static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe { @@ -117,16 +116,16 @@ public: need to use anything other than the default. Throws: - $(LREF DateTimeException) if it fails to get the time. + $(REF std,datetime,common,DateTimeException) if it fails to get the + time. +/ static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted { static if (clockType != ClockType.coarse && - clockType != ClockType.normal && - clockType != ClockType.precise && - clockType != ClockType.second) + clockType != ClockType.normal && + clockType != ClockType.precise && + clockType != ClockType.second) { - import std.format : format; static assert(0, format("ClockType.%s is not supported by Clock.currTime or Clock.currStdTime", clockType)); } @@ -303,41 +302,46 @@ private: /++ $(D SysTime) is the type used to get the current time from the system or doing anything that involves time zones. Unlike - $(LREF DateTime), the time zone is an integral part of $(D SysTime) (though for - local time applications, time zones can be ignored and - it will work, since it defaults to using the local time zone). It holds its - internal time in std time (hnsecs since midnight, January 1st, 1 A.D. UTC), - so it interfaces well with the system time. However, that means that, unlike - $(LREF DateTime), it is not optimized for calendar-based operations, and - getting individual units from it such as years or days is going to involve - conversions and be less efficient. + $(REF std,datetime,datetime,DateTime), the time zone is an integral part of + $(D SysTime) (though for local time applications, time zones can be ignored + and it will work, since it defaults to using the local time zone). It holds + its internal time in std time (hnsecs since midnight, January 1st, 1 A.D. + UTC), so it interfaces well with the system time. However, that means that, + unlike $(REF std,datetime,datetime,DateTime), it is not optimized for + calendar-based operations, and getting individual units from it such as + years or days is going to involve conversions and be less efficient. For calendar-based operations that don't - care about time zones, then $(LREF DateTime) would be the type to - use. For system time, use $(D SysTime). - - $(LREF2 .Clock.currTime, Clock.currTime) will return the current time as a $(D SysTime). - To convert a $(D SysTime) to a $(LREF Date) or $(LREF DateTime), simply cast - it. To convert a $(LREF Date) or $(LREF DateTime) to a - $(D SysTime), use $(D SysTime)'s constructor, and pass in the - intended time zone with it (or don't pass in a $(LREF2 .TimeZone, TimeZone), and the local - time zone will be used). Be aware, however, that converting from a - $(LREF DateTime) to a $(D SysTime) will not necessarily be 100% accurate due to - DST (one hour of the year doesn't exist and another occurs twice). - To not risk any conversion errors, keep times as + care about time zones, then $(REF std,datetime,datetime,DateTime) would be + the type to use. For system time, use $(D SysTime). + + $(LREF Clock.currTime) will return the current time as a $(D SysTime). + To convert a $(D SysTime) to a $(REF std,datetime,date,Date) or + $(REF std,datetime,datetime,DateTime), simply cast it. To convert a + $(REF std,datetime,date,Date) or $(REF std,datetime,datetime,DateTime) to a + $(D SysTime), use $(D SysTime)'s constructor, and pass in the ntended time + zone with it (or don't pass in a $(REF std,datetime,timezone,TimeZone), and + the local time zone will be used). Be aware, however, that converting from a + $(REF std,datetime,datetime,DateTime) to a $(D SysTime) will not necessarily + be 100% accurate due to DST (one hour of the year doesn't exist and another + occurs twice). To not risk any conversion errors, keep times as $(D SysTime)s. Aside from DST though, there shouldn't be any conversion problems. For using time zones other than local time or UTC, use - $(LREF PosixTimeZone) on Posix systems (or on Windows, if providing the TZ - Database files), and use $(LREF WindowsTimeZone) on Windows systems. - The time in $(D SysTime) is kept internally in hnsecs from midnight, - January 1st, 1 A.D. UTC. Conversion error cannot happen when changing - the time zone of a $(D SysTime). $(LREF LocalTime) is the $(LREF2 .TimeZone, TimeZone) class - which represents the local time, and $(D UTC) is the $(LREF2 .TimeZone, TimeZone) class - which represents UTC. $(D SysTime) uses $(LREF LocalTime) if no $(LREF2 .TimeZone, TimeZone) - is provided. For more details on time zones, see the documentation for - $(LREF2 .TimeZone, TimeZone), $(LREF PosixTimeZone), and $(LREF WindowsTimeZone). + $(REF std,datetime,timezone,PosixTimeZone) on Posix systems (or on Windows, + if providing the TZ Database files), and use + $(REF std,datetime,timezone,WindowsTimeZone) on Windows systems. The time in + $(D SysTime) is kept internally in hnsecs from midnight, January 1st, 1 A.D. + UTC. Conversion error cannot happen when changing the time zone of a + $(D SysTime). $(REF std,datetime,timezone,LocalTime) is the + $(REF std,datetime,timezone,TimeZone) class which represents the local time, + and $(D UTC) is the $(REF std,datetime,timezone,TimeZone) class which + represents UTC. $(D SysTime) uses $(REF std,datetime,timezone,LocalTime) if + no $(REF std,datetime,timezone,TimeZone) is provided. For more details on + time zones, see the documentation for $(REF std,datetime,timezone,TimeZone), + $(REF std,datetime,timezone,PosixTimeZone), and + $(REF std,datetime,timezone,WindowsTimeZone). $(D SysTime)'s range is from approximately 29,000 B.C. to approximately 29,000 A.D. @@ -352,12 +356,15 @@ public: /++ Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of + dateTime = The $(REF std,datetime,datetime,DateTime) to use to set + this $(LREF SysTime)'s internal std time. As + $(REF std,datetime,datetime,DateTime) has no concept of time zone, tz is used as its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. + tz = The $(REF std,datetime,timezone,TimeZone) to use for this + $(LREF SysTime). If null, + $(REF std,datetime,timezone,LocalTime) will be used. The + given $(REF std,datetime,datetime,DateTime) is assumed to + be in the given time zone. +/ this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow { @@ -390,16 +397,19 @@ public: /++ Params: - dateTime = The $(LREF DateTime) to use to set this $(LREF SysTime)'s - internal std time. As $(LREF DateTime) has no concept of + dateTime = The $(REF std,datetime,datetime,DateTime) to use to set + this $(LREF SysTime)'s internal std time. As + $(REF std,datetime,datetime,DateTime) has no concept of time zone, tz is used as its time zone. fracSecs = The fractional seconds portion of the time. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF DateTime) is - assumed to be in the given time zone. + tz = The $(REF std,datetime,timezone,TimeZone) to use for this + $(LREF SysTime). If null, + $(REF std,datetime,timezone,LocalTime) will be used. The + given $(REF std,datetime,datetime,DateTime) is assumed to + be in the given time zone. Throws: - $(LREF DateTimeException) if $(D fracSecs) is negative or if it's + $(REF std,datetime,common,DateTimeException) if $(D fracSecs) is negative or if it's greater than or equal to one second. +/ this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe @@ -488,12 +498,15 @@ public: /++ Params: - date = The $(LREF Date) to use to set this $(LREF SysTime)'s internal std - time. As $(LREF Date) has no concept of time zone, tz is used as - its time zone. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. The given $(LREF Date) is assumed - to be in the given time zone. + date = The $(REF std,datetime,date,Date) to use to set this + $(LREF SysTime)'s internal std time. As + $(REF std,datetime,date,Date) has no concept of time zone, tz + is used as its time zone. + tz = The $(REF std,datetime,timezone,TimeZone) to use for this + $(LREF SysTime). If null, + $(REF std,datetime,timezone,LocalTime) will be used. The + given $(REF std,datetime,date,Date) is assumed to be in the + given time zone. +/ this(in Date date, immutable TimeZone tz = null) @safe nothrow { @@ -537,9 +550,11 @@ public: of the arguments to this constructor takes place. Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, - $(LREF LocalTime) will be used. + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. + UTC. + tz = The $(REF std,datetime,timezone,TimeZone) to use for this + $(LREF SysTime). If null, + $(REF std,datetime,timezone,LocalTime) will be used. +/ this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow { @@ -827,8 +842,8 @@ public: year = The year to set this $(LREF SysTime)'s year to. Throws: - $(LREF DateTimeException) if the new year is not a leap year and the - resulting date would be on February 29th. + $(REF std,datetime,common,DateTimeException) if the new year is not + a leap year and the resulting date would be on February 29th. +/ @property void year(int year) @safe { @@ -909,7 +924,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(LREF DateTimeException) if $(D isAD) is true. + $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. +/ @property ushort yearBC() @safe const { @@ -954,7 +969,8 @@ public: year = The year B.C. to set this $(LREF SysTime)'s year to. Throws: - $(LREF DateTimeException) if a non-positive value is given. + $(REF std,datetime,common,DateTimeException) if a non-positive value + is given. +/ @property void yearBC(int year) @safe { @@ -1100,7 +1116,8 @@ public: month = The month to set this $(LREF SysTime)'s month to. Throws: - $(LREF DateTimeException) if the given month is not a valid month. + $(REF std,datetime,common,DateTimeException) if the given month is + not a valid month. +/ @property void month(Month month) @safe { @@ -1259,8 +1276,8 @@ public: day = The day of the month to set this $(LREF SysTime)'s day to. Throws: - $(LREF DateTimeException) if the given day is not a valid day of the - current month. + $(REF std,datetime,common,DateTimeException) if the given day is not + a valid day of the current month. +/ @property void day(int day) @safe { @@ -1418,8 +1435,8 @@ public: hour = The hours to set this $(LREF SysTime)'s hour to. Throws: - $(LREF DateTimeException) if the given hour are not a valid hour of - the day. + $(REF std,datetime,common,DateTimeException) if the given hour are + not a valid hour of the day. +/ @property void hour(int hour) @safe { @@ -1538,8 +1555,8 @@ public: minute = The minute to set this $(LREF SysTime)'s minute to. Throws: - $(LREF DateTimeException) if the given minute are not a valid minute - of an hour. + $(REF std,datetime,common,DateTimeException) if the given minute are + not a valid minute of an hour. +/ @property void minute(int minute) @safe { @@ -1662,8 +1679,8 @@ public: second = The second to set this $(LREF SysTime)'s second to. Throws: - $(LREF DateTimeException) if the given second are not a valid second - of a minute. + $(REF std,datetime,common,DateTimeException) if the given second are + not a valid second of a minute. +/ @property void second(int second) @safe { @@ -1792,8 +1809,8 @@ public: seconds to. Throws: - $(LREF DateTimeException) if the given duration is negative or if - it's greater than or equal to one second. + $(REF std,datetime,common,DateTimeException) if the given duration + is negative or if it's greater than or equal to one second. +/ @property void fracSecs(Duration fracSecs) @safe { @@ -2040,10 +2057,11 @@ public: /++ - The current time zone of this $(LREF SysTime). Its internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. + The current time zone of this $(LREF SysTime). Its internal time is + always kept in UTC, so there are no conversion issues between time zones + due to DST. Functions which return all or part of the time - such as + hours - adjust the time to this $(LREF SysTime)'s time zone before + returning. +/ @property immutable(TimeZone) timezone() @safe const pure nothrow { @@ -2052,13 +2070,15 @@ public: /++ - The current time zone of this $(LREF SysTime). It's internal time is always - kept in UTC, so there are no conversion issues between time zones due to - DST. Functions which return all or part of the time - such as hours - - adjust the time to this $(LREF SysTime)'s time zone before returning. + The current time zone of this $(LREF SysTime). It's internal time is + always kept in UTC, so there are no conversion issues between time zones + due to DST. Functions which return all or part of the time - such as + hours - adjust the time to this $(LREF SysTime)'s time zone before + returning. Params: - timezone = The $(LREF2 .TimeZone, TimeZone) to set this $(LREF SysTime)'s time zone to. + timezone = The $(REF std,datetime,timezone,TimeZone) to set this + $(LREF SysTime)'s time zone to. +/ @property void timezone(immutable TimeZone timezone) @safe pure nothrow { @@ -2091,7 +2111,7 @@ public: /++ Returns a $(LREF SysTime) with the same std time as this one, but with - $(LREF LocalTime) as its time zone. + $(REF std,datetime,timezone,LocalTime) as its time zone. +/ SysTime toLocalTime() @safe const pure nothrow { @@ -3634,8 +3654,8 @@ public: The difference between rolling and adding is that rolling does not affect larger units. Rolling a $(LREF SysTime) 12 months - gets the exact same $(LREF SysTime). However, the days can still be affected - due to the differing number of days in each month. + gets the exact same $(LREF SysTime). However, the days can still be + affected due to the differing number of days in each month. Because there are no units larger than years, there is no difference between adding and rolling years. @@ -4490,7 +4510,8 @@ public: Params: units = The units to add. - value = The number of $(D_PARAM units) to add to this $(LREF SysTime). + value = The number of $(D_PARAM units) to add to this + $(LREF SysTime). +/ ref SysTime roll(string units)(long value) @safe nothrow if (units == "days") @@ -5910,8 +5931,8 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF SysTime). + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF SysTime). The legal types of arithmetic for $(LREF SysTime) using this operator are @@ -6393,7 +6414,8 @@ public: /++ Gives the difference between two $(LREF SysTime)s. - The legal types of arithmetic for $(LREF SysTime) using this operator are + The legal types of arithmetic for $(LREF SysTime) using this operator + are $(BOOKTABLE, $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) @@ -6494,12 +6516,12 @@ public: To get the difference in years, subtract the year property of two $(LREF SysTime)s. To get the difference in days or weeks, - subtract the $(LREF SysTime)s themselves and use the $(REF Duration, core,time) - that results. Because converting between months and smaller - units requires a specific date (which $(REF Duration, core,time)s don't have), - getting the difference in months requires some math using both - the year and month properties, so this is a convenience function for - getting the difference in months. + subtract the $(LREF SysTime)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. Note that the number of days in the months or how far into the month either date is is irrelevant. It is the difference in the month property @@ -7513,8 +7535,9 @@ public: /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for + any time on this date (since, the modified Julian day changes at + midnight). +/ @property long modJulianDay() @safe const nothrow { @@ -7537,7 +7560,7 @@ public: /++ - Returns a $(LREF Date) equivalent to this $(LREF SysTime). + Returns a $(REF std,datetime,date,Date) equivalent to this $(LREF SysTime). +/ Date opCast(T)() @safe const nothrow if (is(Unqual!T == Date)) @@ -7571,7 +7594,8 @@ public: /++ - Returns a $(LREF DateTime) equivalent to this $(LREF SysTime). + Returns a $(REF std,datetime,datetime,DateTime) equivalent to this + $(LREF SysTime). +/ DateTime opCast(T)() @safe const nothrow if (is(Unqual!T == DateTime)) @@ -7630,7 +7654,8 @@ public: /++ - Returns a $(LREF TimeOfDay) equivalent to this $(LREF SysTime). + Returns a $(REF std,datetime,timeofday,TimeOfDay) equivalent to this + $(LREF SysTime). +/ TimeOfDay opCast(T)() @safe const nothrow if (is(Unqual!T == TimeOfDay)) @@ -7701,17 +7726,19 @@ public: (so no trailing zeroes), and if there are no fractional seconds, then there is no decimal point. - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. - If its time zone is $(D UTC), then it is "Z". Otherwise, it is the - offset from UTC (e.g. +0100 or -0700). Note that the offset from UTC - is $(I not) enough to uniquely identify the time zone. + If this $(LREF SysTime)'s time zone is + $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +0100 or -0700). Note that the offset from UTC is $(I not) enough + to uniquely identify the time zone. Time zone offsets will be in the form +HHMM or -HHMM. $(RED Warning: Previously, toISOString did the same as $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time zone when it was not - $(LREF LocalTime) or $(LREF UTC), which is not in conformance with + $(REF std,datetime,timezone,LocalTime) or + $(REF std,datetime,timezone,UTC), which is not in conformance with ISO 9601 for the non-extended string format. This has now been fixed. However, for now, fromISOString will continue to accept the extended format for the time zone so that any code which has been @@ -7839,10 +7866,11 @@ public: (so no trailing zeroes), and if there are no fractional seconds, then there is no decimal point. - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. + If this $(LREF SysTime)'s time zone is + $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) + enough to uniquely identify the time zone. Time zone offsets will be in the form +HH:MM or -HH:MM. +/ @@ -7971,10 +7999,11 @@ public: (so no trailing zeroes), and if there are no fractional seconds, then there is no decimal point. - If this $(LREF SysTime)'s time zone is $(LREF LocalTime), then TZ is empty. If - its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset - from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is - $(I not) enough to uniquely identify the time zone. + If this $(LREF SysTime)'s time zone is + $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC + (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) + enough to uniquely identify the time zone. Time zone offsets will be in the form +HH:MM or -HH:MM. +/ @@ -8123,13 +8152,14 @@ public: all zeroes. However, a decimal point with nothing following it is invalid. - If there is no time zone in the string, then $(LREF LocalTime) is used. - If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). + If there is no time zone in the string, then + $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). The accepted formats for time zone offsets are +HH, -HH, +HHMM, and -HHMM. @@ -8137,12 +8167,13 @@ public: $(RED Warning: Previously, $(LREF toISOString) did the same as $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time - zone when it was not $(LREF LocalTime) or $(LREF UTC), which is not - in conformance with ISO 9601 for the non-extended string format. - This has now been fixed. However, for now, fromISOString will - continue to accept the extended format for the time zone so that any - code which has been writing out the result of toISOString to read in - later will continue to work.) + zone when it was not $(REF std,datetime,timezone,LocalTime) or + $(REF std,datetime,timezone,UTC), which is not in conformance with + ISO 9601 for the non-extended string format. This has now been + fixed. However, for now, fromISOString will continue to accept the + extended format for the time zone so that any code which has been + writing out the result of toISOString to read in later will continue + to work.) Params: isoString = A string formatted in the ISO format for dates and times. @@ -8150,8 +8181,9 @@ public: conversion occurs if null). Throws: - $(LREF DateTimeException) if the given string is not in the ISO - format or if the resulting $(LREF SysTime) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. +/ static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe if (isSomeString!S) @@ -8368,13 +8400,14 @@ public: seconds with all zeroes. However, a decimal point with nothing following it is invalid. - If there is no time zone in the string, then $(LREF LocalTime) is used. - If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). + If there is no time zone in the string, then + $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and -HH:MM. @@ -8386,8 +8419,9 @@ public: conversion occurs if null). Throws: - $(LREF DateTimeException) if the given string is not in the ISO - format or if the resulting $(LREF SysTime) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. +/ static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe if (isSomeString!(S)) @@ -8578,13 +8612,14 @@ public: with all zeroes. However, a decimal point with nothing following it is invalid. - If there is no time zone in the string, then $(LREF LocalTime) is used. If - the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(LREF SimpleTimeZone) which corresponds to the given offset from UTC is - used. To get the returned $(LREF SysTime) to be a particular time - zone, pass in that time zone and the $(LREF SysTime) to be returned - will be converted to that time zone (though it will still be read in as - whatever time zone is in its string). + If there is no time zone in the string, then + $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + then $(D UTC) is used. Otherwise, a + $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + given offset from UTC is used. To get the returned $(LREF SysTime) to be + a particular time zone, pass in that time zone and the $(LREF SysTime) + to be returned will be converted to that time zone (though it will still + be read in as whatever time zone is in its string). The accepted formats for time zone offsets are +HH, -HH, +HH:MM, and -HH:MM. @@ -8596,8 +8631,9 @@ public: conversion occurs if null). Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF SysTime) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF SysTime) would not + be valid. +/ static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe if (isSomeString!(S)) @@ -8869,8 +8905,8 @@ private: the standard, much as it's based on it, so the name "std time" isn't particularly good, but there isn't an official name for it. C# uses "ticks" for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so - it didn't make sense to use the term ticks here. So, for better or worse, + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), + so it didn't make sense to use the term ticks here. So, for better or worse, std.datetime uses the term "std time" for this. Params: @@ -8933,8 +8969,8 @@ long unixTimeToStdTime(long unixTime) @safe pure nothrow the standard, much as it's based on it, so the name "std time" isn't particularly good, but there isn't an official name for it. C# uses "ticks" for the same thing, but they aren't actually clock ticks, and the term - "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), so - it didn't make sense to use the term ticks here. So, for better or worse, + "ticks" $(I is) used for actual clock ticks for $(REF MonoTime, core,time), + so it didn't make sense to use the term ticks here. So, for better or worse, std.datetime uses the term "std time" for this. By default, the return type is time_t (which is normally an alias for @@ -9049,10 +9085,10 @@ version(StdDdoc) or UTC, depending on the call). Throws: - $(LREF DateTimeException) if the given $(D SYSTEMTIME) will not fit in - a $(LREF SysTime), which is highly unlikely to happen given that - $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) - is in 30,827 A.D. + $(REF std,datetime,common,DateTimeException) if the given + $(D SYSTEMTIME) will not fit in a $(LREF SysTime), which is highly + unlikely to happen given that $(D SysTime.max) is in 29,228 A.D. and + the maximum $(D SYSTEMTIME) is in 30,827 A.D. +/ SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; @@ -9070,9 +9106,9 @@ version(StdDdoc) sysTime = The $(LREF SysTime) to convert. Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is - prior to 1601 A.D. + $(REF std,datetime,common,DateTimeException) if the given + $(LREF SysTime) will not fit in a $(D SYSTEMTIME). This will only + happen if the $(LREF SysTime)'s date is prior to 1601 A.D. +/ SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; @@ -9087,8 +9123,8 @@ version(StdDdoc) ft = The $(D FILETIME) struct to convert. Throws: - $(LREF DateTimeException) if the given $(D FILETIME) cannot be - represented as the return value. + $(REF std,datetime,common,DateTimeException) if the given + $(D FILETIME) cannot be represented as the return value. +/ long FILETIMEToStdTime(scope const FILETIME* ft) @safe; @@ -9100,12 +9136,12 @@ version(StdDdoc) Params: ft = The $(D FILETIME) struct to convert. - tz = The time zone that the $(LREF SysTime) will be in ($(D FILETIME)s - are in UTC). + tz = The time zone that the $(LREF SysTime) will be in + ($(D FILETIME)s are in UTC). Throws: - $(LREF DateTimeException) if the given $(D FILETIME) will not fit in a - $(LREF SysTime). + $(REF std,datetime,common,DateTimeException) if the given + $(D FILETIME) will not fit in a $(LREF SysTime). +/ SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; @@ -9117,11 +9153,12 @@ version(StdDdoc) $(D FILETIME) struct. Params: - stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. + stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. + UTC. Throws: - $(LREF DateTimeException) if the given value will not fit in a - $(D FILETIME). + $(REF std,datetime,common,DateTimeException) if the given value will + not fit in a $(D FILETIME). +/ FILETIME stdTimeToFILETIME(long stdTime) @safe; @@ -9137,8 +9174,8 @@ version(StdDdoc) sysTime = The $(LREF SysTime) to convert. Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a - $(D FILETIME). + $(REF std,datetime,common,DateTimeException) if the given + $(LREF SysTime) will not fit in a $(D FILETIME). +/ FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; } @@ -9328,7 +9365,8 @@ alias DosFileTime = uint; tz = The time zone which the DOS file time is assumed to be in. Throws: - $(LREF DateTimeException) if the $(D DosFileTime) is invalid. + $(REF std,datetime,common,DateTimeException) if the $(D DosFileTime) is + invalid. +/ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe { @@ -9365,8 +9403,8 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime( sysTime = The $(LREF SysTime) to convert. Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to - a $(D DosFileTime). + $(REF std,datetime,common,DateTimeException) if the given + $(LREF SysTime) cannot be converted to a $(D DosFileTime). +/ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe { @@ -9415,9 +9453,10 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe day of the week doesn't match the actual day of the week of the given date). If the time zone is $(D "-0000") (or considered to be equivalent to - $(D "-0000") by section 4.3 of the spec), a $(LREF SimpleTimeZone) with a - utc offset of $(D 0) is used rather than $(LREF UTC), whereas $(D "+0000") - uses $(LREF UTC). + $(D "-0000") by section 4.3 of the spec), a + $(REF std,datetime,timezone,SimpleTimeZone) with a utc offset of $(D 0) is + used rather than $(REF std,datetime,timezone,UTC), whereas $(D "+0000") uses + $(REF std,datetime,timezone,UTC). Note that because $(LREF SysTime) does not currently support having a second value of 60 (as is sometimes done for leap seconds), if the date-time value @@ -9428,8 +9467,9 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe HTTP spec requires it. Throws: - $(LREF DateTimeException) if the given string doesn't follow the grammar - for a date-time field or if the resulting $(LREF SysTime) is invalid. + $(REF std,datetime,common,DateTimeException) if the given string doesn't + follow the grammar for a date-time field or if the resulting + $(LREF SysTime) is invalid. +/ SysTime parseRFC822DateTime()(in char[] value) @safe { @@ -10310,9 +10350,6 @@ if (isSomeString!S) This function is used to split out the units without getting the remaining hnsecs. - See_Also: - $(LREF splitUnitsFromHNSecs) - Params: units = The units to split out. hnsecs = The current total hnsecs. @@ -10340,9 +10377,6 @@ if (validTimeUnits(units) && This function is used to split out the units without getting the units but just the remaining hnsecs. - See_Also: - $(LREF splitUnitsFromHNSecs) - Params: units = The units to split out. hnsecs = The current total hnsecs. diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d index 683728bdb04..fccbd8cd129 100644 --- a/std/datetime/timeofday.d +++ b/std/datetime/timeofday.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_timeofday.d) +/ module std.datetime.timeofday; @@ -31,8 +29,8 @@ public: second = Second of the minute [0 - 60$(RPAREN). Throws: - $(LREF DateTimeException) if the resulting $(LREF TimeOfDay) would be not - be valid. + $(REF std,datetime,common,DateTimeException) if the resulting + $(LREF TimeOfDay) would be not be valid. +/ this(int hour, int minute, int second = 0) @safe pure { @@ -170,8 +168,8 @@ public: hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. Throws: - $(LREF DateTimeException) if the given hour would result in an invalid - $(LREF TimeOfDay). + $(REF std,datetime,common,DateTimeException) if the given hour would + result in an invalid $(LREF TimeOfDay). +/ @property void hour(int hour) @safe pure { @@ -221,8 +219,8 @@ public: minute = The minute to set this $(LREF TimeOfDay)'s minute to. Throws: - $(LREF DateTimeException) if the given minute would result in an - invalid $(LREF TimeOfDay). + $(REF std,datetime,common,DateTimeException) if the given minute + would result in an invalid $(LREF TimeOfDay). +/ @property void minute(int minute) @safe pure { @@ -272,8 +270,8 @@ public: second = The second to set this $(LREF TimeOfDay)'s second to. Throws: - $(LREF DateTimeException) if the given second would result in an - invalid $(LREF TimeOfDay). + $(REF std,datetime,common,DateTimeException) if the given second + would result in an invalid $(LREF TimeOfDay). +/ @property void second(int second) @safe pure { @@ -297,8 +295,8 @@ public: /++ - Adds the given number of units to this $(LREF TimeOfDay). A negative number - will subtract. + Adds the given number of units to this $(LREF TimeOfDay). A negative + number will subtract. The difference between rolling and adding is that rolling does not affect larger units. For instance, rolling a $(LREF TimeOfDay) @@ -540,8 +538,8 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF TimeOfDay). + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay). The legal types of arithmetic for $(LREF TimeOfDay) using this operator are @@ -649,8 +647,8 @@ public: /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) from - this $(LREF TimeOfDay), as well as assigning the result to this + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay), as well as assigning the result to this $(LREF TimeOfDay). The legal types of arithmetic for $(LREF TimeOfDay) using this operator @@ -761,7 +759,8 @@ public: /++ Gives the difference between two $(LREF TimeOfDay)s. - The legal types of arithmetic for $(LREF TimeOfDay) using this operator are + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are $(BOOKTABLE, $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) @@ -895,8 +894,9 @@ public: isoString = A string formatted in the ISO format for times. Throws: - $(LREF DateTimeException) if the given string is not in the ISO format - or if the resulting $(LREF TimeOfDay) would not be valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF TimeOfDay) would + not be valid. +/ static TimeOfDay fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) @@ -1000,12 +1000,13 @@ public: Whitespace is stripped from the given string. Params: - isoExtString = A string formatted in the ISO Extended format for times. + isoExtString = A string formatted in the ISO Extended format for + times. Throws: - $(LREF DateTimeException) if the given string is not in the ISO - Extended format or if the resulting $(LREF TimeOfDay) would not be - valid. + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO Extended format or if the resulting $(LREF TimeOfDay) + would not be valid. +/ static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure if (isSomeString!S) diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 65c8ddeb392..a6fece6c854 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -3,9 +3,7 @@ /++ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/_datetime.d) - Macros: - LREF2=$(D $2) + Source: $(PHOBOSSRC std/datetime/_timezone.d) +/ module std.datetime.timezone; @@ -41,8 +39,8 @@ version(unittest) import std.exception : assertThrown; /++ - Represents a time zone. It is used with $(LREF SysTime) to indicate the time - zone of a $(LREF SysTime). + Represents a time zone. It is used with $(REF std,datetime,systime,SysTime) + to indicate the time zone of a $(REF std,datetime,systime,SysTime). +/ abstract class TimeZone { @@ -50,7 +48,7 @@ public: /++ The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). + get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone). See_Also: $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ @@ -155,7 +153,7 @@ public: Phobos and have them be properly up-to-date. TimeZone.getTimeZone will be removed in July 2017.) - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. + Returns a $(LREF TimeZone) with the give name per the TZ Database. This returns a $(LREF PosixTimeZone) on Posix systems and a $(LREF WindowsTimeZone) on Windows systems. For @@ -180,7 +178,8 @@ public: name = The TZ Database name of the desired time zone Throws: - $(LREF DateTimeException) if the given time zone could not be found. + $(REF std,datetime,common,DateTimeException) if the given time zone + could not be found. +/ deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") static immutable(TimeZone) getTimeZone(string name) @safe @@ -553,8 +552,8 @@ public: Throws: $(D FileException) on Posix systems if it fails to read from disk. - $(LREF DateTimeException) on Windows systems if it fails to read the - registry. + $(REF std,datetime,common,DateTimeException) on Windows systems if + it fails to read the registry. +/ deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") static string[] getInstalledTZNames(string subName = "") @safe @@ -632,17 +631,17 @@ private: This uses the underlying C calls to adjust the time rather than using specific D code based off of system settings to calculate the time such as - $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that it will - use whatever the current time zone is on the system, even if the system's - time zone changes while the program is running. + $(LREF PosixTimeZone) and $(LREF WindowsTimeZone) do. That also means that + it will use whatever the current time zone is on the system, even if the + system's time zone changes while the program is running. +/ final class LocalTime : TimeZone { public: /++ - $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its only - instance. + $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its + only instance. +/ static immutable(LocalTime) opCall() @trusted pure nothrow { @@ -654,8 +653,8 @@ public: version(StdDdoc) { /++ - The name of the time zone per the TZ Database. This is the name used to - get a $(LREF2 .TimeZone, TimeZone) by name with $(D TimeZone.getTimeZone). + The name of the time zone per the TZ Database. This is the name used + to get a $(LREF TimeZone) by name with $(D TimeZone.getTimeZone). Note that this always returns the empty string. This is because time zones cannot be uniquely identified by the attributes given by the @@ -1207,7 +1206,7 @@ private: /++ - A $(LREF2 .TimeZone, TimeZone) which represents UTC. + A $(LREF TimeZone) which represents UTC. +/ final class UTC : TimeZone { @@ -1333,8 +1332,9 @@ private: Represents a time zone with an offset (in minutes, west is negative) from UTC but no DST. - It's primarily used as the time zone in the result of $(LREF SysTime)'s - $(D fromISOString), $(D fromISOExtString), and $(D fromSimpleString). + It's primarily used as the time zone in the result of + $(REF std,datetime,systime,SysTime)'s $(D fromISOString), + $(D fromISOExtString), and $(D fromSimpleString). $(D name) and $(D dstName) are always the empty string since this time zone has no DST, and while it may be meant to represent a time zone which is in @@ -2043,9 +2043,9 @@ public: /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the give name per the TZ Database. The time - zone information is fetched from the TZ Database time zone files in the - given directory. + Returns a $(LREF TimeZone) with the give name per the TZ Database. The + time zone information is fetched from the TZ Database time zone files in + the given directory. See_Also: $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ @@ -2062,8 +2062,9 @@ public: use $(LREF PosixTimeZone)s. Throws: - $(LREF DateTimeException) if the given time zone could not be found or - $(D FileException) if the TZ Database file could not be opened. + $(REF std,datetime,common,DateTimeException) if the given time zone + could not be found or $(D FileException) if the TZ Database file + could not be opened. +/ // TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed // directory. @@ -2667,7 +2668,7 @@ private: /+ Throws: - $(LREF DateTimeException) if $(D result) is false. + $(REF std,datetime,common,DateTimeException) if $(D result) is false. +/ static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure { @@ -2891,7 +2892,7 @@ version(StdDdoc) /++ - Returns a $(LREF2 .TimeZone, TimeZone) with the given name per the Windows time + Returns a $(LREF TimeZone) with the given name per the Windows time zone names. The time zone information is fetched from the Windows registry. @@ -2905,8 +2906,8 @@ version(StdDdoc) name = The TZ Database name of the desired time zone. Throws: - $(LREF DateTimeException) if the given time zone could not be - found. + $(REF std,datetime,common,DateTimeException) if the given time + zone could not be found. Example: -------------------- From 491d76d8a6ac9e93209e6c147e331acfa7aa0b85 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 5 May 2017 10:49:34 +0200 Subject: [PATCH 112/262] Fixes to make circleci happy. --- std/datetime/common.d | 8 ++--- std/datetime/date.d | 8 ++++- std/datetime/datetime.d | 57 +++++++++++++++++++++++++++++++ std/datetime/interval.d | 13 ++++--- std/datetime/systime.d | 74 ++++++++++++++++++++++++++++++++++++++++ std/datetime/timeofday.d | 2 ++ 6 files changed, 153 insertions(+), 9 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index 750de5edd95..b48399bcff1 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -453,11 +453,11 @@ private: @safe unittest { import core.time : Duration; - import std.datetime.date; - import std.datetime.datetime; + import std.datetime.date : Date; + import std.datetime.datetime : DateTime; import std.datetime.interval : Interval; - import std.datetime.systime; - import std.datetime.timeofday; + import std.datetime.systime : SysTime; + import std.datetime.timeofday : TimeOfDay; static assert(isTimePoint!Date); static assert(isTimePoint!DateTime); diff --git a/std/datetime/date.d b/std/datetime/date.d index a48585b7ae0..67690a63a90 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -787,6 +787,8 @@ public: /// @safe unittest { + import std.datetime.common : AllowDayOverflow; + auto d1 = Date(2010, 1, 1); d1.add!"months"(11); assert(d1 == Date(2010, 12, 1)); @@ -1554,6 +1556,8 @@ public: /// @safe unittest { + import std.datetime.common : AllowDayOverflow; + auto d1 = Date(2010, 1, 1); d1.roll!"months"(1); assert(d1 == Date(2010, 2, 1)); @@ -2432,6 +2436,8 @@ public: /// @safe unittest { + import core.time : days; + assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1)); assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1)); @@ -4410,7 +4416,7 @@ private: enum daysInYear = 365; // The number of days in a non-leap year. enum daysInLeapYear = 366; // The numbef or days in a leap year. -enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. +enum daysIn4Years = daysInYear * 3 + daysInLeapYear; // Number of days in 4 years. enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d index 20e25f6e69a..5499c62ecd9 100644 --- a/std/datetime/datetime.d +++ b/std/datetime/datetime.d @@ -465,6 +465,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); @@ -509,6 +512,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); @@ -546,6 +552,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); dt.yearBC = 1; assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); @@ -579,6 +588,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); @@ -649,6 +661,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); @@ -961,6 +976,8 @@ public: /// @safe unittest { + import std.datetime.common : AllowDayOverflow; + auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); dt1.add!"months"(11); assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); @@ -1023,6 +1040,8 @@ public: /// @safe unittest { + import std.datetime.common : AllowDayOverflow; + auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); dt1.roll!"months"(1); assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); @@ -2018,6 +2037,8 @@ public: /// @safe unittest { + import core.time : hours, seconds; + assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == DateTime(2016, 1, 1, 0, 0, 0)); @@ -2450,6 +2471,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); @@ -2501,6 +2525,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); @@ -2539,6 +2566,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); dt.dayOfGregorianCal = 1; assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); @@ -2612,6 +2642,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); @@ -2675,6 +2708,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); @@ -2701,6 +2737,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); @@ -2803,6 +2842,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == "20100704T070612"); @@ -2856,6 +2898,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == "2010-07-04T07:06:12"); @@ -2908,6 +2953,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); @@ -3002,6 +3050,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime.fromISOString("20100704T070612") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); @@ -3091,6 +3142,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); @@ -3179,6 +3233,9 @@ public: /// @safe unittest { + import std.datetime.date : Date; + import std.datetime.timeofday : TimeOfDay; + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 7eda15fe8b6..ee9a0e10915 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -7622,7 +7622,8 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.date; + import std.datetime.common : DayOfWeek; + import std.datetime.date : Date; auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); auto func = everyDayOfWeek!Date(DayOfWeek.mon); @@ -7743,7 +7744,8 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.date; + import std.datetime.common : Month; + import std.datetime.date : Date; auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); auto func = everyMonth!Date(Month.feb); @@ -7855,7 +7857,8 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.date; + import core.time : dur; + import std.datetime.date : Date; auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); auto func = everyDuration!Date(dur!"days"(8)); @@ -7981,7 +7984,9 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.date; + import core.time : dur; + import std.datetime.common : AllowDayOverflow; + import std.datetime.date : Date; auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 5ff56e023ac..198d724282a 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -866,6 +866,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); @@ -934,6 +936,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); @@ -1068,6 +1072,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); @@ -1227,6 +1233,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); @@ -1753,6 +1761,9 @@ public: /// @safe unittest { + import core.time : msecs, usecs, hnsecs, nsecs; + import std.datetime.datetime : DateTime; + auto dt = DateTime(1982, 4, 1, 20, 59, 22); assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202)); @@ -1838,6 +1849,9 @@ public: /// @safe unittest { + import core.time : Duration, msecs, hnsecs, nsecs; + import std.datetime.datetime : DateTime; + auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); assert(st.fracSecs == Duration.zero); @@ -2223,6 +2237,10 @@ public: /// @safe unittest { + import core.time : hours; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); auto pst = new immutable SimpleTimeZone(hours(-8)); @@ -2271,6 +2289,10 @@ public: /// @safe unittest { + import core.time : hours; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + assert(SysTime.fromUnixTime(0) == SysTime(DateTime(1970, 1, 1), UTC())); @@ -3676,6 +3698,9 @@ public: /// @safe unittest { + import std.datetime.common : AllowDayOverflow; + import std.datetime.datetime : DateTime; + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); st1.roll!"months"(1); assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); @@ -4543,6 +4568,9 @@ public: /// @safe unittest { + import core.time : msecs, hnsecs; + import std.datetime.datetime : DateTime; + auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); st1.roll!"days"(1); assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); @@ -5958,6 +5986,9 @@ public: /// @safe unittest { + import core.time : hours, seconds; + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == SysTime(DateTime(2016, 1, 1, 0, 0, 0))); @@ -6540,6 +6571,8 @@ public: /// @safe unittest { + import std.datetime.date : Date; + assert(SysTime(Date(1999, 2, 1)).diffMonths( SysTime(Date(1999, 1, 31))) == 1); @@ -6621,6 +6654,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); @@ -6692,6 +6727,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); @@ -7064,6 +7101,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); st.dayOfGregorianCal = 1; assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); @@ -7344,6 +7383,9 @@ public: /// @safe unittest { + import core.time : msecs, usecs, hnsecs; + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); @@ -7410,6 +7452,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); @@ -7466,6 +7510,8 @@ public: /// @safe unittest { + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); @@ -7788,6 +7834,9 @@ public: /// @safe unittest { + import core.time : msecs, hnsecs; + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == "20100704T070612"); @@ -7917,6 +7966,9 @@ public: /// @safe unittest { + import core.time : msecs, hnsecs; + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == "2010-07-04T07:06:12"); @@ -8050,6 +8102,9 @@ public: /// @safe unittest { + import core.time : msecs, hnsecs; + import std.datetime.datetime : DateTime; + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); @@ -8251,6 +8306,10 @@ public: /// @safe unittest { + import core.time : hours, msecs, usecs; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + assert(SysTime.fromISOString("20100704T070612") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); @@ -8486,6 +8545,10 @@ public: /// @safe unittest { + import core.time : hours, msecs, usecs; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); @@ -8698,6 +8761,10 @@ public: /// @safe unittest { + import core.time : hours, msecs, usecs; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); @@ -8923,6 +8990,9 @@ long unixTimeToStdTime(long unixTime) @safe pure nothrow /// @safe unittest { + import std.datetime.datetime : DateTime; + import std.datetime.timezone : UTC; + // Midnight, January 1st, 1970 assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); assert(SysTime(unixTimeToStdTime(0)) == @@ -9684,6 +9754,10 @@ afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); /// @safe unittest { + import core.time : hours; + import std.datetime.common : DateTimeException; + import std.datetime.datetime : DateTime; + import std.datetime.timezone : SimpleTimeZone, UTC; import std.exception : assertThrown; auto tz = new immutable SimpleTimeZone(hours(-8)); diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d index fccbd8cd129..ae9a9acc532 100644 --- a/std/datetime/timeofday.d +++ b/std/datetime/timeofday.d @@ -564,6 +564,8 @@ public: /// @safe unittest { + import core.time : hours, minutes, seconds; + assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); From c16c7bcde4eee9d8b0409ddac8e6dc24b8e410cb Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 5 May 2017 22:56:16 +0200 Subject: [PATCH 113/262] Specify the exact folder location for the style Makefile target of the D source files --- posix.mak | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/posix.mak b/posix.mak index 33eae909424..87f72efe363 100644 --- a/posix.mak +++ b/posix.mak @@ -504,31 +504,31 @@ style: ../dscanner/dsc $(LIB) has_public_example publictests grep -nr '[[:blank:]]$$' etc std ; test $$? -eq 1 @echo "Enforce whitespace before opening parenthesis" - grep -nrE "(for|foreach|foreach_reverse|if|while|switch|catch)\(" $$(find . -name '*.d') ; test $$? -eq 1 + grep -nrE "(for|foreach|foreach_reverse|if|while|switch|catch)\(" $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce whitespace between colon(:) for import statements (doesn't catch everything)" - grep -nr 'import [^/,=]*:.*;' $$(find . -name '*.d') | grep -vE "import ([^ ]+) :\s"; test $$? -eq 1 + grep -nr 'import [^/,=]*:.*;' $$(find etc std -name '*.d') | grep -vE "import ([^ ]+) :\s"; test $$? -eq 1 @echo "Check for package wide std.algorithm imports" - grep -nr 'import std.algorithm : ' $$(find . -name '*.d') ; test $$? -eq 1 + grep -nr 'import std.algorithm : ' $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce Allman style" - grep -nrE '(if|for|foreach|foreach_reverse|while|unittest|switch|else|version) .*{$$' $$(find . -name '*.d'); test $$? -eq 1 + grep -nrE '(if|for|foreach|foreach_reverse|while|unittest|switch|else|version) .*{$$' $$(find etc std -name '*.d'); test $$? -eq 1 @echo "Enforce do { to be in Allman style" - grep -nr 'do *{$$' $$(find . -name '*.d') ; test $$? -eq 1 + grep -nr 'do *{$$' $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce no space between assert and the opening brace, i.e. assert(" - grep -nrE 'assert +\(' $$(find . -name '*.d') ; test $$? -eq 1 + grep -nrE 'assert +\(' $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce space after cast(...)" - grep -nrE '[^"]cast\([^)]*?\)[[:alnum:]]' $$(find . -name '*.d') ; test $$? -eq 1 + grep -nrE '[^"]cast\([^)]*?\)[[:alnum:]]' $$(find etc std -name '*.d') ; test $$? -eq 1 @echo "Enforce space between a .. b" - grep -nrE '[[:alnum:]][.][.][[:alnum:]]|[[:alnum:]] [.][.][[:alnum:]]|[[:alnum:]][.][.] [[:alnum:]]' $$(find . -name '*.d' | grep -vE 'std/string.d|std/uni.d') ; test $$? -eq 1 + grep -nrE '[[:alnum:]][.][.][[:alnum:]]|[[:alnum:]] [.][.][[:alnum:]]|[[:alnum:]][.][.] [[:alnum:]]' $$(find etc std -name '*.d' | grep -vE 'std/string.d|std/uni.d') ; test $$? -eq 1 @echo "Enforce space between binary operators" - grep -nrE "[[:alnum:]](==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]] (==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]](==|!=|<=|<<|>>|>>>|^^) [[:alnum:]]" $$(find . -name '*.d'); test $$? -eq 1 + grep -nrE "[[:alnum:]](==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]] (==|!=|<=|<<|>>|>>>|^^)[[:alnum:]]|[[:alnum:]](==|!=|<=|<<|>>|>>>|^^) [[:alnum:]]" $$(find etc std -name '*.d'); test $$? -eq 1 @echo "Validate changelog files (Do _not_ use REF in the title!)" @for file in $$(find changelog -name '*.dd') ; do \ From 4fd762d1e415c3bb5c054031af7e2774dfea9299 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Fri, 5 May 2017 21:57:54 +0200 Subject: [PATCH 114/262] Add changelog entry for splitting std.datetime. --- changelog/split-std-datetime.dd | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 changelog/split-std-datetime.dd diff --git a/changelog/split-std-datetime.dd b/changelog/split-std-datetime.dd new file mode 100644 index 00000000000..f4c09ba6fce --- /dev/null +++ b/changelog/split-std-datetime.dd @@ -0,0 +1,50 @@ +std.datetime has been split into a package. + +std.datetime is now a package containing the following modules: + +$(UL + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_common.html, std.datetime.common)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_datetime.html, std.datetime.datetime)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timeofday.html, std.datetime.timeofday)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone)) +) + +$(LINK2 $(PHOBOS_PATH)std_datetime.html, std.datetime.package) publicly imports +all of those modules. So, it should be the case that no existing code will +break, as everything in std.datetime will still be imported by importing +std.datetime. New code can choose to import the modules individually or to +import the entire package. + +$(LINK2 $(PHOBOS_PATH)std_datetime_common.html, std.datetime.common) contains a +few types and free functions that are used by the other modules (such as Month +and isTimePoint). + +$(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date) contains Date. + +$(LINK2 $(PHOBOS_PATH)std_datetime_datetime.html, std.datetime.datetime) +contains DateTime. + +$(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval) +contains the *Interval and *IntervalRange types as well as the related free +functions. + +$(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime) contains +SysTime and the related free functions. + +$(LINK2 $(PHOBOS_PATH)std_datetime_timeofday.html, std.datetime.timeofday) +contains TimeOfDay. + +$(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone) +contains the time zone types. + +$(LINK2 $(PHOBOS_PATH)std_datetime.html, std.datetime.package) contains +StopWatch and the benchmarking functions (so, they can only be imported via +std.datetime and not via a submodule). As those functions use TickDuration, +they are slated for deprecation and will be replaced with corresponding +functions that use MonoTime and Duration. Eventually, the new functions will +end up in a submodule of std.datetime, and the old ones will have been removed, +leaving nothing in std.datetime.package except for documentation and the public +imports of the rest of std.datetime. From 3ebc4ec4ff06fd3846aab9eafb721889c14e2fac Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 14:39:01 +0200 Subject: [PATCH 115/262] Move TimeOfDay to std.datetime.date. --- std/datetime/date.d | 1313 +++++++++++++++++++++++++++++++++++++ std/datetime/timeofday.d | 1321 +------------------------------------- 2 files changed, 1314 insertions(+), 1320 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index 67690a63a90..fe3d9f997a7 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -4346,8 +4346,1321 @@ package: } +/++ + Represents a time of day with hours, minutes, and seconds. It uses 24 hour + time. ++/ +struct TimeOfDay +{ +public: + + /++ + Params: + hour = Hour of the day [0 - 24$(RPAREN). + minute = Minute of the hour [0 - 60$(RPAREN). + second = Second of the minute [0 - 60$(RPAREN). + + Throws: + $(REF std,datetime,common,DateTimeException) if the resulting + $(LREF TimeOfDay) would be not be valid. + +/ + this(int hour, int minute, int second = 0) @safe pure + { + enforceValid!"hours"(hour); + enforceValid!"minutes"(minute); + enforceValid!"seconds"(second); + + _hour = cast(ubyte) hour; + _minute = cast(ubyte) minute; + _second = cast(ubyte) second; + } + + @safe unittest + { + assert(TimeOfDay(0, 0) == TimeOfDay.init); + + { + auto tod = TimeOfDay(0, 0); + assert(tod._hour == 0); + assert(tod._minute == 0); + assert(tod._second == 0); + } + + { + auto tod = TimeOfDay(12, 30, 33); + assert(tod._hour == 12); + assert(tod._minute == 30); + assert(tod._second == 33); + } + + { + auto tod = TimeOfDay(23, 59, 59); + assert(tod._hour == 23); + assert(tod._minute == 59); + assert(tod._second == 59); + } + + assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); + } + + + /++ + Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in TimeOfDay rhs) @safe const pure nothrow + { + if (_hour < rhs._hour) + return -1; + if (_hour > rhs._hour) + return 1; + + if (_minute < rhs._minute) + return -1; + if (_minute > rhs._minute) + return 1; + + if (_second < rhs._second) + return -1; + if (_second > rhs._second) + return 1; + + return 0; + } + + @safe unittest + { + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); + + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); + assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); + assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); + + assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); + + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); + + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(ctod.opCmp(itod) == 0); + assert(itod.opCmp(ctod) == 0); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _hour; + } + + @safe unittest + { + assert(TimeOfDay.init.hour == 0); + assert(TimeOfDay(12, 0, 0).hour == 12); + + const ctod = TimeOfDay(12, 0, 0); + immutable itod = TimeOfDay(12, 0, 0); + assert(ctod.hour == 12); + assert(itod.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given hour would + result in an invalid $(LREF TimeOfDay). + +/ + @property void hour(int hour) @safe pure + { + enforceValid!"hours"(hour); + _hour = cast(ubyte) hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.hour = 12; + assert(tod == TimeOfDay(12, 0, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.hour = 12)); + static assert(!__traits(compiles, itod.hour = 12)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _minute; + } + + @safe unittest + { + assert(TimeOfDay.init.minute == 0); + assert(TimeOfDay(0, 30, 0).minute == 30); + + const ctod = TimeOfDay(0, 30, 0); + immutable itod = TimeOfDay(0, 30, 0); + assert(ctod.minute == 30); + assert(itod.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF TimeOfDay)'s minute to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given minute + would result in an invalid $(LREF TimeOfDay). + +/ + @property void minute(int minute) @safe pure + { + enforceValid!"minutes"(minute); + _minute = cast(ubyte) minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.minute = 30; + assert(tod == TimeOfDay(0, 30, 0)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.minute = 30)); + static assert(!__traits(compiles, itod.minute = 30)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _second; + } + + @safe unittest + { + assert(TimeOfDay.init.second == 0); + assert(TimeOfDay(0, 0, 33).second == 33); + + const ctod = TimeOfDay(0, 0, 33); + immutable itod = TimeOfDay(0, 0, 33); + assert(ctod.second == 33); + assert(itod.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF TimeOfDay)'s second to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given second + would result in an invalid $(LREF TimeOfDay). + +/ + @property void second(int second) @safe pure + { + enforceValid!"seconds"(second); + _second = cast(ubyte) second; + } + + @safe unittest + { + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); + + auto tod = TimeOfDay(0, 0, 0); + tod.second = 33; + assert(tod == TimeOfDay(0, 0, 33)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.second = 33)); + static assert(!__traits(compiles, itod.second = 33)); + } + + + /++ + Adds the given number of units to this $(LREF TimeOfDay). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF TimeOfDay) + one hours's worth of minutes gets the exact same + $(LREF TimeOfDay). + + Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF TimeOfDay). + +/ + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "hours") + { + return this += dur!"hours"(value); + } + + /// + @safe unittest + { + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"hours"(1); + assert(tod1 == TimeOfDay(8, 12, 0)); + + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"hours"(-1); + assert(tod2 == TimeOfDay(6, 12, 0)); + + auto tod3 = TimeOfDay(23, 59, 0); + tod3.roll!"minutes"(1); + assert(tod3 == TimeOfDay(23, 0, 0)); + + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"minutes"(-1); + assert(tod4 == TimeOfDay(0, 59, 0)); + + auto tod5 = TimeOfDay(23, 59, 59); + tod5.roll!"seconds"(1); + assert(tod5 == TimeOfDay(23, 59, 0)); + + auto tod6 = TimeOfDay(0, 0, 0); + tod6.roll!"seconds"(-1); + assert(tod6 == TimeOfDay(0, 0, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"hours"(22).roll!"hours"(-7); + assert(tod == TimeOfDay(3, 27, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"hours"(53))); + static assert(!__traits(compiles, itod.roll!"hours"(53))); + } + + + // Shares documentation with "hours" version. + ref TimeOfDay roll(string units)(long value) @safe pure nothrow + if (units == "minutes" || units == "seconds") + { + import std.format : format; + + enum memberVarStr = units[0 .. $ - 1]; + value %= 60; + mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); + + if (value < 0) + { + if (newVal < 0) + newVal += 60; + } + else if (newVal >= 60) + newVal -= 60; + + mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); + return this; + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); + + testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); + + testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); + + testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); + testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); + testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); + + testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); + testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); + testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); + + testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); + testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); + testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"minutes"(97).roll!"minutes"(-102); + assert(tod == TimeOfDay(12, 22, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"minutes"(7))); + static assert(!__traits(compiles, itod.roll!"minutes"(7))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"seconds"(105).roll!"seconds"(-77); + assert(tod == TimeOfDay(12, 27, 30)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"seconds"(7))); + static assert(!__traits(compiles, itod.roll!"seconds"(7))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + import core.time : hours, minutes, seconds; + + assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); + assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); + assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); + assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); + + assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); + assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); + assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); + assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto duration = dur!"hours"(11); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod + duration == TimeOfDay(23, 30, 33)); + assert(ctod + duration == TimeOfDay(23, 30, 33)); + assert(itod + duration == TimeOfDay(23, 30, 33)); + + assert(tod - duration == TimeOfDay(1, 30, 33)); + assert(ctod - duration == TimeOfDay(1, 30, 33)); + assert(itod - duration == TimeOfDay(1, 30, 33)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + TimeOfDay retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto tod = TimeOfDay(12, 30, 33); + + assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + + assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + } + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF TimeOfDay), as well as assigning the result to this + $(LREF TimeOfDay). + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF TimeOfDay). + +/ + ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = duration.total!"seconds"; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + @safe unittest + { + auto duration = dur!"hours"(12); + + assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); + + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + auto tod = TimeOfDay(19, 17, 22); + (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); + assert(tod == TimeOfDay(17, 15, 59)); + + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(!__traits(compiles, ctod += duration)); + static assert(!__traits(compiles, itod += duration)); + static assert(!__traits(compiles, ctod -= duration)); + static assert(!__traits(compiles, itod -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod += TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(-7_000_000); + assert(tod == TimeOfDay(12, 30, 40)); + } + + { + auto tod = TimeOfDay(12, 30, 33); + tod -= TickDuration.from!"usecs"(7_000_000); + assert(tod == TimeOfDay(12, 30, 26)); + } + } + } + + + /++ + Gives the difference between two $(LREF TimeOfDay)s. + + The legal types of arithmetic for $(LREF TimeOfDay) using this operator + are + + $(BOOKTABLE, + $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) + ) + + Params: + rhs = The $(LREF TimeOfDay) to subtract from this one. + +/ + Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow + if (op == "-") + { + immutable lhsSec = _hour * 3600 + _minute * 60 + _second; + immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; + + return dur!"seconds"(lhsSec - rhsSec); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + + assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); + assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); + assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); + assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); + + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod - tod == Duration.zero); + assert(ctod - tod == Duration.zero); + assert(itod - tod == Duration.zero); + + assert(tod - ctod == Duration.zero); + assert(ctod - ctod == Duration.zero); + assert(itod - ctod == Duration.zero); + + assert(tod - itod == Duration.zero); + assert(ctod - itod == Duration.zero); + assert(itod - itod == Duration.zero); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d%02d%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); + assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOString() == "123033"); + assert(ctod.toISOString() == "123033"); + assert(itod.toISOString() == "123033"); + } + + + /++ + Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%02d:%02d:%02d", _hour, _minute, _second); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); + assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toISOExtString() == "12:30:33"); + assert(ctod.toISOExtString() == "12:30:33"); + assert(itod.toISOExtString() == "12:30:33"); + } + + + /++ + Converts this TimeOfDay to a string. + +/ + string toString() @safe const pure nothrow + { + return toISOExtString(); + } + + @safe unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + assert(tod.toString()); + assert(ctod.toString()); + assert(itod.toString()); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for times. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF TimeOfDay) would + not be valid. + +/ + static TimeOfDay fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoString)); + + enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[2 .. 4]; + auto seconds = dstr[4 .. $]; + + enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); + + assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. + Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for + times. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO Extended format or if the resulting $(LREF TimeOfDay) + would not be valid. + +/ + static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : all; + import std.ascii : isDigit; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + auto dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + auto hours = dstr[0 .. 2]; + auto minutes = dstr[3 .. 5]; + auto seconds = dstr[6 .. $]; + + enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(hours), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(minutes), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + enforce(all!isDigit(seconds), + new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + } + + /// + @safe unittest + { + assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); + } + + @safe unittest + { + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); + + assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); + } + + + /++ + Returns midnight. + +/ + @property static TimeOfDay min() @safe pure nothrow + { + return TimeOfDay.init; + } + + @safe unittest + { + assert(TimeOfDay.min.hour == 0); + assert(TimeOfDay.min.minute == 0); + assert(TimeOfDay.min.second == 0); + assert(TimeOfDay.min < TimeOfDay.max); + } + + + /++ + Returns one second short of midnight. + +/ + @property static TimeOfDay max() @safe pure nothrow + { + auto tod = TimeOfDay.init; + tod._hour = maxHour; + tod._minute = maxMinute; + tod._second = maxSecond; + + return tod; + } + + @safe unittest + { + assert(TimeOfDay.max.hour == 23); + assert(TimeOfDay.max.minute == 59); + assert(TimeOfDay.max.second == 59); + assert(TimeOfDay.max > TimeOfDay.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. If the + number of minutes overflows (or underflows), then the minutes will wrap. + If the number of minutes overflows(or underflows), then the hour will + wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). + + Params: + seconds = The number of seconds to add to this TimeOfDay. + +/ + ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_hour); + hnsecs += convert!("minutes", "hnsecs")(_minute); + hnsecs += convert!("seconds", "hnsecs")(_second); + + hnsecs %= convert!("days", "hnsecs")(1); + + if (hnsecs < 0) + hnsecs += convert!("days", "hnsecs")(1); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _hour = cast(ubyte) newHours; + _minute = cast(ubyte) newMinutes; + _second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); + + testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); + testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod._addSeconds(7))); + static assert(!__traits(compiles, itod._addSeconds(7))); + } + + + /+ + Whether the given values form a valid $(LREF TimeOfDay). + +/ + static bool _valid(int hour, int minute, int second) @safe pure nothrow + { + return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); + } + + + @safe pure invariant() + { + import std.format : format; + assert(_valid(_hour, _minute, _second), + format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); + } + + +package: + + ubyte _hour; + ubyte _minute; + ubyte _second; + + enum ubyte maxHour = 24 - 1; + enum ubyte maxMinute = 60 - 1; + enum ubyte maxSecond = 60 - 1; +} + + package: +/+ + Splits out a particular unit from hnsecs and gives the value for that + unit and the remaining hnsecs. It really shouldn't be used unless unless + all units larger than the given units have already been split out. + + Params: + units = The units to split out. + hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left + after splitting out the given units. + + Returns: + The number of the given units from converting hnsecs to those units. + +/ +long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow +if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) +{ + import core.time : convert; + immutable value = convert!("hnsecs", units)(hnsecs); + hnsecs -= convert!(units, "hnsecs")(value); + return value; +} + +@safe unittest +{ + auto hnsecs = 2595000000007L; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 3000000007); + + immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + assert(minutes == 5); + assert(hnsecs == 7); +} + + /+ Returns the day of the week for the given day of the Gregorian Calendar. diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d index ae9a9acc532..68aa1882387 100644 --- a/std/datetime/timeofday.d +++ b/std/datetime/timeofday.d @@ -7,1323 +7,4 @@ +/ module std.datetime.timeofday; -import core.time; -import std.datetime.common; -import std.traits : isSomeString; - -version(unittest) import std.exception : assertThrown; - - -/++ - Represents a time of day with hours, minutes, and seconds. It uses 24 hour - time. -+/ -struct TimeOfDay -{ -public: - - /++ - Params: - hour = Hour of the day [0 - 24$(RPAREN). - minute = Minute of the hour [0 - 60$(RPAREN). - second = Second of the minute [0 - 60$(RPAREN). - - Throws: - $(REF std,datetime,common,DateTimeException) if the resulting - $(LREF TimeOfDay) would be not be valid. - +/ - this(int hour, int minute, int second = 0) @safe pure - { - enforceValid!"hours"(hour); - enforceValid!"minutes"(minute); - enforceValid!"seconds"(second); - - _hour = cast(ubyte) hour; - _minute = cast(ubyte) minute; - _second = cast(ubyte) second; - } - - @safe unittest - { - assert(TimeOfDay(0, 0) == TimeOfDay.init); - - { - auto tod = TimeOfDay(0, 0); - assert(tod._hour == 0); - assert(tod._minute == 0); - assert(tod._second == 0); - } - - { - auto tod = TimeOfDay(12, 30, 33); - assert(tod._hour == 12); - assert(tod._minute == 30); - assert(tod._second == 33); - } - - { - auto tod = TimeOfDay(23, 59, 59); - assert(tod._hour == 23); - assert(tod._minute == 59); - assert(tod._second == 59); - } - - assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); - } - - - /++ - Compares this $(LREF TimeOfDay) with the given $(LREF TimeOfDay). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in TimeOfDay rhs) @safe const pure nothrow - { - if (_hour < rhs._hour) - return -1; - if (_hour > rhs._hour) - return 1; - - if (_minute < rhs._minute) - return -1; - if (_minute > rhs._minute) - return 1; - - if (_second < rhs._second) - return -1; - if (_second > rhs._second) - return 1; - - return 0; - } - - @safe unittest - { - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); - - assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); - assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); - assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); - - assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); - assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); - assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); - - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); - assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - - assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); - assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(ctod.opCmp(itod) == 0); - assert(itod.opCmp(ctod) == 0); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _hour; - } - - @safe unittest - { - assert(TimeOfDay.init.hour == 0); - assert(TimeOfDay(12, 0, 0).hour == 12); - - const ctod = TimeOfDay(12, 0, 0); - immutable itod = TimeOfDay(12, 0, 0); - assert(ctod.hour == 12); - assert(itod.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given hour would - result in an invalid $(LREF TimeOfDay). - +/ - @property void hour(int hour) @safe pure - { - enforceValid!"hours"(hour); - _hour = cast(ubyte) hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.hour = 12; - assert(tod == TimeOfDay(12, 0, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.hour = 12)); - static assert(!__traits(compiles, itod.hour = 12)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _minute; - } - - @safe unittest - { - assert(TimeOfDay.init.minute == 0); - assert(TimeOfDay(0, 30, 0).minute == 30); - - const ctod = TimeOfDay(0, 30, 0); - immutable itod = TimeOfDay(0, 30, 0); - assert(ctod.minute == 30); - assert(itod.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF TimeOfDay)'s minute to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given minute - would result in an invalid $(LREF TimeOfDay). - +/ - @property void minute(int minute) @safe pure - { - enforceValid!"minutes"(minute); - _minute = cast(ubyte) minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.minute = 30; - assert(tod == TimeOfDay(0, 30, 0)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.minute = 30)); - static assert(!__traits(compiles, itod.minute = 30)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _second; - } - - @safe unittest - { - assert(TimeOfDay.init.second == 0); - assert(TimeOfDay(0, 0, 33).second == 33); - - const ctod = TimeOfDay(0, 0, 33); - immutable itod = TimeOfDay(0, 0, 33); - assert(ctod.second == 33); - assert(itod.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF TimeOfDay)'s second to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given second - would result in an invalid $(LREF TimeOfDay). - +/ - @property void second(int second) @safe pure - { - enforceValid!"seconds"(second); - _second = cast(ubyte) second; - } - - @safe unittest - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); - - auto tod = TimeOfDay(0, 0, 0); - tod.second = 33; - assert(tod == TimeOfDay(0, 0, 33)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.second = 33)); - static assert(!__traits(compiles, itod.second = 33)); - } - - - /++ - Adds the given number of units to this $(LREF TimeOfDay). A negative - number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF TimeOfDay) - one hours's worth of minutes gets the exact same - $(LREF TimeOfDay). - - Accepted units are $(D "hours"), $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF TimeOfDay). - +/ - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if (units == "hours") - { - return this += dur!"hours"(value); - } - - /// - @safe unittest - { - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"hours"(1); - assert(tod1 == TimeOfDay(8, 12, 0)); - - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"hours"(-1); - assert(tod2 == TimeOfDay(6, 12, 0)); - - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); - - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); - - auto tod5 = TimeOfDay(23, 59, 59); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(23, 59, 0)); - - auto tod6 = TimeOfDay(0, 0, 0); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(0, 0, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"hours"(22).roll!"hours"(-7); - assert(tod == TimeOfDay(3, 27, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"hours"(53))); - static assert(!__traits(compiles, itod.roll!"hours"(53))); - } - - - // Shares documentation with "hours" version. - ref TimeOfDay roll(string units)(long value) @safe pure nothrow - if (units == "minutes" || units == "seconds") - { - import std.format : format; - - enum memberVarStr = units[0 .. $ - 1]; - value %= 60; - mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); - - if (value < 0) - { - if (newVal < 0) - newVal += 60; - } - else if (newVal >= 60) - newVal -= 60; - - mixin(format("_%s = cast(ubyte) newVal;", memberVarStr)); - return this; - } - - // Test roll!"minutes"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); - - testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); - - testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); - - testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); - testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); - testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); - - testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); - testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); - testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); - - testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); - testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); - testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"minutes"(97).roll!"minutes"(-102); - assert(tod == TimeOfDay(12, 22, 2)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"minutes"(7))); - static assert(!__traits(compiles, itod.roll!"minutes"(7))); - } - - // Test roll!"seconds"(). - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - auto tod = TimeOfDay(12, 27, 2); - tod.roll!"seconds"(105).roll!"seconds"(-77); - assert(tod == TimeOfDay(12, 27, 30)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"seconds"(7))); - static assert(!__traits(compiles, itod.roll!"seconds"(7))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - TimeOfDay opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - TimeOfDay retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - import core.time : hours, minutes, seconds; - - assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13)); - assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12)); - assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12)); - assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0)); - - assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11)); - assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12)); - assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12)); - assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59)); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto duration = dur!"hours"(11); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod + duration == TimeOfDay(23, 30, 33)); - assert(ctod + duration == TimeOfDay(23, 30, 33)); - assert(itod + duration == TimeOfDay(23, 30, 33)); - - assert(tod - duration == TimeOfDay(1, 30, 33)); - assert(ctod - duration == TimeOfDay(1, 30, 33)); - assert(itod - duration == TimeOfDay(1, 30, 33)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - TimeOfDay opBinary(string op)(TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - TimeOfDay retval = this; - immutable seconds = td.seconds; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - // This probably only runs in cases where gettimeofday() is used, but it's - // hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto tod = TimeOfDay(12, 30, 33); - - assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - - assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - } - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF TimeOfDay), as well as assigning the result to this - $(LREF TimeOfDay). - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD +) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - $(TR $(TD TimeOfDay) $(TD -) $(TD Duration) $(TD -->) $(TD TimeOfDay)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF TimeOfDay). - +/ - ref TimeOfDay opOpAssign(string op)(Duration duration) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable seconds = duration.total!"seconds"; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - @safe unittest - { - auto duration = dur!"hours"(12); - - assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); - assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - - auto tod = TimeOfDay(19, 17, 22); - (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); - assert(tod == TimeOfDay(17, 15, 59)); - - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(!__traits(compiles, ctod += duration)); - static assert(!__traits(compiles, itod += duration)); - static assert(!__traits(compiles, ctod -= duration)); - static assert(!__traits(compiles, itod -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref TimeOfDay opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - immutable seconds = td.seconds; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - // This probably only runs in cases where gettimeofday() is used, but it's - // hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto tod = TimeOfDay(12, 30, 33); - tod += TickDuration.from!"usecs"(7_000_000); - assert(tod == TimeOfDay(12, 30, 40)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod += TickDuration.from!"usecs"(-7_000_000); - assert(tod == TimeOfDay(12, 30, 26)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod -= TickDuration.from!"usecs"(-7_000_000); - assert(tod == TimeOfDay(12, 30, 40)); - } - - { - auto tod = TimeOfDay(12, 30, 33); - tod -= TickDuration.from!"usecs"(7_000_000); - assert(tod == TimeOfDay(12, 30, 26)); - } - } - } - - - /++ - Gives the difference between two $(LREF TimeOfDay)s. - - The legal types of arithmetic for $(LREF TimeOfDay) using this operator - are - - $(BOOKTABLE, - $(TR $(TD TimeOfDay) $(TD -) $(TD TimeOfDay) $(TD -->) $(TD duration)) - ) - - Params: - rhs = The $(LREF TimeOfDay) to subtract from this one. - +/ - Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow - if (op == "-") - { - immutable lhsSec = _hour * 3600 + _minute * 60 + _second; - immutable rhsSec = rhs._hour * 3600 + rhs._minute * 60 + rhs._second; - - return dur!"seconds"(lhsSec - rhsSec); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - - assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); - assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); - assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); - assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); - assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); - - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod - tod == Duration.zero); - assert(ctod - tod == Duration.zero); - assert(itod - tod == Duration.zero); - - assert(tod - ctod == Duration.zero); - assert(ctod - ctod == Duration.zero); - assert(itod - ctod == Duration.zero); - - assert(tod - itod == Duration.zero); - assert(ctod - itod == Duration.zero); - assert(itod - itod == Duration.zero); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d%02d%02d", _hour, _minute, _second); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); - assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOString() == "123033"); - assert(ctod.toISOString() == "123033"); - assert(itod.toISOString() == "123033"); - } - - - /++ - Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%02d:%02d:%02d", _hour, _minute, _second); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); - assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toISOExtString() == "12:30:33"); - assert(ctod.toISOExtString() == "12:30:33"); - assert(itod.toISOExtString() == "12:30:33"); - } - - - /++ - Converts this TimeOfDay to a string. - +/ - string toString() @safe const pure nothrow - { - return toISOExtString(); - } - - @safe unittest - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - assert(tod.toString()); - assert(ctod.toString()); - assert(itod.toString()); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for times. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given string is - not in the ISO format or if the resulting $(LREF TimeOfDay) would - not be valid. - +/ - static TimeOfDay fromISOString(S)(in S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[2 .. 4]; - auto seconds = dstr[4 .. $]; - - enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); - - assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Creates a $(LREF TimeOfDay) from a string with the format HH:MM:SS. - Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for - times. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given string is - not in the ISO Extended format or if the resulting $(LREF TimeOfDay) - would not be valid. - +/ - static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - - auto dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length == 8, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - auto hours = dstr[0 .. 2]; - auto minutes = dstr[3 .. 5]; - auto seconds = dstr[6 .. $]; - - enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(hours), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(minutes), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(all!isDigit(seconds), - new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); - } - - /// - @safe unittest - { - assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); - } - - @safe unittest - { - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); - - assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); - assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); - assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); - assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); - } - - - /++ - Returns midnight. - +/ - @property static TimeOfDay min() @safe pure nothrow - { - return TimeOfDay.init; - } - - @safe unittest - { - assert(TimeOfDay.min.hour == 0); - assert(TimeOfDay.min.minute == 0); - assert(TimeOfDay.min.second == 0); - assert(TimeOfDay.min < TimeOfDay.max); - } - - - /++ - Returns one second short of midnight. - +/ - @property static TimeOfDay max() @safe pure nothrow - { - auto tod = TimeOfDay.init; - tod._hour = maxHour; - tod._minute = maxMinute; - tod._second = maxSecond; - - return tod; - } - - @safe unittest - { - assert(TimeOfDay.max.hour == 23); - assert(TimeOfDay.max.minute == 59); - assert(TimeOfDay.max.second == 59); - assert(TimeOfDay.max > TimeOfDay.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. If the - number of minutes overflows (or underflows), then the minutes will wrap. - If the number of minutes overflows(or underflows), then the hour will - wrap. (e.g. adding 90 seconds to 23:59:00 would result in 00:00:30). - - Params: - seconds = The number of seconds to add to this TimeOfDay. - +/ - ref TimeOfDay _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_hour); - hnsecs += convert!("minutes", "hnsecs")(_minute); - hnsecs += convert!("seconds", "hnsecs")(_second); - - hnsecs %= convert!("days", "hnsecs")(1); - - if (hnsecs < 0) - hnsecs += convert!("days", "hnsecs")(1); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _hour = cast(ubyte) newHours; - _minute = cast(ubyte) newMinutes; - _second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); - - testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); - testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod._addSeconds(7))); - static assert(!__traits(compiles, itod._addSeconds(7))); - } - - - /+ - Whether the given values form a valid $(LREF TimeOfDay). - +/ - static bool _valid(int hour, int minute, int second) @safe pure nothrow - { - return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); - } - - - @safe pure invariant() - { - import std.format : format; - assert(_valid(_hour, _minute, _second), - format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); - } - - -package: - - ubyte _hour; - ubyte _minute; - ubyte _second; - - enum ubyte maxHour = 24 - 1; - enum ubyte maxMinute = 60 - 1; - enum ubyte maxSecond = 60 - 1; -} - - -package: - -/+ - Splits out a particular unit from hnsecs and gives the value for that - unit and the remaining hnsecs. It really shouldn't be used unless unless - all units larger than the given units have already been split out. - - Params: - units = The units to split out. - hnsecs = The current total hnsecs. Upon returning, it is the hnsecs left - after splitting out the given units. - - Returns: - The number of the given units from converting hnsecs to those units. - +/ -long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow -if (validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) -{ - import core.time : convert; - immutable value = convert!("hnsecs", units)(hnsecs); - hnsecs -= convert!(units, "hnsecs")(value); - return value; -} - -@safe unittest -{ - auto hnsecs = 2595000000007L; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 3000000007); - - immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - assert(minutes == 5); - assert(hnsecs == 7); -} +public import std.datetime.date; From 7c18f68e04a78b20fcec26463857aab520a3c40b Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 14:55:21 +0200 Subject: [PATCH 116/262] Move DateTime to std.datetime.date. --- std/datetime/date.d | 3520 ++++++++++++++++++++++++++++++++++- std/datetime/datetime.d | 3847 +-------------------------------------- 2 files changed, 3520 insertions(+), 3847 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index fe3d9f997a7..9f5be5227bd 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -9,7 +9,7 @@ module std.datetime.date; import core.time; import std.datetime.common; -import std.traits : isSomeString; +import std.traits : isSomeString, Unqual; version(unittest) import std.exception : assertThrown; @@ -20,6 +20,3493 @@ version(unittest) import std.exception : assertThrown; } +/++ + Combines the $(REF std,datetime,date,Date) and + $(REF std,datetime,timeofday,TimeOfDay) structs to give an object which holds + both the date and the time. It is optimized for calendar-based operations and + has no concept of time zone. For an object which is optimized for time + operations based on the system time, use $(REF std,datetime,systime,SysTime). + $(REF std,datetime,systime,SysTime) has a concept of time zone and has much + higher precision (hnsecs). $(D DateTime) is intended primarily for + calendar-based uses rather than precise time operations. + +/ +struct DateTime +{ +public: + + /++ + Params: + date = The date portion of $(LREF DateTime). + tod = The time portion of $(LREF DateTime). + +/ + this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow + { + _date = date; + _tod = tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Params: + year = The year portion of the date. + month = The month portion of the date. + day = The day portion of the date. + hour = The hour portion of the time; + minute = The minute portion of the time; + second = The second portion of the time; + +/ + this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure + { + _date = Date(year, month, day); + _tod = TimeOfDay(hour, minute, second); + } + + @safe unittest + { + { + auto dt = DateTime(1999, 7 ,6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } + + { + auto dt = DateTime(1999, 7 ,6, 12, 30, 33); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); + } + } + + + /++ + Compares this $(LREF DateTime) with the given $(D DateTime.). + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ + int opCmp(in DateTime rhs) @safe const pure nothrow + { + immutable dateResult = _date.opCmp(rhs._date); + + if (dateResult != 0) + return dateResult; + + return _tod.opCmp(rhs._tod); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); + + assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); + assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); + assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); + + assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); + + assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); + + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + + assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + // Test B.C. + assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + // Test Both + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + assert(dt.opCmp(dt) == 0); + assert(dt.opCmp(cdt) == 0); + assert(dt.opCmp(idt) == 0); + assert(cdt.opCmp(dt) == 0); + assert(cdt.opCmp(cdt) == 0); + assert(cdt.opCmp(idt) == 0); + assert(idt.opCmp(dt) == 0); + assert(idt.opCmp(cdt) == 0); + assert(idt.opCmp(idt) == 0); + } + + + /++ + The date portion of $(LREF DateTime). + +/ + @property Date date() @safe const pure nothrow + { + return _date; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.date == Date.init); + } + + { + auto dt = DateTime(Date(1999, 7, 6)); + assert(dt.date == Date(1999, 7, 6)); + } + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + assert(cdt.date == Date(1999, 7, 6)); + assert(idt.date == Date(1999, 7, 6)); + } + + + /++ + The date portion of $(LREF DateTime). + + Params: + date = The Date to set this $(LREF DateTime)'s date portion to. + +/ + @property void date(in Date date) @safe pure nothrow + { + _date = date; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.date = Date(1999, 7, 6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); + static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); + } + + + /++ + The time portion of $(LREF DateTime). + +/ + @property TimeOfDay timeOfDay() @safe const pure nothrow + { + return _tod; + } + + @safe unittest + { + { + auto dt = DateTime.init; + assert(dt.timeOfDay == TimeOfDay.init); + } + + { + auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); + assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); + assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); + } + + + /++ + The time portion of $(LREF DateTime). + + Params: + tod = The $(REF std,datetime,timeofday,TimeOfDay) to set this + $(LREF DateTime)'s time portion to. + +/ + @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow + { + _tod = tod; + } + + @safe unittest + { + auto dt = DateTime.init; + dt.timeOfDay = TimeOfDay(12, 30, 33); + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay(12, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); + static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + +/ + @property short year() @safe const pure nothrow + { + return _date.year; + } + + @safe unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(idt.year == 1999); + assert(idt.year == 1999); + } + + + /++ + Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive + are B.C. + + Params: + year = The year to set this $(LREF DateTime)'s year to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the new year is not + a leap year and if the resulting date would be on February 29th. + +/ + @property void year(int year) @safe pure + { + _date.year = year; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); + } + + @safe unittest + { + static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + { + dt.year = year; + assert(dt == expected); + } + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 1999, + DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + 0, + DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + -1999, + DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.year = 7)); + static assert(!__traits(compiles, idt.year = 7)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Throws: + $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. + +/ + @property short yearBC() @safe const pure + { + return _date.yearBC; + } + + /// + @safe unittest + { + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); + assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); + assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); + } + + @safe unittest + { + assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. + + Params: + year = The year B.C. to set this $(LREF DateTime)'s year to. + + Throws: + $(REF std,datetime,common,DateTimeException) if a non-positive value + is given. + +/ + @property void yearBC(int year) @safe pure + { + _date.yearBC = year; + } + + /// + @safe unittest + { + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); + dt.yearBC = 1; + assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + + dt.yearBC = 10; + assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); + } + + @safe unittest + { + assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); + + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + dt.yearBC = 12; + assert(dt.yearBC == 12); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); + } + + + /++ + Month of a Gregorian Year. + +/ + @property Month month() @safe const pure nothrow + { + return _date.month; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); + } + + @safe unittest + { + assert(DateTime.init.month == 1); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.month == 7); + assert(idt.month == 7); + } + + + /++ + Month of a Gregorian Year. + + Params: + month = The month to set this $(LREF DateTime)'s month to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given month is + not a valid month. + +/ + @property void month(Month month) @safe pure + { + _date.month = month; + } + + @safe unittest + { + static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) + { + dt.month = month; + assert(expected != DateTime.init); + assert(dt == expected); + } + + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); + + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), + cast(Month) 7, + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.month = 12)); + static assert(!__traits(compiles, idt.month = 12)); + } + + + /++ + Day of a Gregorian Month. + +/ + @property ubyte day() @safe const pure nothrow + { + return _date.day; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); + } + + @safe unittest + { + import std.format : format; + import std.range : chain; + + static void test(DateTime dateTime, int expected) + { + assert(dateTime.day == expected, format("Value given: %s", dateTime)); + } + + foreach (year; chain(testYearsBC, testYearsAD)) + { + foreach (md; testMonthDays) + { + foreach (tod; testTODs) + test(DateTime(Date(year, md.month, md.day), tod), md.day); + } + } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.day == 6); + assert(idt.day == 6); + } + + + /++ + Day of a Gregorian Month. + + Params: + day = The day of the month to set this $(LREF DateTime)'s day to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given day is not + a valid day of the current month. + +/ + @property void day(int day) @safe pure + { + _date.day = day; + } + + @safe unittest + { + import std.exception : assertNotThrown; + + static void testDT(DateTime dt, int day) + { + dt.day = day; + } + + // Test A.D. + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); + + { + auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); + } + + // Test B.C. + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); + + auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.day = 27)); + static assert(!__traits(compiles, idt.day = 27)); + } + + + /++ + Hours past midnight. + +/ + @property ubyte hour() @safe const pure nothrow + { + return _tod.hour; + } + + @safe unittest + { + assert(DateTime.init.hour == 0); + assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.hour == 12); + assert(idt.hour == 12); + } + + + /++ + Hours past midnight. + + Params: + hour = The hour of the day to set this $(LREF DateTime)'s hour to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given hour would + result in an invalid $(LREF DateTime). + +/ + @property void hour(int hour) @safe pure + { + _tod.hour = hour; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); + + auto dt = DateTime.init; + dt.hour = 12; + assert(dt == DateTime(1, 1, 1, 12, 0, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.hour = 27)); + static assert(!__traits(compiles, idt.hour = 27)); + } + + + /++ + Minutes past the hour. + +/ + @property ubyte minute() @safe const pure nothrow + { + return _tod.minute; + } + + @safe unittest + { + assert(DateTime.init.minute == 0); + assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.minute == 30); + assert(idt.minute == 30); + } + + + /++ + Minutes past the hour. + + Params: + minute = The minute to set this $(LREF DateTime)'s minute to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given minute + would result in an invalid $(LREF DateTime). + +/ + @property void minute(int minute) @safe pure + { + _tod.minute = minute; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); + + auto dt = DateTime.init; + dt.minute = 30; + assert(dt == DateTime(1, 1, 1, 0, 30, 0)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.minute = 27)); + static assert(!__traits(compiles, idt.minute = 27)); + } + + + /++ + Seconds past the minute. + +/ + @property ubyte second() @safe const pure nothrow + { + return _tod.second; + } + + @safe unittest + { + assert(DateTime.init.second == 0); + assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.second == 33); + assert(idt.second == 33); + } + + + /++ + Seconds past the minute. + + Params: + second = The second to set this $(LREF DateTime)'s second to. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given seconds + would result in an invalid $(LREF DateTime). + +/ + @property void second(int second) @safe pure + { + _tod.second = second; + } + + @safe unittest + { + assertThrown!DateTimeException((){DateTime.init.second = 60;}()); + + auto dt = DateTime.init; + dt.second = 33; + assert(dt == DateTime(1, 1, 1, 0, 0, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.second = 27)); + static assert(!__traits(compiles, idt.second = 27)); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + Note that if day overflow is allowed, and the date with the adjusted + year/month overflows the number of days in the new month, then the month + will be incremented by one, and the day set to the number of days + overflowed. (e.g. if the day were 31 and the new month were June, then + the month would be incremented to July, and the new day would be 1). If + day overflow is not allowed, then the day will be set to the last valid + day in the month (e.g. June 31st would become June 30th). + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime add(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.add!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + import std.datetime.common : AllowDayOverflow; + + auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); + dt1.add!"months"(11); + assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); + dt2.add!"months"(-11); + assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); + + auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); + dt3.add!"years"(1); + assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); + dt4.add!"years"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.add!"years"(7).add!"months"(-4); + assert(dt == DateTime(2006, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.add!"years"(4))); + static assert(!__traits(compiles, idt.add!"years"(4))); + static assert(!__traits(compiles, cdt.add!"months"(4))); + static assert(!__traits(compiles, idt.add!"months"(4))); + } + + + /++ + Adds the given number of years or months to this $(LREF DateTime). A + negative number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. Rolling a $(LREF DateTime) 12 months + gets the exact same $(LREF DateTime). However, the days can still be + affected due to the differing number of days in each month. + + Because there are no units larger than years, there is no difference + between adding and rolling years. + + Params: + units = The type of units to add ("years" or "months"). + value = The number of months or years to add to this + $(LREF DateTime). + allowOverflow = Whether the days should be allowed to overflow, + causing the month to increment. + +/ + ref DateTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow + if (units == "years" || units == "months") + { + _date.roll!units(value, allowOverflow); + return this; + } + + /// + @safe unittest + { + import std.datetime.common : AllowDayOverflow; + + auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); + dt1.roll!"months"(1); + assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); + + auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); + dt2.roll!"months"(-1); + assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); + + auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); + dt3.roll!"months"(1); + assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); + + auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); + dt4.roll!"months"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); + + auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); + dt5.roll!"years"(1); + assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); + + auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); + dt6.roll!"years"(1, AllowDayOverflow.no); + assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"years"(7).roll!"months"(-4); + assert(dt == DateTime(2007, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"years"(4))); + static assert(!__traits(compiles, idt.roll!"years"(4))); + static assert(!__traits(compiles, cdt.roll!"months"(4))); + static assert(!__traits(compiles, idt.roll!"months"(4))); + } + + + /++ + Adds the given number of units to this $(LREF DateTime). A negative + number will subtract. + + The difference between rolling and adding is that rolling does not + affect larger units. For instance, rolling a $(LREF DateTime) one + year's worth of days gets the exact same $(LREF DateTime). + + Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), + $(D "minutes"), and $(D "seconds"). + + Params: + units = The units to add. + value = The number of $(D_PARAM units) to add to this + $(LREF DateTime). + +/ + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "days") + { + _date.roll!"days"(value); + return this; + } + + /// + @safe unittest + { + auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); + dt1.roll!"days"(1); + assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); + dt1.roll!"days"(365); + assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); + dt1.roll!"days"(-32); + assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); + + auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); + dt2.roll!"hours"(1); + assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); + + auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); + dt3.roll!"seconds"(-1); + assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); + } + + @safe unittest + { + auto dt = DateTime(2000, 1, 31); + dt.roll!"days"(7).roll!"days"(-4); + assert(dt == DateTime(2000, 1, 3)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); + } + + + // Shares documentation with "days" version. + ref DateTime roll(string units)(long value) @safe pure nothrow + if (units == "hours" || + units == "minutes" || + units == "seconds") + { + _tod.roll!units(value); + return this; + } + + // Test roll!"hours"(). + @safe unittest + { + static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"hours"(hours); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, + DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, + DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, + DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, + DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, + DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, + DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, + DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, + DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, + DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, + DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, + DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, + DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, + DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, + DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, + DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, + DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); + + // Test Both + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, + DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, + DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"hours"(27).roll!"hours"(-9); + assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"hours"(4))); + static assert(!__traits(compiles, idt.roll!"hours"(4))); + } + + // Test roll!"minutes"(). + @safe unittest + { + static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"minutes"(92).roll!"minutes"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"minutes"(4))); + static assert(!__traits(compiles, idt.roll!"minutes"(4))); + } + + // Test roll!"seconds"(). + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); + + // Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, + DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); + + // Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, + DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, + DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, + DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, + DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"seconds"(92).roll!"seconds"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"seconds"(4))); + static assert(!__traits(compiles, idt.roll!"seconds"(4))); + } + + + /++ + Gives the result of adding or subtracting a $(REF Duration, core,time) + from this $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The $(REF Duration, core,time) to add to or subtract from + this $(LREF DateTime). + +/ + DateTime opBinary(string op)(Duration duration) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = duration.total!"seconds"; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + /// + @safe unittest + { + import core.time : hours, seconds; + + assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == + DateTime(2016, 1, 1, 0, 0, 0)); + + assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == + DateTime(2016, 1, 1, 0, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + + assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == + DateTime(2015, 12, 31, 23, 59, 59)); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); + assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return retval._addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + + assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + + + /++ + Gives the result of adding or subtracting a duration from this + $(LREF DateTime), as well as assigning the result to this + $(LREF DateTime). + + The legal types of arithmetic for $(LREF DateTime) using this operator + are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) + $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) + ) + + Params: + duration = The duration to add to or subtract from this + $(LREF DateTime). + +/ + ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow + if ((op == "+" || op == "-") && + (is(Unqual!D == Duration) || + is(Unqual!D == TickDuration))) + { + import std.format : format; + + DateTime retval = this; + + static if (is(Unqual!D == Duration)) + immutable hnsecs = duration.total!"hnsecs"; + else static if (is(Unqual!D == TickDuration)) + immutable hnsecs = duration.hnsecs; + + mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); + } + + @safe unittest + { + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == + DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == + DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == + DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == + DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + (dt += dur!"seconds"(92)) -= dur!"days"(-500); + assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt += duration)); + static assert(!__traits(compiles, idt += duration)); + static assert(!__traits(compiles, cdt -= duration)); + static assert(!__traits(compiles, idt -= duration)); + } + + // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ + deprecated("Use Duration instead of TickDuration.") + ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow + if (op == "+" || op == "-") + { + DateTime retval = this; + immutable seconds = td.seconds; + mixin("return _addSeconds(" ~ op ~ "seconds);"); + } + + deprecated @safe unittest + { + // This probably only runs in cases where gettimeofday() is used, but it's + // hard to do this test correctly with variable ticksPerSec. + if (TickDuration.ticksPerSec == 1_000_000) + { + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt += TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(-7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + } + + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt -= TickDuration.from!"usecs"(7_000_000); + assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } + } + } + + + /++ + Gives the difference between two $(LREF DateTime)s. + + The legal types of arithmetic for $(LREF DateTime) using this operator are + + $(BOOKTABLE, + $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) + ) + +/ + Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow + if (op == "-") + { + immutable dateResult = _date - rhs.date; + immutable todResult = _tod - rhs._tod; + + return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); + } + + @safe unittest + { + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == + dur!"seconds"(86_400)); + assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == + dur!"seconds"(3600)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(60)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == + dur!"seconds"(-60)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(1)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == + dur!"seconds"(-1)); + + assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); + assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt - dt == Duration.zero); + assert(cdt - dt == Duration.zero); + assert(idt - dt == Duration.zero); + + assert(dt - cdt == Duration.zero); + assert(cdt - cdt == Duration.zero); + assert(idt - cdt == Duration.zero); + + assert(dt - idt == Duration.zero); + assert(cdt - idt == Duration.zero); + assert(idt - idt == Duration.zero); + } + + + /++ + Returns the difference between the two $(LREF DateTime)s in months. + + To get the difference in years, subtract the year property + of two $(LREF DateTime)s. To get the difference in days or weeks, + subtract the $(LREF DateTime)s themselves and use the + $(REF Duration, core,time) that results. Because converting between + months and smaller units requires a specific date (which + $(REF Duration, core,time)s don't have), getting the difference in + months requires some math using both the year and month properties, so + this is a convenience function for getting the difference in months. + + Note that the number of days in the months or how far into the month + either date is is irrelevant. It is the difference in the month property + combined with the difference in years * 12. So, for instance, + December 31st and January 1st are one month apart just as December 1st + and January 31st are one month apart. + + Params: + rhs = The $(LREF DateTime) to subtract from this one. + +/ + int diffMonths(in DateTime rhs) @safe const pure nothrow + { + return _date.diffMonths(rhs._date); + } + + /// + @safe unittest + { + assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( + DateTime(1999, 1, 31, 23, 59, 59)) == 1); + + assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( + DateTime(1999, 2, 1, 12, 3, 42)) == -1); + + assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( + DateTime(1999, 1, 1, 2, 4, 7)) == 2); + + assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( + DateTime(1999, 3, 31, 0, 30, 58)) == -2); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.diffMonths(dt) == 0); + assert(cdt.diffMonths(dt) == 0); + assert(idt.diffMonths(dt) == 0); + + assert(dt.diffMonths(cdt) == 0); + assert(cdt.diffMonths(cdt) == 0); + assert(idt.diffMonths(cdt) == 0); + + assert(dt.diffMonths(idt) == 0); + assert(cdt.diffMonths(idt) == 0); + assert(idt.diffMonths(idt) == 0); + } + + + /++ + Whether this $(LREF DateTime) is in a leap year. + +/ + @property bool isLeapYear() @safe const pure nothrow + { + return _date.isLeapYear; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(!dt.isLeapYear); + assert(!cdt.isLeapYear); + assert(!idt.isLeapYear); + } + + + /++ + Day of the week this $(LREF DateTime) is on. + +/ + @property DayOfWeek dayOfWeek() @safe const pure nothrow + { + return _date.dayOfWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfWeek == DayOfWeek.tue); + assert(cdt.dayOfWeek == DayOfWeek.tue); + assert(idt.dayOfWeek == DayOfWeek.tue); + } + + + /++ + Day of the year this $(LREF DateTime) is on. + +/ + @property ushort dayOfYear() @safe const pure nothrow + { + return _date.dayOfYear; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); + assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); + assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.dayOfYear == 187); + assert(cdt.dayOfYear == 187); + assert(idt.dayOfYear == 187); + } + + + /++ + Day of the year. + + Params: + day = The day of the year to set which day of the year this + $(LREF DateTime) is on. + +/ + @property void dayOfYear(int day) @safe pure + { + _date.dayOfYear = day; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + dt.dayOfYear = 12; + assert(dt.dayOfYear == 12); + static assert(!__traits(compiles, cdt.dayOfYear = 12)); + static assert(!__traits(compiles, idt.dayOfYear = 12)); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + +/ + @property int dayOfGregorianCal() @safe const pure nothrow + { + return _date.dayOfGregorianCal; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); + assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); + assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); + assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); + assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + + assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.dayOfGregorianCal == 729_941); + assert(idt.dayOfGregorianCal == 729_941); + } + + + /++ + The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. + Setting this property does not affect the time portion of + $(LREF DateTime). + + Params: + days = The day of the Gregorian Calendar to set this $(LREF DateTime) + to. + +/ + @property void dayOfGregorianCal(int days) @safe pure nothrow + { + _date.dayOfGregorianCal = days; + } + + /// + @safe unittest + { + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); + dt.dayOfGregorianCal = 1; + assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 365; + assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 366; + assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 0; + assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -365; + assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = -366; + assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 730_120; + assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + + dt.dayOfGregorianCal = 734_137; + assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); + } + + + /++ + The ISO 8601 week of the year that this $(LREF DateTime) is in. + + See_Also: + $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) + +/ + @property ubyte isoWeek() @safe const pure nothrow + { + return _date.isoWeek; + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.isoWeek == 27); + assert(cdt.isoWeek == 27); + assert(idt.isoWeek == 27); + } + + + /++ + $(LREF DateTime) for the last day in the month that this + $(LREF DateTime) is in. The time portion of endOfMonth is always + 23:59:59. + +/ + @property DateTime endOfMonth() @safe const pure nothrow + { + try + return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); + catch (Exception e) + assert(0, "DateTime constructor threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == + DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == + DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == + DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); + + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == + DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); + assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); + assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); + assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); + assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); + assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); + assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); + assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); + assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); + assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); + assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); + assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); + + // Test B.C. + assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); + assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); + assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); + assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); + assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); + assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); + assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); + assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); + assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); + assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); + assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); + assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); + assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + } + + + /++ + The last day in the month that this $(LREF DateTime) is in. + +/ + @property ubyte daysInMonth() @safe const pure nothrow + { + return _date.daysInMonth; + } + + /// + @safe unittest + { + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.daysInMonth == 31); + assert(idt.daysInMonth == 31); + } + + + /++ + Whether the current year is a date in A.D. + +/ + @property bool isAD() @safe const pure nothrow + { + return _date.isAD; + } + + /// + @safe unittest + { + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); + assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); + assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); + } + + @safe unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.isAD); + assert(idt.isAD); + } + + + /++ + The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF DateTime) at the given time. For example, prior to noon, + 1996-03-31 would be the Julian day number 2_450_173, so this function + returns 2_450_173, while from noon onward, the julian day number would + be 2_450_174, so this function returns 2_450_174. + +/ + @property long julianDay() @safe const pure nothrow + { + if (_tod._hour < 12) + return _date.julianDay - 1; + else + return _date.julianDay; + } + + @safe unittest + { + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); + + assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); + assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); + + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); + + assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); + assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); + + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); + + assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); + assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); + + assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); + assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.julianDay == 2_451_366); + assert(idt.julianDay == 2_451_366); + } + + + /++ + The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any + time on this date (since, the modified Julian day changes at midnight). + +/ + @property long modJulianDay() @safe const pure nothrow + { + return _date.modJulianDay; + } + + @safe unittest + { + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); + + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(cdt.modJulianDay == 51_365); + assert(idt.modJulianDay == 51_365); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. + +/ + string toISOString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOString(), _tod.toISOString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == + "20100704T070612"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == + "19981225T021500"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == + "00000105T230959"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == + "-00040105T000002"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOString() == "19990706T123033"); + assert(idt.toISOString() == "19990706T123033"); + } + + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-MM-DDTHH:MM:SS. + +/ + string toISOExtString() @safe const pure nothrow + { + import std.format : format; + try + return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == + "1998-12-25T02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == + "-0004-01-05T00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); + assert(idt.toISOExtString() == "1999-07-06T12:30:33"); + } + + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-Mon-DD HH:MM:SS. + +/ + string toSimpleString() @safe const pure nothrow + { + import std.format : format; + try + return format("%s %s", _date.toSimpleString(), _tod.toString()); + catch (Exception e) + assert(0, "format() threw."); + } + + /// + @safe unittest + { + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); + + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == + "1998-Dec-25 02:15:00"); + + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); + + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == + "-0004-Jan-05 00:00:02"); + } + + @safe unittest + { + // Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + // Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); + assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); + } + + + /++ + Converts this $(LREF DateTime) to a string. + +/ + string toString() @safe const pure nothrow + { + return toSimpleString(); + } + + @safe unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + assert(dt.toString()); + assert(cdt.toString()); + assert(idt.toString()); + } + + + + /++ + Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. + Whitespace is stripped from the given string. + + Params: + isoString = A string formatted in the ISO format for dates and times. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO format or if the resulting $(LREF DateTime) would not + be valid. + +/ + static DateTime fromISOString(S)(in S isoString) @safe pure + if (isSomeString!S) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); + + immutable date = Date.fromISOString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOString("20100704T070612") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOString("19981225T021500") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOString("00000105T230959") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOString("-00040105T000002") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOString(" 20100704T070612 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. + + Params: + isoExtString = A string formatted in the ISO Extended format for dates + and times. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given string is + not in the ISO Extended format or if the resulting $(LREF DateTime) + would not be valid. + +/ + static DateTime fromISOExtString(S)(in S isoExtString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(isoExtString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + auto t = dstr.countUntil('T'); + + enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); + + immutable date = Date.fromISOExtString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + + assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + + assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + + assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + + assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOExtString("")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Creates a $(LREF DateTime) from a string with the format + YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. + + Params: + simpleString = A string formatted in the way that toSimpleString + formats dates and times. + + Throws: + $(REF std,datetime,common,DateTimeException) if the given string is + not in the correct format or if the resulting $(LREF DateTime) + would not be valid. + +/ + static DateTime fromSimpleString(S)(in S simpleString) @safe pure + if (isSomeString!(S)) + { + import std.algorithm.searching : countUntil; + import std.conv : to; + import std.exception : enforce; + import std.format : format; + import std.string : strip; + + immutable dstr = to!dstring(strip(simpleString)); + + enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); + auto t = dstr.countUntil(' '); + + enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); + + immutable date = Date.fromSimpleString(dstr[0 .. t]); + immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); + + return DateTime(date, tod); + } + + /// + @safe unittest + { + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } + + @safe unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); + + assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == + DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + } + + + /++ + Returns the $(LREF DateTime) farthest in the past which is representable + by $(LREF DateTime). + +/ + @property static DateTime min() @safe pure nothrow + out(result) + { + assert(result._date == Date.min); + assert(result._tod == TimeOfDay.min); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.min; + dt._date._month = Month.jan; + dt._date._day = 1; + + return dt; + } + + @safe unittest + { + assert(DateTime.min.year < 0); + assert(DateTime.min < DateTime.max); + } + + + /++ + Returns the $(LREF DateTime) farthest in the future which is + representable by $(LREF DateTime). + +/ + @property static DateTime max() @safe pure nothrow + out(result) + { + assert(result._date == Date.max); + assert(result._tod == TimeOfDay.max); + } + body + { + auto dt = DateTime.init; + dt._date._year = short.max; + dt._date._month = Month.dec; + dt._date._day = 31; + dt._tod._hour = TimeOfDay.maxHour; + dt._tod._minute = TimeOfDay.maxMinute; + dt._tod._second = TimeOfDay.maxSecond; + + return dt; + } + + @safe unittest + { + assert(DateTime.max.year > 0); + assert(DateTime.max > DateTime.min); + } + + +private: + + /+ + Add seconds to the time of day. Negative values will subtract. If the + number of seconds overflows (or underflows), then the seconds will wrap, + increasing (or decreasing) the number of minutes accordingly. The + same goes for any larger units. + + Params: + seconds = The number of seconds to add to this $(LREF DateTime). + +/ + ref DateTime _addSeconds(long seconds) return @safe pure nothrow + { + long hnsecs = convert!("seconds", "hnsecs")(seconds); + hnsecs += convert!("hours", "hnsecs")(_tod._hour); + hnsecs += convert!("minutes", "hnsecs")(_tod._minute); + hnsecs += convert!("seconds", "hnsecs")(_tod._second); + + auto days = splitUnitsFromHNSecs!"days"(hnsecs); + + if (hnsecs < 0) + { + hnsecs += convert!("days", "hnsecs")(1); + --days; + } + + _date._addDays(days); + + immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); + immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); + + _tod._hour = cast(ubyte) newHours; + _tod._minute = cast(ubyte) newMinutes; + _tod._second = cast(ubyte) newSeconds; + + return this; + } + + @safe unittest + { + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + // Test A.D. + testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); + + testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); + + // Test B.C. + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); + + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); + + // Test Both + testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); + testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); + + testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); + testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); + testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); + testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt._addSeconds(4))); + static assert(!__traits(compiles, idt._addSeconds(4))); + } + + + Date _date; + TimeOfDay _tod; +} + + /++ Represents a date in the $(HTTP en.wikipedia.org/wiki/Proleptic_Gregorian_calendar, Proleptic @@ -5863,9 +9350,28 @@ version(unittest) auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; + auto testTODs = [TimeOfDay(0, 0, 0), + TimeOfDay(0, 0, 1), + TimeOfDay(0, 1, 0), + TimeOfDay(1, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + + auto testHours = [0, 1, 12, 22, 23]; + auto testMinSecs = [0, 1, 30, 58, 59]; + + // Throwing exceptions is incredibly expensive, so we want to use a smaller + // set of values for tests using assertThrown. + auto testTODsThrown = [TimeOfDay(0, 0, 0), + TimeOfDay(13, 13, 13), + TimeOfDay(23, 59, 59)]; + Date[] testDatesBC; Date[] testDatesAD; + DateTime[] testDateTimesBC; + DateTime[] testDateTimesAD; + // I'd use a Tuple, but I get forward reference errors if I try. struct GregDay { int day; Date date; } auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar @@ -6094,5 +9600,17 @@ version(unittest) foreach (md; testMonthDays) testDatesAD ~= Date(year, md.month, md.day); } + + foreach (dt; testDatesBC) + { + foreach (tod; testTODs) + testDateTimesBC ~= DateTime(dt, tod); + } + + foreach (dt; testDatesAD) + { + foreach (tod; testTODs) + testDateTimesAD ~= DateTime(dt, tod); + } } } diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d index 5499c62ecd9..57a13bc616b 100644 --- a/std/datetime/datetime.d +++ b/std/datetime/datetime.d @@ -9,3849 +9,4 @@ +/ module std.datetime.datetime; -import core.time; -import std.datetime.common; -import std.datetime.date; -import std.datetime.timeofday; -import std.traits : isSomeString, Unqual; - -version(unittest) import std.exception : assertThrown; - - -@safe unittest -{ - initializeTests(); -} - - -/++ - Combines the $(REF std,datetime,date,Date) and - $(REF std,datetime,timeofday,TimeOfDay) structs to give an object which holds - both the date and the time. It is optimized for calendar-based operations and - has no concept of time zone. For an object which is optimized for time - operations based on the system time, use $(REF std,datetime,systime,SysTime). - $(REF std,datetime,systime,SysTime) has a concept of time zone and has much - higher precision (hnsecs). $(D DateTime) is intended primarily for - calendar-based uses rather than precise time operations. - +/ -struct DateTime -{ -public: - - /++ - Params: - date = The date portion of $(LREF DateTime). - tod = The time portion of $(LREF DateTime). - +/ - this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow - { - _date = date; - _tod = tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Params: - year = The year portion of the date. - month = The month portion of the date. - day = The day portion of the date. - hour = The hour portion of the time; - minute = The minute portion of the time; - second = The second portion of the time; - +/ - this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure - { - _date = Date(year, month, day); - _tod = TimeOfDay(hour, minute, second); - } - - @safe unittest - { - { - auto dt = DateTime(1999, 7 ,6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - } - - { - auto dt = DateTime(1999, 7 ,6, 12, 30, 33); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay(12, 30, 33)); - } - } - - - /++ - Compares this $(LREF DateTime) with the given $(D DateTime.). - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ - int opCmp(in DateTime rhs) @safe const pure nothrow - { - immutable dateResult = _date.opCmp(rhs._date); - - if (dateResult != 0) - return dateResult; - - return _tod.opCmp(rhs._tod); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); - - assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); - assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); - assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); - - assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); - - assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); - - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); - - assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); - assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); - assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); - - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); - - // Test B.C. - assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); - - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - // Test Both - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); - - assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); - assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - assert(dt.opCmp(dt) == 0); - assert(dt.opCmp(cdt) == 0); - assert(dt.opCmp(idt) == 0); - assert(cdt.opCmp(dt) == 0); - assert(cdt.opCmp(cdt) == 0); - assert(cdt.opCmp(idt) == 0); - assert(idt.opCmp(dt) == 0); - assert(idt.opCmp(cdt) == 0); - assert(idt.opCmp(idt) == 0); - } - - - /++ - The date portion of $(LREF DateTime). - +/ - @property Date date() @safe const pure nothrow - { - return _date; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.date == Date.init); - } - - { - auto dt = DateTime(Date(1999, 7, 6)); - assert(dt.date == Date(1999, 7, 6)); - } - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - assert(cdt.date == Date(1999, 7, 6)); - assert(idt.date == Date(1999, 7, 6)); - } - - - /++ - The date portion of $(LREF DateTime). - - Params: - date = The Date to set this $(LREF DateTime)'s date portion to. - +/ - @property void date(in Date date) @safe pure nothrow - { - _date = date; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.date = Date(1999, 7, 6); - assert(dt._date == Date(1999, 7, 6)); - assert(dt._tod == TimeOfDay.init); - - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); - static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); - } - - - /++ - The time portion of $(LREF DateTime). - +/ - @property TimeOfDay timeOfDay() @safe const pure nothrow - { - return _tod; - } - - @safe unittest - { - { - auto dt = DateTime.init; - assert(dt.timeOfDay == TimeOfDay.init); - } - - { - auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); - assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.timeOfDay == TimeOfDay(12, 30, 33)); - assert(idt.timeOfDay == TimeOfDay(12, 30, 33)); - } - - - /++ - The time portion of $(LREF DateTime). - - Params: - tod = The $(REF std,datetime,timeofday,TimeOfDay) to set this - $(LREF DateTime)'s time portion to. - +/ - @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow - { - _tod = tod; - } - - @safe unittest - { - auto dt = DateTime.init; - dt.timeOfDay = TimeOfDay(12, 30, 33); - assert(dt._date == Date.init); - assert(dt._tod == TimeOfDay(12, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); - static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - +/ - @property short year() @safe const pure nothrow - { - return _date.year; - } - - @safe unittest - { - assert(Date.init.year == 1); - assert(Date(1999, 7, 6).year == 1999); - assert(Date(-1999, 7, 6).year == -1999); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(idt.year == 1999); - assert(idt.year == 1999); - } - - - /++ - Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive - are B.C. - - Params: - year = The year to set this $(LREF DateTime)'s year to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the new year is not - a leap year and if the resulting date would be on February 29th. - +/ - @property void year(int year) @safe pure - { - _date.year = year; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); - } - - @safe unittest - { - static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) - { - dt.year = year; - assert(dt == expected); - } - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 1999, - DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - 0, - DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - -1999, - DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.year = 7)); - static assert(!__traits(compiles, idt.year = 7)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Throws: - $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. - +/ - @property short yearBC() @safe const pure - { - return _date.yearBC; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); - assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); - assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); - } - - @safe unittest - { - assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. - - Params: - year = The year B.C. to set this $(LREF DateTime)'s year to. - - Throws: - $(REF std,datetime,common,DateTimeException) if a non-positive value - is given. - +/ - @property void yearBC(int year) @safe pure - { - _date.yearBC = year; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); - dt.yearBC = 1; - assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - - dt.yearBC = 10; - assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); - } - - @safe unittest - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); - - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - dt.yearBC = 12; - assert(dt.yearBC == 12); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } - - - /++ - Month of a Gregorian Year. - +/ - @property Month month() @safe const pure nothrow - { - return _date.month; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); - } - - @safe unittest - { - assert(DateTime.init.month == 1); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.month == 7); - assert(idt.month == 7); - } - - - /++ - Month of a Gregorian Year. - - Params: - month = The month to set this $(LREF DateTime)'s month to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given month is - not a valid month. - +/ - @property void month(Month month) @safe pure - { - _date.month = month; - } - - @safe unittest - { - static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) - { - dt.month = month; - assert(expected != DateTime.init); - assert(dt == expected); - } - - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month) 13)); - - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), - cast(Month) 7, - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.month = 12)); - static assert(!__traits(compiles, idt.month = 12)); - } - - - /++ - Day of a Gregorian Month. - +/ - @property ubyte day() @safe const pure nothrow - { - return _date.day; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); - } - - @safe unittest - { - import std.format : format; - import std.range : chain; - - static void test(DateTime dateTime, int expected) - { - assert(dateTime.day == expected, format("Value given: %s", dateTime)); - } - - foreach (year; chain(testYearsBC, testYearsAD)) - { - foreach (md; testMonthDays) - { - foreach (tod; testTODs) - test(DateTime(Date(year, md.month, md.day), tod), md.day); - } - } - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.day == 6); - assert(idt.day == 6); - } - - - /++ - Day of a Gregorian Month. - - Params: - day = The day of the month to set this $(LREF DateTime)'s day to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given day is not - a valid day of the current month. - +/ - @property void day(int day) @safe pure - { - _date.day = day; - } - - @safe unittest - { - import std.exception : assertNotThrown; - - static void testDT(DateTime dt, int day) - { - dt.day = day; - } - - // Test A.D. - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); - - { - auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); - } - - // Test B.C. - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); - - auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.day = 27)); - static assert(!__traits(compiles, idt.day = 27)); - } - - - /++ - Hours past midnight. - +/ - @property ubyte hour() @safe const pure nothrow - { - return _tod.hour; - } - - @safe unittest - { - assert(DateTime.init.hour == 0); - assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.hour == 12); - assert(idt.hour == 12); - } - - - /++ - Hours past midnight. - - Params: - hour = The hour of the day to set this $(LREF DateTime)'s hour to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given hour would - result in an invalid $(LREF DateTime). - +/ - @property void hour(int hour) @safe pure - { - _tod.hour = hour; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); - - auto dt = DateTime.init; - dt.hour = 12; - assert(dt == DateTime(1, 1, 1, 12, 0, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.hour = 27)); - static assert(!__traits(compiles, idt.hour = 27)); - } - - - /++ - Minutes past the hour. - +/ - @property ubyte minute() @safe const pure nothrow - { - return _tod.minute; - } - - @safe unittest - { - assert(DateTime.init.minute == 0); - assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.minute == 30); - assert(idt.minute == 30); - } - - - /++ - Minutes past the hour. - - Params: - minute = The minute to set this $(LREF DateTime)'s minute to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given minute - would result in an invalid $(LREF DateTime). - +/ - @property void minute(int minute) @safe pure - { - _tod.minute = minute; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); - - auto dt = DateTime.init; - dt.minute = 30; - assert(dt == DateTime(1, 1, 1, 0, 30, 0)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.minute = 27)); - static assert(!__traits(compiles, idt.minute = 27)); - } - - - /++ - Seconds past the minute. - +/ - @property ubyte second() @safe const pure nothrow - { - return _tod.second; - } - - @safe unittest - { - assert(DateTime.init.second == 0); - assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.second == 33); - assert(idt.second == 33); - } - - - /++ - Seconds past the minute. - - Params: - second = The second to set this $(LREF DateTime)'s second to. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given seconds - would result in an invalid $(LREF DateTime). - +/ - @property void second(int second) @safe pure - { - _tod.second = second; - } - - @safe unittest - { - assertThrown!DateTimeException((){DateTime.init.second = 60;}()); - - auto dt = DateTime.init; - dt.second = 33; - assert(dt == DateTime(1, 1, 1, 0, 0, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.second = 27)); - static assert(!__traits(compiles, idt.second = 27)); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - Note that if day overflow is allowed, and the date with the adjusted - year/month overflows the number of days in the new month, then the month - will be incremented by one, and the day set to the number of days - overflowed. (e.g. if the day were 31 and the new month were June, then - the month would be incremented to July, and the new day would be 1). If - day overflow is not allowed, then the day will be set to the last valid - day in the month (e.g. June 31st would become June 30th). - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime add(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if (units == "years" || units == "months") - { - _date.add!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - import std.datetime.common : AllowDayOverflow; - - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); - dt1.add!"months"(11); - assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); - dt2.add!"months"(-11); - assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - - auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); - dt3.add!"years"(1); - assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); - dt4.add!"years"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.add!"years"(7).add!"months"(-4); - assert(dt == DateTime(2006, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.add!"years"(4))); - static assert(!__traits(compiles, idt.add!"years"(4))); - static assert(!__traits(compiles, cdt.add!"months"(4))); - static assert(!__traits(compiles, idt.add!"months"(4))); - } - - - /++ - Adds the given number of years or months to this $(LREF DateTime). A - negative number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. Rolling a $(LREF DateTime) 12 months - gets the exact same $(LREF DateTime). However, the days can still be - affected due to the differing number of days in each month. - - Because there are no units larger than years, there is no difference - between adding and rolling years. - - Params: - units = The type of units to add ("years" or "months"). - value = The number of months or years to add to this - $(LREF DateTime). - allowOverflow = Whether the days should be allowed to overflow, - causing the month to increment. - +/ - ref DateTime roll(string units) - (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow - if (units == "years" || units == "months") - { - _date.roll!units(value, allowOverflow); - return this; - } - - /// - @safe unittest - { - import std.datetime.common : AllowDayOverflow; - - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); - dt1.roll!"months"(1); - assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - - auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); - dt2.roll!"months"(-1); - assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - - auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); - dt3.roll!"months"(1); - assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - - auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); - dt4.roll!"months"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - - auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); - dt5.roll!"years"(1); - assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - - auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); - dt6.roll!"years"(1, AllowDayOverflow.no); - assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"years"(7).roll!"months"(-4); - assert(dt == DateTime(2007, 10, 1)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"years"(4))); - static assert(!__traits(compiles, idt.roll!"years"(4))); - static assert(!__traits(compiles, cdt.roll!"months"(4))); - static assert(!__traits(compiles, idt.roll!"months"(4))); - } - - - /++ - Adds the given number of units to this $(LREF DateTime). A negative - number will subtract. - - The difference between rolling and adding is that rolling does not - affect larger units. For instance, rolling a $(LREF DateTime) one - year's worth of days gets the exact same $(LREF DateTime). - - Accepted units are $(D "days"), $(D "minutes"), $(D "hours"), - $(D "minutes"), and $(D "seconds"). - - Params: - units = The units to add. - value = The number of $(D_PARAM units) to add to this - $(LREF DateTime). - +/ - ref DateTime roll(string units)(long value) @safe pure nothrow - if (units == "days") - { - _date.roll!"days"(value); - return this; - } - - /// - @safe unittest - { - auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); - dt1.roll!"days"(1); - assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); - dt1.roll!"days"(365); - assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); - dt1.roll!"days"(-32); - assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - - auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); - dt2.roll!"hours"(1); - assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - - auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); - dt3.roll!"seconds"(-1); - assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); - } - - @safe unittest - { - auto dt = DateTime(2000, 1, 31); - dt.roll!"days"(7).roll!"days"(-4); - assert(dt == DateTime(2000, 1, 3)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } - - - // Shares documentation with "days" version. - ref DateTime roll(string units)(long value) @safe pure nothrow - if (units == "hours" || - units == "minutes" || - units == "seconds") - { - _tod.roll!units(value); - return this; - } - - // Test roll!"hours"(). - @safe unittest - { - static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, - DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, - DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, - DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, - DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, - DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, - DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, - DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, - DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, - DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, - DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, - DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, - DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, - DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, - DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, - DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, - DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); - - // Test Both - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, - DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, - DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"hours"(27).roll!"hours"(-9); - assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"hours"(4))); - static assert(!__traits(compiles, idt.roll!"hours"(4))); - } - - // Test roll!"minutes"(). - @safe unittest - { - static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); - - // Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, - DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, - DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"minutes"(92).roll!"minutes"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"minutes"(4))); - static assert(!__traits(compiles, idt.roll!"minutes"(4))); - } - - // Test roll!"seconds"(). - @safe unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); - - // Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, - DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); - - // Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, - DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, - DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, - DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, - DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - dt.roll!"seconds"(92).roll!"seconds"(-292); - assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"seconds"(4))); - static assert(!__traits(compiles, idt.roll!"seconds"(4))); - } - - - /++ - Gives the result of adding or subtracting a $(REF Duration, core,time) - from this $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD Duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD Duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The $(REF Duration, core,time) to add to or subtract from - this $(LREF DateTime). - +/ - DateTime opBinary(string op)(Duration duration) @safe const pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = duration.total!"seconds"; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - /// - @safe unittest - { - import core.time : hours, seconds; - - assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) == - DateTime(2016, 1, 1, 0, 0, 0)); - - assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) == - DateTime(2016, 1, 1, 0, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - - assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) == - DateTime(2015, 12, 31, 23, 59, 59)); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(idt + duration == DateTime(1999, 7, 6, 12, 30, 45)); - assert(cdt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - assert(idt - duration == DateTime(1999, 7, 6, 12, 30, 21)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - DateTime opBinary(string op)(in TickDuration td) @safe const pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = td.seconds; - mixin("return retval._addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - // This probably only runs in cases where gettimeofday() is used, but it's - // hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - - assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - } - - - /++ - Gives the result of adding or subtracting a duration from this - $(LREF DateTime), as well as assigning the result to this - $(LREF DateTime). - - The legal types of arithmetic for $(LREF DateTime) using this operator - are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD +) $(TD duration) $(TD -->) $(TD DateTime)) - $(TR $(TD DateTime) $(TD -) $(TD duration) $(TD -->) $(TD DateTime)) - ) - - Params: - duration = The duration to add to or subtract from this - $(LREF DateTime). - +/ - ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow - if ((op == "+" || op == "-") && - (is(Unqual!D == Duration) || - is(Unqual!D == TickDuration))) - { - import std.format : format; - - DateTime retval = this; - - static if (is(Unqual!D == Duration)) - immutable hnsecs = duration.total!"hnsecs"; - else static if (is(Unqual!D == TickDuration)) - immutable hnsecs = duration.hnsecs; - - mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); - } - - @safe unittest - { - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == - DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == - DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == - DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == - DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == - DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == - DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == - DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == - DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto dt = DateTime(2000, 1, 31, 9, 7, 6); - (dt += dur!"seconds"(92)) -= dur!"days"(-500); - assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt += duration)); - static assert(!__traits(compiles, idt += duration)); - static assert(!__traits(compiles, cdt -= duration)); - static assert(!__traits(compiles, idt -= duration)); - } - - // Explicitly undocumented. It will be removed in January 2018. @@@DEPRECATED_2018-01@@@ - deprecated("Use Duration instead of TickDuration.") - ref DateTime opOpAssign(string op)(TickDuration td) @safe pure nothrow - if (op == "+" || op == "-") - { - DateTime retval = this; - immutable seconds = td.seconds; - mixin("return _addSeconds(" ~ op ~ "seconds);"); - } - - deprecated @safe unittest - { - // This probably only runs in cases where gettimeofday() is used, but it's - // hard to do this test correctly with variable ticksPerSec. - if (TickDuration.ticksPerSec == 1_000_000) - { - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt += TickDuration.from!"usecs"(7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt += TickDuration.from!"usecs"(-7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt -= TickDuration.from!"usecs"(-7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - } - - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt -= TickDuration.from!"usecs"(7_000_000); - assert(dt == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } - } - } - - - /++ - Gives the difference between two $(LREF DateTime)s. - - The legal types of arithmetic for $(LREF DateTime) using this operator are - - $(BOOKTABLE, - $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow - if (op == "-") - { - immutable dateResult = _date - rhs.date; - immutable todResult = _tod - rhs._tod; - - return dur!"hnsecs"(dateResult.total!"hnsecs" + todResult.total!"hnsecs"); - } - - @safe unittest - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(31_536_000)); - assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-31_536_000)); - - assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(26_78_400)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-26_78_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == - dur!"seconds"(86_400)); - assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-86_400)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == - dur!"seconds"(3600)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(-3600)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(60)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == - dur!"seconds"(-60)); - - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == - dur!"seconds"(1)); - assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == - dur!"seconds"(-1)); - - assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); - assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); - assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt - dt == Duration.zero); - assert(cdt - dt == Duration.zero); - assert(idt - dt == Duration.zero); - - assert(dt - cdt == Duration.zero); - assert(cdt - cdt == Duration.zero); - assert(idt - cdt == Duration.zero); - - assert(dt - idt == Duration.zero); - assert(cdt - idt == Duration.zero); - assert(idt - idt == Duration.zero); - } - - - /++ - Returns the difference between the two $(LREF DateTime)s in months. - - To get the difference in years, subtract the year property - of two $(LREF DateTime)s. To get the difference in days or weeks, - subtract the $(LREF DateTime)s themselves and use the - $(REF Duration, core,time) that results. Because converting between - months and smaller units requires a specific date (which - $(REF Duration, core,time)s don't have), getting the difference in - months requires some math using both the year and month properties, so - this is a convenience function for getting the difference in months. - - Note that the number of days in the months or how far into the month - either date is is irrelevant. It is the difference in the month property - combined with the difference in years * 12. So, for instance, - December 31st and January 1st are one month apart just as December 1st - and January 31st are one month apart. - - Params: - rhs = The $(LREF DateTime) to subtract from this one. - +/ - int diffMonths(in DateTime rhs) @safe const pure nothrow - { - return _date.diffMonths(rhs._date); - } - - /// - @safe unittest - { - assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( - DateTime(1999, 1, 31, 23, 59, 59)) == 1); - - assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( - DateTime(1999, 2, 1, 12, 3, 42)) == -1); - - assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( - DateTime(1999, 1, 1, 2, 4, 7)) == 2); - - assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( - DateTime(1999, 3, 31, 0, 30, 58)) == -2); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.diffMonths(dt) == 0); - assert(cdt.diffMonths(dt) == 0); - assert(idt.diffMonths(dt) == 0); - - assert(dt.diffMonths(cdt) == 0); - assert(cdt.diffMonths(cdt) == 0); - assert(idt.diffMonths(cdt) == 0); - - assert(dt.diffMonths(idt) == 0); - assert(cdt.diffMonths(idt) == 0); - assert(idt.diffMonths(idt) == 0); - } - - - /++ - Whether this $(LREF DateTime) is in a leap year. - +/ - @property bool isLeapYear() @safe const pure nothrow - { - return _date.isLeapYear; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(!dt.isLeapYear); - assert(!cdt.isLeapYear); - assert(!idt.isLeapYear); - } - - - /++ - Day of the week this $(LREF DateTime) is on. - +/ - @property DayOfWeek dayOfWeek() @safe const pure nothrow - { - return _date.dayOfWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfWeek == DayOfWeek.tue); - assert(cdt.dayOfWeek == DayOfWeek.tue); - assert(idt.dayOfWeek == DayOfWeek.tue); - } - - - /++ - Day of the year this $(LREF DateTime) is on. - +/ - @property ushort dayOfYear() @safe const pure nothrow - { - return _date.dayOfYear; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); - assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); - assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.dayOfYear == 187); - assert(cdt.dayOfYear == 187); - assert(idt.dayOfYear == 187); - } - - - /++ - Day of the year. - - Params: - day = The day of the year to set which day of the year this - $(LREF DateTime) is on. - +/ - @property void dayOfYear(int day) @safe pure - { - _date.dayOfYear = day; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - dt.dayOfYear = 12; - assert(dt.dayOfYear == 12); - static assert(!__traits(compiles, cdt.dayOfYear = 12)); - static assert(!__traits(compiles, idt.dayOfYear = 12)); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - +/ - @property int dayOfGregorianCal() @safe const pure nothrow - { - return _date.dayOfGregorianCal; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); - assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); - assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); - assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); - assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); - - assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.dayOfGregorianCal == 729_941); - assert(idt.dayOfGregorianCal == 729_941); - } - - - /++ - The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - Setting this property does not affect the time portion of - $(LREF DateTime). - - Params: - days = The day of the Gregorian Calendar to set this $(LREF DateTime) - to. - +/ - @property void dayOfGregorianCal(int days) @safe pure nothrow - { - _date.dayOfGregorianCal = days; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); - dt.dayOfGregorianCal = 1; - assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 365; - assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 366; - assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 0; - assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -365; - assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = -366; - assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 730_120; - assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - - dt.dayOfGregorianCal = 734_137; - assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); - } - - - /++ - The ISO 8601 week of the year that this $(LREF DateTime) is in. - - See_Also: - $(HTTP en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) - +/ - @property ubyte isoWeek() @safe const pure nothrow - { - return _date.isoWeek; - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.isoWeek == 27); - assert(cdt.isoWeek == 27); - assert(idt.isoWeek == 27); - } - - - /++ - $(LREF DateTime) for the last day in the month that this - $(LREF DateTime) is in. The time portion of endOfMonth is always - 23:59:59. - +/ - @property DateTime endOfMonth() @safe const pure nothrow - { - try - return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); - catch (Exception e) - assert(0, "DateTime constructor threw."); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == - DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == - DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == - DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == - DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); - assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); - assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); - assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); - assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); - assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); - assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); - assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); - assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); - assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); - assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); - assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); - - // Test B.C. - assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); - assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); - assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); - assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); - assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); - assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); - assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); - assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); - assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); - assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); - assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); - assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); - assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - assert(idt.endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); - } - - - /++ - The last day in the month that this $(LREF DateTime) is in. - +/ - @property ubyte daysInMonth() @safe const pure nothrow - { - return _date.daysInMonth; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.daysInMonth == 31); - assert(idt.daysInMonth == 31); - } - - - /++ - Whether the current year is a date in A.D. - +/ - @property bool isAD() @safe const pure nothrow - { - return _date.isAD; - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); - assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); - assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); - } - - @safe unittest - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.isAD); - assert(idt.isAD); - } - - - /++ - The $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for this - $(LREF DateTime) at the given time. For example, prior to noon, - 1996-03-31 would be the Julian day number 2_450_173, so this function - returns 2_450_173, while from noon onward, the julian day number would - be 2_450_174, so this function returns 2_450_174. - +/ - @property long julianDay() @safe const pure nothrow - { - if (_tod._hour < 12) - return _date.julianDay - 1; - else - return _date.julianDay; - } - - @safe unittest - { - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); - assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); - - assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); - assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); - - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); - - assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); - assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); - - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); - - assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); - assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); - - assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); - assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.julianDay == 2_451_366); - assert(idt.julianDay == 2_451_366); - } - - - /++ - The modified $(HTTP en.wikipedia.org/wiki/Julian_day, Julian day) for any - time on this date (since, the modified Julian day changes at midnight). - +/ - @property long modJulianDay() @safe const pure nothrow - { - return _date.modJulianDay; - } - - @safe unittest - { - assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); - assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); - - assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); - assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(cdt.modJulianDay == 51_365); - assert(idt.modJulianDay == 51_365); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. - +/ - string toISOString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOString(), _tod.toISOString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == - "20100704T070612"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == - "19981225T021500"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == - "00000105T230959"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == - "-00040105T000002"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOString() == "19990706T123033"); - assert(idt.toISOString() == "19990706T123033"); - } - - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-MM-DDTHH:MM:SS. - +/ - string toISOExtString() @safe const pure nothrow - { - import std.format : format; - try - return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == - "1998-12-25T02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == - "-0004-01-05T00:00:02"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toISOExtString() == "1999-07-06T12:30:33"); - assert(idt.toISOExtString() == "1999-07-06T12:30:33"); - } - - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-Mon-DD HH:MM:SS. - +/ - string toSimpleString() @safe const pure nothrow - { - import std.format : format; - try - return format("%s %s", _date.toSimpleString(), _tod.toString()); - catch (Exception e) - assert(0, "format() threw."); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == - "1998-Dec-25 02:15:00"); - - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == - "-0004-Jan-05 00:00:02"); - } - - @safe unittest - { - // Test A.D. - assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); - assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); - assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); - assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); - assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - - // Test B.C. - assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); - assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); - assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); - assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); - assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); - assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - assert(cdt.toSimpleString() == "1999-Jul-06 12:30:33"); - assert(idt.toSimpleString() == "1999-Jul-06 12:30:33"); - } - - - /++ - Converts this $(LREF DateTime) to a string. - +/ - string toString() @safe const pure nothrow - { - return toSimpleString(); - } - - @safe unittest - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - assert(dt.toString()); - assert(cdt.toString()); - assert(idt.toString()); - } - - - - /++ - Creates a $(LREF DateTime) from a string with the format YYYYMMDDTHHMMSS. - Whitespace is stripped from the given string. - - Params: - isoString = A string formatted in the ISO format for dates and times. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given string is - not in the ISO format or if the resulting $(LREF DateTime) would not - be valid. - +/ - static DateTime fromISOString(S)(in S isoString) @safe pure - if (isSomeString!S) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(isoString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString))); - - immutable date = Date.fromISOString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime.fromISOString("20100704T070612") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOString("19981225T021500") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOString("00000105T230959") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOString("-00040105T000002") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOString(" 20100704T070612 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-MM-DDTHH:MM:SS. Whitespace is stripped from the given string. - - Params: - isoExtString = A string formatted in the ISO Extended format for dates - and times. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given string is - not in the ISO Extended format or if the resulting $(LREF DateTime) - would not be valid. - +/ - static DateTime fromISOExtString(S)(in S isoExtString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(isoExtString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - auto t = dstr.countUntil('T'); - - enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - - immutable date = Date.fromISOExtString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - - assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - - assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - - assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - - assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOExtString("")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); - - assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Creates a $(LREF DateTime) from a string with the format - YYYY-Mon-DD HH:MM:SS. Whitespace is stripped from the given string. - - Params: - simpleString = A string formatted in the way that toSimpleString - formats dates and times. - - Throws: - $(REF std,datetime,common,DateTimeException) if the given string is - not in the correct format or if the resulting $(LREF DateTime) - would not be valid. - +/ - static DateTime fromSimpleString(S)(in S simpleString) @safe pure - if (isSomeString!(S)) - { - import std.algorithm.searching : countUntil; - import std.conv : to; - import std.exception : enforce; - import std.format : format; - import std.string : strip; - - immutable dstr = to!dstring(strip(simpleString)); - - enforce(dstr.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString))); - auto t = dstr.countUntil(' '); - - enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString))); - - immutable date = Date.fromSimpleString(dstr[0 .. t]); - immutable tod = TimeOfDay.fromISOExtString(dstr[t+1 .. $]); - - return DateTime(date, tod); - } - - /// - @safe unittest - { - import std.datetime.date : Date; - import std.datetime.timeofday : TimeOfDay; - - assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } - - @safe unittest - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); - - assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == - DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - } - - - /++ - Returns the $(LREF DateTime) farthest in the past which is representable - by $(LREF DateTime). - +/ - @property static DateTime min() @safe pure nothrow - out(result) - { - assert(result._date == Date.min); - assert(result._tod == TimeOfDay.min); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.min; - dt._date._month = Month.jan; - dt._date._day = 1; - - return dt; - } - - @safe unittest - { - assert(DateTime.min.year < 0); - assert(DateTime.min < DateTime.max); - } - - - /++ - Returns the $(LREF DateTime) farthest in the future which is - representable by $(LREF DateTime). - +/ - @property static DateTime max() @safe pure nothrow - out(result) - { - assert(result._date == Date.max); - assert(result._tod == TimeOfDay.max); - } - body - { - auto dt = DateTime.init; - dt._date._year = short.max; - dt._date._month = Month.dec; - dt._date._day = 31; - dt._tod._hour = TimeOfDay.maxHour; - dt._tod._minute = TimeOfDay.maxMinute; - dt._tod._second = TimeOfDay.maxSecond; - - return dt; - } - - @safe unittest - { - assert(DateTime.max.year > 0); - assert(DateTime.max > DateTime.min); - } - - -private: - - /+ - Add seconds to the time of day. Negative values will subtract. If the - number of seconds overflows (or underflows), then the seconds will wrap, - increasing (or decreasing) the number of minutes accordingly. The - same goes for any larger units. - - Params: - seconds = The number of seconds to add to this $(LREF DateTime). - +/ - ref DateTime _addSeconds(long seconds) return @safe pure nothrow - { - long hnsecs = convert!("seconds", "hnsecs")(seconds); - hnsecs += convert!("hours", "hnsecs")(_tod._hour); - hnsecs += convert!("minutes", "hnsecs")(_tod._minute); - hnsecs += convert!("seconds", "hnsecs")(_tod._second); - - auto days = splitUnitsFromHNSecs!"days"(hnsecs); - - if (hnsecs < 0) - { - hnsecs += convert!("days", "hnsecs")(1); - --days; - } - - _date._addDays(days); - - immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); - immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - immutable newSeconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - - _tod._hour = cast(ubyte) newHours; - _tod._minute = cast(ubyte) newMinutes; - _tod._second = cast(ubyte) newSeconds; - - return this; - } - - @safe unittest - { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig._addSeconds(seconds); - assert(orig == expected); - } - - // Test A.D. - testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); - - testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); - - // Test B.C. - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); - - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); - - // Test Both - testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); - testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); - - testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); - testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); - testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); - testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt._addSeconds(4))); - static assert(!__traits(compiles, idt._addSeconds(4))); - } - - - Date _date; - TimeOfDay _tod; -} - - -version(unittest) -{ - // All of these helper arrays are sorted in ascending order. - auto testYearsBC = [-1999, -1200, -600, -4, -1, 0]; - auto testYearsAD = [1, 4, 1000, 1999, 2000, 2012]; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct MonthDay - { - Month month; - short day; - - this(int m, short d) - { - month = cast(Month) m; - day = d; - } - } - - MonthDay[] testMonthDays = [MonthDay(1, 1), - MonthDay(1, 2), - MonthDay(3, 17), - MonthDay(7, 4), - MonthDay(10, 27), - MonthDay(12, 30), - MonthDay(12, 31)]; - - auto testDays = [1, 2, 9, 10, 16, 20, 25, 28, 29, 30, 31]; - - auto testTODs = [TimeOfDay(0, 0, 0), - TimeOfDay(0, 0, 1), - TimeOfDay(0, 1, 0), - TimeOfDay(1, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - auto testHours = [0, 1, 12, 22, 23]; - auto testMinSecs = [0, 1, 30, 58, 59]; - - // Throwing exceptions is incredibly expensive, so we want to use a smaller - // set of values for tests using assertThrown. - auto testTODsThrown = [TimeOfDay(0, 0, 0), - TimeOfDay(13, 13, 13), - TimeOfDay(23, 59, 59)]; - - Date[] testDatesBC; - Date[] testDatesAD; - - DateTime[] testDateTimesBC; - DateTime[] testDateTimesAD; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct GregDay { int day; Date date; } - auto testGregDaysBC = [GregDay(-1_373_427, Date(-3760, 9, 7)), // Start of the Hebrew Calendar - GregDay(-735_233, Date(-2012, 1, 1)), - GregDay(-735_202, Date(-2012, 2, 1)), - GregDay(-735_175, Date(-2012, 2, 28)), - GregDay(-735_174, Date(-2012, 2, 29)), - GregDay(-735_173, Date(-2012, 3, 1)), - GregDay(-734_502, Date(-2010, 1, 1)), - GregDay(-734_472, Date(-2010, 1, 31)), - GregDay(-734_471, Date(-2010, 2, 1)), - GregDay(-734_444, Date(-2010, 2, 28)), - GregDay(-734_443, Date(-2010, 3, 1)), - GregDay(-734_413, Date(-2010, 3, 31)), - GregDay(-734_412, Date(-2010, 4, 1)), - GregDay(-734_383, Date(-2010, 4, 30)), - GregDay(-734_382, Date(-2010, 5, 1)), - GregDay(-734_352, Date(-2010, 5, 31)), - GregDay(-734_351, Date(-2010, 6, 1)), - GregDay(-734_322, Date(-2010, 6, 30)), - GregDay(-734_321, Date(-2010, 7, 1)), - GregDay(-734_291, Date(-2010, 7, 31)), - GregDay(-734_290, Date(-2010, 8, 1)), - GregDay(-734_260, Date(-2010, 8, 31)), - GregDay(-734_259, Date(-2010, 9, 1)), - GregDay(-734_230, Date(-2010, 9, 30)), - GregDay(-734_229, Date(-2010, 10, 1)), - GregDay(-734_199, Date(-2010, 10, 31)), - GregDay(-734_198, Date(-2010, 11, 1)), - GregDay(-734_169, Date(-2010, 11, 30)), - GregDay(-734_168, Date(-2010, 12, 1)), - GregDay(-734_139, Date(-2010, 12, 30)), - GregDay(-734_138, Date(-2010, 12, 31)), - GregDay(-731_215, Date(-2001, 1, 1)), - GregDay(-730_850, Date(-2000, 1, 1)), - GregDay(-730_849, Date(-2000, 1, 2)), - GregDay(-730_486, Date(-2000, 12, 30)), - GregDay(-730_485, Date(-2000, 12, 31)), - GregDay(-730_484, Date(-1999, 1, 1)), - GregDay(-694_690, Date(-1901, 1, 1)), - GregDay(-694_325, Date(-1900, 1, 1)), - GregDay(-585_118, Date(-1601, 1, 1)), - GregDay(-584_753, Date(-1600, 1, 1)), - GregDay(-584_388, Date(-1600, 12, 31)), - GregDay(-584_387, Date(-1599, 1, 1)), - GregDay(-365_972, Date(-1001, 1, 1)), - GregDay(-365_607, Date(-1000, 1, 1)), - GregDay(-183_351, Date(-501, 1, 1)), - GregDay(-182_986, Date(-500, 1, 1)), - GregDay(-182_621, Date(-499, 1, 1)), - GregDay(-146_827, Date(-401, 1, 1)), - GregDay(-146_462, Date(-400, 1, 1)), - GregDay(-146_097, Date(-400, 12, 31)), - GregDay(-110_302, Date(-301, 1, 1)), - GregDay(-109_937, Date(-300, 1, 1)), - GregDay(-73_778, Date(-201, 1, 1)), - GregDay(-73_413, Date(-200, 1, 1)), - GregDay(-38_715, Date(-105, 1, 1)), - GregDay(-37_254, Date(-101, 1, 1)), - GregDay(-36_889, Date(-100, 1, 1)), - GregDay(-36_524, Date(-99, 1, 1)), - GregDay(-36_160, Date(-99, 12, 31)), - GregDay(-35_794, Date(-97, 1, 1)), - GregDay(-18_627, Date(-50, 1, 1)), - GregDay(-18_262, Date(-49, 1, 1)), - GregDay(-3652, Date(-9, 1, 1)), - GregDay(-2191, Date(-5, 1, 1)), - GregDay(-1827, Date(-5, 12, 31)), - GregDay(-1826, Date(-4, 1, 1)), - GregDay(-1825, Date(-4, 1, 2)), - GregDay(-1462, Date(-4, 12, 30)), - GregDay(-1461, Date(-4, 12, 31)), - GregDay(-1460, Date(-3, 1, 1)), - GregDay(-1096, Date(-3, 12, 31)), - GregDay(-1095, Date(-2, 1, 1)), - GregDay(-731, Date(-2, 12, 31)), - GregDay(-730, Date(-1, 1, 1)), - GregDay(-367, Date(-1, 12, 30)), - GregDay(-366, Date(-1, 12, 31)), - GregDay(-365, Date(0, 1, 1)), - GregDay(-31, Date(0, 11, 30)), - GregDay(-30, Date(0, 12, 1)), - GregDay(-1, Date(0, 12, 30)), - GregDay(0, Date(0, 12, 31))]; - - auto testGregDaysAD = [GregDay(1, Date(1, 1, 1)), - GregDay(2, Date(1, 1, 2)), - GregDay(32, Date(1, 2, 1)), - GregDay(365, Date(1, 12, 31)), - GregDay(366, Date(2, 1, 1)), - GregDay(731, Date(3, 1, 1)), - GregDay(1096, Date(4, 1, 1)), - GregDay(1097, Date(4, 1, 2)), - GregDay(1460, Date(4, 12, 30)), - GregDay(1461, Date(4, 12, 31)), - GregDay(1462, Date(5, 1, 1)), - GregDay(17_898, Date(50, 1, 1)), - GregDay(35_065, Date(97, 1, 1)), - GregDay(36_160, Date(100, 1, 1)), - GregDay(36_525, Date(101, 1, 1)), - GregDay(37_986, Date(105, 1, 1)), - GregDay(72_684, Date(200, 1, 1)), - GregDay(73_049, Date(201, 1, 1)), - GregDay(109_208, Date(300, 1, 1)), - GregDay(109_573, Date(301, 1, 1)), - GregDay(145_732, Date(400, 1, 1)), - GregDay(146_098, Date(401, 1, 1)), - GregDay(182_257, Date(500, 1, 1)), - GregDay(182_622, Date(501, 1, 1)), - GregDay(364_878, Date(1000, 1, 1)), - GregDay(365_243, Date(1001, 1, 1)), - GregDay(584_023, Date(1600, 1, 1)), - GregDay(584_389, Date(1601, 1, 1)), - GregDay(693_596, Date(1900, 1, 1)), - GregDay(693_961, Date(1901, 1, 1)), - GregDay(729_755, Date(1999, 1, 1)), - GregDay(730_120, Date(2000, 1, 1)), - GregDay(730_121, Date(2000, 1, 2)), - GregDay(730_484, Date(2000, 12, 30)), - GregDay(730_485, Date(2000, 12, 31)), - GregDay(730_486, Date(2001, 1, 1)), - GregDay(733_773, Date(2010, 1, 1)), - GregDay(733_774, Date(2010, 1, 2)), - GregDay(733_803, Date(2010, 1, 31)), - GregDay(733_804, Date(2010, 2, 1)), - GregDay(733_831, Date(2010, 2, 28)), - GregDay(733_832, Date(2010, 3, 1)), - GregDay(733_862, Date(2010, 3, 31)), - GregDay(733_863, Date(2010, 4, 1)), - GregDay(733_892, Date(2010, 4, 30)), - GregDay(733_893, Date(2010, 5, 1)), - GregDay(733_923, Date(2010, 5, 31)), - GregDay(733_924, Date(2010, 6, 1)), - GregDay(733_953, Date(2010, 6, 30)), - GregDay(733_954, Date(2010, 7, 1)), - GregDay(733_984, Date(2010, 7, 31)), - GregDay(733_985, Date(2010, 8, 1)), - GregDay(734_015, Date(2010, 8, 31)), - GregDay(734_016, Date(2010, 9, 1)), - GregDay(734_045, Date(2010, 9, 30)), - GregDay(734_046, Date(2010, 10, 1)), - GregDay(734_076, Date(2010, 10, 31)), - GregDay(734_077, Date(2010, 11, 1)), - GregDay(734_106, Date(2010, 11, 30)), - GregDay(734_107, Date(2010, 12, 1)), - GregDay(734_136, Date(2010, 12, 30)), - GregDay(734_137, Date(2010, 12, 31)), - GregDay(734_503, Date(2012, 1, 1)), - GregDay(734_534, Date(2012, 2, 1)), - GregDay(734_561, Date(2012, 2, 28)), - GregDay(734_562, Date(2012, 2, 29)), - GregDay(734_563, Date(2012, 3, 1)), - GregDay(734_858, Date(2012, 12, 21))]; - - // I'd use a Tuple, but I get forward reference errors if I try. - struct DayOfYear { int day; MonthDay md; } - auto testDaysOfYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(3, 1)), - DayOfYear(90, MonthDay(3, 31)), - DayOfYear(91, MonthDay(4, 1)), - DayOfYear(120, MonthDay(4, 30)), - DayOfYear(121, MonthDay(5, 1)), - DayOfYear(151, MonthDay(5, 31)), - DayOfYear(152, MonthDay(6, 1)), - DayOfYear(181, MonthDay(6, 30)), - DayOfYear(182, MonthDay(7, 1)), - DayOfYear(212, MonthDay(7, 31)), - DayOfYear(213, MonthDay(8, 1)), - DayOfYear(243, MonthDay(8, 31)), - DayOfYear(244, MonthDay(9, 1)), - DayOfYear(273, MonthDay(9, 30)), - DayOfYear(274, MonthDay(10, 1)), - DayOfYear(304, MonthDay(10, 31)), - DayOfYear(305, MonthDay(11, 1)), - DayOfYear(334, MonthDay(11, 30)), - DayOfYear(335, MonthDay(12, 1)), - DayOfYear(363, MonthDay(12, 29)), - DayOfYear(364, MonthDay(12, 30)), - DayOfYear(365, MonthDay(12, 31))]; - - auto testDaysOfLeapYear = [DayOfYear(1, MonthDay(1, 1)), - DayOfYear(2, MonthDay(1, 2)), - DayOfYear(3, MonthDay(1, 3)), - DayOfYear(31, MonthDay(1, 31)), - DayOfYear(32, MonthDay(2, 1)), - DayOfYear(59, MonthDay(2, 28)), - DayOfYear(60, MonthDay(2, 29)), - DayOfYear(61, MonthDay(3, 1)), - DayOfYear(91, MonthDay(3, 31)), - DayOfYear(92, MonthDay(4, 1)), - DayOfYear(121, MonthDay(4, 30)), - DayOfYear(122, MonthDay(5, 1)), - DayOfYear(152, MonthDay(5, 31)), - DayOfYear(153, MonthDay(6, 1)), - DayOfYear(182, MonthDay(6, 30)), - DayOfYear(183, MonthDay(7, 1)), - DayOfYear(213, MonthDay(7, 31)), - DayOfYear(214, MonthDay(8, 1)), - DayOfYear(244, MonthDay(8, 31)), - DayOfYear(245, MonthDay(9, 1)), - DayOfYear(274, MonthDay(9, 30)), - DayOfYear(275, MonthDay(10, 1)), - DayOfYear(305, MonthDay(10, 31)), - DayOfYear(306, MonthDay(11, 1)), - DayOfYear(335, MonthDay(11, 30)), - DayOfYear(336, MonthDay(12, 1)), - DayOfYear(364, MonthDay(12, 29)), - DayOfYear(365, MonthDay(12, 30)), - DayOfYear(366, MonthDay(12, 31))]; - - void initializeTests() @safe - { - foreach (year; testYearsBC) - { - foreach (md; testMonthDays) - testDatesBC ~= Date(year, md.month, md.day); - } - - foreach (year; testYearsAD) - { - foreach (md; testMonthDays) - testDatesAD ~= Date(year, md.month, md.day); - } - - foreach (dt; testDatesBC) - { - foreach (tod; testTODs) - testDateTimesBC ~= DateTime(dt, tod); - } - - foreach (dt; testDatesAD) - { - foreach (tod; testTODs) - testDateTimesAD ~= DateTime(dt, tod); - } - } -} +public import std.datetime.date; From 6027d7b449102de7b6ba7782f1d4802d0d299fd5 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 15:34:28 +0200 Subject: [PATCH 117/262] Move stray functionality in std.datetime.common to std.datetime.date. --- std/datetime/common.d | 729 --------------------------------------- std/datetime/date.d | 731 +++++++++++++++++++++++++++++++++++++++- std/datetime/interval.d | 14 +- std/datetime/systime.d | 6 +- std/datetime/timezone.d | 1 - 5 files changed, 732 insertions(+), 749 deletions(-) diff --git a/std/datetime/common.d b/std/datetime/common.d index b48399bcff1..020c1477c6f 100644 --- a/std/datetime/common.d +++ b/std/datetime/common.d @@ -6,732 +6,3 @@ Source: $(PHOBOSSRC std/datetime/_common.d) +/ module std.datetime.common; - -import core.time : TimeException; -import std.typecons : Flag; - - -/++ - Exception type used by std.datetime. It's an alias to - $(REF TimeException, core,time). Either can be caught without concern about - which module it came from. - +/ -alias DateTimeException = TimeException; - - -/++ - Represents the 12 months of the Gregorian year (January is 1). - +/ -enum Month : ubyte { jan = 1, /// - feb, /// - mar, /// - apr, /// - may, /// - jun, /// - jul, /// - aug, /// - sep, /// - oct, /// - nov, /// - dec /// - } - - -/++ - Represents the 7 days of the Gregorian week (Sunday is 0). - +/ -enum DayOfWeek : ubyte { sun = 0, /// - mon, /// - tue, /// - wed, /// - thu, /// - fri, /// - sat /// - } - - -/++ - In some date calculations, adding months or years can cause the date to fall - on a day of the month which is not valid (e.g. February 29th 2001 or - June 31st 2000). If overflow is allowed (as is the default), then the month - will be incremented accordingly (so, February 29th 2001 would become - March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow - is not allowed, then the day will be adjusted to the last valid day in that - month (so, February 29th 2001 would become February 28th 2001 and - June 31st 2000 would become June 30th 2000). - - AllowDayOverflow only applies to calculations involving months or years. - - If set to $(D AllowDayOverflow.no), then day overflow is not allowed. - - Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is - allowed. - +/ -alias AllowDayOverflow = Flag!"allowDayOverflow"; - - -/++ - Array of the strings representing time units, starting with the smallest - unit and going to the largest. It does not include $(D "nsecs"). - - Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), - $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), - $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and - $(D "years") - +/ -immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", - "hours", "days", "weeks", "months", "years"]; - - -/++ - Returns whether the given value is valid for the given unit type when in a - time point. Naturally, a duration is not held to a particular range, but - the values in a time point are (e.g. a month must be in the range of - 1 - 12 inclusive). - - Params: - units = The units of time to validate. - value = The number to validate. - +/ -bool valid(string units)(int value) @safe pure nothrow -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - static if (units == "months") - return value >= Month.jan && value <= Month.dec; - else static if (units == "hours") - return value >= 0 && value <= 23; - else static if (units == "minutes") - return value >= 0 && value <= 59; - else static if (units == "seconds") - return value >= 0 && value <= 59; -} - -/// -@safe unittest -{ - assert(valid!"hours"(12)); - assert(!valid!"hours"(32)); - assert(valid!"months"(12)); - assert(!valid!"months"(13)); -} - -/++ - Returns whether the given day is valid for the given year and month. - - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - +/ -bool valid(string units)(int year, int month, int day) @safe pure nothrow -if (units == "days") -{ - return day > 0 && day <= maxDay(year, month); -} - - -/++ - Params: - units = The units of time to validate. - value = The number to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!units(value)) is false. - +/ -void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "months" || - units == "hours" || - units == "minutes" || - units == "seconds") -{ - import std.format : format; - - static if (units == "months") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); - } - else static if (units == "hours") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); - } - else static if (units == "minutes") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); - } - else static if (units == "seconds") - { - if (!valid!units(value)) - throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); - } -} - - -/++ - Params: - units = The units of time to validate. - year = The year of the day to validate. - month = The month of the day to validate. - day = The day to validate. - file = The file that the $(LREF DateTimeException) will list if thrown. - line = The line number that the $(LREF DateTimeException) will list if - thrown. - - Throws: - $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. - +/ -void enforceValid(string units) - (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure -if (units == "days") -{ - import std.format : format; - if (!valid!"days"(year, month, day)) - throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); -} - - -/++ - Returns the number of days from the current day of the week to the given - day of the week. If they are the same, then the result is 0. - - Params: - currDoW = The current day of the week. - dow = The day of the week to get the number of days to. - +/ -int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow -{ - if (currDoW == dow) - return 0; - if (currDoW < dow) - return dow - currDoW; - return DayOfWeek.sat - currDoW + dow + 1; -} - -@safe unittest -{ - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); - assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); - - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); - assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); - - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); - assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); - - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); - assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); - - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); - assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); - - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); - assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); - - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); - assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); -} - - -/++ - Returns the number of months from the current months of the year to the - given month of the year. If they are the same, then the result is 0. - - Params: - currMonth = The current month of the year. - month = The month of the year to get the number of months to. - +/ -int monthsToMonth(int currMonth, int month) @safe pure -{ - enforceValid!"months"(currMonth); - enforceValid!"months"(month); - - if (currMonth == month) - return 0; - if (currMonth < month) - return month - currMonth; - return Month.dec - currMonth + month; -} - -@safe unittest -{ - assert(monthsToMonth(Month.jan, Month.jan) == 0); - assert(monthsToMonth(Month.jan, Month.feb) == 1); - assert(monthsToMonth(Month.jan, Month.mar) == 2); - assert(monthsToMonth(Month.jan, Month.apr) == 3); - assert(monthsToMonth(Month.jan, Month.may) == 4); - assert(monthsToMonth(Month.jan, Month.jun) == 5); - assert(monthsToMonth(Month.jan, Month.jul) == 6); - assert(monthsToMonth(Month.jan, Month.aug) == 7); - assert(monthsToMonth(Month.jan, Month.sep) == 8); - assert(monthsToMonth(Month.jan, Month.oct) == 9); - assert(monthsToMonth(Month.jan, Month.nov) == 10); - assert(monthsToMonth(Month.jan, Month.dec) == 11); - - assert(monthsToMonth(Month.may, Month.jan) == 8); - assert(monthsToMonth(Month.may, Month.feb) == 9); - assert(monthsToMonth(Month.may, Month.mar) == 10); - assert(monthsToMonth(Month.may, Month.apr) == 11); - assert(monthsToMonth(Month.may, Month.may) == 0); - assert(monthsToMonth(Month.may, Month.jun) == 1); - assert(monthsToMonth(Month.may, Month.jul) == 2); - assert(monthsToMonth(Month.may, Month.aug) == 3); - assert(monthsToMonth(Month.may, Month.sep) == 4); - assert(monthsToMonth(Month.may, Month.oct) == 5); - assert(monthsToMonth(Month.may, Month.nov) == 6); - assert(monthsToMonth(Month.may, Month.dec) == 7); - - assert(monthsToMonth(Month.oct, Month.jan) == 3); - assert(monthsToMonth(Month.oct, Month.feb) == 4); - assert(monthsToMonth(Month.oct, Month.mar) == 5); - assert(monthsToMonth(Month.oct, Month.apr) == 6); - assert(monthsToMonth(Month.oct, Month.may) == 7); - assert(monthsToMonth(Month.oct, Month.jun) == 8); - assert(monthsToMonth(Month.oct, Month.jul) == 9); - assert(monthsToMonth(Month.oct, Month.aug) == 10); - assert(monthsToMonth(Month.oct, Month.sep) == 11); - assert(monthsToMonth(Month.oct, Month.oct) == 0); - assert(monthsToMonth(Month.oct, Month.nov) == 1); - assert(monthsToMonth(Month.oct, Month.dec) == 2); - - assert(monthsToMonth(Month.dec, Month.jan) == 1); - assert(monthsToMonth(Month.dec, Month.feb) == 2); - assert(monthsToMonth(Month.dec, Month.mar) == 3); - assert(monthsToMonth(Month.dec, Month.apr) == 4); - assert(monthsToMonth(Month.dec, Month.may) == 5); - assert(monthsToMonth(Month.dec, Month.jun) == 6); - assert(monthsToMonth(Month.dec, Month.jul) == 7); - assert(monthsToMonth(Month.dec, Month.aug) == 8); - assert(monthsToMonth(Month.dec, Month.sep) == 9); - assert(monthsToMonth(Month.dec, Month.oct) == 10); - assert(monthsToMonth(Month.dec, Month.nov) == 11); - assert(monthsToMonth(Month.dec, Month.dec) == 0); -} - - -/++ - Whether the given Gregorian Year is a leap year. - - Params: - year = The year to to be tested. - +/ -bool yearIsLeapYear(int year) @safe pure nothrow -{ - if (year % 400 == 0) - return true; - if (year % 100 == 0) - return false; - return year % 4 == 0; -} - -@safe unittest -{ - import std.format : format; - foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, - 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) - { - assert(!yearIsLeapYear(year), format("year: %s.", year)); - assert(!yearIsLeapYear(-year), format("year: %s.", year)); - } - - foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) - { - assert(yearIsLeapYear(year), format("year: %s.", year)); - assert(yearIsLeapYear(-year), format("year: %s.", year)); - } -} - - -/++ - Whether the given type defines all of the necessary functions for it to - function as a time point. - - 1. $(D T) must define a static property named $(D min) which is the smallest - value of $(D T) as $(Unqual!T). - - 2. $(D T) must define a static property named $(D max) which is the largest - value of $(D T) as $(Unqual!T). - - 3. $(D T) must define an $(D opBinary) for addition and subtraction that - accepts $(REF Duration, core,time) and returns $(D Unqual!T). - - 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that - accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). - - 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T) - and returns returns $(REF Duration, core,time). - +/ -template isTimePoint(T) -{ - import core.time : Duration; - import std.traits : FunctionAttribute, functionAttributes, Unqual; - - enum isTimePoint = hasMin && - hasMax && - hasOverloadedOpBinaryWithDuration && - hasOverloadedOpAssignWithDuration && - hasOverloadedOpBinaryWithSelf && - !is(U == Duration); - -private: - - alias U = Unqual!T; - - enum hasMin = __traits(hasMember, T, "min") && - is(typeof(T.min) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.min));})); - - enum hasMax = __traits(hasMember, T, "max") && - is(typeof(T.max) == U) && - is(typeof({static assert(__traits(isStaticFunction, T.max));})); - - enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && - is(typeof(T.init - Duration.init) == U); - - enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && - is(typeof(U.init -= Duration.init) == U) && - is(typeof( - { - // Until the overload with TickDuration is removed, this is ambiguous. - //alias add = U.opOpAssign!"+"; - //alias sub = U.opOpAssign!"-"; - U u; - auto ref add() { return u += Duration.init; } - auto ref sub() { return u -= Duration.init; } - alias FA = FunctionAttribute; - static assert((functionAttributes!add & FA.ref_) != 0); - static assert((functionAttributes!sub & FA.ref_) != 0); - })); - - enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); -} - -/// -@safe unittest -{ - import core.time : Duration; - import std.datetime.date : Date; - import std.datetime.datetime : DateTime; - import std.datetime.interval : Interval; - import std.datetime.systime : SysTime; - import std.datetime.timeofday : TimeOfDay; - - static assert(isTimePoint!Date); - static assert(isTimePoint!DateTime); - static assert(isTimePoint!SysTime); - static assert(isTimePoint!TimeOfDay); - - static assert(!isTimePoint!int); - static assert(!isTimePoint!Duration); - static assert(!isTimePoint!(Interval!SysTime)); -} - -@safe unittest -{ - import core.time; - import std.datetime.date; - import std.datetime.datetime; - import std.datetime.interval; - import std.datetime.systime; - import std.datetime.timeofday; - import std.meta : AliasSeq; - - foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) - { - static assert(isTimePoint!(const TP), TP.stringof); - static assert(isTimePoint!(immutable TP), TP.stringof); - } - - foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) - static assert(!isTimePoint!T, T.stringof); -} - - -/++ - Whether all of the given strings are valid units of time. - - $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime - can handle precision greater than hnsecs, and the few functions in core.time - which deal with "nsecs" deal with it explicitly. - +/ -bool validTimeUnits(string[] units...) @safe pure nothrow -{ - import std.algorithm.searching : canFind; - foreach (str; units) - { - if (!canFind(timeStrings[], str)) - return false; - } - return true; -} - - -/++ - Compares two time unit strings. $(D "years") are the largest units and - $(D "hnsecs") are the smallest. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - - Throws: - $(LREF DateTimeException) if either of the given strings is not a valid - time unit string. - +/ -int cmpTimeUnits(string lhs, string rhs) @safe pure -{ - import std.algorithm.searching : countUntil; - import std.exception : enforce; - import std.format : format; - - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); - enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -@safe unittest -{ - foreach (i, outerUnits; timeStrings) - { - assert(cmpTimeUnits(outerUnits, outerUnits) == 0); - - // For some reason, $ won't compile. - foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length]) - assert(cmpTimeUnits(outerUnits, innerUnits) == -1); - } - - foreach (i, outerUnits; timeStrings) - { - foreach (innerUnits; timeStrings[0 .. i]) - assert(cmpTimeUnits(outerUnits, innerUnits) == 1); - } -} - - -/++ - Compares two time unit strings at compile time. $(D "years") are the largest - units and $(D "hnsecs") are the smallest. - - This template is used instead of $(D cmpTimeUnits) because exceptions - can't be thrown at compile time and $(D cmpTimeUnits) must enforce that - the strings it's given are valid time unit strings. This template uses a - template constraint instead. - - Returns: - $(BOOKTABLE, - $(TR $(TD this < rhs) $(TD < 0)) - $(TR $(TD this == rhs) $(TD 0)) - $(TR $(TD this > rhs) $(TD > 0)) - ) - +/ -template CmpTimeUnits(string lhs, string rhs) -if (validTimeUnits(lhs, rhs)) -{ - enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); -} - - -/+ - Helper function for $(D CmpTimeUnits). - +/ -private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow -{ - import std.algorithm.searching : countUntil; - auto tstrings = timeStrings; - immutable indexOfLHS = countUntil(tstrings, lhs); - immutable indexOfRHS = countUntil(tstrings, rhs); - - if (indexOfLHS < indexOfRHS) - return -1; - if (indexOfLHS > indexOfRHS) - return 1; - - return 0; -} - -@safe unittest -{ - import std.format : format; - import std.meta : AliasSeq; - - static string genTest(size_t index) - { - auto currUnits = timeStrings[index]; - auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); - - foreach (units; timeStrings[index + 1 .. $]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); - - foreach (units; timeStrings[0 .. index]) - test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); - - return test; - } - - static assert(timeStrings.length == 10); - foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) - mixin(genTest(n)); -} - - -//============================================================================== -// Section with package-level helper functions and templates. -//============================================================================== - -package: - -/+ - Array of the short (three letter) names of each month. - +/ -immutable string[12] _monthNames = ["Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec"]; - -/+ - The maximum valid Day in the given month in the given year. - - Params: - year = The year to get the day for. - month = The month of the Gregorian Calendar to get the day for. - +/ -ubyte maxDay(int year, int month) @safe pure nothrow -in -{ - assert(valid!"months"(month)); -} -body -{ - switch (month) - { - case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: - return 31; - case Month.feb: - return yearIsLeapYear(year) ? 29 : 28; - case Month.apr, Month.jun, Month.sep, Month.nov: - return 30; - default: - assert(0, "Invalid month."); - } -} - -@safe unittest -{ - // Test A.D. - assert(maxDay(1999, 1) == 31); - assert(maxDay(1999, 2) == 28); - assert(maxDay(1999, 3) == 31); - assert(maxDay(1999, 4) == 30); - assert(maxDay(1999, 5) == 31); - assert(maxDay(1999, 6) == 30); - assert(maxDay(1999, 7) == 31); - assert(maxDay(1999, 8) == 31); - assert(maxDay(1999, 9) == 30); - assert(maxDay(1999, 10) == 31); - assert(maxDay(1999, 11) == 30); - assert(maxDay(1999, 12) == 31); - - assert(maxDay(2000, 1) == 31); - assert(maxDay(2000, 2) == 29); - assert(maxDay(2000, 3) == 31); - assert(maxDay(2000, 4) == 30); - assert(maxDay(2000, 5) == 31); - assert(maxDay(2000, 6) == 30); - assert(maxDay(2000, 7) == 31); - assert(maxDay(2000, 8) == 31); - assert(maxDay(2000, 9) == 30); - assert(maxDay(2000, 10) == 31); - assert(maxDay(2000, 11) == 30); - assert(maxDay(2000, 12) == 31); - - // Test B.C. - assert(maxDay(-1999, 1) == 31); - assert(maxDay(-1999, 2) == 28); - assert(maxDay(-1999, 3) == 31); - assert(maxDay(-1999, 4) == 30); - assert(maxDay(-1999, 5) == 31); - assert(maxDay(-1999, 6) == 30); - assert(maxDay(-1999, 7) == 31); - assert(maxDay(-1999, 8) == 31); - assert(maxDay(-1999, 9) == 30); - assert(maxDay(-1999, 10) == 31); - assert(maxDay(-1999, 11) == 30); - assert(maxDay(-1999, 12) == 31); - - assert(maxDay(-2000, 1) == 31); - assert(maxDay(-2000, 2) == 29); - assert(maxDay(-2000, 3) == 31); - assert(maxDay(-2000, 4) == 30); - assert(maxDay(-2000, 5) == 31); - assert(maxDay(-2000, 6) == 30); - assert(maxDay(-2000, 7) == 31); - assert(maxDay(-2000, 8) == 31); - assert(maxDay(-2000, 9) == 30); - assert(maxDay(-2000, 10) == 31); - assert(maxDay(-2000, 11) == 30); - assert(maxDay(-2000, 12) == 31); -} diff --git a/std/datetime/date.d b/std/datetime/date.d index 9f5be5227bd..e0ee9ca08f4 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -10,6 +10,7 @@ module std.datetime.date; import core.time; import std.datetime.common; import std.traits : isSomeString, Unqual; +import std.typecons : Flag; version(unittest) import std.exception : assertThrown; @@ -20,6 +21,82 @@ version(unittest) import std.exception : assertThrown; } +/++ + Exception type used by std.datetime. It's an alias to + $(REF TimeException,core,time). Either can be caught without concern about + which module it came from. + +/ +alias DateTimeException = TimeException; + + +/++ + Represents the 12 months of the Gregorian year (January is 1). + +/ +enum Month : ubyte +{ + jan = 1, /// + feb, /// + mar, /// + apr, /// + may, /// + jun, /// + jul, /// + aug, /// + sep, /// + oct, /// + nov, /// + dec /// +} + + +/++ + Represents the 7 days of the Gregorian week (Sunday is 0). + +/ +enum DayOfWeek : ubyte +{ + sun = 0, /// + mon, /// + tue, /// + wed, /// + thu, /// + fri, /// + sat /// +} + + +/++ + In some date calculations, adding months or years can cause the date to fall + on a day of the month which is not valid (e.g. February 29th 2001 or + June 31st 2000). If overflow is allowed (as is the default), then the month + will be incremented accordingly (so, February 29th 2001 would become + March 1st 2001, and June 31st 2000 would become July 1st 2000). If overflow + is not allowed, then the day will be adjusted to the last valid day in that + month (so, February 29th 2001 would become February 28th 2001 and + June 31st 2000 would become June 30th 2000). + + AllowDayOverflow only applies to calculations involving months or years. + + If set to $(D AllowDayOverflow.no), then day overflow is not allowed. + + Otherwise, if set to $(D AllowDayOverflow.yes), then day overflow is + allowed. + +/ +alias AllowDayOverflow = Flag!"allowDayOverflow"; + + +/++ + Array of the strings representing time units, starting with the smallest + unit and going to the largest. It does not include $(D "nsecs"). + + Includes $(D "hnsecs") (hecto-nanoseconds (100 ns)), + $(D "usecs") (microseconds), $(D "msecs") (milliseconds), $(D "seconds"), + $(D "minutes"), $(D "hours"), $(D "days"), $(D "weeks"), $(D "months"), and + $(D "years") + +/ +immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minutes", + "hours", "days", "weeks", "months", "years"]; + + /++ Combines the $(REF std,datetime,date,Date) and $(REF std,datetime,timeofday,TimeOfDay) structs to give an object which holds @@ -957,8 +1034,6 @@ public: /// @safe unittest { - import std.datetime.common : AllowDayOverflow; - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); dt1.add!"months"(11); assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); @@ -1021,8 +1096,6 @@ public: /// @safe unittest { - import std.datetime.common : AllowDayOverflow; - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); dt1.roll!"months"(1); assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); @@ -4274,8 +4347,6 @@ public: /// @safe unittest { - import std.datetime.common : AllowDayOverflow; - auto d1 = Date(2010, 1, 1); d1.add!"months"(11); assert(d1 == Date(2010, 12, 1)); @@ -5043,8 +5114,6 @@ public: /// @safe unittest { - import std.datetime.common : AllowDayOverflow; - auto d1 = Date(2010, 1, 1); d1.roll!"months"(1); assert(d1 == Date(2010, 2, 1)); @@ -9111,8 +9180,654 @@ package: } +/++ + Returns whether the given value is valid for the given unit type when in a + time point. Naturally, a duration is not held to a particular range, but + the values in a time point are (e.g. a month must be in the range of + 1 - 12 inclusive). + + Params: + units = The units of time to validate. + value = The number to validate. + +/ +bool valid(string units)(int value) @safe pure nothrow +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + static if (units == "months") + return value >= Month.jan && value <= Month.dec; + else static if (units == "hours") + return value >= 0 && value <= 23; + else static if (units == "minutes") + return value >= 0 && value <= 59; + else static if (units == "seconds") + return value >= 0 && value <= 59; +} + +/// +@safe unittest +{ + assert(valid!"hours"(12)); + assert(!valid!"hours"(32)); + assert(valid!"months"(12)); + assert(!valid!"months"(13)); +} + +/++ + Returns whether the given day is valid for the given year and month. + + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + +/ +bool valid(string units)(int year, int month, int day) @safe pure nothrow +if (units == "days") +{ + return day > 0 && day <= maxDay(year, month); +} + + +/++ + Params: + units = The units of time to validate. + value = The number to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!units(value)) is false. + +/ +void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "months" || + units == "hours" || + units == "minutes" || + units == "seconds") +{ + import std.format : format; + + static if (units == "months") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); + } + else static if (units == "hours") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); + } + else static if (units == "minutes") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); + } + else static if (units == "seconds") + { + if (!valid!units(value)) + throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); + } +} + + +/++ + Params: + units = The units of time to validate. + year = The year of the day to validate. + month = The month of the day to validate. + day = The day to validate. + file = The file that the $(LREF DateTimeException) will list if thrown. + line = The line number that the $(LREF DateTimeException) will list if + thrown. + + Throws: + $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. + +/ +void enforceValid(string units) + (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure +if (units == "days") +{ + import std.format : format; + if (!valid!"days"(year, month, day)) + throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); +} + + +/++ + Returns the number of days from the current day of the week to the given + day of the week. If they are the same, then the result is 0. + + Params: + currDoW = The current day of the week. + dow = The day of the week to get the number of days to. + +/ +int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow +{ + if (currDoW == dow) + return 0; + if (currDoW < dow) + return dow - currDoW; + return DayOfWeek.sat - currDoW + dow + 1; +} + +@safe unittest +{ + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); + + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); + + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); + + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); + + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); + + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); + + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); +} + + +/++ + Returns the number of months from the current months of the year to the + given month of the year. If they are the same, then the result is 0. + + Params: + currMonth = The current month of the year. + month = The month of the year to get the number of months to. + +/ +int monthsToMonth(int currMonth, int month) @safe pure +{ + enforceValid!"months"(currMonth); + enforceValid!"months"(month); + + if (currMonth == month) + return 0; + if (currMonth < month) + return month - currMonth; + return Month.dec - currMonth + month; +} + +@safe unittest +{ + assert(monthsToMonth(Month.jan, Month.jan) == 0); + assert(monthsToMonth(Month.jan, Month.feb) == 1); + assert(monthsToMonth(Month.jan, Month.mar) == 2); + assert(monthsToMonth(Month.jan, Month.apr) == 3); + assert(monthsToMonth(Month.jan, Month.may) == 4); + assert(monthsToMonth(Month.jan, Month.jun) == 5); + assert(monthsToMonth(Month.jan, Month.jul) == 6); + assert(monthsToMonth(Month.jan, Month.aug) == 7); + assert(monthsToMonth(Month.jan, Month.sep) == 8); + assert(monthsToMonth(Month.jan, Month.oct) == 9); + assert(monthsToMonth(Month.jan, Month.nov) == 10); + assert(monthsToMonth(Month.jan, Month.dec) == 11); + + assert(monthsToMonth(Month.may, Month.jan) == 8); + assert(monthsToMonth(Month.may, Month.feb) == 9); + assert(monthsToMonth(Month.may, Month.mar) == 10); + assert(monthsToMonth(Month.may, Month.apr) == 11); + assert(monthsToMonth(Month.may, Month.may) == 0); + assert(monthsToMonth(Month.may, Month.jun) == 1); + assert(monthsToMonth(Month.may, Month.jul) == 2); + assert(monthsToMonth(Month.may, Month.aug) == 3); + assert(monthsToMonth(Month.may, Month.sep) == 4); + assert(monthsToMonth(Month.may, Month.oct) == 5); + assert(monthsToMonth(Month.may, Month.nov) == 6); + assert(monthsToMonth(Month.may, Month.dec) == 7); + + assert(monthsToMonth(Month.oct, Month.jan) == 3); + assert(monthsToMonth(Month.oct, Month.feb) == 4); + assert(monthsToMonth(Month.oct, Month.mar) == 5); + assert(monthsToMonth(Month.oct, Month.apr) == 6); + assert(monthsToMonth(Month.oct, Month.may) == 7); + assert(monthsToMonth(Month.oct, Month.jun) == 8); + assert(monthsToMonth(Month.oct, Month.jul) == 9); + assert(monthsToMonth(Month.oct, Month.aug) == 10); + assert(monthsToMonth(Month.oct, Month.sep) == 11); + assert(monthsToMonth(Month.oct, Month.oct) == 0); + assert(monthsToMonth(Month.oct, Month.nov) == 1); + assert(monthsToMonth(Month.oct, Month.dec) == 2); + + assert(monthsToMonth(Month.dec, Month.jan) == 1); + assert(monthsToMonth(Month.dec, Month.feb) == 2); + assert(monthsToMonth(Month.dec, Month.mar) == 3); + assert(monthsToMonth(Month.dec, Month.apr) == 4); + assert(monthsToMonth(Month.dec, Month.may) == 5); + assert(monthsToMonth(Month.dec, Month.jun) == 6); + assert(monthsToMonth(Month.dec, Month.jul) == 7); + assert(monthsToMonth(Month.dec, Month.aug) == 8); + assert(monthsToMonth(Month.dec, Month.sep) == 9); + assert(monthsToMonth(Month.dec, Month.oct) == 10); + assert(monthsToMonth(Month.dec, Month.nov) == 11); + assert(monthsToMonth(Month.dec, Month.dec) == 0); +} + + +/++ + Whether the given Gregorian Year is a leap year. + + Params: + year = The year to to be tested. + +/ +bool yearIsLeapYear(int year) @safe pure nothrow +{ + if (year % 400 == 0) + return true; + if (year % 100 == 0) + return false; + return year % 4 == 0; +} + +@safe unittest +{ + import std.format : format; + foreach (year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, + 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) + { + assert(!yearIsLeapYear(year), format("year: %s.", year)); + assert(!yearIsLeapYear(-year), format("year: %s.", year)); + } + + foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012]) + { + assert(yearIsLeapYear(year), format("year: %s.", year)); + assert(yearIsLeapYear(-year), format("year: %s.", year)); + } +} + + +/++ + Whether the given type defines all of the necessary functions for it to + function as a time point. + + 1. $(D T) must define a static property named $(D min) which is the smallest + value of $(D T) as $(Unqual!T). + + 2. $(D T) must define a static property named $(D max) which is the largest + value of $(D T) as $(Unqual!T). + + 3. $(D T) must define an $(D opBinary) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D Unqual!T). + + 4. $(D T) must define an $(D opOpAssign) for addition and subtraction that + accepts $(REF Duration, core,time) and returns $(D ref Unqual!T). + + 5. $(D T) must define a $(D opBinary) for subtraction which accepts $(D T) + and returns returns $(REF Duration, core,time). + +/ +template isTimePoint(T) +{ + import core.time : Duration; + import std.traits : FunctionAttribute, functionAttributes, Unqual; + + enum isTimePoint = hasMin && + hasMax && + hasOverloadedOpBinaryWithDuration && + hasOverloadedOpAssignWithDuration && + hasOverloadedOpBinaryWithSelf && + !is(U == Duration); + +private: + + alias U = Unqual!T; + + enum hasMin = __traits(hasMember, T, "min") && + is(typeof(T.min) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.min));})); + + enum hasMax = __traits(hasMember, T, "max") && + is(typeof(T.max) == U) && + is(typeof({static assert(__traits(isStaticFunction, T.max));})); + + enum hasOverloadedOpBinaryWithDuration = is(typeof(T.init + Duration.init) == U) && + is(typeof(T.init - Duration.init) == U); + + enum hasOverloadedOpAssignWithDuration = is(typeof(U.init += Duration.init) == U) && + is(typeof(U.init -= Duration.init) == U) && + is(typeof( + { + // Until the overload with TickDuration is removed, this is ambiguous. + //alias add = U.opOpAssign!"+"; + //alias sub = U.opOpAssign!"-"; + U u; + auto ref add() { return u += Duration.init; } + auto ref sub() { return u -= Duration.init; } + alias FA = FunctionAttribute; + static assert((functionAttributes!add & FA.ref_) != 0); + static assert((functionAttributes!sub & FA.ref_) != 0); + })); + + enum hasOverloadedOpBinaryWithSelf = is(typeof(T.init - T.init) == Duration); +} + +/// +@safe unittest +{ + import core.time : Duration; + import std.datetime.date : Date; + import std.datetime.datetime : DateTime; + import std.datetime.interval : Interval; + import std.datetime.systime : SysTime; + import std.datetime.timeofday : TimeOfDay; + + static assert(isTimePoint!Date); + static assert(isTimePoint!DateTime); + static assert(isTimePoint!SysTime); + static assert(isTimePoint!TimeOfDay); + + static assert(!isTimePoint!int); + static assert(!isTimePoint!Duration); + static assert(!isTimePoint!(Interval!SysTime)); +} + +@safe unittest +{ + import core.time; + import std.datetime.date; + import std.datetime.datetime; + import std.datetime.interval; + import std.datetime.systime; + import std.datetime.timeofday; + import std.meta : AliasSeq; + + foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) + { + static assert(isTimePoint!(const TP), TP.stringof); + static assert(isTimePoint!(immutable TP), TP.stringof); + } + + foreach (T; AliasSeq!(float, string, Duration, Interval!Date, PosInfInterval!Date, NegInfInterval!Date)) + static assert(!isTimePoint!T, T.stringof); +} + + +/++ + Whether all of the given strings are valid units of time. + + $(D "nsecs") is not considered a valid unit of time. Nothing in std.datetime + can handle precision greater than hnsecs, and the few functions in core.time + which deal with "nsecs" deal with it explicitly. + +/ +bool validTimeUnits(string[] units...) @safe pure nothrow +{ + import std.algorithm.searching : canFind; + foreach (str; units) + { + if (!canFind(timeStrings[], str)) + return false; + } + return true; +} + + +/++ + Compares two time unit strings. $(D "years") are the largest units and + $(D "hnsecs") are the smallest. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + + Throws: + $(LREF DateTimeException) if either of the given strings is not a valid + time unit string. + +/ +int cmpTimeUnits(string lhs, string rhs) @safe pure +{ + import std.algorithm.searching : countUntil; + import std.exception : enforce; + import std.format : format; + + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + enforce(indexOfLHS != -1, format("%s is not a valid TimeString", lhs)); + enforce(indexOfRHS != -1, format("%s is not a valid TimeString", rhs)); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + foreach (i, outerUnits; timeStrings) + { + assert(cmpTimeUnits(outerUnits, outerUnits) == 0); + + // For some reason, $ won't compile. + foreach (innerUnits; timeStrings[i + 1 .. timeStrings.length]) + assert(cmpTimeUnits(outerUnits, innerUnits) == -1); + } + + foreach (i, outerUnits; timeStrings) + { + foreach (innerUnits; timeStrings[0 .. i]) + assert(cmpTimeUnits(outerUnits, innerUnits) == 1); + } +} + + +/++ + Compares two time unit strings at compile time. $(D "years") are the largest + units and $(D "hnsecs") are the smallest. + + This template is used instead of $(D cmpTimeUnits) because exceptions + can't be thrown at compile time and $(D cmpTimeUnits) must enforce that + the strings it's given are valid time unit strings. This template uses a + template constraint instead. + + Returns: + $(BOOKTABLE, + $(TR $(TD this < rhs) $(TD < 0)) + $(TR $(TD this == rhs) $(TD 0)) + $(TR $(TD this > rhs) $(TD > 0)) + ) + +/ +template CmpTimeUnits(string lhs, string rhs) +if (validTimeUnits(lhs, rhs)) +{ + enum CmpTimeUnits = cmpTimeUnitsCTFE(lhs, rhs); +} + + +// Helper function for CmpTimeUnits. +private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow +{ + import std.algorithm.searching : countUntil; + auto tstrings = timeStrings; + immutable indexOfLHS = countUntil(tstrings, lhs); + immutable indexOfRHS = countUntil(tstrings, rhs); + + if (indexOfLHS < indexOfRHS) + return -1; + if (indexOfLHS > indexOfRHS) + return 1; + + return 0; +} + +@safe unittest +{ + import std.format : format; + import std.meta : AliasSeq; + + static string genTest(size_t index) + { + auto currUnits = timeStrings[index]; + auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); + + foreach (units; timeStrings[index + 1 .. $]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); + + foreach (units; timeStrings[0 .. index]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); + + return test; + } + + static assert(timeStrings.length == 10); + foreach (n; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + mixin(genTest(n)); +} + + package: + +/+ + Array of the short (three letter) names of each month. + +/ +immutable string[12] _monthNames = ["Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"]; + +/+ + The maximum valid Day in the given month in the given year. + + Params: + year = The year to get the day for. + month = The month of the Gregorian Calendar to get the day for. + +/ +ubyte maxDay(int year, int month) @safe pure nothrow +in +{ + assert(valid!"months"(month)); +} +body +{ + switch (month) + { + case Month.jan, Month.mar, Month.may, Month.jul, Month.aug, Month.oct, Month.dec: + return 31; + case Month.feb: + return yearIsLeapYear(year) ? 29 : 28; + case Month.apr, Month.jun, Month.sep, Month.nov: + return 30; + default: + assert(0, "Invalid month."); + } +} + +@safe unittest +{ + // Test A.D. + assert(maxDay(1999, 1) == 31); + assert(maxDay(1999, 2) == 28); + assert(maxDay(1999, 3) == 31); + assert(maxDay(1999, 4) == 30); + assert(maxDay(1999, 5) == 31); + assert(maxDay(1999, 6) == 30); + assert(maxDay(1999, 7) == 31); + assert(maxDay(1999, 8) == 31); + assert(maxDay(1999, 9) == 30); + assert(maxDay(1999, 10) == 31); + assert(maxDay(1999, 11) == 30); + assert(maxDay(1999, 12) == 31); + + assert(maxDay(2000, 1) == 31); + assert(maxDay(2000, 2) == 29); + assert(maxDay(2000, 3) == 31); + assert(maxDay(2000, 4) == 30); + assert(maxDay(2000, 5) == 31); + assert(maxDay(2000, 6) == 30); + assert(maxDay(2000, 7) == 31); + assert(maxDay(2000, 8) == 31); + assert(maxDay(2000, 9) == 30); + assert(maxDay(2000, 10) == 31); + assert(maxDay(2000, 11) == 30); + assert(maxDay(2000, 12) == 31); + + // Test B.C. + assert(maxDay(-1999, 1) == 31); + assert(maxDay(-1999, 2) == 28); + assert(maxDay(-1999, 3) == 31); + assert(maxDay(-1999, 4) == 30); + assert(maxDay(-1999, 5) == 31); + assert(maxDay(-1999, 6) == 30); + assert(maxDay(-1999, 7) == 31); + assert(maxDay(-1999, 8) == 31); + assert(maxDay(-1999, 9) == 30); + assert(maxDay(-1999, 10) == 31); + assert(maxDay(-1999, 11) == 30); + assert(maxDay(-1999, 12) == 31); + + assert(maxDay(-2000, 1) == 31); + assert(maxDay(-2000, 2) == 29); + assert(maxDay(-2000, 3) == 31); + assert(maxDay(-2000, 4) == 30); + assert(maxDay(-2000, 5) == 31); + assert(maxDay(-2000, 6) == 30); + assert(maxDay(-2000, 7) == 31); + assert(maxDay(-2000, 8) == 31); + assert(maxDay(-2000, 9) == 30); + assert(maxDay(-2000, 10) == 31); + assert(maxDay(-2000, 11) == 30); + assert(maxDay(-2000, 12) == 31); +} + /+ Splits out a particular unit from hnsecs and gives the value for that unit and the remaining hnsecs. It really shouldn't be used unless unless diff --git a/std/datetime/interval.d b/std/datetime/interval.d index ee9a0e10915..6dd4ab76023 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -8,7 +8,8 @@ module std.datetime.interval; import core.time : Duration, dur; -import std.datetime.common; +import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek, + DayOfWeek, isTimePoint, Month; import std.exception : enforce; import std.traits : isIntegral, Unqual; import std.typecons : Flag; @@ -7622,8 +7623,7 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.common : DayOfWeek; - import std.datetime.date : Date; + import std.datetime.date : Date, DayOfWeek; auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); auto func = everyDayOfWeek!Date(DayOfWeek.mon); @@ -7715,6 +7715,8 @@ if (isTimePoint!TP && !__traits(isStaticFunction, TP.month) && is(typeof(TP.month) == Month)) { + import std.datetime.date : enforceValid, monthsToMonth; + enforceValid!"months"(month); TP func(in TP tp) @@ -7744,8 +7746,7 @@ if (isTimePoint!TP && /// @system unittest { - import std.datetime.common : Month; - import std.datetime.date : Date; + import std.datetime.date : Date, Month; auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); auto func = everyMonth!Date(Month.feb); @@ -7985,8 +7986,7 @@ if (isTimePoint!TP && @system unittest { import core.time : dur; - import std.datetime.common : AllowDayOverflow; - import std.datetime.date : Date; + import std.datetime.date : AllowDayOverflow, Date; auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 198d724282a..02a58eede14 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -3698,8 +3698,7 @@ public: /// @safe unittest { - import std.datetime.common : AllowDayOverflow; - import std.datetime.datetime : DateTime; + import std.datetime.date : AllowDayOverflow, DateTime; auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); st1.roll!"months"(1); @@ -9755,8 +9754,7 @@ afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); @safe unittest { import core.time : hours; - import std.datetime.common : DateTimeException; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime, DateTimeException; import std.datetime.timezone : SimpleTimeZone, UTC; import std.exception : assertThrown; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index a6fece6c854..98a19d0bcb4 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -8,7 +8,6 @@ module std.datetime.timezone; import core.time; -import std.datetime.common; import std.datetime.date; import std.datetime.datetime; import std.datetime.systime; From 10a7292b6b26d1760045744b8ba89e314c3e62e1 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 15:41:57 +0200 Subject: [PATCH 118/262] Remove uses of common, datetime, and timeofday. --- std/datetime/date.d | 7 ------ std/datetime/datetime.d | 2 -- std/datetime/interval.d | 33 --------------------------- std/datetime/package.d | 3 --- std/datetime/systime.d | 49 +++++++++++++++++++--------------------- std/datetime/timeofday.d | 2 -- std/datetime/timezone.d | 1 - 7 files changed, 23 insertions(+), 74 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index e0ee9ca08f4..3ea39e5361c 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -8,7 +8,6 @@ module std.datetime.date; import core.time; -import std.datetime.common; import std.traits : isSomeString, Unqual; import std.typecons : Flag; @@ -9550,11 +9549,8 @@ private: @safe unittest { import core.time : Duration; - import std.datetime.date : Date; - import std.datetime.datetime : DateTime; import std.datetime.interval : Interval; import std.datetime.systime : SysTime; - import std.datetime.timeofday : TimeOfDay; static assert(isTimePoint!Date); static assert(isTimePoint!DateTime); @@ -9569,11 +9565,8 @@ private: @safe unittest { import core.time; - import std.datetime.date; - import std.datetime.datetime; import std.datetime.interval; import std.datetime.systime; - import std.datetime.timeofday; import std.meta : AliasSeq; foreach (TP; AliasSeq!(Date, DateTime, SysTime, TimeOfDay)) diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d index 57a13bc616b..5b1e3f53c75 100644 --- a/std/datetime/datetime.d +++ b/std/datetime/datetime.d @@ -8,5 +8,3 @@ LREF2=$(D $2) +/ module std.datetime.datetime; - -public import std.datetime.date; diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 6dd4ab76023..50a528db68b 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -1590,9 +1590,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); @@ -1657,9 +1655,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); @@ -1682,9 +1678,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); @@ -4161,9 +4155,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; PosInfInterval!Date(Date.init); PosInfInterval!TimeOfDay(TimeOfDay.init); @@ -4196,9 +4188,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); @@ -6387,9 +6377,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; NegInfInterval!Date(Date.init); NegInfInterval!TimeOfDay(TimeOfDay.init); @@ -6419,9 +6407,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); @@ -7648,9 +7634,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); @@ -7777,7 +7761,6 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; auto funcFwd = everyMonth!Date(Month.jun); @@ -7884,9 +7867,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; auto funcFwd = everyDuration!Date(dur!"days"(27)); auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); @@ -8011,9 +7992,7 @@ if (isTimePoint!TP && @system unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; { auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); @@ -8296,9 +8275,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; import std.range.primitives; static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); @@ -8325,9 +8302,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; { Date dateFunc(in Date date) { return date; } @@ -8744,9 +8719,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; import std.range.primitives; static assert(isInputRange!(PosInfIntervalRange!Date)); @@ -8772,9 +8745,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; { Date dateFunc(in Date date) { return date; } @@ -9032,8 +9003,6 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; - import std.datetime.timeofday; import std.range.primitives; static assert(isInputRange!(NegInfIntervalRange!Date)); @@ -9058,9 +9027,7 @@ private: @safe unittest { import std.datetime.date; - import std.datetime.datetime; import std.datetime.systime; - import std.datetime.timeofday; { Date dateFunc(in Date date) { return date; } diff --git a/std/datetime/package.d b/std/datetime/package.d index b6044a0eeff..367ec7a102a 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -110,12 +110,9 @@ auto restoredTime = SysTime.fromISOExtString(timeString); module std.datetime; public import core.time; -public import std.datetime.common; public import std.datetime.date; -public import std.datetime.datetime; public import std.datetime.interval; public import std.datetime.systime; -public import std.datetime.timeofday; public import std.datetime.timezone; import core.exception : AssertError; diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 02a58eede14..e170ca036f2 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -8,10 +8,7 @@ module std.datetime.systime; import core.time; -import std.datetime.common; import std.datetime.date; -import std.datetime.datetime; -import std.datetime.timeofday; import std.datetime.timezone; import std.format : format; import std.exception : enforce; @@ -866,7 +863,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); @@ -936,7 +933,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); @@ -1072,7 +1069,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); @@ -1233,7 +1230,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); @@ -1762,7 +1759,7 @@ public: @safe unittest { import core.time : msecs, usecs, hnsecs, nsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; auto dt = DateTime(1982, 4, 1, 20, 59, 22); assert(SysTime(dt, msecs(213)).fracSecs == msecs(213)); @@ -1850,7 +1847,7 @@ public: @safe unittest { import core.time : Duration, msecs, hnsecs, nsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); assert(st.fracSecs == Duration.zero); @@ -2238,7 +2235,7 @@ public: @safe unittest { import core.time : hours; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); @@ -2290,7 +2287,7 @@ public: @safe unittest { import core.time : hours; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; assert(SysTime.fromUnixTime(0) == @@ -4568,7 +4565,7 @@ public: @safe unittest { import core.time : msecs, hnsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); st1.roll!"days"(1); @@ -5986,7 +5983,7 @@ public: @safe unittest { import core.time : hours, seconds; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) == SysTime(DateTime(2016, 1, 1, 0, 0, 0))); @@ -6653,7 +6650,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); @@ -6726,7 +6723,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); @@ -7100,7 +7097,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); st.dayOfGregorianCal = 1; @@ -7383,7 +7380,7 @@ public: @safe unittest { import core.time : msecs, usecs, hnsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999))); @@ -7451,7 +7448,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); @@ -7509,7 +7506,7 @@ public: /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); @@ -7834,7 +7831,7 @@ public: @safe unittest { import core.time : msecs, hnsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == "20100704T070612"); @@ -7966,7 +7963,7 @@ public: @safe unittest { import core.time : msecs, hnsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == "2010-07-04T07:06:12"); @@ -8102,7 +8099,7 @@ public: @safe unittest { import core.time : msecs, hnsecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); @@ -8306,7 +8303,7 @@ public: @safe unittest { import core.time : hours, msecs, usecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; assert(SysTime.fromISOString("20100704T070612") == @@ -8545,7 +8542,7 @@ public: @safe unittest { import core.time : hours, msecs, usecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == @@ -8761,7 +8758,7 @@ public: @safe unittest { import core.time : hours, msecs, usecs; - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == @@ -8989,7 +8986,7 @@ long unixTimeToStdTime(long unixTime) @safe pure nothrow /// @safe unittest { - import std.datetime.datetime : DateTime; + import std.datetime.date : DateTime; import std.datetime.timezone : UTC; // Midnight, January 1st, 1970 diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d index 68aa1882387..88ea423af1a 100644 --- a/std/datetime/timeofday.d +++ b/std/datetime/timeofday.d @@ -6,5 +6,3 @@ Source: $(PHOBOSSRC std/datetime/_timeofday.d) +/ module std.datetime.timeofday; - -public import std.datetime.date; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 98a19d0bcb4..009483dea0a 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -9,7 +9,6 @@ module std.datetime.timezone; import core.time; import std.datetime.date; -import std.datetime.datetime; import std.datetime.systime; import std.exception : enforce; import std.range.primitives; From 3042d4fe7af03a9395cbb922f4bd6cc16ee8aa19 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 15:48:57 +0200 Subject: [PATCH 119/262] Remove std.datetime.common,datetime,timeofday. --- posix.mak | 4 ++-- std/datetime/common.d | 8 -------- std/datetime/datetime.d | 10 ---------- std/datetime/timeofday.d | 8 -------- win32.mak | 18 ------------------ win64.mak | 15 --------------- 6 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 std/datetime/common.d delete mode 100644 std/datetime/datetime.d delete mode 100644 std/datetime/timeofday.d diff --git a/posix.mak b/posix.mak index b294784d582..5cca1f4f0d1 100644 --- a/posix.mak +++ b/posix.mak @@ -181,7 +181,7 @@ PACKAGE_std_experimental = checkedint typecons PACKAGE_std_algorithm = comparison iteration mutation package searching setops \ sorting PACKAGE_std_container = array binaryheap dlist package rbtree slist util -PACKAGE_std_datetime = common date datetime interval package systime timeofday timezone +PACKAGE_std_datetime = date interval package systime timezone PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha PACKAGE_std_experimental_logger = core filelogger \ nulllogger multilogger package @@ -558,7 +558,7 @@ publictests: $(LIB) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/common.d,datetime/date.d,datetime/datetime.d,datetime/interval.d,datetime/package.d,datetime/systime.d,datetime/timeofday.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" .PHONY : auto-tester-build auto-tester-build: all checkwhitespace diff --git a/std/datetime/common.d b/std/datetime/common.d deleted file mode 100644 index 020c1477c6f..00000000000 --- a/std/datetime/common.d +++ /dev/null @@ -1,8 +0,0 @@ -// Written in the D programming language - -/++ - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/datetime/_common.d) -+/ -module std.datetime.common; diff --git a/std/datetime/datetime.d b/std/datetime/datetime.d deleted file mode 100644 index 5b1e3f53c75..00000000000 --- a/std/datetime/datetime.d +++ /dev/null @@ -1,10 +0,0 @@ -// Written in the D programming language - -/++ - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/datetime/_datetime.d) - Macros: - LREF2=$(D $2) -+/ -module std.datetime.datetime; diff --git a/std/datetime/timeofday.d b/std/datetime/timeofday.d deleted file mode 100644 index 88ea423af1a..00000000000 --- a/std/datetime/timeofday.d +++ /dev/null @@ -1,8 +0,0 @@ -// Written in the D programming language - -/++ - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Jonathan M Davis - Source: $(PHOBOSSRC std/datetime/_timeofday.d) -+/ -module std.datetime.timeofday; diff --git a/win32.mak b/win32.mak index 9e7a42456cc..896ec954066 100644 --- a/win32.mak +++ b/win32.mak @@ -196,13 +196,10 @@ SRC_STD_CONTAINER= \ std\container\package.d SRC_STD_DATETIME= \ - std\datetime\common.d \ std\datetime\date.d \ - std\datetime\datetime.d \ std\datetime\interval.d \ std\datetime\package.d \ std\datetime\systime.d \ - std\datetime\timeofday.d \ std\datetime\timezone.d SRC_STD_DIGEST= \ @@ -455,12 +452,9 @@ DOCS= \ $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ - $(DOC)\std_datetime_common.html \ $(DOC)\std_datetime_date.html \ - $(DOC)\std_datetime_datetime.html \ $(DOC)\std_datetime_interval.html \ $(DOC)\std_datetime_systime.html \ - $(DOC)\std_datetime_timeofday.html \ $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ @@ -642,13 +636,10 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=92 -unittest -main -run std\exception.d $(DMD) -conf= -cov=73 -unittest -main -run std\concurrency.d $(DMD) -conf= -cov=100 -unittest -main -run std\concurrencybase.d - $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\common.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\date.d - $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\datetime.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\interval.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\systime.d - $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timeofday.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timezone.d $(DMD) -conf= -cov=96 -unittest -main -run std\uuid.d $(DMD) -conf= -cov=100 -unittest -main -run std\digest\crc.d @@ -847,24 +838,15 @@ $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d -$(DOC)\std_datetime_common.html : $(STDDOC) std\datetime\common.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_common.html $(STDDOC) std\datetime\common.d - $(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d -$(DOC)\std_datetime_datetime.html : $(STDDOC) std\datetime\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_datetime.html $(STDDOC) std\datetime\datetime.d - $(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d $(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d -$(DOC)\std_datetime_timeofday.html : $(STDDOC) std\datetime\timeofday.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timeofday.html $(STDDOC) std\datetime\timeofday.d - $(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d diff --git a/win64.mak b/win64.mak index cdb5ea9cf87..292a6ddce5b 100644 --- a/win64.mak +++ b/win64.mak @@ -221,13 +221,10 @@ SRC_STD_CONTAINER= \ std\container\package.d SRC_STD_DATETIME= \ - std\datetime\common.d \ std\datetime\date.d \ - std\datetime\datetime.d \ std\datetime\interval.d \ std\datetime\package.d \ std\datetime\systime.d \ - std\datetime\timeofday.d \ std\datetime\timezone.d SRC_STD_DIGEST= \ @@ -480,12 +477,9 @@ DOCS= \ $(DOC)\std_digest_hmac.html \ $(DOC)\std_csv.html \ $(DOC)\std_datetime.html \ - $(DOC)\std_datetime_common.html \ $(DOC)\std_datetime_date.html \ - $(DOC)\std_datetime_datetime.html \ $(DOC)\std_datetime_interval.html \ $(DOC)\std_datetime_systime.html \ - $(DOC)\std_datetime_timeofday.html \ $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ $(DOC)\std_encoding.html \ @@ -820,24 +814,15 @@ $(DOC)\std_csv.html : $(STDDOC) std\csv.d $(DOC)\std_datetime.html : $(STDDOC) std\datetime\package.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime.html $(STDDOC) std\datetime\package.d -$(DOC)\std_datetime_common.html : $(STDDOC) std\datetime\common.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_common.html $(STDDOC) std\datetime\common.d - $(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_date.html $(STDDOC) std\datetime\date.d -$(DOC)\std_datetime_datetime.html : $(STDDOC) std\datetime\datetime.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_datetime.html $(STDDOC) std\datetime\datetime.d - $(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d $(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d -$(DOC)\std_datetime_timeofday.html : $(STDDOC) std\datetime\timeofday.d - $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timeofday.html $(STDDOC) std\datetime\timeofday.d - $(DOC)\std_datetime_timezone.html : $(STDDOC) std\datetime\timezone.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_timezone.html $(STDDOC) std\datetime\timezone.d From b7bb3bcc467605e0b6c5084f417d209fff7bf9b4 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 16:39:33 +0200 Subject: [PATCH 120/262] Fix links in std.datetime. --- std/datetime/date.d | 66 +++++++-------- std/datetime/interval.d | 176 ++++++++++++++++++++-------------------- std/datetime/package.d | 30 +++---- std/datetime/systime.d | 160 ++++++++++++++++++------------------ std/datetime/timezone.d | 16 ++-- 5 files changed, 224 insertions(+), 224 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index 3ea39e5361c..8fde94edad6 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -97,12 +97,12 @@ immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minute /++ - Combines the $(REF std,datetime,date,Date) and - $(REF std,datetime,timeofday,TimeOfDay) structs to give an object which holds + Combines the $(REF Date,std,datetime,date) and + $(REF TimeOfDay,std,datetime,date) structs to give an object which holds both the date and the time. It is optimized for calendar-based operations and has no concept of time zone. For an object which is optimized for time - operations based on the system time, use $(REF std,datetime,systime,SysTime). - $(REF std,datetime,systime,SysTime) has a concept of time zone and has much + operations based on the system time, use $(REF SysTime,std,datetime,systime). + $(REF SysTime,std,datetime,systime) has a concept of time zone and has much higher precision (hnsecs). $(D DateTime) is intended primarily for calendar-based uses rather than precise time operations. +/ @@ -474,7 +474,7 @@ public: The time portion of $(LREF DateTime). Params: - tod = The $(REF std,datetime,timeofday,TimeOfDay) to set this + tod = The $(REF TimeOfDay,std,datetime,date) to set this $(LREF DateTime)'s time portion to. +/ @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow @@ -526,7 +526,7 @@ public: year = The year to set this $(LREF DateTime)'s year to. Throws: - $(REF std,datetime,common,DateTimeException) if the new year is not + $(REF DateTimeException,std,datetime,date) if the new year is not a leap year and if the resulting date would be on February 29th. +/ @property void year(int year) @safe pure @@ -571,7 +571,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. +/ @property short yearBC() @safe const pure { @@ -607,7 +607,7 @@ public: year = The year B.C. to set this $(LREF DateTime)'s year to. Throws: - $(REF std,datetime,common,DateTimeException) if a non-positive value + $(REF DateTimeException,std,datetime,date) if a non-positive value is given. +/ @property void yearBC(int year) @safe pure @@ -676,7 +676,7 @@ public: month = The month to set this $(LREF DateTime)'s month to. Throws: - $(REF std,datetime,common,DateTimeException) if the given month is + $(REF DateTimeException,std,datetime,date) if the given month is not a valid month. +/ @property void month(Month month) @safe pure @@ -759,7 +759,7 @@ public: day = The day of the month to set this $(LREF DateTime)'s day to. Throws: - $(REF std,datetime,common,DateTimeException) if the given day is not + $(REF DateTimeException,std,datetime,date) if the given day is not a valid day of the current month. +/ @property void day(int day) @safe pure @@ -880,7 +880,7 @@ public: hour = The hour of the day to set this $(LREF DateTime)'s hour to. Throws: - $(REF std,datetime,common,DateTimeException) if the given hour would + $(REF DateTimeException,std,datetime,date) if the given hour would result in an invalid $(LREF DateTime). +/ @property void hour(int hour) @safe pure @@ -930,7 +930,7 @@ public: minute = The minute to set this $(LREF DateTime)'s minute to. Throws: - $(REF std,datetime,common,DateTimeException) if the given minute + $(REF DateTimeException,std,datetime,date) if the given minute would result in an invalid $(LREF DateTime). +/ @property void minute(int minute) @safe pure @@ -980,7 +980,7 @@ public: second = The second to set this $(LREF DateTime)'s second to. Throws: - $(REF std,datetime,common,DateTimeException) if the given seconds + $(REF DateTimeException,std,datetime,date) if the given seconds would result in an invalid $(LREF DateTime). +/ @property void second(int second) @safe pure @@ -3047,7 +3047,7 @@ public: isoString = A string formatted in the ISO format for dates and times. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF DateTime) would not be valid. +/ @@ -3136,7 +3136,7 @@ public: and times. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO Extended format or if the resulting $(LREF DateTime) would not be valid. +/ @@ -3224,7 +3224,7 @@ public: formats dates and times. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the correct format or if the resulting $(LREF DateTime) would not be valid. +/ @@ -3602,7 +3602,7 @@ public: /++ Throws: - $(REF std,datetime,common,DateTimeException) if the resulting + $(REF DateTimeException,std,datetime,date) if the resulting $(LREF Date) would not be valid. Params: @@ -3972,7 +3972,7 @@ public: year = The year to set this Date's year to. Throws: - $(REF std,datetime,common,DateTimeException) if the new year is not + $(REF DateTimeException,std,datetime,date) if the new year is not a leap year and the resulting date would be on February 29th. +/ @property void year(int year) @safe pure @@ -4019,7 +4019,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. +/ @property ushort yearBC() @safe const pure { @@ -4058,7 +4058,7 @@ public: year = The year B.C. to set this $(LREF Date)'s year to. Throws: - $(REF std,datetime,common,DateTimeException) if a non-positive value + $(REF DateTimeException,std,datetime,date) if a non-positive value is given. +/ @property void yearBC(int year) @safe pure @@ -4128,7 +4128,7 @@ public: month = The month to set this $(LREF Date)'s month to. Throws: - $(REF std,datetime,common,DateTimeException) if the given month is + $(REF DateTimeException,std,datetime,date) if the given month is not a valid month or if the current day would not be valid in the given month. +/ @@ -4208,7 +4208,7 @@ public: day = The day of the month to set this $(LREF Date)'s day to. Throws: - $(REF std,datetime,common,DateTimeException) if the given day is not + $(REF DateTimeException,std,datetime,date) if the given day is not a valid day of the current month. +/ @property void day(int day) @safe pure @@ -6591,7 +6591,7 @@ public: $(LREF Date) is on. Throws: - $(REF std,datetime,common,DateTimeException) if the given day is an + $(REF DateTimeException,std,datetime,date) if the given day is an invalid day of the year. +/ @property void dayOfYear(int day) @safe pure @@ -7273,7 +7273,7 @@ public: isoString = A string formatted in the ISO format for dates. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF Date) would not be valid. +/ @@ -7397,7 +7397,7 @@ public: dates. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO Extended format or if the resulting $(LREF Date) would not be valid. +/ @@ -7526,7 +7526,7 @@ public: formats dates. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the correct format or if the resulting $(LREF Date) would not be valid. +/ @@ -7916,7 +7916,7 @@ public: second = Second of the minute [0 - 60$(RPAREN). Throws: - $(REF std,datetime,common,DateTimeException) if the resulting + $(REF DateTimeException,std,datetime,date) if the resulting $(LREF TimeOfDay) would be not be valid. +/ this(int hour, int minute, int second = 0) @safe pure @@ -8055,7 +8055,7 @@ public: hour = The hour of the day to set this $(LREF TimeOfDay)'s hour to. Throws: - $(REF std,datetime,common,DateTimeException) if the given hour would + $(REF DateTimeException,std,datetime,date) if the given hour would result in an invalid $(LREF TimeOfDay). +/ @property void hour(int hour) @safe pure @@ -8106,7 +8106,7 @@ public: minute = The minute to set this $(LREF TimeOfDay)'s minute to. Throws: - $(REF std,datetime,common,DateTimeException) if the given minute + $(REF DateTimeException,std,datetime,date) if the given minute would result in an invalid $(LREF TimeOfDay). +/ @property void minute(int minute) @safe pure @@ -8157,7 +8157,7 @@ public: second = The second to set this $(LREF TimeOfDay)'s second to. Throws: - $(REF std,datetime,common,DateTimeException) if the given second + $(REF DateTimeException,std,datetime,date) if the given second would result in an invalid $(LREF TimeOfDay). +/ @property void second(int second) @safe pure @@ -8783,7 +8783,7 @@ public: isoString = A string formatted in the ISO format for times. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF TimeOfDay) would not be valid. +/ @@ -8893,7 +8893,7 @@ public: times. Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO Extended format or if the resulting $(LREF TimeOfDay) would not be valid. +/ @@ -9973,7 +9973,7 @@ string monthToString(Month month) @safe pure monthStr = The string representation of the month to get the Month for. Throws: - $(REF std,datetime,common,DateTimeException) if the given month is not a + $(REF DateTimeException,std,datetime,date) if the given month is not a valid month string. +/ Month monthFromString(string monthStr) @safe pure diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 50a528db68b..302b4c28e99 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -100,7 +100,7 @@ public: interval. Throws: - $(REF std,datetime,common,DateTimeException) if $(D_PARAM end) is + $(REF DateTimeException,std,datetime,date) if $(D_PARAM end) is before $(D_PARAM begin). Example: @@ -124,7 +124,7 @@ public: duration = The duration from the starting point to the end point. Throws: - $(REF std,datetime,common,DateTimeException) if the resulting + $(REF DateTimeException,std,datetime,date) if the resulting $(D end) is before $(D begin). Example: @@ -189,7 +189,7 @@ public: timePoint = The time point to set $(D begin) to. Throws: - $(REF std,datetime,common,DateTimeException) if the resulting + $(REF DateTimeException,std,datetime,date) if the resulting interval would be invalid. +/ @property void begin(TP timePoint) pure @@ -222,7 +222,7 @@ public: timePoint = The time point to set end to. Throws: - $(REF std,datetime,common,DateTimeException) if the resulting + $(REF DateTimeException,std,datetime,date) if the resulting interval would be invalid. +/ @property void end(TP timePoint) pure @@ -270,7 +270,7 @@ public: timePoint = The time point to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -298,7 +298,7 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -334,7 +334,7 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -361,7 +361,7 @@ public: interval = The interval to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -385,7 +385,7 @@ public: it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -415,7 +415,7 @@ public: interval = The interval to check for against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -446,7 +446,7 @@ public: interval = The interval to check for against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -476,7 +476,7 @@ public: interval = The interval to check for against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -500,7 +500,7 @@ public: it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -530,7 +530,7 @@ public: interval = The interval to check against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -564,7 +564,7 @@ public: interval = The interval to check against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -588,7 +588,7 @@ public: interval = The interval to check against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -611,7 +611,7 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -641,7 +641,7 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -667,7 +667,7 @@ public: interval = The interval to check for intersection with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -693,7 +693,7 @@ public: interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect or if either interval is empty. Example: @@ -728,7 +728,7 @@ public: interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect or if this interval is empty. Example: @@ -760,7 +760,7 @@ public: interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect or if this interval is empty. Example: @@ -793,7 +793,7 @@ public: interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -824,7 +824,7 @@ public: interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -851,7 +851,7 @@ public: interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -877,7 +877,7 @@ public: interval = The interval to merge with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect and are not adjacent or if either interval is empty. Example: @@ -912,7 +912,7 @@ public: interval = The interval to merge with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect and are not adjacent or if this interval is empty. Example: @@ -944,7 +944,7 @@ public: interval = The interval to merge with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect and are not adjacent or if this interval is empty. Example: @@ -978,7 +978,7 @@ public: interval = The interval to create a span together with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if either interval is + $(REF DateTimeException,std,datetime,date) if either interval is empty. Example: @@ -1013,7 +1013,7 @@ public: interval = The interval to create a span together with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -1043,7 +1043,7 @@ public: interval = The interval to create a span together with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Example: @@ -1074,7 +1074,7 @@ public: duration = The duration to shift the interval by. Throws: - $(REF std,datetime,common,DateTimeException) this interval is empty + $(REF DateTimeException,std,datetime,date) this interval is empty or if the resulting interval would be invalid. Example: @@ -1124,7 +1124,7 @@ public: to increment. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty or if the resulting interval would be invalid. Example: @@ -1171,7 +1171,7 @@ public: dir = The direction in time to expand the interval. Throws: - $(REF std,datetime,common,DateTimeException) this interval is empty + $(REF DateTimeException,std,datetime,date) this interval is empty or if the resulting interval would be invalid. Example: @@ -1249,7 +1249,7 @@ public: dir = The direction in time to expand the interval. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty or if the resulting interval would be invalid. Example: @@ -1336,7 +1336,7 @@ public: If $(D_PARAM func) ever generates a time point less than or equal to the current $(D front) of the range, then a - $(REF std,datetime,common,DateTimeException) will be thrown. The range + $(REF DateTimeException,std,datetime,date) will be thrown. The range will be empty and iteration complete when $(D_PARAM func) generates a time point equal to or beyond the $(D end) of the interval. @@ -1351,7 +1351,7 @@ public: before returning it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Warning: @@ -1430,7 +1430,7 @@ public: If $(D_PARAM func) ever generates a time point greater than or equal to the current $(D front) of the range, then a - $(REF std,datetime,common,DateTimeException) will be thrown. The range + $(REF DateTimeException,std,datetime,date) will be thrown. The range will be empty and iteration complete when $(D_PARAM func) generates a time point equal to or less than the $(D begin) of the interval. @@ -1445,7 +1445,7 @@ public: before returning it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Warning: @@ -1553,7 +1553,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. +/ void _enforceNotEmpty(size_t line = __LINE__) const pure @@ -3236,7 +3236,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); interval = The interval to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -3334,7 +3334,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); interval = The interval to check for against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -3427,7 +3427,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); interval = The interval to check against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -3503,7 +3503,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter( interval = The interval to check for intersection with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -3579,7 +3579,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects( interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect or if the given interval is empty. Example: @@ -3636,7 +3636,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect. Example: @@ -3669,7 +3669,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection( interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -3742,7 +3742,7 @@ assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent( interval = The interval to merge with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect and are not adjacent or if the given interval is empty. @@ -3813,7 +3813,7 @@ assert(PosInfInterval!Date(Date(1996, 1, 2)).merge( interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Note: @@ -3923,7 +3923,7 @@ assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); on $(D begin), causing its month to increment. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty or if the resulting interval would be invalid. Example: @@ -3991,7 +3991,7 @@ assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); on $(D begin), causing its month to increment. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty or if the resulting interval would be invalid. Example: @@ -4032,7 +4032,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); If $(D_PARAM func) ever generates a time point less than or equal to the current $(D front) of the range, then a - $(REF std,datetime,common,DateTimeException) will be thrown. + $(REF DateTimeException,std,datetime,date) will be thrown. There are helper functions in this module which generate common delegates to pass to $(D fwdRange). Their documentation starts with @@ -4045,7 +4045,7 @@ assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); before returning it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Warning: @@ -5447,7 +5447,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); interval = The interval to check for inclusion in this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -5539,7 +5539,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); interval = The interval to check for against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty Example: @@ -5644,7 +5644,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); interval = The interval to check against this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -5723,7 +5723,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter( interval = The interval to check for intersection with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -5798,7 +5798,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects( interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect or if the given interval is empty. Example: @@ -5832,7 +5832,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( interval = The interval to intersect with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect. Example: @@ -5888,7 +5888,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection( interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Example: @@ -5967,7 +5967,7 @@ assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent( interval = The interval to merge with this interval. Throws: - $(REF std,datetime,common,DateTimeException) if the two intervals do + $(REF DateTimeException,std,datetime,date) if the two intervals do not intersect and are not adjacent or if the given interval is empty. Note: @@ -6037,7 +6037,7 @@ assert(NegInfInterval!Date(Date(2012, 3, 1)).merge( interval. Throws: - $(REF std,datetime,common,DateTimeException) if the given interval + $(REF DateTimeException,std,datetime,date) if the given interval is empty. Note: @@ -6146,7 +6146,7 @@ assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); on $(D end), causing its month to increment. Throws: - $(REF std,datetime,common,DateTimeException) if empty is true or + $(REF DateTimeException,std,datetime,date) if empty is true or if the resulting interval would be invalid. Example: @@ -6214,7 +6214,7 @@ assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); on $(D end), causing their month to increment. Throws: - $(REF std,datetime,common,DateTimeException) if empty is true or + $(REF DateTimeException,std,datetime,date) if empty is true or if the resulting interval would be invalid. Example: @@ -6255,7 +6255,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); If $(D_PARAM func) ever generates a time point greater than or equal to the current $(D front) of the range, then a - $(REF std,datetime,common,DateTimeException) will be thrown. + $(REF DateTimeException,std,datetime,date) will be thrown. There are helper functions in this module which generate common delegates to pass to $(D bwdRange). Their documentation starts with @@ -6268,7 +6268,7 @@ assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); before returning it. Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. Warning: @@ -8044,20 +8044,20 @@ if (isTimePoint!TP && point and returns a time point of the same type. For instance, to iterate over all of the days in the interval $(D Interval!Date), pass a function to $(LREF Interval)'s - $(D fwdRange) where that function took a $(REF std,datetime,date,Date) and - returned a $(REF std,datetime,date,Date) which was one day later. That + $(D fwdRange) where that function took a $(REF Date,std,datetime,date) and + returned a $(REF Date,std,datetime,date) which was one day later. That function would then be used by $(D IntervalRange)'s $(D popFront) to iterate - over the $(REF std,datetime,date,Date)s in the interval. + over the $(REF Date,std,datetime,date)s in the interval. If $(D dir == Direction.fwd), then a range iterates forward in time, whereas if $(D dir == Direction.bwd), then it iterates backwards in time. So, if $(D dir == Direction.fwd) then $(D front == interval.begin), whereas if $(D dir == Direction.bwd) then $(D front == interval.end). $(D func) must generate a time point going in the proper direction of iteration, or a - $(REF std,datetime,common,DateTimeException) will be thrown. So, to iterate + $(REF DateTimeException,std,datetime,date) will be thrown. So, to iterate forward in time, the time point that $(D func) generates must be later in time than the one passed to it. If it's either identical or earlier in time, - then a $(REF std,datetime,common,DateTimeException) will be thrown. To + then a $(REF DateTimeException,std,datetime,date) will be thrown. To iterate backwards, then the generated time point must be before the time point which was passed in. @@ -8116,7 +8116,7 @@ public: The first time point in the range. Throws: - $(REF std,datetime,common,DateTimeException) if the range is empty. + $(REF DateTimeException,std,datetime,date) if the range is empty. +/ @property TP front() const pure { @@ -8139,7 +8139,7 @@ public: than the interval's $(D begin), then $(D front) is set to $(D begin). Throws: - $(REF std,datetime,common,DateTimeException) if the range is empty + $(REF DateTimeException,std,datetime,date) if the range is empty or if the generated time point is in the wrong direction (i.e. if iterating forward and the generated time point is before $(D front), or if iterating backwards and the generated time point is after @@ -8227,7 +8227,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if this interval is + $(REF DateTimeException,std,datetime,date) if this interval is empty. +/ void _enforceNotEmpty(size_t line = __LINE__) const pure @@ -8239,7 +8239,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const @@ -8581,20 +8581,20 @@ private: instance, to iterate over all of the days in the interval $(D PosInfInterval!Date), pass a function to $(D PosInfInterval)'s $(D fwdRange) where that function took a - $(REF std,datetime,date,Date) and returned a $(REF std,datetime,date,Date) + $(REF Date,std,datetime,date) and returned a $(REF Date,std,datetime,date) which was one day later. That function would then be used by $(D PosInfIntervalRange)'s $(D popFront) to iterate over the - $(REF std,datetime,date,Date)s in the interval - though obviously, since the + $(REF Date,std,datetime,date)s in the interval - though obviously, since the range is infinite, use a function such as $(D std.range.take) with it rather than iterating over $(I all) of the dates. As the interval goes to positive infinity, the range is always iterated over forwards, never backwards. $(D func) must generate a time point going in the proper direction of iteration, or a - $(REF std,datetime,common,DateTimeException) will be thrown. So, the time + $(REF DateTimeException,std,datetime,date) will be thrown. So, the time points that $(D func) generates must be later in time than the one passed to it. If it's either identical or earlier in time, then a - $(REF std,datetime,common,DateTimeException) will be thrown. + $(REF DateTimeException,std,datetime,date) will be thrown. +/ struct PosInfIntervalRange(TP) if (isTimePoint!TP) @@ -8640,7 +8640,7 @@ public: time point in the range. Throws: - $(REF std,datetime,common,DateTimeException) if the generated time + $(REF DateTimeException,std,datetime,date) if the generated time point is less than $(D front). +/ void popFront() @@ -8695,7 +8695,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const @@ -8855,20 +8855,20 @@ private: takes a time point and returns a time point of the same type. For instance, to iterate over all of the days in the interval $(D NegInfInterval!Date), pass a function to $(D NegInfInterval)'s - $(D bwdRange) where that function took a $(REF std,datetime,date,Date) and - returned a $(REF std,datetime,date,Date) which was one day earlier. That + $(D bwdRange) where that function took a $(REF Date,std,datetime,date) and + returned a $(REF Date,std,datetime,date) which was one day earlier. That function would then be used by $(D NegInfIntervalRange)'s $(D popFront) to - iterate over the $(REF std,datetime,date,Date)s in the interval - though + iterate over the $(REF Date,std,datetime,date)s in the interval - though obviously, since the range is infinite, use a function such as $(D std.range.take) with it rather than iterating over $(I all) of the dates. As the interval goes to negative infinity, the range is always iterated over backwards, never forwards. $(D func) must generate a time point going in the proper direction of iteration, or a - $(REF std,datetime,common,DateTimeException) will be thrown. So, the time + $(REF DateTimeException,std,datetime,date) will be thrown. So, the time points that $(D func) generates must be earlier in time than the one passed to it. If it's either identical or later in time, then a - $(REF std,datetime,common,DateTimeException) will be thrown. + $(REF DateTimeException,std,datetime,date) will be thrown. Also note that while normally the $(D end) of an interval is excluded from it, $(D NegInfIntervalRange) treats it as if it were included. This allows @@ -8924,7 +8924,7 @@ public: time point in the range. Throws: - $(REF std,datetime,common,DateTimeException) if the generated time + $(REF DateTimeException,std,datetime,date) if the generated time point is greater than $(D front). +/ void popFront() @@ -8979,7 +8979,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if $(D_PARAM newTP) is + $(REF DateTimeException,std,datetime,date) if $(D_PARAM newTP) is in the wrong direction. +/ void _enforceCorrectDirection(in TP newTP, size_t line = __LINE__) const diff --git a/std/datetime/package.d b/std/datetime/package.d index 367ec7a102a..1cb0af7cd92 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -6,14 +6,14 @@ This module provides: $(UL $(LI Types to represent points in time: - $(REF std,datetime,systime,SysTime), - $(REF std,datetime,date,Date), - $(REF std,datetime,timeofday,TimeOfDay), - $(REF std,datetime,datetime,DateTime).) + $(REF SysTime,std,datetime,systime), + $(REF Date,std,datetime,date), + $(REF TimeOfDay,std,datetime,date), + $(REF DateTime,std,datetime,date).) $(LI Types to represent intervals of time.) $(LI Types to represent ranges over intervals of time.) $(LI Types to represent time zones (used by - $(REF std,datetime,systime,SysTime)).) + $(REF SysTime,std,datetime,systime)).) $(LI A platform-independent, high precision stopwatch type: $(LREF StopWatch)) $(LI Benchmarking functions.) @@ -49,22 +49,22 @@ be done on a series of time points. The types that the typical user is most likely to be interested in are - $(REF std,datetime,date,Date) (if they want dates but don't care about - time), $(REF std,datetime,datetime,DateTime) (if they want dates and times - but don't care about time zones), $(REF std,datetime,systime,SysTime) (if + $(REF Date,std,datetime,date) (if they want dates but don't care about + time), $(REF DateTime,std,datetime,date) (if they want dates and times + but don't care about time zones), $(REF SysTime,std,datetime,systime) (if they want the date and time from the OS and/or do care about time zones), and StopWatch (a platform-independent, high precision stop watch). - $(REF std,datetime,date,Date) and $(REF std,datetime,datetime,DateTime) are + $(REF Date,std,datetime,date) and $(REF DateTime,std,datetime,date) are optimized for calendar-based operations, while - $(REF std,datetime,systime,SysTime) is designed for dealing with time from + $(REF SysTime,std,datetime,systime) is designed for dealing with time from the OS. Check out their specific documentation for more details. - To get the current time, use $(REF std,datetime,systime,Clock.currTime). - It will return the current time as a $(REF std,datetime,systime,SysTime). To + To get the current time, use $(REF Clock.currTime,std,datetime,systime). + It will return the current time as a $(REF SysTime,std,datetime,systime). To print it, $(D toString) is sufficient, but if using $(D toISOString), $(D toISOExtString), or $(D toSimpleString), use the corresponding $(D fromISOString), $(D fromISOExtString), or $(D fromSimpleString) to - create a $(REF std,datetime,systime,SysTime) from the string. + create a $(REF SysTime,std,datetime,systime) from the string. -------------------- auto currentTime = Clock.currTime(); @@ -87,12 +87,12 @@ auto restoredTime = SysTime.fromISOExtString(timeString); weren't). Note: - $(REF std,datetime,common,DateTimeException) is an alias for + $(REF DateTimeException,std,datetime,date) is an alias for $(REF TimeException, core,time), so you don't need to worry about core.time functions and std.datetime functions throwing different exception types (except in the rare case that they throw something other than $(REF TimeException, core,time) or - $(REF std,datetime,common,DateTimeException)). + $(REF DateTimeException,std,datetime,date)). See_Also: $(DDLINK intro-to-_datetime, Introduction to std.datetime, diff --git a/std/datetime/systime.d b/std/datetime/systime.d index e170ca036f2..e215c3e5f03 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -58,7 +58,7 @@ public: tz = The time zone for the SysTime that's returned. Throws: - $(REF std,datetime,common,DateTimeException) if it fails to get the + $(REF DateTimeException,std,datetime,date) if it fails to get the time. +/ static SysTime currTime(ClockType clockType = ClockType.normal)(immutable TimeZone tz = LocalTime()) @safe @@ -113,7 +113,7 @@ public: need to use anything other than the default. Throws: - $(REF std,datetime,common,DateTimeException) if it fails to get the + $(REF DateTimeException,std,datetime,date) if it fails to get the time. +/ static @property long currStdTime(ClockType clockType = ClockType.normal)() @trusted @@ -299,46 +299,46 @@ private: /++ $(D SysTime) is the type used to get the current time from the system or doing anything that involves time zones. Unlike - $(REF std,datetime,datetime,DateTime), the time zone is an integral part of + $(REF DateTime,std,datetime,date), the time zone is an integral part of $(D SysTime) (though for local time applications, time zones can be ignored and it will work, since it defaults to using the local time zone). It holds its internal time in std time (hnsecs since midnight, January 1st, 1 A.D. UTC), so it interfaces well with the system time. However, that means that, - unlike $(REF std,datetime,datetime,DateTime), it is not optimized for + unlike $(REF DateTime,std,datetime,date), it is not optimized for calendar-based operations, and getting individual units from it such as years or days is going to involve conversions and be less efficient. For calendar-based operations that don't - care about time zones, then $(REF std,datetime,datetime,DateTime) would be + care about time zones, then $(REF DateTime,std,datetime,date) would be the type to use. For system time, use $(D SysTime). $(LREF Clock.currTime) will return the current time as a $(D SysTime). - To convert a $(D SysTime) to a $(REF std,datetime,date,Date) or - $(REF std,datetime,datetime,DateTime), simply cast it. To convert a - $(REF std,datetime,date,Date) or $(REF std,datetime,datetime,DateTime) to a + To convert a $(D SysTime) to a $(REF Date,std,datetime,date) or + $(REF DateTime,std,datetime,date), simply cast it. To convert a + $(REF Date,std,datetime,date) or $(REF DateTime,std,datetime,date) to a $(D SysTime), use $(D SysTime)'s constructor, and pass in the ntended time - zone with it (or don't pass in a $(REF std,datetime,timezone,TimeZone), and + zone with it (or don't pass in a $(REF TimeZone,std,datetime,timezone), and the local time zone will be used). Be aware, however, that converting from a - $(REF std,datetime,datetime,DateTime) to a $(D SysTime) will not necessarily + $(REF DateTime,std,datetime,date) to a $(D SysTime) will not necessarily be 100% accurate due to DST (one hour of the year doesn't exist and another occurs twice). To not risk any conversion errors, keep times as $(D SysTime)s. Aside from DST though, there shouldn't be any conversion problems. For using time zones other than local time or UTC, use - $(REF std,datetime,timezone,PosixTimeZone) on Posix systems (or on Windows, + $(REF PosixTimeZone,std,datetime,timezone) on Posix systems (or on Windows, if providing the TZ Database files), and use - $(REF std,datetime,timezone,WindowsTimeZone) on Windows systems. The time in + $(REF WindowsTimeZone,std,datetime,timezone) on Windows systems. The time in $(D SysTime) is kept internally in hnsecs from midnight, January 1st, 1 A.D. UTC. Conversion error cannot happen when changing the time zone of a - $(D SysTime). $(REF std,datetime,timezone,LocalTime) is the - $(REF std,datetime,timezone,TimeZone) class which represents the local time, - and $(D UTC) is the $(REF std,datetime,timezone,TimeZone) class which - represents UTC. $(D SysTime) uses $(REF std,datetime,timezone,LocalTime) if - no $(REF std,datetime,timezone,TimeZone) is provided. For more details on - time zones, see the documentation for $(REF std,datetime,timezone,TimeZone), - $(REF std,datetime,timezone,PosixTimeZone), and - $(REF std,datetime,timezone,WindowsTimeZone). + $(D SysTime). $(REF LocalTime,std,datetime,timezone) is the + $(REF TimeZone,std,datetime,timezone) class which represents the local time, + and $(D UTC) is the $(REF TimeZone,std,datetime,timezone) class which + represents UTC. $(D SysTime) uses $(REF LocalTime,std,datetime,timezone) if + no $(REF TimeZone,std,datetime,timezone) is provided. For more details on + time zones, see the documentation for $(REF TimeZone,std,datetime,timezone), + $(REF PosixTimeZone,std,datetime,timezone), and + $(REF WindowsTimeZone,std,datetime,timezone). $(D SysTime)'s range is from approximately 29,000 B.C. to approximately 29,000 A.D. @@ -353,14 +353,14 @@ public: /++ Params: - dateTime = The $(REF std,datetime,datetime,DateTime) to use to set + dateTime = The $(REF DateTime,std,datetime,date) to use to set this $(LREF SysTime)'s internal std time. As - $(REF std,datetime,datetime,DateTime) has no concept of + $(REF DateTime,std,datetime,date) has no concept of time zone, tz is used as its time zone. - tz = The $(REF std,datetime,timezone,TimeZone) to use for this + tz = The $(REF TimeZone,std,datetime,timezone) to use for this $(LREF SysTime). If null, - $(REF std,datetime,timezone,LocalTime) will be used. The - given $(REF std,datetime,datetime,DateTime) is assumed to + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF DateTime,std,datetime,date) is assumed to be in the given time zone. +/ this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow @@ -394,19 +394,19 @@ public: /++ Params: - dateTime = The $(REF std,datetime,datetime,DateTime) to use to set + dateTime = The $(REF DateTime,std,datetime,date) to use to set this $(LREF SysTime)'s internal std time. As - $(REF std,datetime,datetime,DateTime) has no concept of + $(REF DateTime,std,datetime,date) has no concept of time zone, tz is used as its time zone. fracSecs = The fractional seconds portion of the time. - tz = The $(REF std,datetime,timezone,TimeZone) to use for this + tz = The $(REF TimeZone,std,datetime,timezone) to use for this $(LREF SysTime). If null, - $(REF std,datetime,timezone,LocalTime) will be used. The - given $(REF std,datetime,datetime,DateTime) is assumed to + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF DateTime,std,datetime,date) is assumed to be in the given time zone. Throws: - $(REF std,datetime,common,DateTimeException) if $(D fracSecs) is negative or if it's + $(REF DateTimeException,std,datetime,date) if $(D fracSecs) is negative or if it's greater than or equal to one second. +/ this(in DateTime dateTime, in Duration fracSecs, immutable TimeZone tz = null) @safe @@ -495,14 +495,14 @@ public: /++ Params: - date = The $(REF std,datetime,date,Date) to use to set this + date = The $(REF Date,std,datetime,date) to use to set this $(LREF SysTime)'s internal std time. As - $(REF std,datetime,date,Date) has no concept of time zone, tz + $(REF Date,std,datetime,date) has no concept of time zone, tz is used as its time zone. - tz = The $(REF std,datetime,timezone,TimeZone) to use for this + tz = The $(REF TimeZone,std,datetime,timezone) to use for this $(LREF SysTime). If null, - $(REF std,datetime,timezone,LocalTime) will be used. The - given $(REF std,datetime,date,Date) is assumed to be in the + $(REF LocalTime,std,datetime,timezone) will be used. The + given $(REF Date,std,datetime,date) is assumed to be in the given time zone. +/ this(in Date date, immutable TimeZone tz = null) @safe nothrow @@ -549,9 +549,9 @@ public: Params: stdTime = The number of hnsecs since midnight, January 1st, 1 A.D. UTC. - tz = The $(REF std,datetime,timezone,TimeZone) to use for this + tz = The $(REF TimeZone,std,datetime,timezone) to use for this $(LREF SysTime). If null, - $(REF std,datetime,timezone,LocalTime) will be used. + $(REF LocalTime,std,datetime,timezone) will be used. +/ this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow { @@ -839,7 +839,7 @@ public: year = The year to set this $(LREF SysTime)'s year to. Throws: - $(REF std,datetime,common,DateTimeException) if the new year is not + $(REF DateTimeException,std,datetime,date) if the new year is not a leap year and the resulting date would be on February 29th. +/ @property void year(int year) @safe @@ -923,7 +923,7 @@ public: Year B.C. of the Gregorian Calendar counting year 0 as 1 B.C. Throws: - $(REF std,datetime,common,DateTimeException) if $(D isAD) is true. + $(REF DateTimeException,std,datetime,date) if $(D isAD) is true. +/ @property ushort yearBC() @safe const { @@ -970,7 +970,7 @@ public: year = The year B.C. to set this $(LREF SysTime)'s year to. Throws: - $(REF std,datetime,common,DateTimeException) if a non-positive value + $(REF DateTimeException,std,datetime,date) if a non-positive value is given. +/ @property void yearBC(int year) @safe @@ -1119,7 +1119,7 @@ public: month = The month to set this $(LREF SysTime)'s month to. Throws: - $(REF std,datetime,common,DateTimeException) if the given month is + $(REF DateTimeException,std,datetime,date) if the given month is not a valid month. +/ @property void month(Month month) @safe @@ -1281,7 +1281,7 @@ public: day = The day of the month to set this $(LREF SysTime)'s day to. Throws: - $(REF std,datetime,common,DateTimeException) if the given day is not + $(REF DateTimeException,std,datetime,date) if the given day is not a valid day of the current month. +/ @property void day(int day) @safe @@ -1440,7 +1440,7 @@ public: hour = The hours to set this $(LREF SysTime)'s hour to. Throws: - $(REF std,datetime,common,DateTimeException) if the given hour are + $(REF DateTimeException,std,datetime,date) if the given hour are not a valid hour of the day. +/ @property void hour(int hour) @safe @@ -1560,7 +1560,7 @@ public: minute = The minute to set this $(LREF SysTime)'s minute to. Throws: - $(REF std,datetime,common,DateTimeException) if the given minute are + $(REF DateTimeException,std,datetime,date) if the given minute are not a valid minute of an hour. +/ @property void minute(int minute) @safe @@ -1684,7 +1684,7 @@ public: second = The second to set this $(LREF SysTime)'s second to. Throws: - $(REF std,datetime,common,DateTimeException) if the given second are + $(REF DateTimeException,std,datetime,date) if the given second are not a valid second of a minute. +/ @property void second(int second) @safe @@ -1817,7 +1817,7 @@ public: seconds to. Throws: - $(REF std,datetime,common,DateTimeException) if the given duration + $(REF DateTimeException,std,datetime,date) if the given duration is negative or if it's greater than or equal to one second. +/ @property void fracSecs(Duration fracSecs) @safe @@ -2088,7 +2088,7 @@ public: returning. Params: - timezone = The $(REF std,datetime,timezone,TimeZone) to set this + timezone = The $(REF TimeZone,std,datetime,timezone) to set this $(LREF SysTime)'s time zone to. +/ @property void timezone(immutable TimeZone timezone) @safe pure nothrow @@ -2122,7 +2122,7 @@ public: /++ Returns a $(LREF SysTime) with the same std time as this one, but with - $(REF std,datetime,timezone,LocalTime) as its time zone. + $(REF LocalTime,std,datetime,timezone) as its time zone. +/ SysTime toLocalTime() @safe const pure nothrow { @@ -7602,7 +7602,7 @@ public: /++ - Returns a $(REF std,datetime,date,Date) equivalent to this $(LREF SysTime). + Returns a $(REF Date,std,datetime,date) equivalent to this $(LREF SysTime). +/ Date opCast(T)() @safe const nothrow if (is(Unqual!T == Date)) @@ -7636,7 +7636,7 @@ public: /++ - Returns a $(REF std,datetime,datetime,DateTime) equivalent to this + Returns a $(REF DateTime,std,datetime,date) equivalent to this $(LREF SysTime). +/ DateTime opCast(T)() @safe const nothrow @@ -7696,7 +7696,7 @@ public: /++ - Returns a $(REF std,datetime,timeofday,TimeOfDay) equivalent to this + Returns a $(REF TimeOfDay,std,datetime,date) equivalent to this $(LREF SysTime). +/ TimeOfDay opCast(T)() @safe const nothrow @@ -7769,7 +7769,7 @@ public: there is no decimal point. If this $(LREF SysTime)'s time zone is - $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC (e.g. +0100 or -0700). Note that the offset from UTC is $(I not) enough to uniquely identify the time zone. @@ -7779,8 +7779,8 @@ public: $(RED Warning: Previously, toISOString did the same as $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time zone when it was not - $(REF std,datetime,timezone,LocalTime) or - $(REF std,datetime,timezone,UTC), which is not in conformance with + $(REF LocalTime,std,datetime,timezone) or + $(REF UTC,std,datetime,timezone), which is not in conformance with ISO 9601 for the non-extended string format. This has now been fixed. However, for now, fromISOString will continue to accept the extended format for the time zone so that any code which has been @@ -7912,7 +7912,7 @@ public: there is no decimal point. If this $(LREF SysTime)'s time zone is - $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) enough to uniquely identify the time zone. @@ -8048,7 +8048,7 @@ public: there is no decimal point. If this $(LREF SysTime)'s time zone is - $(REF std,datetime,timezone,LocalTime), then TZ is empty. If its time + $(REF LocalTime,std,datetime,timezone), then TZ is empty. If its time zone is $(D UTC), then it is "Z". Otherwise, it is the offset from UTC (e.g. +01:00 or -07:00). Note that the offset from UTC is $(I not) enough to uniquely identify the time zone. @@ -8204,9 +8204,9 @@ public: invalid. If there is no time zone in the string, then - $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the given offset from UTC is used. To get the returned $(LREF SysTime) to be a particular time zone, pass in that time zone and the $(LREF SysTime) to be returned will be converted to that time zone (though it will still @@ -8218,8 +8218,8 @@ public: $(RED Warning: Previously, $(LREF toISOString) did the same as $(LREF toISOExtString) and generated +HH:MM or -HH:MM for the time - zone when it was not $(REF std,datetime,timezone,LocalTime) or - $(REF std,datetime,timezone,UTC), which is not in conformance with + zone when it was not $(REF LocalTime,std,datetime,timezone) or + $(REF UTC,std,datetime,timezone), which is not in conformance with ISO 9601 for the non-extended string format. This has now been fixed. However, for now, fromISOString will continue to accept the extended format for the time zone so that any code which has been @@ -8232,7 +8232,7 @@ public: conversion occurs if null). Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ @@ -8456,9 +8456,9 @@ public: it is invalid. If there is no time zone in the string, then - $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the given offset from UTC is used. To get the returned $(LREF SysTime) to be a particular time zone, pass in that time zone and the $(LREF SysTime) to be returned will be converted to that time zone (though it will still @@ -8474,7 +8474,7 @@ public: conversion occurs if null). Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ @@ -8672,9 +8672,9 @@ public: invalid. If there is no time zone in the string, then - $(REF std,datetime,timezone,LocalTime) is used. If the time zone is "Z", + $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", then $(D UTC) is used. Otherwise, a - $(REF std,datetime,timezone,SimpleTimeZone) which corresponds to the + $(REF SimpleTimeZone,std,datetime,timezone) which corresponds to the given offset from UTC is used. To get the returned $(LREF SysTime) to be a particular time zone, pass in that time zone and the $(LREF SysTime) to be returned will be converted to that time zone (though it will still @@ -8690,7 +8690,7 @@ public: conversion occurs if null). Throws: - $(REF std,datetime,common,DateTimeException) if the given string is + $(REF DateTimeException,std,datetime,date) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. +/ @@ -9151,7 +9151,7 @@ version(StdDdoc) or UTC, depending on the call). Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(D SYSTEMTIME) will not fit in a $(LREF SysTime), which is highly unlikely to happen given that $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) is in 30,827 A.D. @@ -9172,7 +9172,7 @@ version(StdDdoc) sysTime = The $(LREF SysTime) to convert. Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(LREF SysTime) will not fit in a $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is prior to 1601 A.D. +/ @@ -9189,7 +9189,7 @@ version(StdDdoc) ft = The $(D FILETIME) struct to convert. Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(D FILETIME) cannot be represented as the return value. +/ long FILETIMEToStdTime(scope const FILETIME* ft) @safe; @@ -9206,7 +9206,7 @@ version(StdDdoc) ($(D FILETIME)s are in UTC). Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(D FILETIME) will not fit in a $(LREF SysTime). +/ SysTime FILETIMEToSysTime(scope const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; @@ -9223,7 +9223,7 @@ version(StdDdoc) UTC. Throws: - $(REF std,datetime,common,DateTimeException) if the given value will + $(REF DateTimeException,std,datetime,date) if the given value will not fit in a $(D FILETIME). +/ FILETIME stdTimeToFILETIME(long stdTime) @safe; @@ -9240,7 +9240,7 @@ version(StdDdoc) sysTime = The $(LREF SysTime) to convert. Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(LREF SysTime) will not fit in a $(D FILETIME). +/ FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; @@ -9431,7 +9431,7 @@ alias DosFileTime = uint; tz = The time zone which the DOS file time is assumed to be in. Throws: - $(REF std,datetime,common,DateTimeException) if the $(D DosFileTime) is + $(REF DateTimeException,std,datetime,date) if the $(D DosFileTime) is invalid. +/ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe @@ -9469,7 +9469,7 @@ SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime( sysTime = The $(LREF SysTime) to convert. Throws: - $(REF std,datetime,common,DateTimeException) if the given + $(REF DateTimeException,std,datetime,date) if the given $(LREF SysTime) cannot be converted to a $(D DosFileTime). +/ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe @@ -9520,9 +9520,9 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe If the time zone is $(D "-0000") (or considered to be equivalent to $(D "-0000") by section 4.3 of the spec), a - $(REF std,datetime,timezone,SimpleTimeZone) with a utc offset of $(D 0) is - used rather than $(REF std,datetime,timezone,UTC), whereas $(D "+0000") uses - $(REF std,datetime,timezone,UTC). + $(REF SimpleTimeZone,std,datetime,timezone) with a utc offset of $(D 0) is + used rather than $(REF UTC,std,datetime,timezone), whereas $(D "+0000") uses + $(REF UTC,std,datetime,timezone). Note that because $(LREF SysTime) does not currently support having a second value of 60 (as is sometimes done for leap seconds), if the date-time value @@ -9533,7 +9533,7 @@ DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe HTTP spec requires it. Throws: - $(REF std,datetime,common,DateTimeException) if the given string doesn't + $(REF DateTimeException,std,datetime,date) if the given string doesn't follow the grammar for a date-time field or if the resulting $(LREF SysTime) is invalid. +/ diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 009483dea0a..9f73484ce70 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -37,8 +37,8 @@ version(unittest) import std.exception : assertThrown; /++ - Represents a time zone. It is used with $(REF std,datetime,systime,SysTime) - to indicate the time zone of a $(REF std,datetime,systime,SysTime). + Represents a time zone. It is used with $(REF SysTime,std,datetime,systime) + to indicate the time zone of a $(REF SysTime,std,datetime,systime). +/ abstract class TimeZone { @@ -176,7 +176,7 @@ public: name = The TZ Database name of the desired time zone Throws: - $(REF std,datetime,common,DateTimeException) if the given time zone + $(REF DateTimeException,std,datetime,date) if the given time zone could not be found. +/ deprecated("Use PosixTimeZone.getTimeZone or WindowsTimeZone.getTimeZone instead") @@ -550,7 +550,7 @@ public: Throws: $(D FileException) on Posix systems if it fails to read from disk. - $(REF std,datetime,common,DateTimeException) on Windows systems if + $(REF DateTimeException,std,datetime,date) on Windows systems if it fails to read the registry. +/ deprecated("Use PosixTimeZone.getInstalledTZNames or WindowsTimeZone.getInstalledTZNames instead") @@ -1331,7 +1331,7 @@ private: UTC but no DST. It's primarily used as the time zone in the result of - $(REF std,datetime,systime,SysTime)'s $(D fromISOString), + $(REF SysTime,std,datetime,systime)'s $(D fromISOString), $(D fromISOExtString), and $(D fromSimpleString). $(D name) and $(D dstName) are always the empty string since this time zone @@ -2060,7 +2060,7 @@ public: use $(LREF PosixTimeZone)s. Throws: - $(REF std,datetime,common,DateTimeException) if the given time zone + $(REF DateTimeException,std,datetime,date) if the given time zone could not be found or $(D FileException) if the TZ Database file could not be opened. +/ @@ -2666,7 +2666,7 @@ private: /+ Throws: - $(REF std,datetime,common,DateTimeException) if $(D result) is false. + $(REF DateTimeException,std,datetime,date) if $(D result) is false. +/ static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure { @@ -2904,7 +2904,7 @@ version(StdDdoc) name = The TZ Database name of the desired time zone. Throws: - $(REF std,datetime,common,DateTimeException) if the given time + $(REF DateTimeException,std,datetime,date) if the given time zone could not be found. Example: From 75213fdc337d9834042180d40cbd7797316350b7 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 6 May 2017 16:45:27 +0200 Subject: [PATCH 121/262] Update changelog entry for splitting std.datetime. --- changelog/split-std-datetime.dd | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/changelog/split-std-datetime.dd b/changelog/split-std-datetime.dd index f4c09ba6fce..67c42db0a98 100644 --- a/changelog/split-std-datetime.dd +++ b/changelog/split-std-datetime.dd @@ -3,12 +3,9 @@ std.datetime has been split into a package. std.datetime is now a package containing the following modules: $(UL - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_common.html, std.datetime.common)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_datetime.html, std.datetime.datetime)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timeofday.html, std.datetime.timeofday)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone)) ) @@ -18,24 +15,16 @@ break, as everything in std.datetime will still be imported by importing std.datetime. New code can choose to import the modules individually or to import the entire package. -$(LINK2 $(PHOBOS_PATH)std_datetime_common.html, std.datetime.common) contains a -few types and free functions that are used by the other modules (such as Month -and isTimePoint). - -$(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date) contains Date. - -$(LINK2 $(PHOBOS_PATH)std_datetime_datetime.html, std.datetime.datetime) -contains DateTime. +$(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date) contains Date, +TimeOfDay, DateTime, and the related free functions. It also contains +DateTimeException. $(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval) contains the *Interval and *IntervalRange types as well as the related free functions. -$(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime) contains -SysTime and the related free functions. - -$(LINK2 $(PHOBOS_PATH)std_datetime_timeofday.html, std.datetime.timeofday) -contains TimeOfDay. +$(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime) +contains SysTime and the related free functions. $(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone) contains the time zone types. From 3d6b8bb4368619cb3942a34a8759c298ace9e564 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Sat, 6 May 2017 22:04:58 +0200 Subject: [PATCH 122/262] Fix Ddoc links in std.datetime --- std/datetime/package.d | 30 +++++++++++++++--------------- std/datetime/systime.d | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/std/datetime/package.d b/std/datetime/package.d index 1cb0af7cd92..82611bc8d66 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -6,14 +6,14 @@ This module provides: $(UL $(LI Types to represent points in time: - $(REF SysTime,std,datetime,systime), - $(REF Date,std,datetime,date), - $(REF TimeOfDay,std,datetime,date), - $(REF DateTime,std,datetime,date).) + $(REF SysTime,std,_datetime,systime), + $(REF Date,std,_datetime,date), + $(REF TimeOfDay,std,_datetime,date), + $(REF DateTime,std,_datetime,date).) $(LI Types to represent intervals of time.) $(LI Types to represent ranges over intervals of time.) $(LI Types to represent time zones (used by - $(REF SysTime,std,datetime,systime)).) + $(REF SysTime,std,_datetime,systime)).) $(LI A platform-independent, high precision stopwatch type: $(LREF StopWatch)) $(LI Benchmarking functions.) @@ -49,22 +49,22 @@ be done on a series of time points. The types that the typical user is most likely to be interested in are - $(REF Date,std,datetime,date) (if they want dates but don't care about - time), $(REF DateTime,std,datetime,date) (if they want dates and times - but don't care about time zones), $(REF SysTime,std,datetime,systime) (if + $(REF Date,std,_datetime,date) (if they want dates but don't care about + time), $(REF DateTime,std,_datetime,date) (if they want dates and times + but don't care about time zones), $(REF SysTime,std,_datetime,systime) (if they want the date and time from the OS and/or do care about time zones), and StopWatch (a platform-independent, high precision stop watch). - $(REF Date,std,datetime,date) and $(REF DateTime,std,datetime,date) are + $(REF Date,std,_datetime,date) and $(REF DateTime,std,_datetime,date) are optimized for calendar-based operations, while - $(REF SysTime,std,datetime,systime) is designed for dealing with time from + $(REF SysTime,std,_datetime,systime) is designed for dealing with time from the OS. Check out their specific documentation for more details. - To get the current time, use $(REF Clock.currTime,std,datetime,systime). - It will return the current time as a $(REF SysTime,std,datetime,systime). To + To get the current time, use $(REF Clock.currTime,std,_datetime,systime). + It will return the current time as a $(REF SysTime,std,_datetime,systime). To print it, $(D toString) is sufficient, but if using $(D toISOString), $(D toISOExtString), or $(D toSimpleString), use the corresponding $(D fromISOString), $(D fromISOExtString), or $(D fromSimpleString) to - create a $(REF SysTime,std,datetime,systime) from the string. + create a $(REF SysTime,std,_datetime,systime) from the string. -------------------- auto currentTime = Clock.currTime(); @@ -87,12 +87,12 @@ auto restoredTime = SysTime.fromISOExtString(timeString); weren't). Note: - $(REF DateTimeException,std,datetime,date) is an alias for + $(REF DateTimeException,std,_datetime,date) is an alias for $(REF TimeException, core,time), so you don't need to worry about core.time functions and std.datetime functions throwing different exception types (except in the rare case that they throw something other than $(REF TimeException, core,time) or - $(REF DateTimeException,std,datetime,date)). + $(REF DateTimeException,std,_datetime,date)). See_Also: $(DDLINK intro-to-_datetime, Introduction to std.datetime, diff --git a/std/datetime/systime.d b/std/datetime/systime.d index e215c3e5f03..c8e7ac0641a 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -2088,7 +2088,7 @@ public: returning. Params: - timezone = The $(REF TimeZone,std,datetime,timezone) to set this + timezone = The $(REF _TimeZone,std,datetime,_timezone) to set this $(LREF SysTime)'s time zone to. +/ @property void timezone(immutable TimeZone timezone) @safe pure nothrow From ac62197c36c666a02dd342f73cacd9b4156afc7e Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 00:51:00 +0200 Subject: [PATCH 123/262] Add the MonoTime equivalents of std.datetime.StopWatch/benchmark. std.datetime.package has StopWatch, benchmark, comparingBenchmark, and measureTime, all of which use TickDuration (which would be deprecated, but it can't be deprecated as long as those functions in std.datetime are deprecated). This commit introduces std.datetime.stopwatch to replace those functions in std.datetime. In order to avoid symbol conflicts, std.datetime.stopwatch will not be publicly import in std.datetime.package until the old symbols have been removed. std.datetime.experimental.stopwatch contains StopWatch and benchmark which have essentially the same APIs as the ones in std.datetime.package, but they use MonoTime and Duration. comparingBenchmark has not been ported to MonoTime and Duration, because it is simply a wrapper around benchmark. measureTime has not been ported to MonoTime and Duration, because it is equivalent to using StopWatch with a scope(exit) statement. The old functionality will be deprecated the major release after the new symbols have been introduced. --- posix.mak | 4 +- std/datetime/stopwatch.d | 425 +++++++++++++++++++++++++++++++++++++++ win32.mak | 6 + win64.mak | 5 + 4 files changed, 438 insertions(+), 2 deletions(-) create mode 100644 std/datetime/stopwatch.d diff --git a/posix.mak b/posix.mak index 81c5c2856ba..0cb66aadefa 100644 --- a/posix.mak +++ b/posix.mak @@ -181,7 +181,7 @@ PACKAGE_std_experimental = checkedint typecons PACKAGE_std_algorithm = comparison iteration mutation package searching setops \ sorting PACKAGE_std_container = array binaryheap dlist package rbtree slist util -PACKAGE_std_datetime = date interval package systime timezone +PACKAGE_std_datetime = date interval package stopwatch systime timezone PACKAGE_std_digest = crc digest hmac md murmurhash ripemd sha PACKAGE_std_experimental_logger = core filelogger \ nulllogger multilogger package @@ -558,7 +558,7 @@ publictests: $(LIB) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/stopwatch.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" .PHONY : auto-tester-build auto-tester-build: all checkwhitespace diff --git a/std/datetime/stopwatch.d b/std/datetime/stopwatch.d new file mode 100644 index 00000000000..69ab822921d --- /dev/null +++ b/std/datetime/stopwatch.d @@ -0,0 +1,425 @@ +// Written in the D programming language + +/++ + Module containing some basic benchmarking and timing functionality. + + For convenience, this module publicly imports $(MREF core,time). + + $(RED Unlike the other modules in std.datetime, this module is not currently + publicly imported in std.datetime.package, because the old + versions of this functionality which use + $(REF TickDuration,core,time) are in std.datetime.package and would + conflict with the symbols in this module. After the old symbols have + gone through the deprecation cycle and have been removed, then this + module will be publicly imported in std.datetime.package.) + + License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + Authors: Jonathan M Davis and Kato Shoichi + Source: $(PHOBOSSRC std/datetime/_stopwatch.d) ++/ +module std.datetime.stopwatch; + +public import core.time; +import std.typecons : Flag; + +/++ + Used by StopWatch to indicate whether it should start immediately upon + construction. + + If set to $(D AutoStart.no), then the StopWatch is not started when it is + constructed. + + Otherwise, if set to $(D AutoStart.yes), then the StopWatch is started when + it is constructed. + +/ +alias AutoStart = Flag!"autoStart"; + + +/++ + StopWatch is used to measure time just like one would do with a physical + stopwatch, including stopping, restarting, and/or resetting it. + + $(REF MonoTime,core,time) is used to hold the time, and it uses the system's + monotonic clock, which is high precision and never counts backwards (unlike + the wall clock time, which $(I can) count backwards, which is why + $(REF SysTime,std,datetime,systime) should not be used for timing). + + Note that the precision of StopWatch differs from system to system. It is + impossible for it to be the same for all systems, since the precision of the + system clock and other system-dependent and situation-dependent factors + (such as the overhead of a context switch between threads) varies from system + to system and can affect StopWatch's accuracy. + +/ +struct StopWatch +{ +public: + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + + Duration t1 = sw.peek(); + Thread.sleep(usecs(1)); + Duration t2 = sw.peek(); + assert(t2 > t1); + + Thread.sleep(usecs(1)); + sw.stop(); + + Duration t3 = sw.peek(); + assert(t3 > t2); + Duration t4 = sw.peek(); + assert(t3 == t4); + + sw.start(); + Thread.sleep(usecs(1)); + + Duration t5 = sw.peek(); + assert(t5 > t4); + + // If stopping or resetting the StopWatch is not required, then + // MonoTime can easily be used by itself without StopWatch. + auto before = MonoTime.currTime; + // do stuff... + auto timeElapsed = MonoTime.currTime - before; + } + + /++ + Constructs a StopWatch. Whether it starts immediately depends on the + $(LREF AutoStart) argument. + + If $(D StopWatch.init) is used, then the constructed StopWatch isn't + running (and can't be, since no constructor ran). + +/ + this(AutoStart autostart) @safe nothrow @nogc + { + if (autostart) + start(); + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + { + auto sw = StopWatch(AutoStart.yes); + assert(sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() > Duration.zero); + } + { + auto sw = StopWatch(AutoStart.no); + assert(!sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() == Duration.zero); + } + { + StopWatch sw; + assert(!sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() == Duration.zero); + } + + assert(StopWatch.init == StopWatch(AutoStart.no)); + assert(StopWatch.init != StopWatch(AutoStart.yes)); + } + + + /++ + Resets the StopWatch. + + The StopWatch can be reset while it's running, and resetting it while + it's running will not cause it to stop. + +/ + void reset() @safe nothrow @nogc + { + if (_running) + _timeStarted = MonoTime.currTime; + _ticksElapsed = 0; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + Thread.sleep(usecs(1)); + sw.stop(); + assert(sw.peek() > Duration.zero); + sw.reset(); + assert(sw.peek() == Duration.zero); + } + + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + Thread.sleep(msecs(1)); + assert(sw.peek() > msecs(1)); + immutable before = MonoTime.currTime; + + // Just in case the system clock is slow enough or the system is fast + // enough for the call to MonoTime.currTime inside of reset to get + // the same that we just got by calling MonoTime.currTime. + Thread.sleep(usecs(1)); + + sw.reset(); + assert(sw.peek() < msecs(1)); + assert(sw._timeStarted > before); + assert(sw._timeStarted < MonoTime.currTime); + } + + + /++ + Starts the StopWatch. + + start should not be called if the StopWatch is already running. + +/ + void start() @safe nothrow @nogc + in { assert(!_running, "start was called when the StopWatch was already running."); } + body + { + _running = true; + _timeStarted = MonoTime.currTime; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + StopWatch sw; + assert(!sw.running); + assert(sw.peek() == Duration.zero); + sw.start(); + assert(sw.running); + Thread.sleep(usecs(1)); + assert(sw.peek() > Duration.zero); + } + + + /++ + Stops the StopWatch. + + stop should not be called if the StopWatch is not running. + +/ + void stop() @safe nothrow @nogc + in { assert(_running, "stop was called when the StopWatch was not running."); } + body + { + _running = false; + _ticksElapsed += MonoTime.currTime.ticks - _timeStarted.ticks; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.yes); + assert(sw.running); + Thread.sleep(usecs(1)); + immutable t1 = sw.peek(); + assert(t1 > Duration.zero); + + sw.stop(); + assert(!sw.running); + immutable t2 = sw.peek(); + assert(t2 > t1); + immutable t3 = sw.peek(); + assert(t2 == t3); + } + + + /++ + Peek at the amount of time that the the StopWatch has been running. + + This does not include any time during which the StopWatch was stopped but + does include $(I all) of the time that it was running and not just the + time since it was started last. + + Calling $(LREF reset) will reset this to $(D Duration.zero). + +/ + Duration peek() @safe const nothrow @nogc + { + enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); + immutable hnsecsMeasured = convClockFreq(_ticksElapsed, MonoTime.ticksPerSecond, hnsecsPerSecond); + return _running ? MonoTime.currTime - _timeStarted + hnsecs(hnsecsMeasured) + : hnsecs(hnsecsMeasured); + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + auto sw = StopWatch(AutoStart.no); + assert(sw.peek() == Duration.zero); + sw.start(); + + Thread.sleep(usecs(1)); + assert(sw.peek() >= usecs(1)); + + Thread.sleep(usecs(1)); + assert(sw.peek() >= usecs(2)); + + sw.stop(); + immutable stopped = sw.peek(); + Thread.sleep(usecs(1)); + assert(sw.peek() == stopped); + + sw.start(); + Thread.sleep(usecs(1)); + assert(sw.peek() > stopped); + } + + @safe nothrow @nogc unittest + { + assert(StopWatch.init.peek() == Duration.zero); + } + + + /++ + Sets the total time which the StopWatch has been running (i.e. what peek + returns). + + The StopWatch does not have to be stopped for setTimeElapsed to be + called, nor will calling it cause the StopWatch to stop. + +/ + void setTimeElapsed(Duration timeElapsed) @safe nothrow @nogc + { + enum hnsecsPerSecond = convert!("seconds", "hnsecs")(1); + _ticksElapsed = convClockFreq(timeElapsed.total!"hnsecs", hnsecsPerSecond, MonoTime.ticksPerSecond); + _timeStarted = MonoTime.currTime; + } + + /// + @system nothrow @nogc unittest + { + import core.thread : Thread; + + StopWatch sw; + sw.setTimeElapsed(hours(1)); + + // As discussed in MonoTime's documentation, converting between + // Duration and ticks is not exact, though it will be close. + // How exact it is depends on the frequency/resolution of the + // system's monotonic clock. + assert(abs(sw.peek() - hours(1)) < usecs(1)); + + sw.start(); + Thread.sleep(usecs(1)); + assert(sw.peek() > hours(1) + usecs(1)); + } + + + /++ + Returns whether this StopWatch is currently running. + +/ + @property bool running() @safe const pure nothrow @nogc + { + return _running; + } + + /// + @safe nothrow @nogc unittest + { + StopWatch sw; + assert(!sw.running); + sw.start(); + assert(sw.running); + sw.stop(); + assert(!sw.running); + } + + +private: + + // We track the ticks for the elapsed time rather than a Duration so that we + // don't lose any precision. + + bool _running = false; // Whether the StopWatch is currently running + MonoTime _timeStarted; // The time the StopWatch started measuring (i.e. when it was started or reset). + long _ticksElapsed; // Total time that the StopWatch ran before it was stopped last. +} + + +/++ + Benchmarks code for speed assessment and comparison. + + Params: + fun = aliases of callable objects (e.g. function names). Each callable + object should take no arguments. + n = The number of times each function is to be executed. + + Returns: + The amount of time (as a $(REF Duration,core,time)) that it took to call + each function $(D n) times. The first value is the length of time that + it took to call $(D fun[0]) $(D n) times. The second value is the length + of time it took to call $(D fun[1]) $(D n) times. Etc. + +/ +Duration[fun.length] benchmark(fun...)(uint n) +{ + Duration[fun.length] result; + auto sw = StopWatch(AutoStart.yes); + + foreach (i, unused; fun) + { + sw.reset(); + foreach (_; 0 .. n) + fun[i](); + result[i] = sw.peek(); + } + + return result; +} + +/// +@safe unittest +{ + import std.conv : to; + + int a; + void f0() {} + void f1() { auto b = a; } + void f2() { auto b = to!string(a); } + auto r = benchmark!(f0, f1, f2)(10_000); + Duration f0Result = r[0]; // time f0 took to run 10,000 times + Duration f1Result = r[1]; // time f1 took to run 10,000 times + Duration f2Result = r[2]; // time f2 took to run 10,000 times +} + +@safe nothrow unittest +{ + import std.conv : to; + + int a; + void f0() nothrow {} + void f1() nothrow { auto b = to!string(a); } + auto r = benchmark!(f0, f1)(1000); + assert(r[0] > Duration.zero); + assert(r[1] > Duration.zero); + assert(r[1] > r[0]); + assert(r[0] < seconds(1)); + assert(r[1] < seconds(1)); +} + +@safe nothrow @nogc unittest +{ + int f0Count; + int f1Count; + int f2Count; + void f0() nothrow @nogc { ++f0Count; } + void f1() nothrow @nogc { ++f1Count; } + void f2() nothrow @nogc { ++f2Count; } + auto r = benchmark!(f0, f1, f2)(552); + assert(f0Count == 552); + assert(f1Count == 552); + assert(f2Count == 552); +} diff --git a/win32.mak b/win32.mak index 896ec954066..3a9f3b48d37 100644 --- a/win32.mak +++ b/win32.mak @@ -199,6 +199,7 @@ SRC_STD_DATETIME= \ std\datetime\date.d \ std\datetime\interval.d \ std\datetime\package.d \ + std\datetime\stopwatch.d \ std\datetime\systime.d \ std\datetime\timezone.d @@ -454,6 +455,7 @@ DOCS= \ $(DOC)\std_datetime.html \ $(DOC)\std_datetime_date.html \ $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_stopwatch.html \ $(DOC)\std_datetime_systime.html \ $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ @@ -639,6 +641,7 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\date.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\interval.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d + $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\stopwatch.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\systime.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\timezone.d $(DMD) -conf= -cov=96 -unittest -main -run std\uuid.d @@ -844,6 +847,9 @@ $(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d $(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d +$(DOC)\std_datetime_stopwatch.html : $(STDDOC) std\datetime\stopwatch.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_stopwatch.html $(STDDOC) std\datetime\stopwatch.d + $(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d diff --git a/win64.mak b/win64.mak index 292a6ddce5b..80bc8467ea0 100644 --- a/win64.mak +++ b/win64.mak @@ -224,6 +224,7 @@ SRC_STD_DATETIME= \ std\datetime\date.d \ std\datetime\interval.d \ std\datetime\package.d \ + std\datetime\stopwatch.d \ std\datetime\systime.d \ std\datetime\timezone.d @@ -479,6 +480,7 @@ DOCS= \ $(DOC)\std_datetime.html \ $(DOC)\std_datetime_date.html \ $(DOC)\std_datetime_interval.html \ + $(DOC)\std_datetime_stopwatch.html \ $(DOC)\std_datetime_systime.html \ $(DOC)\std_datetime_timezone.html \ $(DOC)\std_demangle.html \ @@ -820,6 +822,9 @@ $(DOC)\std_datetime_date.html : $(STDDOC) std\datetime\date.d $(DOC)\std_datetime_interval.html : $(STDDOC) std\datetime\interval.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_interval.html $(STDDOC) std\datetime\interval.d +$(DOC)\std_datetime_stopwatch.html : $(STDDOC) std\datetime\stopwatch.d + $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_stopwatch.html $(STDDOC) std\datetime\stopwatch.d + $(DOC)\std_datetime_systime.html : $(STDDOC) std\datetime\systime.d $(DMD) -c -o- $(DDOCFLAGS) -Df$(DOC)\std_datetime_systime.html $(STDDOC) std\datetime\systime.d From 55dffcfa95915849ed4a68bd2121cbfb726cf42f Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 01:13:26 +0200 Subject: [PATCH 124/262] Update the changelog entry for splitting std.datetime. Now, it includes information on std.datetime.stopwatch. --- changelog/split-std-datetime.dd | 34 +++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/changelog/split-std-datetime.dd b/changelog/split-std-datetime.dd index 67c42db0a98..5e6f9b67578 100644 --- a/changelog/split-std-datetime.dd +++ b/changelog/split-std-datetime.dd @@ -5,6 +5,7 @@ std.datetime is now a package containing the following modules: $(UL $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval)) + $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_stopwatch.html, std.datetime.stopwatch)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime)) $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone)) ) @@ -31,9 +32,30 @@ contains the time zone types. $(LINK2 $(PHOBOS_PATH)std_datetime.html, std.datetime.package) contains StopWatch and the benchmarking functions (so, they can only be imported via -std.datetime and not via a submodule). As those functions use TickDuration, -they are slated for deprecation and will be replaced with corresponding -functions that use MonoTime and Duration. Eventually, the new functions will -end up in a submodule of std.datetime, and the old ones will have been removed, -leaving nothing in std.datetime.package except for documentation and the public -imports of the rest of std.datetime. +std.datetime and not via a submodule). As those functions use +$(REF TickDuration,core,time) (which is being replaced by +$(REF MonoTime,core,time), they are slated for deprecation. + +$(LINK2 $(PHOBOS_PATH)std_datetime_stopwatch.html, std.datetime.stopwatch) has +been added. It contains versions of StopWatch and benchmark which have almost +the same API as the existing symbols, but they use $(REF MonoTime,core,time) and +$(REF Duration,core,time) instead of $(REF TickDuration,core,time). In the next +major release, the old functions in std.datetime.package will be deprecated, so +code which uses the old benchmarking functions should be updated to use +std.datetime.stopwatch. + +However, note that in order to avoid irreconcilable symbol conflicts between +the new and old versions, std.datetime.stopwatch will not be publicly imported +by std.datetime.package until the old symbols have been removed. So, for the +time being, code using $(REF StopWatch,std,datetime,stopwatch) or +$(REF StopWatch,std,datetime,benchmark) will need to import +std.datetime.stopwatch directly. Code which imports both std.datetime and +std.datetime.stopwatch will need to either use selective imports or fully +qualified symbols to reconcile the symbol conflicts, but no code will be +affected by the changes until it's updated to import std.datetime.stopwatch, +and when the old symbols are finally removed, the selective imports and fully +qualified paths to the new symbols will continue to work and won't break +(though at that point, simply importing std.datetime will work, since +std.datetime.package will have been updated to publicly import +std.datetime.stopwatch). Code that simply imporst std.datetime.stopwatch without +importing std.datetime will not have to worry about symbol conflicts. From dfd7de87778c18f432c45d7577a04ac6b17b643a Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 01:16:25 +0200 Subject: [PATCH 125/262] Update changelog entry to use MREF instead of LINK2. --- changelog/split-std-datetime.dd | 55 ++++++++++++++------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/changelog/split-std-datetime.dd b/changelog/split-std-datetime.dd index 5e6f9b67578..656979a6cac 100644 --- a/changelog/split-std-datetime.dd +++ b/changelog/split-std-datetime.dd @@ -3,46 +3,39 @@ std.datetime has been split into a package. std.datetime is now a package containing the following modules: $(UL - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_stopwatch.html, std.datetime.stopwatch)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime)) - $(LI $(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone)) + $(LI $(MREF std,datetime,date)) + $(LI $(MREF std,datetime,interval)) + $(LI $(MREF std,datetime,stopwatch)) + $(LI $(MREF std,datetime,systime)) + $(LI $(MREF std,datetime,timezone)) ) -$(LINK2 $(PHOBOS_PATH)std_datetime.html, std.datetime.package) publicly imports -all of those modules. So, it should be the case that no existing code will -break, as everything in std.datetime will still be imported by importing -std.datetime. New code can choose to import the modules individually or to -import the entire package. +$(MREF std,datetime,package) publicly imports all of those modules. So, it +should be the case that no existing code will break, as everything in +std.datetime will still be imported by importing std.datetime. New code can +choose to import the modules individually or to import the entire package. -$(LINK2 $(PHOBOS_PATH)std_datetime_date.html, std.datetime.date) contains Date, -TimeOfDay, DateTime, and the related free functions. It also contains -DateTimeException. +$(MREF std,datetime,date) contains Date, TimeOfDay, DateTime, and the related +free functions. It also contains DateTimeException. -$(LINK2 $(PHOBOS_PATH)std_datetime_interval.html, std.datetime.interval) -contains the *Interval and *IntervalRange types as well as the related free -functions. +$(MREF std,datetime,interval) contains the *Interval and *IntervalRange types +as well as the related free functions. -$(LINK2 $(PHOBOS_PATH)std_datetime_systime.html, std.datetime.systime) -contains SysTime and the related free functions. +$(MREF std,datetime,systime) contains SysTime and the related free functions. -$(LINK2 $(PHOBOS_PATH)std_datetime_timezone.html, std.datetime.timezone) -contains the time zone types. +$(MREF std,datetime,timezone) contains the time zone types. -$(LINK2 $(PHOBOS_PATH)std_datetime.html, std.datetime.package) contains -StopWatch and the benchmarking functions (so, they can only be imported via -std.datetime and not via a submodule). As those functions use -$(REF TickDuration,core,time) (which is being replaced by +$(MREF std,datetime,package) contains StopWatch and the benchmarking functions +(so, they can only be imported via std.datetime and not via a submodule). As +those functions use $(REF TickDuration,core,time) (which is being replaced by $(REF MonoTime,core,time), they are slated for deprecation. -$(LINK2 $(PHOBOS_PATH)std_datetime_stopwatch.html, std.datetime.stopwatch) has -been added. It contains versions of StopWatch and benchmark which have almost -the same API as the existing symbols, but they use $(REF MonoTime,core,time) and -$(REF Duration,core,time) instead of $(REF TickDuration,core,time). In the next -major release, the old functions in std.datetime.package will be deprecated, so -code which uses the old benchmarking functions should be updated to use -std.datetime.stopwatch. +$(MREF std,datetime,stopwatch) has been added. It contains versions of +StopWatch and benchmark which have almost the same API as the existing symbols, +but they use $(REF MonoTime,core,time) and $(REF Duration,core,time) instead of +$(REF TickDuration,core,time). In the next major release, the old functions in +std.datetime.package will be deprecated, so code which uses the old +benchmarking functions should be updated to use std.datetime.stopwatch. However, note that in order to avoid irreconcilable symbol conflicts between the new and old versions, std.datetime.stopwatch will not be publicly imported From 90971797e6956c301ebf0d2a996e2a6888cce3ac Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 01:21:30 +0200 Subject: [PATCH 126/262] Warn about impending deprecation of functions in std.datetime.package. --- std/datetime/package.d | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/std/datetime/package.d b/std/datetime/package.d index 82611bc8d66..9395590a002 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -170,6 +170,12 @@ alias AutoStart = Flag!"autoStart"; /++ + $(RED This will be deprecated in 2.076. Please use + $(REF StopWatch,std,datetime,stopwatch) instead. It uses + $(REF Monotime,core,time) and $(REF Duration,core,time) rather + than $(REF TickDuration,core,time), which will also be deprecated in + 2.076.) + $(D StopWatch) measures time as precisely as possible. This class uses a high-performance counter. On Windows systems, it uses @@ -416,6 +422,12 @@ private: /++ + $(RED This will be deprecated in 2.076. Please use + $(REF benchmark,std,datetime,stopwatch) instead. It uses + $(REF Monotime,core,time) and $(REF Duration,core,time) rather + than $(REF TickDuration,core,time), which will also be deprecated in + 2.076.) + Benchmarks code for speed assessment and comparison. Params: @@ -526,6 +538,12 @@ private: /++ + $(RED This will be deprecated in 2.076. Please use + $(REF benchmark,std,datetime,stopwatch) instead. This function has + not been ported to $(REF Monotime,core,time) and + $(REF Duration,core,time), because it is a trivial wrapper around + benchmark.) + Benchmark with two functions comparing. Params: @@ -568,6 +586,12 @@ ComparingBenchmarkResult comparingBenchmark(alias baseFunc, /++ + $(RED This will be deprecated in 2.076. Please use + $(REF StopWatch,std,datetime,stopwatch) instead. This function has + not been ported to $(REF Monotime,core,time) and + $(REF Duration,core,time), because it is a trivial wrapper around + StopWatch.) + Function for starting to a stop watch time when the function is called and stopping it when its return value goes out of scope and is destroyed. From 7d7ce4a5ebaca7155e754b1bf7b45366e87d0194 Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Sun, 7 May 2017 10:54:53 +0200 Subject: [PATCH 127/262] Logger sharedLog comment on thread-safety fix Issue 16232 - std.experimental.logger.core.sharedLog isn't thread-safe --- std/experimental/logger/core.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 22ba7d25283..e8e8d4c0531 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -1661,6 +1661,9 @@ While getting and setting $(D sharedLog) is thread-safe, it has to be considered that the returned reference is only a current snapshot and in the following code, you must make sure no other thread reassigns to it between reading and writing $(D sharedLog). + +$(D sharedLog) is only thread-safe if the the used $(D Logger) is thread-safe. +The default $(D Logger) is thread-safe. ------------- if (sharedLog !is myLogger) sharedLog = new myLogger; From 07fddbb3884448bd4f580da4e0472c138f5e58e8 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 11:27:48 +0200 Subject: [PATCH 128/262] Move deprecations along. --- std/algorithm/package.d | 3 --- std/math.d | 8 -------- std/meta.d | 11 ----------- std/net/curl.d | 10 ---------- 4 files changed, 32 deletions(-) diff --git a/std/algorithm/package.d b/std/algorithm/package.d index 2fc25abb22f..fab759773df 100644 --- a/std/algorithm/package.d +++ b/std/algorithm/package.d @@ -195,6 +195,3 @@ public import std.algorithm.searching; public import std.algorithm.sorting; static import std.functional; -// Explicitly undocumented. It will be removed in March 2017. @@@DEPRECATED_2017-03@@@ -deprecated("Please use std.functional.forward instead.") -alias forward = std.functional.forward; diff --git a/std/math.d b/std/math.d index d26638e7c61..8c394ab1730 100644 --- a/std/math.d +++ b/std/math.d @@ -7217,14 +7217,6 @@ bool approxEqual(T, U)(T lhs, U rhs) assert(approxEqual(10, a)); } -// Explicitly undocumented. They will be removed in March 2017. @@@DEPRECATED_2017-03@@@ -// Included for backwards compatibility with Phobos1 -deprecated("Phobos1 math functions are deprecated, use isNaN") alias isnan = isNaN; -deprecated("Phobos1 math functions are deprecated, use isFinite ") alias isfinite = isFinite; -deprecated("Phobos1 math functions are deprecated, use isNormal ") alias isnormal = isNormal; -deprecated("Phobos1 math functions are deprecated, use isSubnormal ") alias issubnormal = isSubnormal; -deprecated("Phobos1 math functions are deprecated, use isInfinity ") alias isinf = isInfinity; - @safe pure nothrow @nogc unittest { real num = real.infinity; diff --git a/std/meta.d b/std/meta.d index 3e2cd98d589..9e7a061f887 100644 --- a/std/meta.d +++ b/std/meta.d @@ -252,13 +252,6 @@ if (!isAggregateType!T || is(Unqual!T == T)) alias OldAlias = T; } -deprecated("Alias will stop to unqualify user defined types.") -package template OldAlias(T) -if (isAggregateType!T && !is(Unqual!T == T)) -{ - alias OldAlias = Unqual!T; -} - @safe unittest { static struct Foo {} @@ -348,10 +341,6 @@ if (args.length >= 1) static assert(staticIndexOf!("void", 0, void, "void") == 2); } -// Explicitly undocumented. It will be removed in February 2017. @@@DEPRECATED_2017-02@@@ -deprecated("Please use staticIndexOf") -alias IndexOf = staticIndexOf; - /** * Returns a typetuple created from TList with the first occurrence, * if any, of T removed. diff --git a/std/net/curl.d b/std/net/curl.d index 15389f7c513..711c49819c6 100644 --- a/std/net/curl.d +++ b/std/net/curl.d @@ -826,16 +826,6 @@ if (is(T == char) || is(T == ubyte)) return _basicHTTP!(T)(url, null, conn); } -// Explicitly undocumented. It will be removed in February 2017. @@@DEPRECATED_2017-02@@@ -deprecated("options does not send any data") -T[] options(T = char, OptionsUnit)(const(char)[] url, - const(OptionsUnit)[] optionsData = null, - HTTP conn = HTTP()) -if (is(T == char) || is(T == ubyte)) -{ - return options!T(url, conn); -} - @system unittest { import std.algorithm.searching : canFind; From 29273f261c94e1bbe1042ec58a362d70cb344188 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Sun, 7 May 2017 13:02:25 +0200 Subject: [PATCH 129/262] remove .deps file generation - for development, people can use the much faster std/algorithm.test targets - for batch unittest recompiling isn't of much interest as all the testers just start from a clean slate anyhow - furthermore the .deps generation is broken anyhow, see Issue 7016 --- posix.mak | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/posix.mak b/posix.mak index 87f72efe363..46632ade023 100644 --- a/posix.mak +++ b/posix.mak @@ -275,10 +275,6 @@ unittest-%: $(MAKE) -f $(MAKEFILE) unittest OS=$(OS) MODEL=$(MODEL) DMD=$(DMD) BUILD=$* endif -depend: $(addprefix $(ROOT)/unittest/,$(addsuffix .deps,$(D_MODULES))) - --include $(addprefix $(ROOT)/unittest/,$(addsuffix .deps,$(D_MODULES))) - ################################################################################ # Patterns begin here ################################################################################ @@ -323,13 +319,11 @@ $(addprefix $(ROOT)/unittest/,$(DISABLED_TESTS)) : @echo Testing $@ - disabled UT_D_OBJS:=$(addprefix $(ROOT)/unittest/,$(addsuffix .o,$(D_MODULES))) +# need to recompile all unittest objects whenever sth. changes +$(UT_D_OBJS): $(ALL_D_FILES) $(UT_D_OBJS): $(ROOT)/unittest/%.o: %.d @mkdir -p $(dir $@) - $(DMD) $(DFLAGS) -unittest -c -of$@ -deps=$(@:.o=.deps.tmp) $< - @echo $@: `sed 's|.*(\(.*\)).*|\1|' $(@:.o=.deps.tmp) | sort | uniq` \ - >$(@:.o=.deps) - @rm $(@:.o=.deps.tmp) -# $(DMD) $(DFLAGS) -unittest -c -of$@ $*.d + $(DMD) $(DFLAGS) -unittest -c -of$@ $< ifneq (1,$(SHARED)) From 1e7327b0592b5137384d3ac139e1651a58d1fed2 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 7 May 2017 14:22:39 +0000 Subject: [PATCH 130/262] posix.mak: Discard DDoc output of DDoc warning test Send documentation output to /dev/null when checking for warnings. --- posix.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posix.mak b/posix.mak index 87f72efe363..dbd217db0db 100644 --- a/posix.mak +++ b/posix.mak @@ -541,7 +541,7 @@ style: ../dscanner/dsc $(LIB) has_public_example publictests done @echo "Check that Ddoc runs without errors" - $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -main -c -o- $$(find etc std -type f -name '*.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1 + $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -w -D -Df/dev/null -main -c -o- $$(find etc std -type f -name '*.d') 2>&1 | grep -v "Deprecation:"; test $$? -eq 1 # at the moment libdparse has problems to parse some modules (->excludes) @echo "Running DScanner" From 76c6dcc9844d30e5247503d235851de724834370 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 7 May 2017 15:25:18 +0000 Subject: [PATCH 131/262] Allow ignoring individual lines for coverage analysis This adds a work-around to the non-deterministic coverage fluctuations in std.parallelism. Lines with `nocoverage` on them will be excluded from coverage analysis, and be considered as not containing any code. --- circleci.sh | 4 ++++ std/parallelism.d | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/circleci.sh b/circleci.sh index 218958e4f66..b8e9d89e34a 100755 --- a/circleci.sh +++ b/circleci.sh @@ -121,6 +121,10 @@ coverage() # instead we run all tests individually make -f posix.mak $(find std etc -name "*.d" | sed "s/[.]d$/.test") + + # Remove coverage information from lines with non-deterministic coverage. + # These lines are annotated with a comment containing "nocoverage". + sed -i 's/^ *[0-9]*\(|.*nocoverage.*\)$/ \1/' ./*.lst } case $1 in diff --git a/std/parallelism.d b/std/parallelism.d index 7a5bebaeae0..a079458422c 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -629,7 +629,7 @@ struct Task(alias fun, Args...) if (exception) { - throw exception; + throw exception; // nocoverage } static if (!is(ReturnType == void)) From 522952b0c9c161b8f8e2f7fbbc3ace4b7f201295 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sun, 7 May 2017 17:43:39 +0200 Subject: [PATCH 132/262] Fix std.datetime autotester failure for FreeBSD 10.3/11. The tests fail depending on your timezone due to a known FreeBSD bug, so we need to disable them until the bug in FreeBSD gets fixed. --- std/datetime/timezone.d | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 9f73484ce70..17841bbb2f3 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -728,17 +728,29 @@ public: @safe unittest { - assert(LocalTime().stdName !is null); - - version(Posix) + version(FreeBSD) { - scope(exit) clearTZEnvVar(); + // A bug on FreeBSD 9+ makes it so that this test fails. + // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=168862 + } + else version(NetBSD) + { + // The same bug on NetBSD 7+ + } + else + { + assert(LocalTime().stdName !is null); - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().stdName == "PST"); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("America/New_York"); - assert(LocalTime().stdName == "EST"); + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().stdName == "PST"); + + setTZEnvVar("America/New_York"); + assert(LocalTime().stdName == "EST"); + } } } From 3a367d3f4e72f690d90b5c8af7cc0a65144c8467 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Mon, 8 May 2017 12:32:24 +0100 Subject: [PATCH 133/262] Test positional width/precision --- std/format.d | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/std/format.d b/std/format.d index 7c4c45255b6..a72d77532d5 100644 --- a/std/format.d +++ b/std/format.d @@ -4092,8 +4092,13 @@ private T getNth(string kind, alias Condition, T, A...)(uint index, A args) // width/precision assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2)) == "integer width expected, not double for argument #1"); + assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2)) + == "integer width expected, not double for argument #1"); + assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2)) == "integer precision expected, not char for argument #1"); + assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3)) + == "integer precision expected, not double for argument #1"); assert(collectExceptionMsg!FormatException(format("%.*d", 5)) == "Orphan format specifier: %d"); assert(collectExceptionMsg!FormatException(format("%*.*d", 5)) @@ -5601,12 +5606,18 @@ private bool needToSwapEndianess(Char)(const ref FormatSpec!Char f) r = format("%-3d", 7); assert(r == "7 "); + r = format("%-1*d", 4, 3); + assert(r == "3 "); + r = format("%*d", -3, 7); assert(r == "7 "); r = format("%.*d", -3, 7); assert(r == "7"); + r = format("%-1.*f", 2, 3.1415); + assert(r == "3.14"); + r = format("abc"c); assert(r == "abc"); From 25fc16fa219d6e0e1bc48f3c422b3f10b3cd3a67 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 8 May 2017 12:01:18 -0400 Subject: [PATCH 134/262] Removed level of indirection from internal std.xml.ElementParser code --- std/xml.d | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/std/xml.d b/std/xml.d index 7cb56b00c68..fb7e4b6b2f8 100644 --- a/std/xml.d +++ b/std/xml.d @@ -564,7 +564,7 @@ class Document : Element this(xml.tag); prolog = s[0 .. tagString.ptr - s.ptr]; parse(xml); - epilog = *xml.s; + epilog = xml.s; } /** @@ -1707,7 +1707,7 @@ class DocumentParser : ElementParser body { xmlText = xmlText_; - s = &xmlText; + s = xmlText; super(); // Initialize everything parse(); // Parse through the root tag (but not beyond) } @@ -1734,7 +1734,7 @@ class ElementParser { Tag tag_; string elementStart; - string* s; + string s; Handler commentHandler = null; Handler cdataHandler = null; @@ -1752,7 +1752,7 @@ class ElementParser } // Private constructor for empty tags - this(Tag tag, string* t) @safe @nogc pure nothrow + this(Tag tag, string t) @safe @nogc pure nothrow { s = t; this(); @@ -1835,7 +1835,7 @@ class ElementParser protected this() @safe @nogc pure nothrow { - elementStart = *s; + elementStart = s; } /** @@ -1991,37 +1991,37 @@ class ElementParser while (s.length != 0) { - if (startsWith(*s,"")); + chop(s,4); + t = chop(s,indexOf(s,"-->")); if (commentHandler.funcptr !is null) commentHandler(t); - chop(*s,3); + chop(s,3); } - else if (startsWith(*s,"")); + chop(s,9); + t = chop(s,indexOf(s,"]]>")); if (cdataHandler.funcptr !is null) cdataHandler(t); - chop(*s,3); + chop(s,3); } - else if (startsWith(*s,"")); + chop(s,2); + t = chop(s,indexOf(s,">")); if (xiHandler.funcptr !is null) xiHandler(t); - chop(*s,1); + chop(s,1); } - else if (startsWith(*s,"")); + chop(s,2); + t = chop(s,indexOf(s,"?>")); if (piHandler.funcptr !is null) piHandler(t); - chop(*s,2); + chop(s,2); } - else if (startsWith(*s,"<")) + else if (startsWith(s,"<")) { - tag_ = new Tag(*s,true); + tag_ = new Tag(s,true); if (root is null) return; // Return to constructor of derived class @@ -2074,7 +2074,7 @@ class ElementParser // Handle the pretend start tag string s2; - auto parser = new ElementParser(startTag,&s2); + auto parser = new ElementParser(startTag,s2); auto handler1 = startTag.name in onStartTag; if (handler1 !is null) (*handler1)(parser); else @@ -2096,7 +2096,7 @@ class ElementParser } else { - t = chop(*s,indexOf(*s,"<")); + t = chop(s,indexOf(s,"<")); if (rawTextHandler.funcptr !is null) rawTextHandler(t); else if (textHandler.funcptr !is null) From 4f527acbc90ae22ed2e8b2af6b39ba32b706b22c Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 8 May 2017 21:37:23 +0200 Subject: [PATCH 135/262] Allow parallel execution of public unittests --- posix.mak | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/posix.mak b/posix.mak index 3da3fdc8685..3fcecc004a1 100644 --- a/posix.mak +++ b/posix.mak @@ -252,6 +252,18 @@ MAKEFILE = $(firstword $(MAKEFILE_LIST)) # build with shared library support (defaults to true on supported platforms) SHARED=$(if $(findstring $(OS),linux freebsd),1,) +# Check for missing imports in public unittest examples. +# A blacklist of ignored module is provided as not all public unittest in +# Phobos are independently runnable yet +IGNORED_PUBLICTESTS= $(addprefix std/, \ + base64 $(addprefix experimental/allocator/, \ + building_blocks/free_list building_blocks/quantizer \ + ) digest/hmac \ + file math stdio traits typecons uuid) +PUBLICTESTS= $(addsuffix .publictests,$(filter-out $(IGNORED_PUBLICTESTS), $(D_MODULES))) +TEST_EXTRACTOR=$(TOOLS_DIR)/styles/test_extractor +PUBLICTESTS_DIR=$(ROOT)/publictests + ################################################################################ # Rules begin here ################################################################################ @@ -550,12 +562,22 @@ style_lint: ../dscanner/dsc $(LIB) @echo "Running DScanner" ../dscanner/dsc --config .dscanner.ini --styleCheck $$(find etc std -type f -name '*.d' | grep -vE 'std/traits.d|std/typecons.d') -I. -publictests: $(LIB) - # parse all public unittests from Phobos and runs them (for now some modules are excluded) - rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c tests_extractor -- --inputdir . --ignore "base64.d,building_blocks/free_list,building_blocks/quantizer,digest/hmac.d,file.d,index.d,math.d,stdio.d,traits.d,typecons.d,uuid.d" --outputdir ./out - # execute all parsed tests - for file in $$(find out -name '*.d'); do echo "executing $${file}" && $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $$file || exit 1 ; done +################################################################################ +# Check for missing imports in public unittest examples. +################################################################################ +publictests: $(PUBLICTESTS) + +$(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --compiler=$${PWD}/$(DMD) --root=$(TOOLS_DIR)/styles -c tests_extractor + +################################################################################ +# Extract public tests of a module and test them in an separate file (i.e. without its module) +# This is done to check for potentially missing imports in the examples, e.g. +# make -f posix.mak std/format.publictests +################################################################################ +%.publictests: %.d $(LIB) $(TEST_EXTRACTOR) + @$(TEST_EXTRACTOR) --inputdir $< --outputdir $(PUBLICTESTS_DIR) + @$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $(PUBLICTESTS_DIR)/$(subst /,_,$<) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) From a980f66350b609be666b74f9e3c6516df718fb08 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 8 May 2017 21:37:41 +0200 Subject: [PATCH 136/262] Check for an existent tools repository on more files --- posix.mak | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/posix.mak b/posix.mak index 3fcecc004a1..35ef857e832 100644 --- a/posix.mak +++ b/posix.mak @@ -485,6 +485,16 @@ html_consolidated : changelog.html: changelog.dd $(DMD) -Df$@ $< +################################################################################ +# Automatically create dlang/tools repository if non-existent +################################################################################ + +${TOOLS_DIR}: + git clone --depth=1 ${GIT_HOME}/$(@F) $@ +$(TOOLS_DIR)/checkwhitespace.d: | $(TOOLS_DIR) +$(TOOLS_DIR)/styles/tests_extractor.d: | $(TOOLS_DIR) +$(TOOLS_DIR)/styles/has_public_example.d: | $(TOOLS_DIR) + #################### test for undesired white spaces ########################## CWS_TOCHECK = posix.mak win32.mak win64.mak osmodel.mak CWS_TOCHECK += $(ALL_D_FILES) index.d @@ -492,9 +502,6 @@ CWS_TOCHECK += $(ALL_D_FILES) index.d checkwhitespace: $(LIB) $(TOOLS_DIR)/checkwhitespace.d $(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -run $(TOOLS_DIR)/checkwhitespace.d $(CWS_TOCHECK) -$(TOOLS_DIR)/checkwhitespace.d: - git clone --depth=1 ${GIT_HOME}/tools $(TOOLS_DIR) - ############################# # Submission to Phobos are required to conform to the DStyle # The tests below automate some, but not all parts of the DStyle guidelines. From 8807639ff5fe46207fc5857a5d18f3c48bd2d06d Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Wed, 5 Apr 2017 17:38:33 +0300 Subject: [PATCH 137/262] Make behavior of environment.opIndexAssign on Posix to match the behavior on Windows. Setting null as environment value leads to removing this variable according to SetEnvironmentVariable documentation while Posix leaves unspecified what setenev should do if value is null. --- std/process.d | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/std/process.d b/std/process.d index bf95e6af725..e0182cb40be 100644 --- a/std/process.d +++ b/std/process.d @@ -3103,6 +3103,7 @@ static: /** Assigns the given $(D value) to the environment variable with the given $(D name). + If $(D value) is null the variable is removed from environment. If the variable does not exist, it will be created. If it already exists, it will be overwritten. @@ -3124,6 +3125,11 @@ static: version (Posix) { import std.exception : enforce, errnoEnforce; + if (value is null) + { + remove(name); + return value; + } if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1) { return value; @@ -3432,6 +3438,10 @@ private: import std.conv : text; assert(aa == aa2, text(aa, " != ", aa2)); assert("std_process" in environment); + + // Setting null must have the same effect as remove + environment["std_process"] = null; + assert("std_process" !in environment); } From 93b569ce473ee9cd69398b5ee0c5da32fbfea526 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 8 May 2017 17:29:19 -0400 Subject: [PATCH 138/262] Add @safe to two common functions in std.xml --- std/xml.d | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/std/xml.d b/std/xml.d index 7cb56b00c68..c3504620722 100644 --- a/std/xml.d +++ b/std/xml.d @@ -430,13 +430,13 @@ enum DecodeMode * writefln(decode("a > b")); // writes "a > b" * -------------- */ -string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @system pure +string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure { import std.algorithm.searching : startsWith; if (mode == DecodeMode.NONE) return s; - char[] buffer; + string buffer; foreach (ref i; 0 .. s.length) { char c = s[i]; @@ -482,10 +482,10 @@ string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @system pure } } } - return (buffer.length == 0) ? s : cast(string) buffer; + return (buffer.length == 0) ? s : buffer; } -@system pure unittest +@safe pure unittest { void assertNot(string s) pure { @@ -1054,12 +1054,11 @@ class Tag * The second parameter is a dummy parameter only, required solely to * distinguish this constructor from the public one. */ - private this(ref string s, bool dummy) @system + private this(ref string s, bool dummy) @safe pure { import std.ascii : whitespace; import std.string : munch; - // @system because of decode tagString = s; try { @@ -1086,11 +1085,11 @@ class Tag type = TagType.EMPTY; } reqc(s,'>'); - tagString.length = (s.ptr - tagString.ptr); + tagString.length = tagString.length - s.length; } catch (XMLException e) { - tagString.length = (s.ptr - tagString.ptr); + tagString.length = tagString.length - s.length; throw new TagException(tagString); } } From 2ac9ad78aa492f5ee5e263168a77ad5081ecc0a9 Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Thu, 4 May 2017 18:52:06 +0200 Subject: [PATCH 139/262] Debugger target for posix It is desirable to be able to attach a debugger to the execution of tests of phobos. This patch add this feature. By calling make -f posix.mak DEBUGGER=gdb std/XXXXX.debug phobos and the module XXXXX will be compiled with debug symbols and after its compilation the debugger gdb starts the compiled executable. DEBUGGER=gdb can be omitted as it is the default debugger. --- posix.mak | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/posix.mak b/posix.mak index f49a79f33db..8b599071f74 100644 --- a/posix.mak +++ b/posix.mak @@ -13,6 +13,8 @@ # # make BUILD=debug unittest => builds all unittests (for debug) and runs them # +# make DEBUGGER=ddd std/XXXXX.debug => builds the module XXXXX and executes it in the debugger ddd +# # make html => makes html documentation # # make install => copies library to /usr/lib @@ -28,6 +30,8 @@ QUIET:= +DEBUGGER=gdb + include osmodel.mak ifeq (osx,$(OS)) @@ -370,6 +374,22 @@ unittest/%.run : $(ROOT)/unittest/test_runner %.test : $(LIB) $(MAKE) -f $(MAKEFILE) $(addsuffix .test,$(patsubst %.d,%,$(wildcard $*/*))) +# Recursive target for %.debug +# It has to be recursive as %.debug depends on $(LIB) and we don't want to +# force the user to call make with BUILD=debug. +# Therefore we call %.debug_with_debugger and pass BUILD=debug from %.debug +# This forces all of phobos to have debug symbols, which we need as we don't +# know where debugging is leading us. +%.debug_with_debugger : %.d $(LIB) + $(DMD) $(DFLAGS) -main -unittest $(LIB) -defaultlib= -debuglib= $(LINKDL) $< + $(DEBUGGER) ./$(basename $(notdir $<)) + +# Target for quickly debugging a single module +# For example: make -f posix.mak DEBUGGER=ddd std/format.debug +# ddd in this case is a graphical frontend to gdb +%.debug : %.d + BUILD=debug $(MAKE) -f $(MAKEFILE) $(basename $<).debug_with_debugger + ################################################################################ # More stuff ################################################################################ From 73ef3cb35f2f911c6f61d43b08c7a9f97be76f76 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 9 May 2017 11:51:05 -0400 Subject: [PATCH 140/262] Fixed some typos in std.string docs --- std/string.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/string.d b/std/string.d index e5e7f53b597..5e4103366b8 100644 --- a/std/string.d +++ b/std/string.d @@ -1551,7 +1551,7 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurence of any of the elements in $(D + Returns the index of the first occurrence of any of the elements in $(D needles) in $(D haystack). If no element of $(D needles) is found, then $(D -1) is returned. The $(D startIdx) slices $(D haystack) in the following way $(D haystack[startIdx .. $]). $(D startIdx) represents a @@ -1722,7 +1722,7 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the last occurence of any of the elements in $(D + Returns the index of the last occurrence of any of the elements in $(D needles) in $(D haystack). If no element of $(D needles) is found, then $(D -1) is returned. The $(D stopIdx) slices $(D haystack) in the following way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit @@ -1909,7 +1909,7 @@ if (isSomeChar!Char && isSomeChar!Char2) } /** - Returns the index of the first occurence of any character not an elements + Returns the index of the first occurrence of any character not an elements in $(D needles) in $(D haystack). If all element of $(D haystack) are element of $(D needles) $(D -1) is returned. @@ -6348,7 +6348,7 @@ body * * This is useful in cases where the user is expected to type * in one of a known set of strings, and the program will helpfully - * autocomplete the string once sufficient characters have been + * auto-complete the string once sufficient characters have been * entered that uniquely identify it. */ From c6819e2d6ecda6e0987fbcf8a66300c2e7ba415a Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Sat, 18 Feb 2017 03:47:16 +0100 Subject: [PATCH 141/262] [Static if] replace overload constraints with static if (mutation.d) --- std/algorithm/mutation.d | 501 +++++++++++++++++++-------------------- 1 file changed, 247 insertions(+), 254 deletions(-) diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index f1aa9ee4769..cf2b40bb7ea 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -368,57 +368,61 @@ See_Also: $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy) */ TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) -if (areCopyCompatibleArrays!(SourceRange, TargetRange)) +if (isInputRange!SourceRange) { - const tlen = target.length; - const slen = source.length; - assert(tlen >= slen, - "Cannot copy a source range into a smaller target range."); + static if (areCopyCompatibleArrays!(SourceRange, TargetRange)) + { + const tlen = target.length; + const slen = source.length; + assert(tlen >= slen, + "Cannot copy a source range into a smaller target range."); - immutable overlaps = __ctfe || () @trusted { - return source.ptr < target.ptr + tlen && - target.ptr < source.ptr + slen; }(); + immutable overlaps = __ctfe || () @trusted { + return source.ptr < target.ptr + tlen && + target.ptr < source.ptr + slen; }(); - if (overlaps) - { - foreach (idx; 0 .. slen) - target[idx] = source[idx]; - return target[slen .. tlen]; + if (overlaps) + { + foreach (idx; 0 .. slen) + target[idx] = source[idx]; + return target[slen .. tlen]; + } + else + { + // Array specialization. This uses optimized memory copying + // routines under the hood and is about 10-20x faster than the + // generic implementation. + target[0 .. slen] = source[]; + return target[slen .. $]; + } } - else + else static if (isOutputRange!(TargetRange, ElementType!SourceRange)) { - // Array specialization. This uses optimized memory copying - // routines under the hood and is about 10-20x faster than the - // generic implementation. - target[0 .. slen] = source[]; - return target[slen .. $]; - } -} - -/// ditto -TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) -if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && - isInputRange!SourceRange && - isOutputRange!(TargetRange, ElementType!SourceRange)) -{ - // Specialize for 2 random access ranges. - // Typically 2 random access ranges are faster iterated by common - // index than by x.popFront(), y.popFront() pair - static if (isRandomAccessRange!SourceRange && - hasLength!SourceRange && - hasSlicing!TargetRange && - isRandomAccessRange!TargetRange && - hasLength!TargetRange) - { - auto len = source.length; - foreach (idx; 0 .. len) - target[idx] = source[idx]; - return target[len .. target.length]; + // Specialize for 2 random access ranges. + // Typically 2 random access ranges are faster iterated by common + // index than by x.popFront(), y.popFront() pair + static if (isRandomAccessRange!SourceRange && + hasLength!SourceRange && + hasSlicing!TargetRange && + isRandomAccessRange!TargetRange && + hasLength!TargetRange) + { + auto len = source.length; + foreach (idx; 0 .. len) + target[idx] = source[idx]; + return target[len .. target.length]; + } + else + { + put(target, source); + return target; + } } else { - put(target, source); - return target; + enum msg = "TargetRange is neither copy-compatible with the SourceRange" ~ + "nor an OutputRange with the same ElementType."; + static assert(0, msg); } } @@ -843,59 +847,60 @@ See_Also: $(LREF uninitializeFill) */ void initializeAll(Range)(Range range) -if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) +if (isInputRange!Range) { - import core.stdc.string : memset, memcpy; - import std.traits : hasElaborateAssign, isDynamicArray; - - alias T = ElementType!Range; - static if (hasElaborateAssign!T) + static if (hasLvalueElements!Range && hasAssignableElements!Range) { - import std.algorithm.internal : addressOf; - //Elaborate opAssign. Must go the memcpy road. - //We avoid calling emplace here, because our goal is to initialize to - //the static state of T.init, - //So we want to avoid any un-necassarilly CC'ing of T.init - auto p = typeid(T).initializer(); - if (p.ptr) + import core.stdc.string : memset, memcpy; + import std.traits : hasElaborateAssign, isDynamicArray; + + alias T = ElementType!Range; + static if (hasElaborateAssign!T) { - for ( ; !range.empty ; range.popFront() ) + import std.algorithm.internal : addressOf; + //Elaborate opAssign. Must go the memcpy road. + //We avoid calling emplace here, because our goal is to initialize to + //the static state of T.init, + //So we want to avoid any un-necassarilly CC'ing of T.init + auto p = typeid(T).initializer(); + if (p.ptr) { - static if (__traits(isStaticArray, T)) + for ( ; !range.empty ; range.popFront() ) { - // static array initializer only contains initialization - // for one element of the static array. - auto elemp = cast(void *) addressOf(range.front); - auto endp = elemp + T.sizeof; - while (elemp < endp) + static if (__traits(isStaticArray, T)) { - memcpy(elemp, p.ptr, p.length); - elemp += p.length; + // static array initializer only contains initialization + // for one element of the static array. + auto elemp = cast(void *) addressOf(range.front); + auto endp = elemp + T.sizeof; + while (elemp < endp) + { + memcpy(elemp, p.ptr, p.length); + elemp += p.length; + } + } + else + { + memcpy(addressOf(range.front), p.ptr, T.sizeof); } - } - else - { - memcpy(addressOf(range.front), p.ptr, T.sizeof); } } + else + static if (isDynamicArray!Range) + memset(range.ptr, 0, range.length * T.sizeof); + else + for ( ; !range.empty ; range.popFront() ) + memset(addressOf(range.front), 0, T.sizeof); } else - static if (isDynamicArray!Range) - memset(range.ptr, 0, range.length * T.sizeof); - else - for ( ; !range.empty ; range.popFront() ) - memset(addressOf(range.front), 0, T.sizeof); + fill(range, T.init); } - else - fill(range, T.init); -} - -/// ditto -void initializeAll(Range)(Range range) -if (is(Range == char[]) || is(Range == wchar[])) -{ - alias T = ElementEncodingType!Range; - range[] = T.init; + else static if (is(Range == char[]) || is(Range == wchar[])) + { + alias T = ElementEncodingType!Range; + range[] = T.init; + } + else static assert(0, "Range doesn't have assignable elements."); } /// @@ -1745,138 +1750,133 @@ Returns: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) -if (s != SwapStrategy.stable - && isBidirectionalRange!Range +if (isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1) { - Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; - foreach (i, v; offset) + static if (s == SwapStrategy.unstable) { - static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) + Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; + foreach (i, v; offset) { - blackouts[i].pos = v[0]; - blackouts[i].len = v[1] - v[0]; - } - else - { - static assert(is(typeof(v) : size_t), typeof(v).stringof); - blackouts[i].pos = v; - blackouts[i].len = 1; - } - static if (i > 0) - { - import std.exception : enforce; + static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) + { + blackouts[i].pos = v[0]; + blackouts[i].len = v[1] - v[0]; + } + else + { + static assert(is(typeof(v) : size_t), typeof(v).stringof); + blackouts[i].pos = v; + blackouts[i].len = 1; + } + static if (i > 0) + { + import std.exception : enforce; - enforce(blackouts[i - 1].pos + blackouts[i - 1].len - <= blackouts[i].pos, - "remove(): incorrect ordering of elements to remove"); + enforce(blackouts[i - 1].pos + blackouts[i - 1].len + <= blackouts[i].pos, + "remove(): incorrect ordering of elements to remove"); + } } - } - size_t left = 0, right = offset.length - 1; - auto tgt = range.save; - size_t tgtPos = 0; + size_t left = 0, right = offset.length - 1; + auto tgt = range.save; + size_t tgtPos = 0; - while (left <= right) - { - // Look for a blackout on the right - if (blackouts[right].pos + blackouts[right].len >= range.length) + while (left <= right) { - range.popBackExactly(blackouts[right].len); + // Look for a blackout on the right + if (blackouts[right].pos + blackouts[right].len >= range.length) + { + range.popBackExactly(blackouts[right].len); - // Since right is unsigned, we must check for this case, otherwise - // we might turn it into size_t.max and the loop condition will not - // fail when it should. - if (right > 0) + // Since right is unsigned, we must check for this case, otherwise + // we might turn it into size_t.max and the loop condition will not + // fail when it should. + if (right > 0) + { + --right; + continue; + } + else + break; + } + // Advance to next blackout on the left + assert(blackouts[left].pos >= tgtPos); + tgt.popFrontExactly(blackouts[left].pos - tgtPos); + tgtPos = blackouts[left].pos; + + // Number of elements to the right of blackouts[right] + immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); + size_t toMove = void; + if (tailLen < blackouts[left].len) { - --right; - continue; + toMove = tailLen; + blackouts[left].pos += toMove; + blackouts[left].len -= toMove; } else - break; - } - // Advance to next blackout on the left - assert(blackouts[left].pos >= tgtPos); - tgt.popFrontExactly(blackouts[left].pos - tgtPos); - tgtPos = blackouts[left].pos; - - // Number of elements to the right of blackouts[right] - immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); - size_t toMove = void; - if (tailLen < blackouts[left].len) - { - toMove = tailLen; - blackouts[left].pos += toMove; - blackouts[left].len -= toMove; - } - else - { - toMove = blackouts[left].len; - ++left; - } - tgtPos += toMove; - foreach (i; 0 .. toMove) - { - move(range.back, tgt.front); - range.popBack(); - tgt.popFront(); + { + toMove = blackouts[left].len; + ++left; + } + tgtPos += toMove; + foreach (i; 0 .. toMove) + { + move(range.back, tgt.front); + range.popBack(); + tgt.popFront(); + } } - } - return range; -} - -/// Ditto -Range remove -(SwapStrategy s = SwapStrategy.stable, Range, Offset...) -(Range range, Offset offset) -if (s == SwapStrategy.stable - && isBidirectionalRange!Range - && hasLvalueElements!Range - && Offset.length >= 1) -{ - auto result = range; - auto src = range, tgt = range; - size_t pos; - foreach (pass, i; offset) + return range; + } + else static if (s == SwapStrategy.stable) { - static if (is(typeof(i[0])) && is(typeof(i[1]))) - { - auto from = i[0], delta = i[1] - i[0]; - } - else + auto result = range; + auto src = range, tgt = range; + size_t pos; + foreach (pass, i; offset) { - auto from = i; - enum delta = 1; - } + static if (is(typeof(i[0])) && is(typeof(i[1]))) + { + auto from = i[0], delta = i[1] - i[0]; + } + else + { + auto from = i; + enum delta = 1; + } - static if (pass > 0) - { - import std.exception : enforce; - enforce(pos <= from, - "remove(): incorrect ordering of elements to remove"); + static if (pass > 0) + { + import std.exception : enforce; + enforce(pos <= from, + "remove(): incorrect ordering of elements to remove"); - for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + { + move(src.front, tgt.front); + } + } + else { - move(src.front, tgt.front); + src.popFrontExactly(from); + tgt.popFrontExactly(from); + pos = from; } + // now skip source to the "to" position + src.popFrontExactly(delta); + result.popBackExactly(delta); + pos += delta; } - else - { - src.popFrontExactly(from); - tgt.popFrontExactly(from); - pos = from; - } - // now skip source to the "to" position - src.popFrontExactly(delta); - result.popBackExactly(delta); - pos += delta; + // leftover move + moveAll(src, tgt); + return result; } - // leftover move - moveAll(src, tgt); - return result; + else static assert(0, "SwapStrategy.semistable is not supported."); } /// @@ -2132,24 +2132,67 @@ if (isBidirectionalRange!Range /** Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D swap). +UTF sequences consisting of multiple code units are preserved properly. + Params: r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - with swappable elements or a random access range with a length member + with swappable elements, a random access range with a length member or a + narrow string. See_Also: $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view + +Bugs: + When passing a sting with unicode modifiers on characters, such as $(D \u0301), + this function will not properly keep the position of the modifier. For example, + reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of + $(D da\u0301b) ("dáb"). */ void reverse(Range)(Range r) -if (isBidirectionalRange!Range && !isRandomAccessRange!Range - && hasSwappableElements!Range) +if (isBidirectionalRange!Range) { - while (!r.empty) + static if (isRandomAccessRange!Range) { - swap(r.front, r.back); - r.popFront(); - if (r.empty) break; - r.popBack(); + //swapAt is in fact the only way to swap non lvalue ranges + immutable last = r.length-1; + immutable steps = r.length/2; + for (size_t i = 0; i < steps; i++) + { + r.swapAt(i, last-i); + } + } + else static if (hasSwappableElements!Range) + { + while (!r.empty) + { + swap(r.front, r.back); + r.popFront(); + if (r.empty) break; + r.popBack(); + } + } + else static if (isNarrowString!Range && !is(ElementType!Range == const) && !is(ElementType!Range == immutable)) + { + import std.string : representation; + import std.utf : stride; + + auto repr = representation(r); + for (size_t i = 0; i < r.length; ) + { + immutable step = stride(r, i); + if (step > 1) + { + .reverse(repr[i .. i + step]); + i += step; + } + else + { + ++i; + } + } + reverse(repr); } + else static assert(0, "Range is neither RandomAccess, has swappable elements nor a narrow string."); } /// @@ -2160,17 +2203,12 @@ if (isBidirectionalRange!Range && !isRandomAccessRange!Range assert(arr == [ 3, 2, 1 ]); } -///ditto -void reverse(Range)(Range r) -if (isRandomAccessRange!Range && hasLength!Range) +/// +@safe unittest { - //swapAt is in fact the only way to swap non lvalue ranges - immutable last = r.length-1; - immutable steps = r.length/2; - for (size_t i = 0; i < steps; i++) - { - r.swapAt(i, last-i); - } + char[] arr = "hello\U00010143\u0100\U00010143".dup; + reverse(arr); + assert(arr == "\U00010143\u0100\U00010143olleh"); } @safe unittest @@ -2188,51 +2226,6 @@ if (isRandomAccessRange!Range && hasLength!Range) assert(range == [3, 2, 1]); } -/** -Reverses $(D r) in-place, where $(D r) is a narrow string (having -elements of type $(D char) or $(D wchar)). UTF sequences consisting of -multiple code units are preserved properly. - -Params: - s = a narrow string - -Bugs: - When passing a sting with unicode modifiers on characters, such as $(D \u0301), - this function will not properly keep the position of the modifier. For example, - reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of - $(D da\u0301b) ("dáb"). -*/ -void reverse(Char)(Char[] s) -if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) -{ - import std.string : representation; - import std.utf : stride; - - auto r = representation(s); - for (size_t i = 0; i < s.length; ) - { - immutable step = stride(s, i); - if (step > 1) - { - .reverse(r[i .. i + step]); - i += step; - } - else - { - ++i; - } - } - reverse(r); -} - -/// -@safe unittest -{ - char[] arr = "hello\U00010143\u0100\U00010143".dup; - reverse(arr); - assert(arr == "\U00010143\u0100\U00010143olleh"); -} - @safe unittest { void test(string a, string b) From 451138141f9dc4417436b4d57d303398aa81182f Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Sun, 7 May 2017 19:34:45 -0400 Subject: [PATCH 142/262] Deprecate obsolete pattern matching functions in std.string --- std/string.d | 52 ++++++++++++++++++++++++++++++++++--------- std/xml.d | 63 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/std/string.d b/std/string.d index 5e4103366b8..fa59969fed7 100644 --- a/std/string.d +++ b/std/string.d @@ -5230,8 +5230,13 @@ body assert(buffer.data == "h5 rd"); } - +//@@@DEPRECATED_2018-05@@@ /*********************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * See if character c is in the pattern. * Patterns: * @@ -5248,7 +5253,7 @@ body * Note: In the future, the pattern syntax may be improved * to be more like regular expression character classes. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") bool inPattern(S)(dchar c, in S pattern) @safe pure @nogc if (isSomeString!S) { @@ -5314,11 +5319,16 @@ if (isSomeString!S) }); } - +//@@@DEPRECATED_2018-05@@@ /*********************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * See if character c is in the intersection of the patterns. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") bool inPattern(S)(dchar c, S[] patterns) @safe pure @nogc if (isSomeString!S) { @@ -5332,11 +5342,16 @@ if (isSomeString!S) return true; } - +//@@@DEPRECATED_2018-05@@@ /******************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Count characters in s that match pattern. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") size_t countchars(S, S1)(S s, in S1 pattern) @safe pure @nogc if (isSomeString!S && isSomeString!S1) { @@ -5362,11 +5377,16 @@ if (isSomeString!S && isSomeString!S1) }); } - +//@@@DEPRECATED_2018-05@@@ /******************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Return string that is s with all characters removed that match pattern. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") S removechars(S)(S s, in S pattern) @safe pure if (isSomeString!S) { @@ -5418,13 +5438,18 @@ if (isSomeString!S) assert(removechars("abc", "x") == "abc"); } - +//@@@DEPRECATED_2018-05@@@ /*************************************************** + * $(RED This function is deprecated and will be removed May 2018.) + * Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + * instead. If you still need this function, it will be available in + * $(LINK2 https://github.com/dlang/undeaD, undeaD). + * * Return string where sequences of a character in s[] from pattern[] * are replaced with a single instance of that character. * If pattern is null, it defaults to all characters. */ - +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") S squeeze(S)(S s, in S pattern = null) { import std.utf : encode, stride; @@ -5490,7 +5515,13 @@ S squeeze(S)(S s, in S pattern = null) }); } +//@@@DEPRECATED_2018-05@@@ /*************************************************************** + $(RED This function is deprecated and will be removed May 2018.) + Please use the functions in $(MREF std, regex) and $(MREF std, algorithm) + instead. If you still need this function, it will be available in + $(LINK2 https://github.com/dlang/undeaD, undeaD). + Finds the position $(D_PARAM pos) of the first character in $(D_PARAM s) that does not match $(D_PARAM pattern) (in the terminology used by $(REF inPattern, std,string)). Updates $(D_PARAM s = @@ -5501,6 +5532,7 @@ The $(D_PARAM munch) function is mostly convenient for skipping certain category of characters (e.g. whitespace) when parsing strings. (In such cases, the return value is not used.) */ +deprecated("This function is obsolete and will be removed May 2018. See the docs for more details") S1 munch(S1, S2)(ref S1 s, S2 pattern) @safe pure @nogc { size_t j = s.length; diff --git a/std/xml.d b/std/xml.d index c3374cb8258..d2a28d87c8c 100644 --- a/std/xml.d +++ b/std/xml.d @@ -1056,27 +1056,42 @@ class Tag */ private this(ref string s, bool dummy) @safe pure { - import std.ascii : whitespace; - import std.string : munch; + import std.algorithm.searching : countUntil; + import std.ascii : isWhite; + import std.utf : byCodeUnit; tagString = s; try { reqc(s,'<'); if (optc(s,'/')) type = TagType.END; - name = munch(s,"^/>"~whitespace); - munch(s,whitespace); + ptrdiff_t i = s.byCodeUnit.countUntil(">", "/>", " ", "\t", "\v", "\r", "\n", "\f"); + name = s[0 .. i]; + s = s[i .. $]; + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; + while (s.length > 0 && s[0] != '>' && s[0] != '/') { - string key = munch(s,"^="~whitespace); - munch(s,whitespace); + i = s.byCodeUnit.countUntil("=", " ", "\t", "\v", "\r", "\n", "\f"); + string key = s[0 .. i]; + s = s[i .. $]; + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; reqc(s,'='); - munch(s,whitespace); + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; + immutable char quote = requireOneOf(s,"'\""); - char[2] notQuote = ['^', quote]; - string val = decode(munch(s,notQuote[]), DecodeMode.LOOSE); + i = s.byCodeUnit.countUntil(quote); + string val = decode(s[0 .. i], DecodeMode.LOOSE); + s = s[i .. $]; reqc(s,quote); - munch(s,whitespace); + + i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + s = s[i .. $]; attr[key] = val; } if (optc(s,'/')) @@ -2194,10 +2209,16 @@ private void checkSpace(ref string s) @safe pure // rule 3 { - import std.string : munch; + import std.algorithm.searching : countUntil; + import std.ascii : isWhite; + import std.utf : byCodeUnit; mixin Check!("Whitespace"); - munch(s,"\u0020\u0009\u000A\u000D"); + ptrdiff_t i = s.byCodeUnit.countUntil!(a => !isWhite(a)); + if (i == -1 && s.length > 0 && isWhite(s[0])) + s = s[$ .. $]; + else if (i > -1) + s = s[i .. $]; if (s is old) fail(); } @@ -2222,7 +2243,8 @@ private void checkAttValue(ref string s) @safe pure // rule 10 { - import std.string : munch; + import std.algorithm.searching : countUntil; + import std.utf : byCodeUnit; mixin Check!("AttValue"); @@ -2233,7 +2255,7 @@ private s = s[1..$]; for (;;) { - munch(s,"^<&"~c); + s = s[s.byCodeUnit.countUntil(c) .. $]; if (s.length == 0) fail("unterminated attribute value"); if (s[0] == '<') fail("< found in attribute value"); if (s[0] == c) break; @@ -2356,11 +2378,12 @@ private void checkVersionNum(ref string s) @safe pure // rule 26 { - import std.string : munch; + import std.algorithm.searching : countUntil; + import std.utf : byCodeUnit; mixin Check!("VersionNum"); - munch(s,"a-zA-Z0-9_.:-"); + s = s[s.byCodeUnit.countUntil('\"') .. $]; if (s is old) fail(); } @@ -2583,13 +2606,15 @@ private void checkEncName(ref string s) @safe pure // rule 81 { - import std.string : munch; + import std.algorithm.searching : countUntil; + import std.ascii : isAlpha; + import std.utf : byCodeUnit; mixin Check!("EncName"); - munch(s,"a-zA-Z"); + s = s[s.byCodeUnit.countUntil!(a => !isAlpha(a)) .. $]; if (s is old) fail(); - munch(s,"a-zA-Z0-9_.-"); + s = s[s.byCodeUnit.countUntil('\"', '\'') .. $]; } void checkEncodingDecl(ref string s) @safe pure // rule 80 From 4e8cab7cdfbb18753072ed469f37e8fabb4c94ef Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 13 May 2017 02:14:33 -0700 Subject: [PATCH 143/262] Fix some bad/slangy English in std.getopt. I don't think that it makes sense to use slang like "dunno" in a standard library. --- std/getopt.d | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/std/getopt.d b/std/getopt.d index badf2829b2e..98d4eb62ebd 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -283,8 +283,7 @@ int main(string[] args) case "verbose": verbosityLevel = 2; break; case "shouting": verbosityLevel = verbosityLevel.max; break; default : - stderr.writeln("Dunno how verbose you want me to be by saying ", - value); + stderr.writeln("Unknown verbosity level ", value); handlerFailed = true; break; } @@ -965,10 +964,7 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, setHash(receiver, val.splitter(arraySep)); } else - { - static assert(false, "Dunno how to deal with type " ~ - typeof(receiver).stringof); - } + static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof); } } From e50531d921b368e26e5fca0ebce053c3e5454d23 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 12 May 2017 15:58:22 +0200 Subject: [PATCH 144/262] Fix issue 17394 - mkdirRecurse isn't @safe --- std/file.d | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/std/file.d b/std/file.d index 8f3c8947bea..7be68d84594 100644 --- a/std/file.d +++ b/std/file.d @@ -2241,7 +2241,7 @@ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) && !isConvertibleToString!R) { // Place outside of @trusted block - auto pathz = pathname.tempCString!FSChar(); + const pathz = pathname.tempCString!FSChar(); version(Windows) { @@ -2287,13 +2287,14 @@ if (isConvertibleToString!R) // Same as mkdir but ignores "already exists" errors. // Returns: "true" if the directory was created, // "false" if it already existed. -private bool ensureDirExists(in char[] pathname) +private bool ensureDirExists()(in char[] pathname) { import std.exception : enforce; + const pathz = pathname.tempCString!FSChar(); version(Windows) { - if (CreateDirectoryW(pathname.tempCStringW(), null)) + if (() @trusted { return CreateDirectoryW(pathz, null); }()) return true; cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup); } @@ -2301,7 +2302,7 @@ private bool ensureDirExists(in char[] pathname) { import std.conv : octal; - if (core.sys.posix.sys.stat.mkdir(pathname.tempCString(), octal!777) == 0) + if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0) return true; cenforce(errno == EEXIST || errno == EISDIR, pathname); } @@ -2318,7 +2319,7 @@ private bool ensureDirExists(in char[] pathname) * Throws: $(D FileException) on error. */ -void mkdirRecurse(in char[] pathname) +void mkdirRecurse()(in char[] pathname) { import std.path : dirName, baseName; @@ -2333,14 +2334,14 @@ void mkdirRecurse(in char[] pathname) } } -@system unittest +@safe unittest { import std.exception : assertThrown; { import std.path : buildPath, buildNormalizedPath; immutable basepath = deleteme ~ "_dir"; - scope(exit) rmdirRecurse(basepath); + scope(exit) () @trusted { rmdirRecurse(basepath); }(); auto path = buildPath(basepath, "a", "..", "b"); mkdirRecurse(path); @@ -2375,7 +2376,7 @@ void mkdirRecurse(in char[] pathname) mkdirRecurse(path); assert(basepath.exists && basepath.isDir); - scope(exit) rmdirRecurse(basepath); + scope(exit) () @trusted { rmdirRecurse(basepath); }(); assert(path.exists && path.isDir); } } From 21c09f18d33b65475a89c7d6ec75d87556e6c1e5 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 13 May 2017 06:26:22 -0700 Subject: [PATCH 145/262] Issue 16053: Fix it so that SysTime's from*String supports more than 7 digits. ISO 8601 says that it's up to the application to decide how many digits to put in the fractional seconds if they're present. SysTime.to*String puts up to 7 (stripping trailing zeroes), because that's hecto-nanosecond precision, and SysTime holds the time in hecto-nanoseconds. Currently, from*String only accepts up to 7 digits in the fractional seconds, which _does_ follow the spec in that (per the spec) the number of digits is up to the applications. However, while we never emit more than 7 digits, other applications do, so only accepting 7 digits makes us incompatible with them, whereas accepting them would make us more compatible with other programs, and it would actually be more efficient, since we'd have fewer checks in the code. So, these changes make is so that SysTime.from*String accepts more than 7 digits in the fractional seconds, but the additional digits are truncated (since SysTime doesn't support more than 7 digits of precision). --- std/datetime/systime.d | 56 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/std/datetime/systime.d b/std/datetime/systime.d index c8e7ac0641a..8bc284eeffe 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -8201,7 +8201,11 @@ public: The exact format is exactly as described in $(D toISOString) except that trailing zeroes are permitted - including having fractional seconds with all zeroes. However, a decimal point with nothing following it is - invalid. + invalid. Also, while $(LREF toISOString) will never generate a string + with more than 7 digits in the fractional seconds (because that's the + limit with hecto-nanosecond precision), it will allow more than 7 digits + in order to read strings from other sources that have higher precision + (however, any digits beyond 7 will be truncated). If there is no time zone in the string, then $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", @@ -8315,6 +8319,9 @@ public: assert(SysTime.fromISOString("00000105T230959.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + assert(SysTime.fromISOString("20130207T043937.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + assert(SysTime.fromISOString("-00040105T000002") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); @@ -8337,7 +8344,7 @@ public: { foreach (str; ["", "20100704000000", "20100704 000000", "20100704t000000", "20100704T000000.", "20100704T000000.A", "20100704T000000.Z", - "20100704T000000.00000000", "20100704T000000.00000000", + "20100704T000000.0000000A", "20100704T000000.00000000A", "20100704T000000+", "20100704T000000-", "20100704T000000:", "20100704T000000-:", "20100704T000000+:", "20100704T000000-1:", "20100704T000000+1:", "20100704T000000+1:0", @@ -8386,6 +8393,9 @@ public: test("19070707T121212.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); test("19070707T121212.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); test("19070707T121212.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("20100704T000000.00000000", SysTime(Date(2010, 07, 04))); + test("20100704T000000.00000009", SysTime(Date(2010, 07, 04))); + test("20100704T000000.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); test("19070707T121212.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); test("19070707T121212.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); test("19070707T121212.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); @@ -8453,7 +8463,11 @@ public: The exact format is exactly as described in $(D toISOExtString) except that trailing zeroes are permitted - including having fractional seconds with all zeroes. However, a decimal point with nothing following - it is invalid. + it is invalid. Also, while $(LREF toISOExtString) will never generate a + string with more than 7 digits in the fractional seconds (because that's + the limit with hecto-nanosecond precision), it will allow more than 7 + digits in order to read strings from other sources that have higher + precision (however, any digits beyond 7 will be truncated). If there is no time zone in the string, then $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", @@ -8554,6 +8568,9 @@ public: assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + assert(SysTime.fromISOExtString("2013-02-07T04:39:37.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); @@ -8578,7 +8595,7 @@ public: "2010-07:0400:00:00", "2010-07-04 00:00:00", "2010-07-04 00:00:00", "2010-07-04t00:00:00", "2010-07-04T00:00:00.", "2010-07-04T00:00:00.A", "2010-07-04T00:00:00.Z", - "2010-07-04T00:00:00.00000000", "2010-07-04T00:00:00.00000000", + "2010-07-04T00:00:00.0000000A", "2010-07-04T00:00:00.00000000A", "2010-07-04T00:00:00+", "2010-07-04T00:00:00-", "2010-07-04T00:00:00:", "2010-07-04T00:00:00-:", "2010-07-04T00:00:00+:", "2010-07-04T00:00:00-1:", "2010-07-04T00:00:00+1:", "2010-07-04T00:00:00+1:0", @@ -8624,6 +8641,9 @@ public: test("1907-07-07T12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); test("1907-07-07T12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); test("1907-07-07T12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); + test("2010-07-04T00:00:00.00000000", SysTime(Date(2010, 07, 04))); + test("2010-07-04T00:00:00.00000009", SysTime(Date(2010, 07, 04))); + test("2010-07-04T00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); test("1907-07-07T12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); test("1907-07-07T12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); test("1907-07-07T12:12:12.001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), msecs(1))); @@ -8669,7 +8689,11 @@ public: The exact format is exactly as described in $(D toSimpleString) except that trailing zeroes are permitted - including having fractional seconds with all zeroes. However, a decimal point with nothing following it is - invalid. + invalid. Also, while $(LREF toSimpleString) will never generate a + string with more than 7 digits in the fractional seconds (because that's + the limit with hecto-nanosecond precision), it will allow more than 7 + digits in order to read strings from other sources that have higher + precision (however, any digits beyond 7 will be truncated). If there is no time zone in the string, then $(REF LocalTime,std,datetime,timezone) is used. If the time zone is "Z", @@ -8770,6 +8794,9 @@ public: assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20))); + assert(SysTime.fromSimpleString("2013-Feb-07 04:39:37.000050392") == + SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503))); + assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); @@ -8796,7 +8823,7 @@ public: "2010-07-04T00:00:00.", "2010-07-04T00:00:00.0", "2010-Jul-0400:00:00", "2010-Jul-04t00:00:00", "2010-Jul-04T00:00:00", "2010-Jul-04 00:00:00.", "2010-Jul-04 00:00:00.A", "2010-Jul-04 00:00:00.Z", - "2010-Jul-04 00:00:00.00000000", "2010-Jul-04 00:00:00.00000000", + "2010-Jul-04 00:00:00.0000000A", "2010-Jul-04 00:00:00.00000000A", "2010-Jul-04 00:00:00+", "2010-Jul-04 00:00:00-", "2010-Jul-04 00:00:00:", "2010-Jul-04 00:00:00-:", "2010-Jul-04 00:00:00+:", "2010-Jul-04 00:00:00-1:", @@ -8842,6 +8869,9 @@ public: test("1907-Jul-07 12:12:12.0", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); test("1907-Jul-07 12:12:12.0000000", SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + test("2010-Jul-04 00:00:00.00000000", SysTime(Date(2010, 07, 04))); + test("2010-Jul-04 00:00:00.00000009", SysTime(Date(2010, 07, 04))); + test("2010-Jul-04 00:00:00.00000019", SysTime(DateTime(2010, 07, 04), hnsecs(1))); test("1907-Jul-07 12:12:12.0000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), hnsecs(1))); test("1907-Jul-07 12:12:12.000001", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); test("1907-Jul-07 12:12:12.0000010", SysTime(DateTime(1907, 07, 07, 12, 12, 12), usecs(1))); @@ -10347,8 +10377,7 @@ if (isSomeString!S) enforce(str[0] == '.', new DateTimeException("Invalid ISO String")); str.popFront(); - enforce(!str.empty && str.length <= 7, new DateTimeException("Invalid ISO String")); - enforce(all!isDigit(str), new DateTimeException("Invalid ISO String")); + enforce(!str.empty && all!isDigit(str), new DateTimeException("Invalid ISO String")); dchar[7] fullISOString = void; foreach (i, ref dchar c; fullISOString) @@ -10373,11 +10402,13 @@ if (isSomeString!S) assertThrown!DateTimeException(testFSInvalid("0.")); assertThrown!DateTimeException(testFSInvalid("0")); assertThrown!DateTimeException(testFSInvalid("0000000")); - assertThrown!DateTimeException(testFSInvalid(".00000000")); - assertThrown!DateTimeException(testFSInvalid(".00000001")); assertThrown!DateTimeException(testFSInvalid("T")); assertThrown!DateTimeException(testFSInvalid("T.")); assertThrown!DateTimeException(testFSInvalid(".T")); + assertThrown!DateTimeException(testFSInvalid(".00000Q0")); + assertThrown!DateTimeException(testFSInvalid(".000000Q")); + assertThrown!DateTimeException(testFSInvalid(".0000000Q")); + assertThrown!DateTimeException(testFSInvalid(".0000000000Q")); assert(fracSecsFromISOString("") == Duration.zero); assert(fracSecsFromISOString(".0000001") == hnsecs(1)); @@ -10412,6 +10443,11 @@ if (isSomeString!S) assert(fracSecsFromISOString(".00999") == hnsecs(99_900)); assert(fracSecsFromISOString(".0999000") == hnsecs(999_000)); assert(fracSecsFromISOString(".0999") == hnsecs(999_000)); + assert(fracSecsFromISOString(".00000000") == Duration.zero); + assert(fracSecsFromISOString(".00000001") == Duration.zero); + assert(fracSecsFromISOString(".00000009") == Duration.zero); + assert(fracSecsFromISOString(".1234567890") == hnsecs(1_234_567)); + assert(fracSecsFromISOString(".12345678901234567890") == hnsecs(1_234_567)); } From b96c926b67eed4646de8dff450e225c908b957cc Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Sat, 13 May 2017 15:52:44 -0400 Subject: [PATCH 146/262] Add changelog entry for deprecation of pattern functions --- changelog/pattern-deprecate.dd | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog/pattern-deprecate.dd diff --git a/changelog/pattern-deprecate.dd b/changelog/pattern-deprecate.dd new file mode 100644 index 00000000000..2fe5214fe66 --- /dev/null +++ b/changelog/pattern-deprecate.dd @@ -0,0 +1,10 @@ +Several functions in `std.string` have been deprecated + +The functions $(REF inPattern, std, string), $(REF countchars, std, string), +$(REF removechars, std, string), $(REF squeeze, std, string), and +$(REF munch, std, string), have all been deprecated. These functions are +obsolete, as their functionality is better covered by the functions in +$(MREF std, regex) and $(MREF std, algorithm). They will be removed from +$(MREF std, string) on May 2018. + +If you still need to use these, please see $(LINK2 https://github.com/dlang/undeaD, undeaD). From 6ff81f14057530484249dd4055e19c996b0485a7 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 14 May 2017 08:19:20 -0400 Subject: [PATCH 147/262] Fix issue 16246 - cannot call iota with 3 [u]bytes or 3 [u]shorts --- std/range/package.d | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 1582754713c..75fbdf8cf28 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -5209,6 +5209,7 @@ auto sequence(alias fun, State...)(State args) assert(s.front != s.front); // no caching } +// iota /** Construct a range of values that span the given starting and stopping values. @@ -5277,24 +5278,23 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) this(Value current, Value pastLast, StepType step) { - if ((current < pastLast && step >= 0) || - (current > pastLast && step <= 0)) + if ((current < pastLast && step > 0) || + (current > pastLast && step < 0)) { this.step = step; this.current = current; if (step > 0) { assert(unsigned((pastLast - current) / step) <= size_t.max); - - this.pastLast = pastLast - 1; + // Cast below can't fail because current < pastLast + this.pastLast = cast(Value) (pastLast - 1); this.pastLast -= (this.pastLast - current) % step; } else { - if (step < 0) - assert(unsigned((current - pastLast) / -step) <= size_t.max); - - this.pastLast = pastLast + 1; + assert(step == 0 || unsigned((current - pastLast) / -step) <= size_t.max); + // Cast below can't fail because current > pastLast + this.pastLast = cast(Value) (pastLast + 1); this.pastLast += (current - this.pastLast) % -step; } this.pastLast += step; @@ -5311,7 +5311,11 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) @property inout(Value) front() inout { assert(!empty); return current; } void popFront() { assert(!empty); current += step; } - @property inout(Value) back() inout { assert(!empty); return pastLast - step; } + @property inout(Value) back() inout + { + assert(!empty); + return cast(Value) (pastLast - step); + } void popBack() { assert(!empty); pastLast -= step; } @property auto save() { return this; } @@ -5747,6 +5751,18 @@ debug @system unittest } } +@safe @nogc nothrow unittest +{ + { + ushort start = 0, end = 10, step = 2; + foreach (i; iota(start, end, step)) {} + } + { + ubyte start = 0, end = 10, step = 2; + foreach (i; iota(start, end, step)) {} + } +} + /* Generic overload that handles arbitrary types that support arithmetic * operations. * From 0e441a9d9e0f56b7a69d2c3fa4e157857239ee35 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 14 May 2017 08:58:18 -0400 Subject: [PATCH 148/262] Fix Issue 16326 - filter is not lazy enough & has weird save behavior --- std/algorithm/iteration.d | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 1dc964edc54..c731799af75 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -1070,6 +1070,7 @@ public: assert(s.x == 2); } +// filter /** $(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));) @@ -1130,14 +1131,27 @@ private struct FilterResult(alias pred, Range) { alias R = Unqual!Range; R _input; + private bool _primed; - this(R r) + private void prime() { - _input = r; + if (_primed) return; while (!_input.empty && !pred(_input.front)) { _input.popFront(); } + _primed = true; + } + + this(R r) + { + _input = r; + } + + private this(R r, bool primed) + { + _input = r; + _primed = primed; } auto opSlice() { return this; } @@ -1148,7 +1162,7 @@ private struct FilterResult(alias pred, Range) } else { - @property bool empty() { return _input.empty; } + @property bool empty() { prime; return _input.empty; } } void popFront() @@ -1157,10 +1171,12 @@ private struct FilterResult(alias pred, Range) { _input.popFront(); } while (!_input.empty && !pred(_input.front)); + _primed = true; } @property auto ref front() { + prime; assert(!empty, "Attempting to fetch the front of an empty filter."); return _input.front; } @@ -1169,7 +1185,7 @@ private struct FilterResult(alias pred, Range) { @property auto save() { - return typeof(this)(_input.save); + return typeof(this)(_input.save, _primed); } } } @@ -1180,6 +1196,8 @@ private struct FilterResult(alias pred, Range) import std.internal.test.dummyrange; import std.range; + auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0); + int[] a = [ 3, 4, 2 ]; auto r = filter!("a > 3")(a); static assert(isForwardRange!(typeof(r))); From 74514bc7e5fed4dccec05e55253e70f1e6c06cc9 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 14 May 2017 12:11:41 -0400 Subject: [PATCH 149/262] Improve behavior on overflow --- std/range/package.d | 95 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 75fbdf8cf28..bec3091f95c 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -5273,50 +5273,57 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) static struct Result { - private Value current, pastLast; - private StepType step; + private Value current, last; + private StepType step; // by convention, 0 if range is empty this(Value current, Value pastLast, StepType step) { - if ((current < pastLast && step > 0) || - (current > pastLast && step < 0)) + if (current < pastLast && step > 0) { - this.step = step; - this.current = current; - if (step > 0) - { - assert(unsigned((pastLast - current) / step) <= size_t.max); - // Cast below can't fail because current < pastLast - this.pastLast = cast(Value) (pastLast - 1); - this.pastLast -= (this.pastLast - current) % step; - } - else - { - assert(step == 0 || unsigned((current - pastLast) / -step) <= size_t.max); - // Cast below can't fail because current > pastLast - this.pastLast = cast(Value) (pastLast + 1); - this.pastLast += (current - this.pastLast) % -step; - } - this.pastLast += step; + // Iterating upward + assert(unsigned((pastLast - current) / step) <= size_t.max); + // Cast below can't fail because current < pastLast + this.last = cast(Value) (pastLast - 1); + this.last -= unsigned(this.last - current) % step; + } + else if (current > pastLast && step < 0) + { + // Iterating downward + assert(unsigned((current - pastLast) / -step) <= size_t.max); + // Cast below can't fail because current > pastLast + this.last = cast(Value) (pastLast + 1); + this.last += unsigned(current - this.last) % -step; } else { // Initialize an empty range - this.current = this.pastLast = current; - this.step = 1; + this.step = 0; + return; } + this.step = step; + this.current = current; } - @property bool empty() const { return current == pastLast; } + @property bool empty() const { return step == 0; } @property inout(Value) front() inout { assert(!empty); return current; } - void popFront() { assert(!empty); current += step; } + void popFront() + { + assert(!empty); + if (current == last) step = 0; + else current += step; + } @property inout(Value) back() inout { assert(!empty); - return cast(Value) (pastLast - step); + return last; + } + void popBack() + { + assert(!empty); + if (current == last) step = 0; + else last -= step; } - void popBack() { assert(!empty); pastLast -= step; } @property auto save() { return this; } @@ -5333,20 +5340,18 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { assert(upper >= lower && upper <= this.length); - return cast(inout Result) Result(cast(Value)(current + lower * step), - cast(Value)(pastLast - (length - upper) * step), - step); + return cast(inout Result) Result( + cast(Value)(current + lower * step), + cast(Value)(current + upper * step), + step); } @property size_t length() const { if (step > 0) - { - return cast(size_t)((pastLast - current) / step); - } - else - { - return cast(size_t)((current - pastLast) / -step); - } + return 1 + cast(size_t) ((last - current) / step); + if (step < 0) + return 1 + cast(size_t) ((current - last) / -step); + return 0; } alias opDollar = length; @@ -5751,15 +5756,23 @@ debug @system unittest } } -@safe @nogc nothrow unittest +@nogc nothrow pure @safe +unittest { { ushort start = 0, end = 10, step = 2; - foreach (i; iota(start, end, step)) {} + foreach (i; iota(start, end, step)) + static assert(is(typeof(i) == ushort)); } { - ubyte start = 0, end = 10, step = 2; - foreach (i; iota(start, end, step)) {} + ubyte start = 0, end = 255, step = 128; + uint x; + foreach (i; iota(start, end, step)) + { + static assert(is(typeof(i) == ubyte)); + ++x; + } + assert(x == 2); } } From 6dbcf464131568b7ee1522b51bae86aa4abfb7c7 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 15 May 2017 04:26:12 +0000 Subject: [PATCH 150/262] std.datetime.stopwatch: Fix random test failure on Win32 --- std/datetime/stopwatch.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/datetime/stopwatch.d b/std/datetime/stopwatch.d index 69ab822921d..96e8e2be920 100644 --- a/std/datetime/stopwatch.d +++ b/std/datetime/stopwatch.d @@ -231,7 +231,7 @@ public: sw.stop(); assert(!sw.running); immutable t2 = sw.peek(); - assert(t2 > t1); + assert(t2 >= t1); immutable t3 = sw.peek(); assert(t2 == t3); } From 49ee158a9e5887ad835dc0f04c0309adf22ff965 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 16 May 2017 12:55:30 -0400 Subject: [PATCH 151/262] Fix Issue 15720 - iota(long.max, long.min, step) does not work properly --- std/range/package.d | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index c16014763b4..718209b95db 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -5217,7 +5217,7 @@ auto sequence(alias fun, State...)(State args) // iota /** - Construct a range of values that span the given starting and stopping + Creates a range of values that span the given starting and stopping values. Params: @@ -5354,9 +5354,9 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) @property size_t length() const { if (step > 0) - return 1 + cast(size_t) ((last - current) / step); + return 1 + cast(size_t) (unsigned(last - current) / step); if (step < 0) - return 1 + cast(size_t) ((current - last) / -step); + return 1 + cast(size_t) (unsigned(current - last) / -step); return 0; } @@ -5553,6 +5553,12 @@ nothrow @nogc @safe unittest auto t1 = iota(0, 10, 2); auto t2 = iota(1, 1, 0); //float overloads use std.conv.to so can't be @nogc or nothrow + alias ssize_t = Signed!size_t; + assert(iota(ssize_t.max, 0, -1).length == ssize_t.max); + assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max); + assert(iota(ssize_t.max, ssize_t.min, -2).length == 1 + size_t.max / 2); + assert(iota(ssize_t.min, ssize_t.max, 2).length == 1 + size_t.max / 2); + assert(iota(ssize_t.max, ssize_t.min, -3).length == size_t.max / 3); } debug @system unittest From cc44bc4e51c2d7139afb96534f910fffb2b04c55 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 15 May 2017 10:47:59 -0400 Subject: [PATCH 152/262] Small clean-up for PR #4027 --- std/range/package.d | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index c16014763b4..c5e69dda137 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7427,8 +7427,8 @@ Params: windowSize = Sliding window size stepSize = Steps between the windows (by default 1) -Returns: Range of all sliding windows with propagated bidirectionality, - forwarding, conditional random access and slicing. +Returns: Range of all sliding windows with propagated bi-directionality, + forwarding, conditional random access, and slicing. See_Also: $(LREF chunks) */ @@ -7454,15 +7454,15 @@ private: else { // if there's no information about the length, track needs to be kept manually - private Source _nextSource; + Source _nextSource; enum needsEndTracker = true; } - private bool _empty; + bool _empty; static if (hasSlicing!Source) { - private enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); + enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); } public: @@ -7516,7 +7516,7 @@ public: /// Forward range primitives. Always present. @property auto front() { - assert(!empty, "Attempting to access front on an empty range"); + assert(!empty, "Attempting to access front on an empty slides"); static if (hasSlicing!Source && hasLength!Source) { import std.algorithm.comparison : min; @@ -7531,7 +7531,7 @@ public: /// Ditto void popFront() { - assert(!empty, "Attempting to call popFront() on an empty range"); + assert(!empty, "Attempting to call popFront() on an empty slides"); _source.popFrontN(_stepSize); // if the range has less elements than its window size, @@ -7593,7 +7593,7 @@ public: { /** Indexing and slicing operations. Provided only if - $(D hasSlicing!Source) is $(D true). + `hasSlicing!Source` is `true`. */ auto opIndex(size_t index) { @@ -7617,6 +7617,7 @@ public: static if (!isInfinite!Source) { + /// ditto typeof(this) opSlice(size_t lower, size_t upper) { import std.algorithm.comparison : min; @@ -7738,7 +7739,7 @@ public: { import std.algorithm.comparison : max; - assert(!empty, "Attempting to access front on an empty slice"); + assert(!empty, "Attempting to access front on an empty slides"); immutable len = _source.length; /* @@ -7793,7 +7794,7 @@ public: /// Ditto void popBack() { - assert(!empty, "Attempting to call popBack() on an empty range"); + assert(!empty, "Attempting to call popBack() on an empty slides"); immutable end = _source.length > _stepSize ? _source.length - _stepSize : 0; _source = _source[0 .. end]; From 41578f734e50608efbde0a38ee57cf12d558e857 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 16 May 2017 14:05:02 -0400 Subject: [PATCH 153/262] Rename slides to slide --- changelog/std-range-slides.dd | 22 +- std/range/package.d | 376 +++++++++++++++++----------------- 2 files changed, 199 insertions(+), 199 deletions(-) diff --git a/changelog/std-range-slides.dd b/changelog/std-range-slides.dd index c2898806eb6..ae34a51293c 100644 --- a/changelog/std-range-slides.dd +++ b/changelog/std-range-slides.dd @@ -1,33 +1,33 @@ -`std.range.slides` (a fixed-size sliding window range) was added +`std.range.slide` (a fixed-size sliding window range) was added -$(REF slides, std, range) allows to iterate a range in sliding windows: +$(REF slide, std, range) allows to iterate a range in sliding windows: --- import std.array : array; import std.algorithm.comparison : equal; -assert([0, 1, 2, 3].slides(2).equal!equal( +assert([0, 1, 2, 3].slide(2).equal!equal( [[0, 1], [1, 2], [2, 3]] )); -assert(5.iota.slides(3).equal!equal( +assert(5.iota.slide(3).equal!equal( [[0, 1, 2], [1, 2, 3], [2, 3, 4]] )); -assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); -assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); +assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); +assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); // set a custom stepsize (default 1) -assert(6.iota.slides(1, 2).equal!equal( +assert(6.iota.slide(1, 2).equal!equal( [[0], [2], [4]] )); -assert(6.iota.slides(2, 4).equal!equal( +assert(6.iota.slide(2, 4).equal!equal( [[0, 1], [4, 5]] )); -// allow slides with less elements than the window size -assert(3.iota.slides!(No.slidesWithLessElements)(4).empty); -assert(3.iota.slides!(Yes.slidesWithLessElements)(4).equal!equal( +// allow slide with less elements than the window size +assert(3.iota.slide!(No.slideWithLessElements)(4).empty); +assert(3.iota.slide!(Yes.slideWithLessElements)(4).equal!equal( [[0, 1, 2]] )); --- diff --git a/std/range/package.d b/std/range/package.d index c5e69dda137..86e624351c8 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -161,7 +161,7 @@ $(BOOKTABLE , $(TD Similar to $(D recurrence), except that a random-access _range is created. )) - $(TR $(TD $(D $(LREF slides))) + $(TR $(TD $(D $(LREF slide))) $(TD Creates a _range that returns a fixed-size sliding window over the original _range. Unlike chunks, it advances a configurable number of items at a time, @@ -6915,7 +6915,7 @@ Params: r = Range from which the chunks will be selected chunkSize = Chunk size -See_Also: $(LREF slides) +See_Also: $(LREF slide) Returns: Forward range of all chunks with propagated bidirectionality, conditional random access and slicing. @@ -7419,11 +7419,11 @@ For `windowSize = 1` it splits the range into single element groups (aka `unflat For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. Params: - f = If `Yes.slidesWithLessElements` slides with fewer + f = If `Yes.slideWithLessElements` slide with fewer elements than `windowSize`. This can only happen if the initial range contains less elements than `windowSize`. In this case - if `No.slidesWithLessElements` an empty range will be returned. - r = Range from which the slides will be selected + if `No.slideWithLessElements` an empty range will be returned. + r = Range from which the slide will be selected windowSize = Sliding window size stepSize = Steps between the windows (by default 1) @@ -7432,14 +7432,14 @@ Returns: Range of all sliding windows with propagated bi-directionality, See_Also: $(LREF chunks) */ -auto slides(Flag!"slidesWithLessElements" f = Yes.slidesWithLessElements, +auto slide(Flag!"slideWithLessElements" f = Yes.slideWithLessElements, Source)(Source source, size_t windowSize, size_t stepSize = 1) if (isForwardRange!Source) { return Slides!(f, Source)(source, windowSize, stepSize); } -private struct Slides(Flag!"slidesWithLessElements" slidesWithLessElements = Yes.slidesWithLessElements, Source) +private struct Slides(Flag!"slideWithLessElements" slideWithLessElements = Yes.slideWithLessElements, Source) if (isForwardRange!Source) { private: @@ -7482,7 +7482,7 @@ public: _nextSource.popFrontN(windowSize); } - static if (!slidesWithLessElements) + static if (!slideWithLessElements) { // empty source range is needed, s.t. length, slicing etc. works properly static if (needsEndTracker) @@ -7516,7 +7516,7 @@ public: /// Forward range primitives. Always present. @property auto front() { - assert(!empty, "Attempting to access front on an empty slides"); + assert(!empty, "Attempting to access front on an empty slide"); static if (hasSlicing!Source && hasLength!Source) { import std.algorithm.comparison : min; @@ -7531,7 +7531,7 @@ public: /// Ditto void popFront() { - assert(!empty, "Attempting to call popFront() on an empty slides"); + assert(!empty, "Attempting to call popFront() on an empty slide"); _source.popFrontN(_stepSize); // if the range has less elements than its window size, @@ -7577,7 +7577,7 @@ public: { if (_source.length < _windowSize) { - static if (slidesWithLessElements) + static if (slideWithLessElements) return 1; else return 0; @@ -7608,7 +7608,7 @@ public: import std.algorithm.comparison : min; immutable len = _source.length; - assert(start < len, "slides index out of bounds"); + assert(start < len, "slide index out of bounds"); immutable end = min(start + _windowSize, len); } @@ -7621,7 +7621,7 @@ public: typeof(this) opSlice(size_t lower, size_t upper) { import std.algorithm.comparison : min; - assert(lower <= upper && upper <= length, "slides slicing index out of bounds"); + assert(lower <= upper && upper <= length, "slide slicing index out of bounds"); lower *= _stepSize; upper *= _stepSize; @@ -7631,14 +7631,14 @@ public: /* * Notice that we only need to move for windowSize - 1 to the right: * source = [0, 1, 2, 3] (length: 4) - * - source.slides(2) -> s = [[0, 1], [1, 2], [2, 3]] + * - source.slide(2) -> s = [[0, 1], [1, 2], [2, 3]] * right pos for s[0..3]: 3 (upper) + 2 (windowSize) - 1 = 4 * - * - source.slides(3) -> s = [[0, 1, 2], [1, 2, 3]] + * - source.slide(3) -> s = [[0, 1, 2], [1, 2, 3]] * right pos for s[0..2]: 2 (upper) + 3 (windowSize) - 1 = 4 * * source = [0, 1, 2, 3, 4] (length: 5) - * - source.slides(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] + * - source.slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] * right pos for s[0..2]: 2 (upper) + 4 (windowSize) - 1 = 5 */ return typeof(this) @@ -7651,7 +7651,7 @@ public: //For slicing an infinite chunk, we need to slice the source to the infinite end. auto opSlice(size_t lower, size_t upper) { - assert(lower <= upper, "slides slicing index out of bounds"); + assert(lower <= upper, "slide slicing index out of bounds"); return typeof(this)(_source[lower * _stepSize .. $], _windowSize, _stepSize).takeExactly(upper - lower); } @@ -7677,7 +7677,7 @@ public: { //Dollar token carries a static type, with no extra information. //It can lazily transform into _source.length on algorithmic - //operations such as : slides[$/2, $-1]; + //operations such as : slide[$/2, $-1]; private static struct DollarToken { private size_t _length; @@ -7707,7 +7707,7 @@ public: typeof(this) opSlice(size_t lower, DollarToken) { import std.algorithm.comparison : min; - assert(lower <= length, "slides slicing index out of bounds"); + assert(lower <= length, "slide slicing index out of bounds"); lower *= _stepSize; static if (hasSliceToEnd) { @@ -7723,7 +7723,7 @@ public: // Optimized slice overloads optimized for using dollar. typeof(this) opSlice(DollarToken, size_t upper) { - assert(upper == length, "slides slicing index out of bounds"); + assert(upper == length, "slide slicing index out of bounds"); return this[$ .. $]; } } @@ -7739,7 +7739,7 @@ public: { import std.algorithm.comparison : max; - assert(!empty, "Attempting to access front on an empty slides"); + assert(!empty, "Attempting to access front on an empty slide"); immutable len = _source.length; /* @@ -7747,16 +7747,16 @@ public: * - `end` in the following is the exclusive end as used in opSlice * - For the trivial case with `stepSize = 1` `end` is at `len`: * - * iota(4).slides(2) = [[0, 1], [1, 2], [2, 3] (end = 4) - * iota(4).slides(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) + * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3] (end = 4) + * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) * * - For the non-trivial cases, we need to calculate the gap * between `len` and `end` - this is the number of missing elements * from the input range: * - * iota(7).slides(2, 3) = [[0, 1], [3, 4]] || 6 - * iota(7).slides(2, 4) = [[0, 1], [4, 5]] || 6 - * iota(7).slides(1, 5) = [[0], [5]] || 6 + * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || 6 + * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || 6 + * iota(7).slide(1, 5) = [[0], [5]] || 6 * * As it can be seen `gap` can be at most `stepSize - 1` * More generally the elements of the sliding window with @@ -7775,11 +7775,11 @@ public: * * So for example: * - * iota(7).slides(2, 3) = [[0, 1], [3, 4]] + * iota(7).slide(2, 3) = [[0, 1], [3, 4]] * gap: (7 - 2) % 3 = 5 % 3 = 2 * end: 7 - 2 = 5 * - * iota(7).slides(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] + * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] * gap: (7 - 4) % 2 = 3 % 2 = 1 * end: 7 - 1 = 6 */ @@ -7794,7 +7794,7 @@ public: /// Ditto void popBack() { - assert(!empty, "Attempting to call popBack() on an empty slides"); + assert(!empty, "Attempting to call popBack() on an empty slide"); immutable end = _source.length > _stepSize ? _source.length - _stepSize : 0; _source = _source[0 .. end]; @@ -7812,28 +7812,28 @@ public: import std.array : array; import std.algorithm.comparison : equal; - assert([0, 1, 2, 3].slides(2).equal!equal( + assert([0, 1, 2, 3].slide(2).equal!equal( [[0, 1], [1, 2], [2, 3]] )); - assert(5.iota.slides(3).equal!equal( + assert(5.iota.slide(3).equal!equal( [[0, 1, 2], [1, 2, 3], [2, 3, 4]] )); - assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); - assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); // set a custom stepsize (default 1) - assert(6.iota.slides(1, 2).equal!equal( + assert(6.iota.slide(1, 2).equal!equal( [[0], [2], [4]] )); - assert(6.iota.slides(2, 4).equal!equal( + assert(6.iota.slide(2, 4).equal!equal( [[0, 1], [4, 5]] )); - // allow slides with less elements than the window size - assert(3.iota.slides!(No.slidesWithLessElements)(4).empty); - assert(3.iota.slides!(Yes.slidesWithLessElements)(4).equal!equal( + // allow slide with less elements than the window size + assert(3.iota.slide!(No.slideWithLessElements)(4).empty); + assert(3.iota.slide!(Yes.slideWithLessElements)(4).equal!equal( [[0, 1, 2]] )); } @@ -7845,7 +7845,7 @@ public: import std.algorithm.iteration : each; int[dstring] d; - "AGAGA"d.slides(2).each!(a => d[a]++); + "AGAGA"d.slide(2).each!(a => d[a]++); assert(d == ["AG"d: 2, "GA"d: 2]); } @@ -7855,10 +7855,10 @@ public: import std.algorithm.comparison : equal; static immutable res1 = [[0], [1], [2], [3]]; - assert(4.iota.slides(1).equal!equal(res1)); + assert(4.iota.slide(1).equal!equal(res1)); static immutable res2 = [[0, 1], [1, 2], [2, 3]]; - assert(4.iota.slides(2).equal!equal(res2)); + assert(4.iota.slide(2).equal!equal(res2)); } // different window sizes @@ -7867,83 +7867,83 @@ public: import std.array : array; import std.algorithm.comparison : equal; - assert([0, 1, 2, 3].slides(1).array == [[0], [1], [2], [3]]); - assert([0, 1, 2, 3].slides(2).array == [[0, 1], [1, 2], [2, 3]]); - assert([0, 1, 2, 3].slides(3).array == [[0, 1, 2], [1, 2, 3]]); - assert([0, 1, 2, 3].slides(4).array == [[0, 1, 2, 3]]); - assert([0, 1, 2, 3].slides(5).array == [[0, 1, 2, 3]]); + assert([0, 1, 2, 3].slide(1).array == [[0], [1], [2], [3]]); + assert([0, 1, 2, 3].slide(2).array == [[0, 1], [1, 2], [2, 3]]); + assert([0, 1, 2, 3].slide(3).array == [[0, 1, 2], [1, 2, 3]]); + assert([0, 1, 2, 3].slide(4).array == [[0, 1, 2, 3]]); + assert([0, 1, 2, 3].slide(5).array == [[0, 1, 2, 3]]); - assert(iota(2).slides(2).front.equal([0, 1])); - assert(iota(3).slides(2).equal!equal([[0, 1],[1, 2]])); - assert(iota(3).slides(3).equal!equal([[0, 1, 2]])); - assert(iota(3).slides(4).equal!equal([[0, 1, 2]])); - assert(iota(1, 4).slides(1).equal!equal([[1], [2], [3]])); - assert(iota(1, 4).slides(3).equal!equal([[1, 2, 3]])); + assert(iota(2).slide(2).front.equal([0, 1])); + assert(iota(3).slide(2).equal!equal([[0, 1],[1, 2]])); + assert(iota(3).slide(3).equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4).equal!equal([[0, 1, 2]])); + assert(iota(1, 4).slide(1).equal!equal([[1], [2], [3]])); + assert(iota(1, 4).slide(3).equal!equal([[1, 2, 3]])); } unittest { import std.algorithm.comparison : equal; - assert(6.iota.slides(1, 1).equal!equal( + assert(6.iota.slide(1, 1).equal!equal( [[0], [1], [2], [3], [4], [5]] )); - assert(6.iota.slides(1, 2).equal!equal( + assert(6.iota.slide(1, 2).equal!equal( [[0], [2], [4]] )); - assert(6.iota.slides(1, 3).equal!equal( + assert(6.iota.slide(1, 3).equal!equal( [[0], [3]] )); - assert(6.iota.slides(1, 4).equal!equal( + assert(6.iota.slide(1, 4).equal!equal( [[0], [4]] )); - assert(6.iota.slides(1, 5).equal!equal( + assert(6.iota.slide(1, 5).equal!equal( [[0], [5]] )); - assert(6.iota.slides(2, 1).equal!equal( + assert(6.iota.slide(2, 1).equal!equal( [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]] )); - assert(6.iota.slides(2, 2).equal!equal( + assert(6.iota.slide(2, 2).equal!equal( [[0, 1], [2, 3], [4, 5]] )); - assert(6.iota.slides(2, 3).equal!equal( + assert(6.iota.slide(2, 3).equal!equal( [[0, 1], [3, 4]] )); - assert(6.iota.slides(2, 4).equal!equal( + assert(6.iota.slide(2, 4).equal!equal( [[0, 1], [4, 5]] )); - assert(6.iota.slides(2, 5).equal!equal( + assert(6.iota.slide(2, 5).equal!equal( [[0, 1]] )); - assert(6.iota.slides(3, 1).equal!equal( + assert(6.iota.slide(3, 1).equal!equal( [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]] )); - assert(6.iota.slides(3, 2).equal!equal( + assert(6.iota.slide(3, 2).equal!equal( [[0, 1, 2], [2, 3, 4]] )); - assert(6.iota.slides(3, 3).equal!equal( + assert(6.iota.slide(3, 3).equal!equal( [[0, 1, 2], [3, 4, 5]] )); - assert(6.iota.slides(3, 4).equal!equal( + assert(6.iota.slide(3, 4).equal!equal( [[0, 1, 2]] )); - assert(6.iota.slides(4, 1).equal!equal( + assert(6.iota.slide(4, 1).equal!equal( [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]] )); - assert(6.iota.slides(4, 2).equal!equal( + assert(6.iota.slide(4, 2).equal!equal( [[0, 1, 2, 3], [2, 3, 4, 5]] )); - assert(6.iota.slides(4, 3).equal!equal( + assert(6.iota.slide(4, 3).equal!equal( [[0, 1, 2, 3]] )); - assert(6.iota.slides(5, 1).equal!equal( + assert(6.iota.slide(5, 1).equal!equal( [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]] )); - assert(6.iota.slides(5, 2).equal!equal( + assert(6.iota.slide(5, 2).equal!equal( [[0, 1, 2, 3, 4]] )); - assert(6.iota.slides(5, 3).equal!equal( + assert(6.iota.slide(5, 3).equal!equal( [[0, 1, 2, 3, 4]] )); } @@ -7956,11 +7956,11 @@ unittest // check with empty input int[] d; - assert(d.slides(2).empty); - assert(d.slides(2, 2).empty); + assert(d.slide(2).empty); + assert(d.slide(2, 2).empty); // is copyable? - auto e = iota(5).slides(2); + auto e = iota(5).slide(2); e.popFront; assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); @@ -7968,11 +7968,11 @@ unittest // test with strings int[dstring] f; - "AGAGA"d.slides(3).each!(a => f[a]++); + "AGAGA"d.slide(3).each!(a => f[a]++); assert(f == ["AGA"d: 2, "GAG"d: 1]); int[dstring] g; - "ABCDEFG"d.slides(3, 3).each!(a => g[a]++); + "ABCDEFG"d.slide(3, 3).each!(a => g[a]++); assert(g == ["ABC"d:1, "DEF"d:1]); } @@ -7983,71 +7983,71 @@ unittest import std.algorithm.comparison : equal; // test index - assert(iota(3).slides(4)[0].equal([0, 1, 2])); - assert(iota(5).slides(4)[1].equal([1, 2, 3, 4])); - assert(iota(3).slides(4, 2)[0].equal([0, 1, 2])); - assert(iota(5).slides(4, 2)[1].equal([2, 3, 4])); - assert(iota(3).slides(4, 3)[0].equal([0, 1, 2])); - assert(iota(5).slides(4, 3)[1].equal([3, 4,])); + assert(iota(3).slide(4)[0].equal([0, 1, 2])); + assert(iota(5).slide(4)[1].equal([1, 2, 3, 4])); + assert(iota(3).slide(4, 2)[0].equal([0, 1, 2])); + assert(iota(5).slide(4, 2)[1].equal([2, 3, 4])); + assert(iota(3).slide(4, 3)[0].equal([0, 1, 2])); + assert(iota(5).slide(4, 3)[1].equal([3, 4,])); // test slicing - assert(iota(3).slides(4)[0 .. $].equal!equal([[0, 1, 2]])); - assert(iota(3).slides(2)[1 .. $].equal!equal([[1, 2]])); - assert(iota(1, 5).slides(2)[0 .. 1].equal!equal([[1, 2]])); - assert(iota(1, 5).slides(2)[0 .. 2].equal!equal([[1, 2], [2, 3]])); - assert(iota(1, 5).slides(3)[0 .. 1].equal!equal([[1, 2, 3]])); - assert(iota(1, 5).slides(3)[0 .. 2].equal!equal([[1, 2, 3], [2, 3, 4]])); - assert(iota(1, 6).slides(3)[2 .. 3].equal!equal([[3, 4, 5]])); - assert(iota(1, 5).slides(4)[0 .. 1].equal!equal([[1, 2, 3, 4]])); + assert(iota(3).slide(4)[0 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(2)[1 .. $].equal!equal([[1, 2]])); + assert(iota(1, 5).slide(2)[0 .. 1].equal!equal([[1, 2]])); + assert(iota(1, 5).slide(2)[0 .. 2].equal!equal([[1, 2], [2, 3]])); + assert(iota(1, 5).slide(3)[0 .. 1].equal!equal([[1, 2, 3]])); + assert(iota(1, 5).slide(3)[0 .. 2].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(1, 6).slide(3)[2 .. 3].equal!equal([[3, 4, 5]])); + assert(iota(1, 5).slide(4)[0 .. 1].equal!equal([[1, 2, 3, 4]])); // length - assert(iota(3).slides(1).length == 3); - assert(iota(3).slides(1, 2).length == 2); - assert(iota(3).slides(1, 3).length == 1); - assert(iota(3).slides(1, 4).length == 1); - assert(iota(3).slides(2).length == 2); - assert(iota(3).slides(2, 2).length == 1); - assert(iota(3).slides(2, 3).length == 1); - assert(iota(3).slides(3).length == 1); - assert(iota(3).slides(3, 2).length == 1); + assert(iota(3).slide(1).length == 3); + assert(iota(3).slide(1, 2).length == 2); + assert(iota(3).slide(1, 3).length == 1); + assert(iota(3).slide(1, 4).length == 1); + assert(iota(3).slide(2).length == 2); + assert(iota(3).slide(2, 2).length == 1); + assert(iota(3).slide(2, 3).length == 1); + assert(iota(3).slide(3).length == 1); + assert(iota(3).slide(3, 2).length == 1); // opDollar - assert(iota(3).slides(4)[$/2 .. $].equal!equal([[0, 1, 2]])); - assert(iota(3).slides(4)[$ .. $].empty); - assert(iota(3).slides(4)[$ .. 1].empty); + assert(iota(3).slide(4)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4)[$ .. $].empty); + assert(iota(3).slide(4)[$ .. 1].empty); - assert(iota(5).slides(3, 1)[$/2 .. $].equal!equal([[1, 2, 3], [2, 3, 4]])); - assert(iota(5).slides(3, 2)[$/2 .. $].equal!equal([[2, 3, 4]])); - assert(iota(5).slides(3, 3)[$/2 .. $].equal!equal([[0, 1, 2]])); - assert(iota(3).slides(4, 3)[$ .. $].empty); - assert(iota(3).slides(4, 3)[$ .. 1].empty); + assert(iota(5).slide(3, 1)[$/2 .. $].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(iota(5).slide(3, 2)[$/2 .. $].equal!equal([[2, 3, 4]])); + assert(iota(5).slide(3, 3)[$/2 .. $].equal!equal([[0, 1, 2]])); + assert(iota(3).slide(4, 3)[$ .. $].empty); + assert(iota(3).slide(4, 3)[$ .. 1].empty); } -// test No.slidesWithLessElements +// test No.slideWithLessElements @safe pure nothrow unittest { - assert(iota(3).slides(4).length == 1); - assert(iota(3).slides(4, 4).length == 1); + assert(iota(3).slide(4).length == 1); + assert(iota(3).slide(4, 4).length == 1); - assert(iota(3).slides!(No.slidesWithLessElements)(4).empty); - assert(iota(3, 3).slides!(No.slidesWithLessElements)(4).empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4).length == 0); - assert(iota(3).slides!(No.slidesWithLessElements)(4, 4).length == 0); + assert(iota(3).slide!(No.slideWithLessElements)(4).empty); + assert(iota(3, 3).slide!(No.slideWithLessElements)(4).empty); + assert(iota(3).slide!(No.slideWithLessElements)(4).length == 0); + assert(iota(3).slide!(No.slideWithLessElements)(4, 4).length == 0); - assert(iota(3).slides!(No.slidesWithLessElements)(400).empty); - assert(iota(3).slides!(No.slidesWithLessElements)(400).length == 0); - assert(iota(3).slides!(No.slidesWithLessElements)(400, 10).length == 0); + assert(iota(3).slide!(No.slideWithLessElements)(400).empty); + assert(iota(3).slide!(No.slideWithLessElements)(400).length == 0); + assert(iota(3).slide!(No.slideWithLessElements)(400, 10).length == 0); - assert(iota(3).slides!(No.slidesWithLessElements)(4)[0 .. $].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4)[$ .. $].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4)[$ .. 0].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4)[$/2 .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4)[0 .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4)[$ .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4)[$ .. 0].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4)[$/2 .. $].empty); // with different step sizes - assert(iota(3).slides!(No.slidesWithLessElements)(4, 5)[0 .. $].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4, 6)[$ .. $].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4, 7)[$ .. 0].empty); - assert(iota(3).slides!(No.slidesWithLessElements)(4, 8)[$/2 .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4, 5)[0 .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4, 6)[$ .. $].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4, 7)[$ .. 0].empty); + assert(iota(3).slide!(No.slideWithLessElements)(4, 8)[$/2 .. $].empty); } // test with infinite ranges @@ -8057,12 +8057,12 @@ unittest // InfiniteRange without RandomAccess auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); - assert(fibs.slides(2).take(2).equal!equal([[1, 1], [1, 2]])); - assert(fibs.slides(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); + assert(fibs.slide(2).take(2).equal!equal([[1, 1], [1, 2]])); + assert(fibs.slide(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); // InfiniteRange with RandomAccess and slicing auto odds = sequence!("a[0] + n * a[1]")(1, 2); - auto oddsByPairs = odds.slides(2); + auto oddsByPairs = odds.slide(2); assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); assert(oddsByPairs[1].equal([3, 5])); assert(oddsByPairs[4].equal([9, 11])); @@ -8071,7 +8071,7 @@ unittest assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); - auto oddsWithGaps = odds.slides(2, 4); + auto oddsWithGaps = odds.slide(2, 4); assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); assert(oddsWithGaps[2].equal([17, 19])); assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); @@ -8083,15 +8083,15 @@ unittest { import std.algorithm.comparison : equal; - auto e = iota(3).slides(2); + auto e = iota(3).slide(2); assert(e.retro.equal!equal([[1, 2], [0, 1]])); assert(e.retro.array.equal(e.array.retro)); - auto e2 = iota(5).slides(3); + auto e2 = iota(5).slide(3); assert(e2.retro.equal!equal([[2, 3, 4], [1, 2, 3], [0, 1, 2]])); assert(e2.retro.array.equal(e2.array.retro)); - auto e3 = iota(3).slides(4); + auto e3 = iota(3).slide(4); assert(e3.retro.equal!equal([[0, 1, 2]])); assert(e3.retro.array.equal(e3.array.retro)); } @@ -8101,49 +8101,49 @@ unittest { import std.algorithm.comparison : equal; - assert(iota(7).slides(2, 1).retro.equal!equal( + assert(iota(7).slide(2, 1).retro.equal!equal( [[5, 6], [4, 5], [3, 4], [2, 3], [1, 2], [0, 1]] )); - assert(iota(7).slides(2, 2).retro.equal!equal( + assert(iota(7).slide(2, 2).retro.equal!equal( [[4, 5], [2, 3], [0, 1]] )); - assert(iota(7).slides(2, 3).retro.equal!equal( + assert(iota(7).slide(2, 3).retro.equal!equal( [[3, 4], [0, 1]] )); - assert(iota(7).slides(2, 4).retro.equal!equal( + assert(iota(7).slide(2, 4).retro.equal!equal( [[4, 5], [0, 1]] )); - assert(iota(7).slides(2, 5).retro.equal!equal( + assert(iota(7).slide(2, 5).retro.equal!equal( [[5, 6], [0, 1]] )); - assert(iota(7).slides(3, 1).retro.equal!equal( + assert(iota(7).slide(3, 1).retro.equal!equal( [[4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] )); - assert(iota(7).slides(3, 2).retro.equal!equal( + assert(iota(7).slide(3, 2).retro.equal!equal( [[4, 5, 6], [2, 3, 4], [0, 1, 2]] )); - assert(iota(7).slides(4, 1).retro.equal!equal( + assert(iota(7).slide(4, 1).retro.equal!equal( [[3, 4, 5, 6], [2, 3, 4, 5], [1, 2, 3, 4], [0, 1, 2, 3]] )); - assert(iota(7).slides(4, 2).retro.equal!equal( + assert(iota(7).slide(4, 2).retro.equal!equal( [[2, 3, 4, 5], [0, 1, 2, 3]] )); - assert(iota(7).slides(4, 3).retro.equal!equal( + assert(iota(7).slide(4, 3).retro.equal!equal( [[3, 4, 5, 6], [0, 1, 2, 3]] )); - assert(iota(7).slides(4, 4).retro.equal!equal( + assert(iota(7).slide(4, 4).retro.equal!equal( [[0, 1, 2, 3]] )); - assert(iota(7).slides(5, 1).retro.equal!equal( + assert(iota(7).slide(5, 1).retro.equal!equal( [[2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [0, 1, 2, 3, 4]] )); - assert(iota(7).slides(5, 2).retro.equal!equal( + assert(iota(7).slide(5, 2).retro.equal!equal( [[2, 3, 4, 5, 6], [0, 1, 2, 3, 4]] )); - assert(iota(7).slides(5, 3).retro.equal!equal( + assert(iota(7).slide(5, 3).retro.equal!equal( [[0, 1, 2, 3, 4]] )); - assert(iota(7).slides(5, 4).retro.equal!equal( + assert(iota(7).slide(5, 4).retro.equal!equal( [[0, 1, 2, 3, 4]] )); } @@ -8153,11 +8153,11 @@ unittest { import std.algorithm.comparison : equal; - assert(iota(7).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); - assert(iota(8).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); - assert(iota(9).slides(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); - assert(iota(12).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); - assert(iota(13).slides(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + assert(iota(7).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5]])); + assert(iota(8).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(9).slide(2, 2).equal!equal([[0, 1], [2, 3], [4, 5], [6, 7]])); + assert(iota(12).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); + assert(iota(13).slide(2, 4).equal!equal([[0, 1], [4, 5], [8, 9]])); } // test with dummy ranges @@ -8185,25 +8185,25 @@ unittest foreach (Range; AliasSeq!AllForwardDummyRanges) { Range r; - assert(r.slides(1).equal!equal( + assert(r.slide(1).equal!equal( [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] )); - assert(r.slides(2).equal!equal( + assert(r.slide(2).equal!equal( [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] )); - assert(r.slides(3).equal!equal( + assert(r.slide(3).equal!equal( [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] )); - assert(r.slides(6).equal!equal( + assert(r.slide(6).equal!equal( [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] )); - assert(r.slides(15).equal!equal( + assert(r.slide(15).equal!equal( [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] )); - assert(r.slides!(No.slidesWithLessElements)(15).empty); + assert(r.slide!(No.slideWithLessElements)(15).empty); } alias BackwardsDummyRanges = AliasSeq!( @@ -8214,32 +8214,32 @@ unittest foreach (Range; AliasSeq!BackwardsDummyRanges) { Range r; - assert(r.slides(1).retro.equal!equal( + assert(r.slide(1).retro.equal!equal( [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]] )); - assert(r.slides(2).retro.equal!equal( + assert(r.slide(2).retro.equal!equal( [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]] )); - assert(r.slides(5).retro.equal!equal( + assert(r.slide(5).retro.equal!equal( [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]] )); - assert(r.slides(15).retro.equal!equal( + assert(r.slide(15).retro.equal!equal( [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] )); // different step sizes - assert(r.slides(2, 4)[2].equal([9, 10])); - assert(r.slides(2, 1).equal!equal( + assert(r.slide(2, 4)[2].equal([9, 10])); + assert(r.slide(2, 1).equal!equal( [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] )); - assert(r.slides(2, 2).equal!equal( + assert(r.slide(2, 2).equal!equal( [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] )); - assert(r.slides(2, 3).equal!equal( + assert(r.slide(2, 3).equal!equal( [[1, 2], [4, 5], [7, 8]] )); - assert(r.slides(2, 4).equal!equal( + assert(r.slide(2, 4).equal!equal( [[1, 2], [5, 6], [9, 10]] )); @@ -8247,17 +8247,17 @@ unittest foreach (windowSize; 1..10) foreach (stepSize; 1..10) { - auto slider = r.slides(windowSize, stepSize); + auto slider = r.slide(windowSize, stepSize); assert(slider.retro.retro.equal!equal(slider)); } } - assert(iota(1, 12).slides(2, 4)[0..3].equal!equal([[1, 2], [5, 6], [9, 10]])); - assert(iota(1, 12).slides(2, 4)[0..$].equal!equal([[1, 2], [5, 6], [9, 10]])); - assert(iota(1, 12).slides(2, 4)[$/2..$].equal!equal([[5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[0..3].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[0..$].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[$/2..$].equal!equal([[5, 6], [9, 10]])); // reverse - assert(iota(1, 12).slides(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]])); + assert(iota(1, 12).slide(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]])); } // test different sliceable ranges @@ -8329,16 +8329,16 @@ unittest static assert (isForwardRange!Range); enum hasSliceToEnd = hasSlicing!Range && is(typeof(Range.init[0 .. $]) == Range); - assert(r.slides(2)[0].equal([0, 1])); - assert(r.slides(2)[1].equal([1, 2])); + assert(r.slide(2)[0].equal([0, 1])); + assert(r.slide(2)[1].equal([1, 2])); // saveable - auto s = r.slides(2); + auto s = r.slide(2); assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); s.save.popFront; assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); - assert(r.slides(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); + assert(r.slide(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); } alias SliceableDummyRangesWithoutInfinity = AliasSeq!( @@ -8356,30 +8356,30 @@ unittest Range r; r.arr = 10.iota.array; // for clarity - assert(r.slides!(No.slidesWithLessElements)(6).equal!equal( + assert(r.slide!(No.slideWithLessElements)(6).equal!equal( [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] )); - assert(r.slides!(No.slidesWithLessElements)(16).empty); + assert(r.slide!(No.slideWithLessElements)(16).empty); - assert(r.slides(4)[0 .. $].equal(r.slides(4))); - assert(r.slides(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); - assert(r.slides(2)[$ .. $].empty); + assert(r.slide(4)[0 .. $].equal(r.slide(4))); + assert(r.slide(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); + assert(r.slide(2)[$ .. $].empty); - assert(r.slides(3).retro.equal!equal( + assert(r.slide(3).retro.equal!equal( [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] )); } // separate checks for infinity auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); - assert(infIndex.slides(2)[0].equal([0, 1])); - assert(infIndex.slides(2)[1].equal([1, 2])); + assert(infIndex.slide(2)[0].equal([0, 1])); + assert(infIndex.slide(2)[1].equal([1, 2])); auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); - assert(infDollar.slides(2)[1 .. $].front.equal([1, 2])); - assert(infDollar.slides(4)[0 .. $].front.equal([0, 1, 2, 3])); - assert(infDollar.slides(4)[2 .. $].front.equal([2, 3, 4, 5])); + assert(infDollar.slide(2)[1 .. $].front.equal([1, 2])); + assert(infDollar.slide(4)[0 .. $].front.equal([0, 1, 2, 3])); + assert(infDollar.slide(4)[2 .. $].front.equal([2, 3, 4, 5])); } private struct OnlyResult(T, size_t arity) From a2400418760af8c3f1f7b95bf7ebde8d84c89745 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 16 May 2017 14:11:37 -0400 Subject: [PATCH 154/262] Rename slideWithLessElements to withFewerElements --- changelog/std-range-slides.dd | 4 +-- std/range/package.d | 54 +++++++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/changelog/std-range-slides.dd b/changelog/std-range-slides.dd index ae34a51293c..1da68532597 100644 --- a/changelog/std-range-slides.dd +++ b/changelog/std-range-slides.dd @@ -26,8 +26,8 @@ assert(6.iota.slide(2, 4).equal!equal( )); // allow slide with less elements than the window size -assert(3.iota.slide!(No.slideWithLessElements)(4).empty); -assert(3.iota.slide!(Yes.slideWithLessElements)(4).equal!equal( +assert(3.iota.slide!(No.withFewerElements)(4).empty); +assert(3.iota.slide!(Yes.withFewerElements)(4).equal!equal( [[0, 1, 2]] )); --- diff --git a/std/range/package.d b/std/range/package.d index 86e624351c8..512d2d710f4 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7419,10 +7419,10 @@ For `windowSize = 1` it splits the range into single element groups (aka `unflat For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. Params: - f = If `Yes.slideWithLessElements` slide with fewer + f = If `Yes.withFewerElements` slide with fewer elements than `windowSize`. This can only happen if the initial range contains less elements than `windowSize`. In this case - if `No.slideWithLessElements` an empty range will be returned. + if `No.withFewerElements` an empty range will be returned. r = Range from which the slide will be selected windowSize = Sliding window size stepSize = Steps between the windows (by default 1) @@ -7432,14 +7432,14 @@ Returns: Range of all sliding windows with propagated bi-directionality, See_Also: $(LREF chunks) */ -auto slide(Flag!"slideWithLessElements" f = Yes.slideWithLessElements, +auto slide(Flag!"withFewerElements" f = Yes.withFewerElements, Source)(Source source, size_t windowSize, size_t stepSize = 1) if (isForwardRange!Source) { return Slides!(f, Source)(source, windowSize, stepSize); } -private struct Slides(Flag!"slideWithLessElements" slideWithLessElements = Yes.slideWithLessElements, Source) +private struct Slides(Flag!"withFewerElements" withFewerElements = Yes.withFewerElements, Source) if (isForwardRange!Source) { private: @@ -7482,7 +7482,7 @@ public: _nextSource.popFrontN(windowSize); } - static if (!slideWithLessElements) + static if (!withFewerElements) { // empty source range is needed, s.t. length, slicing etc. works properly static if (needsEndTracker) @@ -7577,7 +7577,7 @@ public: { if (_source.length < _windowSize) { - static if (slideWithLessElements) + static if (withFewerElements) return 1; else return 0; @@ -7832,8 +7832,8 @@ public: )); // allow slide with less elements than the window size - assert(3.iota.slide!(No.slideWithLessElements)(4).empty); - assert(3.iota.slide!(Yes.slideWithLessElements)(4).equal!equal( + assert(3.iota.slide!(No.withFewerElements)(4).empty); + assert(3.iota.slide!(Yes.withFewerElements)(4).equal!equal( [[0, 1, 2]] )); } @@ -8023,31 +8023,31 @@ unittest assert(iota(3).slide(4, 3)[$ .. 1].empty); } -// test No.slideWithLessElements +// test No.withFewerElements @safe pure nothrow unittest { assert(iota(3).slide(4).length == 1); assert(iota(3).slide(4, 4).length == 1); - assert(iota(3).slide!(No.slideWithLessElements)(4).empty); - assert(iota(3, 3).slide!(No.slideWithLessElements)(4).empty); - assert(iota(3).slide!(No.slideWithLessElements)(4).length == 0); - assert(iota(3).slide!(No.slideWithLessElements)(4, 4).length == 0); + assert(iota(3).slide!(No.withFewerElements)(4).empty); + assert(iota(3, 3).slide!(No.withFewerElements)(4).empty); + assert(iota(3).slide!(No.withFewerElements)(4).length == 0); + assert(iota(3).slide!(No.withFewerElements)(4, 4).length == 0); - assert(iota(3).slide!(No.slideWithLessElements)(400).empty); - assert(iota(3).slide!(No.slideWithLessElements)(400).length == 0); - assert(iota(3).slide!(No.slideWithLessElements)(400, 10).length == 0); + assert(iota(3).slide!(No.withFewerElements)(400).empty); + assert(iota(3).slide!(No.withFewerElements)(400).length == 0); + assert(iota(3).slide!(No.withFewerElements)(400, 10).length == 0); - assert(iota(3).slide!(No.slideWithLessElements)(4)[0 .. $].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4)[$ .. $].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4)[$ .. 0].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4)[$/2 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[0 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$ .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$ .. 0].empty); + assert(iota(3).slide!(No.withFewerElements)(4)[$/2 .. $].empty); // with different step sizes - assert(iota(3).slide!(No.slideWithLessElements)(4, 5)[0 .. $].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4, 6)[$ .. $].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4, 7)[$ .. 0].empty); - assert(iota(3).slide!(No.slideWithLessElements)(4, 8)[$/2 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 5)[0 .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 6)[$ .. $].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 7)[$ .. 0].empty); + assert(iota(3).slide!(No.withFewerElements)(4, 8)[$/2 .. $].empty); } // test with infinite ranges @@ -8203,7 +8203,7 @@ unittest [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] )); - assert(r.slide!(No.slideWithLessElements)(15).empty); + assert(r.slide!(No.withFewerElements)(15).empty); } alias BackwardsDummyRanges = AliasSeq!( @@ -8356,11 +8356,11 @@ unittest Range r; r.arr = 10.iota.array; // for clarity - assert(r.slide!(No.slideWithLessElements)(6).equal!equal( + assert(r.slide!(No.withFewerElements)(6).equal!equal( [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] )); - assert(r.slide!(No.slideWithLessElements)(16).empty); + assert(r.slide!(No.withFewerElements)(16).empty); assert(r.slide(4)[0 .. $].equal(r.slide(4))); assert(r.slide(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); From 5ada634e4f194c0236b1b7371c02a01a915acb3e Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 16 May 2017 15:54:17 -0400 Subject: [PATCH 155/262] Fixed spelling mistakes in std.range --- std/range/package.d | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 512d2d710f4..edcec629152 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -2503,7 +2503,7 @@ The type returned by $(D takeOne) is a random-access range with length regardless of $(D R)'s capabilities, as long as it is a forward range. (another feature that distinguishes $(D takeOne) from $(D take)). If (D R) is an input range but not a forward range, return type is an input -range with all random-access capabilites except save. +range with all random-access capabilities except save. */ auto takeOne(R)(R source) if (isInputRange!R) @@ -9714,7 +9714,7 @@ $(D SortedRange) is currently restricted to random-access ranges. No copy of the original range is ever made. If the underlying range is changed concurrently with its corresponding $(D SortedRange) in ways -that break its sortedness, $(D SortedRange) will work erratically. +that break its sorted-ness, $(D SortedRange) will work erratically. */ @safe unittest { @@ -9854,13 +9854,13 @@ that break its sortedness, $(D SortedRange) will work erratically. Assumes $(D r) is sorted by predicate $(D pred) and returns the corresponding $(D SortedRange!(pred, R)) having $(D r) as support. To keep the checking costs low, the cost is $(BIGOH 1) in release mode -(no checks for sortedness are performed). In debug mode, a few random -elements of $(D r) are checked for sortedness. The size of the sample +(no checks for sorted-ness are performed). In debug mode, a few random +elements of $(D r) are checked for sorted-ness. The size of the sample is proportional $(BIGOH log(r.length)). That way, checking has no effect on the complexity of subsequent operations specific to sorted ranges (such as binary search). The probability of an arbitrary unsorted range failing the test is very high (however, an -almost-sorted range is likely to pass it). To check for sortedness at +almost-sorted range is likely to pass it). To check for sorted-ness at cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). */ auto assumeSorted(alias pred = "a < b", R)(R r) @@ -11256,7 +11256,7 @@ struct NullSink calling `front` is enough to have `tee` mirror elements to `outputRange` (or, respectively, `fun`). If `No.pipeOnPop`, only elements for which `front` does get called will be also sent to `outputRange`/`fun`. - inputRange = The input range beeing passed through. + inputRange = The input range being passed through. outputRange = This range will receive elements of `inputRange` progressively as iteration proceeds. fun = This function will be called with elements of `inputRange` From 50bdbff3fe397c2f7f3ea38a318e792fc1e1c487 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Wed, 17 May 2017 00:13:56 +0000 Subject: [PATCH 156/262] std.datetime.stopwatch: Fix another random test failure on Win32 --- std/datetime/stopwatch.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/datetime/stopwatch.d b/std/datetime/stopwatch.d index 96e8e2be920..12605a92045 100644 --- a/std/datetime/stopwatch.d +++ b/std/datetime/stopwatch.d @@ -172,7 +172,7 @@ public: sw.reset(); assert(sw.peek() < msecs(1)); assert(sw._timeStarted > before); - assert(sw._timeStarted < MonoTime.currTime); + assert(sw._timeStarted <= MonoTime.currTime); } From fabf19affc893ef51e57a380200552edbf13299c Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Wed, 17 May 2017 08:32:43 -0400 Subject: [PATCH 157/262] Defer addition of template until we need it for ranges. --- std/file.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/file.d b/std/file.d index 7be68d84594..09d611a8141 100644 --- a/std/file.d +++ b/std/file.d @@ -2319,7 +2319,7 @@ private bool ensureDirExists()(in char[] pathname) * Throws: $(D FileException) on error. */ -void mkdirRecurse()(in char[] pathname) +void mkdirRecurse(in char[] pathname) @safe { import std.path : dirName, baseName; From 99ccc950fa62d4837ccc154f1a8b1a73604b8b2a Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Thu, 18 May 2017 15:56:54 -0400 Subject: [PATCH 158/262] Removed unsafe pointer access and marked some functions as @safe in std.xml --- std/xml.d | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/std/xml.d b/std/xml.d index d2a28d87c8c..a193471cf54 100644 --- a/std/xml.d +++ b/std/xml.d @@ -373,7 +373,7 @@ S encode(S)(S s) return result.data; } -@safe unittest +@safe pure unittest { auto s = "hello"; assert(encode(s) is s); @@ -2058,9 +2058,12 @@ class ElementParser const startTag = startTags[tag_.name]; string text; + if (startTag.tagString.length == 0) + assert(0); + immutable(char)* p = startTag.tagString.ptr + startTag.tagString.length; - immutable(char)* q = tag_.tagString.ptr; + immutable(char)* q = &tag_.tagString[0]; text = decode(p[0..(q-p)], DecodeMode.LOOSE); auto element = new Element(startTag); @@ -2713,7 +2716,7 @@ private * parse failure (the XML equivalent of a stack trace), giving the line and * column number of every failure at every level. */ -void check(string s) pure +void check(string s) @safe pure { try { @@ -2903,16 +2906,15 @@ class CheckException : XMLException this.err = err; } - private void complete(string entire) pure + private void complete(string entire) @safe pure { - import std.encoding : transcode; import std.string : count, lastIndexOf; + import std.utf : toUTF32; string head = entire[0..$-tail.length]; ptrdiff_t n = head.lastIndexOf('\n') + 1; line = head.count("\n") + 1; - dstring t; - transcode(head[n..$],t); + dstring t = toUTF32(head[n..$]); column = t.length + 1; if (err !is null) err.complete(entire); } From bf43110f5a57a95144c55f8855030ba38186d9e8 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Thu, 18 May 2017 21:07:39 -0400 Subject: [PATCH 159/262] Add asOriginalType function for enums --- std/conv.d | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/std/conv.d b/std/conv.d index 7bcf2bfa436..473e7d0f600 100644 --- a/std/conv.d +++ b/std/conv.d @@ -4235,6 +4235,27 @@ private bool isOctalLiteral(const string num) assert(b == 1); } +// asOriginalType +/** +Returns the representation of an enumerated value, i.e. the value converted to +the base type of the enumeration. +*/ +OriginalType!E asOriginalType(E)(E value) if (is(E == enum)) +{ + return value; +} + +/// +unittest +{ + enum A { a = 42 } + static assert(is(typeof(A.a.asOriginalType) == int)); + assert(A.a.asOriginalType == 42); + enum B : double { a = 43 } + static assert(is(typeof(B.a.asOriginalType) == double)); + assert(B.a.asOriginalType == 43); +} + /+ emplaceRef is a package function for phobos internal use. It works like emplace, but takes its argument by ref (as opposed to "by pointer"). From c6f694d633776f04d04bb88a72992114a6dc32f5 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 19 May 2017 12:03:54 +0200 Subject: [PATCH 160/262] Revert "[Static if] replace overload constraints with static if (mutation.d)" --- std/algorithm/mutation.d | 501 ++++++++++++++++++++------------------- 1 file changed, 254 insertions(+), 247 deletions(-) diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index cf2b40bb7ea..f1aa9ee4769 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -368,61 +368,57 @@ See_Also: $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy) */ TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) -if (isInputRange!SourceRange) +if (areCopyCompatibleArrays!(SourceRange, TargetRange)) { - static if (areCopyCompatibleArrays!(SourceRange, TargetRange)) - { - const tlen = target.length; - const slen = source.length; - assert(tlen >= slen, - "Cannot copy a source range into a smaller target range."); + const tlen = target.length; + const slen = source.length; + assert(tlen >= slen, + "Cannot copy a source range into a smaller target range."); - immutable overlaps = __ctfe || () @trusted { - return source.ptr < target.ptr + tlen && - target.ptr < source.ptr + slen; }(); + immutable overlaps = __ctfe || () @trusted { + return source.ptr < target.ptr + tlen && + target.ptr < source.ptr + slen; }(); - if (overlaps) - { - foreach (idx; 0 .. slen) - target[idx] = source[idx]; - return target[slen .. tlen]; - } - else - { - // Array specialization. This uses optimized memory copying - // routines under the hood and is about 10-20x faster than the - // generic implementation. - target[0 .. slen] = source[]; - return target[slen .. $]; - } + if (overlaps) + { + foreach (idx; 0 .. slen) + target[idx] = source[idx]; + return target[slen .. tlen]; } - else static if (isOutputRange!(TargetRange, ElementType!SourceRange)) + else { - // Specialize for 2 random access ranges. - // Typically 2 random access ranges are faster iterated by common - // index than by x.popFront(), y.popFront() pair - static if (isRandomAccessRange!SourceRange && - hasLength!SourceRange && - hasSlicing!TargetRange && - isRandomAccessRange!TargetRange && - hasLength!TargetRange) - { - auto len = source.length; - foreach (idx; 0 .. len) - target[idx] = source[idx]; - return target[len .. target.length]; - } - else - { - put(target, source); - return target; - } + // Array specialization. This uses optimized memory copying + // routines under the hood and is about 10-20x faster than the + // generic implementation. + target[0 .. slen] = source[]; + return target[slen .. $]; + } +} + +/// ditto +TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) +if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && + isInputRange!SourceRange && + isOutputRange!(TargetRange, ElementType!SourceRange)) +{ + // Specialize for 2 random access ranges. + // Typically 2 random access ranges are faster iterated by common + // index than by x.popFront(), y.popFront() pair + static if (isRandomAccessRange!SourceRange && + hasLength!SourceRange && + hasSlicing!TargetRange && + isRandomAccessRange!TargetRange && + hasLength!TargetRange) + { + auto len = source.length; + foreach (idx; 0 .. len) + target[idx] = source[idx]; + return target[len .. target.length]; } else { - enum msg = "TargetRange is neither copy-compatible with the SourceRange" ~ - "nor an OutputRange with the same ElementType."; - static assert(0, msg); + put(target, source); + return target; } } @@ -847,60 +843,59 @@ See_Also: $(LREF uninitializeFill) */ void initializeAll(Range)(Range range) -if (isInputRange!Range) +if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) { - static if (hasLvalueElements!Range && hasAssignableElements!Range) - { - import core.stdc.string : memset, memcpy; - import std.traits : hasElaborateAssign, isDynamicArray; + import core.stdc.string : memset, memcpy; + import std.traits : hasElaborateAssign, isDynamicArray; - alias T = ElementType!Range; - static if (hasElaborateAssign!T) + alias T = ElementType!Range; + static if (hasElaborateAssign!T) + { + import std.algorithm.internal : addressOf; + //Elaborate opAssign. Must go the memcpy road. + //We avoid calling emplace here, because our goal is to initialize to + //the static state of T.init, + //So we want to avoid any un-necassarilly CC'ing of T.init + auto p = typeid(T).initializer(); + if (p.ptr) { - import std.algorithm.internal : addressOf; - //Elaborate opAssign. Must go the memcpy road. - //We avoid calling emplace here, because our goal is to initialize to - //the static state of T.init, - //So we want to avoid any un-necassarilly CC'ing of T.init - auto p = typeid(T).initializer(); - if (p.ptr) + for ( ; !range.empty ; range.popFront() ) { - for ( ; !range.empty ; range.popFront() ) + static if (__traits(isStaticArray, T)) { - static if (__traits(isStaticArray, T)) - { - // static array initializer only contains initialization - // for one element of the static array. - auto elemp = cast(void *) addressOf(range.front); - auto endp = elemp + T.sizeof; - while (elemp < endp) - { - memcpy(elemp, p.ptr, p.length); - elemp += p.length; - } - } - else + // static array initializer only contains initialization + // for one element of the static array. + auto elemp = cast(void *) addressOf(range.front); + auto endp = elemp + T.sizeof; + while (elemp < endp) { - memcpy(addressOf(range.front), p.ptr, T.sizeof); + memcpy(elemp, p.ptr, p.length); + elemp += p.length; } } - } - else - static if (isDynamicArray!Range) - memset(range.ptr, 0, range.length * T.sizeof); else - for ( ; !range.empty ; range.popFront() ) - memset(addressOf(range.front), 0, T.sizeof); + { + memcpy(addressOf(range.front), p.ptr, T.sizeof); + } + } } else - fill(range, T.init); - } - else static if (is(Range == char[]) || is(Range == wchar[])) - { - alias T = ElementEncodingType!Range; - range[] = T.init; + static if (isDynamicArray!Range) + memset(range.ptr, 0, range.length * T.sizeof); + else + for ( ; !range.empty ; range.popFront() ) + memset(addressOf(range.front), 0, T.sizeof); } - else static assert(0, "Range doesn't have assignable elements."); + else + fill(range, T.init); +} + +/// ditto +void initializeAll(Range)(Range range) +if (is(Range == char[]) || is(Range == wchar[])) +{ + alias T = ElementEncodingType!Range; + range[] = T.init; } /// @@ -1750,133 +1745,138 @@ Returns: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) -if (isBidirectionalRange!Range +if (s != SwapStrategy.stable + && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1) { - static if (s == SwapStrategy.unstable) + Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; + foreach (i, v; offset) { - Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; - foreach (i, v; offset) + static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) { - static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) - { - blackouts[i].pos = v[0]; - blackouts[i].len = v[1] - v[0]; - } - else - { - static assert(is(typeof(v) : size_t), typeof(v).stringof); - blackouts[i].pos = v; - blackouts[i].len = 1; - } - static if (i > 0) - { - import std.exception : enforce; + blackouts[i].pos = v[0]; + blackouts[i].len = v[1] - v[0]; + } + else + { + static assert(is(typeof(v) : size_t), typeof(v).stringof); + blackouts[i].pos = v; + blackouts[i].len = 1; + } + static if (i > 0) + { + import std.exception : enforce; - enforce(blackouts[i - 1].pos + blackouts[i - 1].len - <= blackouts[i].pos, - "remove(): incorrect ordering of elements to remove"); - } + enforce(blackouts[i - 1].pos + blackouts[i - 1].len + <= blackouts[i].pos, + "remove(): incorrect ordering of elements to remove"); } + } - size_t left = 0, right = offset.length - 1; - auto tgt = range.save; - size_t tgtPos = 0; + size_t left = 0, right = offset.length - 1; + auto tgt = range.save; + size_t tgtPos = 0; - while (left <= right) + while (left <= right) + { + // Look for a blackout on the right + if (blackouts[right].pos + blackouts[right].len >= range.length) { - // Look for a blackout on the right - if (blackouts[right].pos + blackouts[right].len >= range.length) - { - range.popBackExactly(blackouts[right].len); + range.popBackExactly(blackouts[right].len); - // Since right is unsigned, we must check for this case, otherwise - // we might turn it into size_t.max and the loop condition will not - // fail when it should. - if (right > 0) - { - --right; - continue; - } - else - break; - } - // Advance to next blackout on the left - assert(blackouts[left].pos >= tgtPos); - tgt.popFrontExactly(blackouts[left].pos - tgtPos); - tgtPos = blackouts[left].pos; - - // Number of elements to the right of blackouts[right] - immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); - size_t toMove = void; - if (tailLen < blackouts[left].len) + // Since right is unsigned, we must check for this case, otherwise + // we might turn it into size_t.max and the loop condition will not + // fail when it should. + if (right > 0) { - toMove = tailLen; - blackouts[left].pos += toMove; - blackouts[left].len -= toMove; + --right; + continue; } else - { - toMove = blackouts[left].len; - ++left; - } - tgtPos += toMove; - foreach (i; 0 .. toMove) - { - move(range.back, tgt.front); - range.popBack(); - tgt.popFront(); - } + break; + } + // Advance to next blackout on the left + assert(blackouts[left].pos >= tgtPos); + tgt.popFrontExactly(blackouts[left].pos - tgtPos); + tgtPos = blackouts[left].pos; + + // Number of elements to the right of blackouts[right] + immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); + size_t toMove = void; + if (tailLen < blackouts[left].len) + { + toMove = tailLen; + blackouts[left].pos += toMove; + blackouts[left].len -= toMove; + } + else + { + toMove = blackouts[left].len; + ++left; + } + tgtPos += toMove; + foreach (i; 0 .. toMove) + { + move(range.back, tgt.front); + range.popBack(); + tgt.popFront(); } - - return range; } - else static if (s == SwapStrategy.stable) + + return range; +} + +/// Ditto +Range remove +(SwapStrategy s = SwapStrategy.stable, Range, Offset...) +(Range range, Offset offset) +if (s == SwapStrategy.stable + && isBidirectionalRange!Range + && hasLvalueElements!Range + && Offset.length >= 1) +{ + auto result = range; + auto src = range, tgt = range; + size_t pos; + foreach (pass, i; offset) { - auto result = range; - auto src = range, tgt = range; - size_t pos; - foreach (pass, i; offset) + static if (is(typeof(i[0])) && is(typeof(i[1]))) { - static if (is(typeof(i[0])) && is(typeof(i[1]))) - { - auto from = i[0], delta = i[1] - i[0]; - } - else - { - auto from = i; - enum delta = 1; - } + auto from = i[0], delta = i[1] - i[0]; + } + else + { + auto from = i; + enum delta = 1; + } - static if (pass > 0) - { - import std.exception : enforce; - enforce(pos <= from, - "remove(): incorrect ordering of elements to remove"); + static if (pass > 0) + { + import std.exception : enforce; + enforce(pos <= from, + "remove(): incorrect ordering of elements to remove"); - for (; pos < from; ++pos, src.popFront(), tgt.popFront()) - { - move(src.front, tgt.front); - } - } - else + for (; pos < from; ++pos, src.popFront(), tgt.popFront()) { - src.popFrontExactly(from); - tgt.popFrontExactly(from); - pos = from; + move(src.front, tgt.front); } - // now skip source to the "to" position - src.popFrontExactly(delta); - result.popBackExactly(delta); - pos += delta; } - // leftover move - moveAll(src, tgt); - return result; + else + { + src.popFrontExactly(from); + tgt.popFrontExactly(from); + pos = from; + } + // now skip source to the "to" position + src.popFrontExactly(delta); + result.popBackExactly(delta); + pos += delta; } - else static assert(0, "SwapStrategy.semistable is not supported."); + // leftover move + moveAll(src, tgt); + return result; } /// @@ -2132,67 +2132,24 @@ if (isBidirectionalRange!Range /** Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D swap). -UTF sequences consisting of multiple code units are preserved properly. - Params: r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) - with swappable elements, a random access range with a length member or a - narrow string. + with swappable elements or a random access range with a length member See_Also: $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view - -Bugs: - When passing a sting with unicode modifiers on characters, such as $(D \u0301), - this function will not properly keep the position of the modifier. For example, - reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of - $(D da\u0301b) ("dáb"). */ void reverse(Range)(Range r) -if (isBidirectionalRange!Range) +if (isBidirectionalRange!Range && !isRandomAccessRange!Range + && hasSwappableElements!Range) { - static if (isRandomAccessRange!Range) + while (!r.empty) { - //swapAt is in fact the only way to swap non lvalue ranges - immutable last = r.length-1; - immutable steps = r.length/2; - for (size_t i = 0; i < steps; i++) - { - r.swapAt(i, last-i); - } - } - else static if (hasSwappableElements!Range) - { - while (!r.empty) - { - swap(r.front, r.back); - r.popFront(); - if (r.empty) break; - r.popBack(); - } - } - else static if (isNarrowString!Range && !is(ElementType!Range == const) && !is(ElementType!Range == immutable)) - { - import std.string : representation; - import std.utf : stride; - - auto repr = representation(r); - for (size_t i = 0; i < r.length; ) - { - immutable step = stride(r, i); - if (step > 1) - { - .reverse(repr[i .. i + step]); - i += step; - } - else - { - ++i; - } - } - reverse(repr); + swap(r.front, r.back); + r.popFront(); + if (r.empty) break; + r.popBack(); } - else static assert(0, "Range is neither RandomAccess, has swappable elements nor a narrow string."); } /// @@ -2203,12 +2160,17 @@ if (isBidirectionalRange!Range) assert(arr == [ 3, 2, 1 ]); } -/// -@safe unittest +///ditto +void reverse(Range)(Range r) +if (isRandomAccessRange!Range && hasLength!Range) { - char[] arr = "hello\U00010143\u0100\U00010143".dup; - reverse(arr); - assert(arr == "\U00010143\u0100\U00010143olleh"); + //swapAt is in fact the only way to swap non lvalue ranges + immutable last = r.length-1; + immutable steps = r.length/2; + for (size_t i = 0; i < steps; i++) + { + r.swapAt(i, last-i); + } } @safe unittest @@ -2226,6 +2188,51 @@ if (isBidirectionalRange!Range) assert(range == [3, 2, 1]); } +/** +Reverses $(D r) in-place, where $(D r) is a narrow string (having +elements of type $(D char) or $(D wchar)). UTF sequences consisting of +multiple code units are preserved properly. + +Params: + s = a narrow string + +Bugs: + When passing a sting with unicode modifiers on characters, such as $(D \u0301), + this function will not properly keep the position of the modifier. For example, + reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of + $(D da\u0301b) ("dáb"). +*/ +void reverse(Char)(Char[] s) +if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) +{ + import std.string : representation; + import std.utf : stride; + + auto r = representation(s); + for (size_t i = 0; i < s.length; ) + { + immutable step = stride(s, i); + if (step > 1) + { + .reverse(r[i .. i + step]); + i += step; + } + else + { + ++i; + } + } + reverse(r); +} + +/// +@safe unittest +{ + char[] arr = "hello\U00010143\u0100\U00010143".dup; + reverse(arr); + assert(arr == "\U00010143\u0100\U00010143olleh"); +} + @safe unittest { void test(string a, string b) From eade7d35faf538f8c990c41502c6591038eaa44e Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Fri, 19 May 2017 16:02:14 -0400 Subject: [PATCH 161/262] Optimized std.datetime.date.fromISOString --- std/datetime/date.d | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index 8fde94edad6..fb3b05f386e 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -7282,33 +7282,39 @@ public: { import std.algorithm.searching : all, startsWith; import std.ascii : isDigit; - import std.conv : to; + import std.conv : to, text, ConvException; import std.exception : enforce; - import std.format : format; import std.string : strip; - auto dstr = to!dstring(strip(isoString)); + auto str = isoString.strip; - enforce(dstr.length >= 8, new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString)); - auto day = dstr[$-2 .. $]; - auto month = dstr[$-4 .. $-2]; - auto year = dstr[0 .. $-4]; + int day, month; - enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); + try + { + day = to!int(str[$ - 2 .. $]); + month = to!int(str[$ - 4 .. $ - 2]); + } + catch (ConvException) + { + throw new DateTimeException(text("Invalid ISO String: ", isoString)); + } + + auto year = str[0 .. $ - 4]; if (year.length > 4) { - enforce(year.startsWith('-', '+'), - new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(year[1..$]), - new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce!DateTimeException(year.startsWith('-', '+'), + text("Invalid ISO String: ", isoString)); + enforce!DateTimeException(all!isDigit(year[1 .. $]), + text("Invalid ISO String: ", isoString)); } else - enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce!DateTimeException(all!isDigit(year), text("Invalid ISO String: ", isoString)); - return Date(to!short(year), to!ubyte(month), to!ubyte(day)); + return Date(to!int(year), month, day); } /// From 5e496439e54f35eb401dfc30b66b35cb3202d4dd Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Fri, 19 May 2017 16:22:13 -0400 Subject: [PATCH 162/262] Add top documentation for asOriginalType and move its definition appropriately --- std/conv.d | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/std/conv.d b/std/conv.d index 473e7d0f600..3faff951482 100644 --- a/std/conv.d +++ b/std/conv.d @@ -7,6 +7,7 @@ $(SCRIPT inhibitQuickIndex = 1;) $(BOOKTABLE, $(TR $(TH Category) $(TH Functions)) $(TR $(TD Generic) $(TD + $(LREF asOriginalType) $(LREF castFrom) $(LREF emplace) $(LREF parse) @@ -4235,27 +4236,6 @@ private bool isOctalLiteral(const string num) assert(b == 1); } -// asOriginalType -/** -Returns the representation of an enumerated value, i.e. the value converted to -the base type of the enumeration. -*/ -OriginalType!E asOriginalType(E)(E value) if (is(E == enum)) -{ - return value; -} - -/// -unittest -{ - enum A { a = 42 } - static assert(is(typeof(A.a.asOriginalType) == int)); - assert(A.a.asOriginalType == 42); - enum B : double { a = 43 } - static assert(is(typeof(B.a.asOriginalType) == double)); - assert(B.a.asOriginalType == 43); -} - /+ emplaceRef is a package function for phobos internal use. It works like emplace, but takes its argument by ref (as opposed to "by pointer"). @@ -5670,6 +5650,27 @@ if (isIntegral!T) auto t = l.to!Test; } +// asOriginalType +/** +Returns the representation of an enumerated value, i.e. the value converted to +the base type of the enumeration. +*/ +OriginalType!E asOriginalType(E)(E value) if (is(E == enum)) +{ + return value; +} + +/// +unittest +{ + enum A { a = 42 } + static assert(is(typeof(A.a.asOriginalType) == int)); + assert(A.a.asOriginalType == 42); + enum B : double { a = 43 } + static assert(is(typeof(B.a.asOriginalType) == double)); + assert(B.a.asOriginalType == 43); +} + /** A wrapper on top of the built-in cast operator that allows one to restrict casting of the original type of the value. From 784a547ffc6a549c2a93c2e06c0d93484c27dc80 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Fri, 19 May 2017 21:45:05 -0400 Subject: [PATCH 163/262] Further improvements --- std/datetime/date.d | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index fb3b05f386e..f089141566e 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -7280,8 +7280,7 @@ public: static Date fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : all, startsWith; - import std.ascii : isDigit; + import std.algorithm.searching : startsWith; import std.conv : to, text, ConvException; import std.exception : enforce; import std.string : strip; @@ -7290,31 +7289,33 @@ public: enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString)); - int day, month; + int day, month, year; + auto yearStr = str[0 .. $ - 4]; try { - day = to!int(str[$ - 2 .. $]); - month = to!int(str[$ - 4 .. $ - 2]); + // using conversion to uint plus cast because it checks for +/- + // for us quickly while throwing ConvException + day = cast(int) to!uint(str[$ - 2 .. $]); + month = cast(int) to!uint(str[$ - 4 .. $ - 2]); + + if (yearStr.length > 4) + { + enforce!DateTimeException(yearStr.startsWith('-', '+'), + text("Invalid ISO String: ", isoString)); + year = to!int(yearStr); + } + else + { + year = cast(int) to!uint(yearStr); + } } catch (ConvException) { throw new DateTimeException(text("Invalid ISO String: ", isoString)); } - auto year = str[0 .. $ - 4]; - - if (year.length > 4) - { - enforce!DateTimeException(year.startsWith('-', '+'), - text("Invalid ISO String: ", isoString)); - enforce!DateTimeException(all!isDigit(year[1 .. $]), - text("Invalid ISO String: ", isoString)); - } - else - enforce!DateTimeException(all!isDigit(year), text("Invalid ISO String: ", isoString)); - - return Date(to!int(year), month, day); + return Date(year, month, day); } /// From cc8bd1cdfc60b9efed15e8c3be44e0ff0495f217 Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 20 May 2017 08:43:01 -0700 Subject: [PATCH 164/262] Fix it so that dirName does not use isConvertibleToString. --- std/path.d | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/std/path.d b/std/path.d index d2d19a18f8c..d2a4fce6877 100644 --- a/std/path.d +++ b/std/path.d @@ -510,9 +510,21 @@ if (isSomeChar!C && isSomeChar!C1) (with suitable adaptations for Windows paths). */ auto dirName(R)(R path) -if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || - isNarrowString!R) && - !isConvertibleToString!R) +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R) +{ + return _dirName(path); +} + +/// ditto +auto dirName(C)(C[] path) +if (isSomeChar!C) +{ + return _dirName(path); +} + +private auto _dirName(R)(R path) +if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { static auto result(bool dot, typeof(path[0 .. 1]) p) { @@ -596,15 +608,15 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element } } -auto dirName(R)(auto ref R path) -if (isConvertibleToString!R) -{ - return dirName!(StringTypeOf!R)(path); -} - @safe unittest { assert(testAliasedString!dirName("file")); + + enum S : string { a = "file/path/to/test" } + assert(S.a.dirName == "file/path/to"); + + char[S.a.length] sa = S.a[]; + assert(sa.dirName == "file/path/to"); } @system unittest From edc049d7ddc361c3b107737823624e0ca31f896d Mon Sep 17 00:00:00 2001 From: Jonathan M Davis Date: Sat, 20 May 2017 08:48:39 -0700 Subject: [PATCH 165/262] Fix it so that using convertible types with baseName is @safe. --- std/path.d | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/std/path.d b/std/path.d index d2a4fce6877..643348e8fc4 100644 --- a/std/path.d +++ b/std/path.d @@ -392,18 +392,28 @@ else static assert(0); (with suitable adaptations for Windows paths). */ auto baseName(R)(R path) -if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || - is(StringTypeOf!R)) +if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R) { - auto p1 = stripDrive!(BaseOf!R)(path); + return _baseName(path); +} + +/// ditto +auto baseName(C)(C[] path) +if (isSomeChar!C) +{ + return _baseName(path); +} + +private R _baseName(R)(R path) +if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R) +{ + auto p1 = stripDrive(path); if (p1.empty) { - version (Windows) if (isUNC!(BaseOf!R)(path)) - { + version (Windows) if (isUNC(path)) return path[0 .. 1]; - } - static if (is(StringTypeOf!R)) - return StringTypeOf!R.init[]; // which is null + static if (isSomeString!R) + return null; else return p1; // which is empty } @@ -429,7 +439,6 @@ if (isSomeChar!C && isSomeChar!C1) else return p; } - @safe unittest { assert(baseName("").empty); @@ -491,8 +500,16 @@ if (isSomeChar!C && isSomeChar!C1) assert(baseName(DirEntry("dir/file.ext")) == "file.ext"); } +@safe unittest +{ + assert(testAliasedString!baseName("file")); + enum S : string { a = "file/path/to/test" } + assert(S.a.baseName == "test"); + char[S.a.length] sa = S.a[]; + assert(sa.baseName == "test"); +} /** Returns the directory part of a path. On Windows, this includes the drive letter if present. From c0403e15272150dc65bc7f9b2adddbf79baa1b17 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 4 Aug 2016 16:18:17 -0700 Subject: [PATCH 166/262] path.d: add overflow checks --- std/path.d | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/std/path.d b/std/path.d index d2d19a18f8c..40f618c1f85 100644 --- a/std/path.d +++ b/std/path.d @@ -3913,7 +3913,10 @@ string expandTilde(string inputPath) nothrow assert(last_char > 1); // Reserve C memory for the getpwnam_r() function. - int extra_memory_size = 5 * 1024; + version (unittest) + uint extra_memory_size = 2; + else + uint extra_memory_size = 5 * 1024; char* extra_memory; scope(exit) free(extra_memory); @@ -3937,11 +3940,16 @@ string expandTilde(string inputPath) nothrow break; } - if (errno != ERANGE) + if (errno != ERANGE && + // On FreeBSD and OSX, errno can be left at 0 instead of set to ERANGE + errno != 0) onOutOfMemoryError(); // extra_memory isn't large enough - extra_memory_size *= 2; + import core.checkedint : mulu; + bool overflow; + extra_memory_size = mulu(extra_memory_size, 2, overflow); + if (overflow) assert(0); } return path; } From 6af2c7c6c02477155f7a0bda719b826df383e290 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Wed, 8 Feb 2017 13:38:26 -0800 Subject: [PATCH 167/262] std.conv.toTextRange(): don't reinvent ToTempString() --- std/conv.d | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/std/conv.d b/std/conv.d index 3faff951482..7389b944cb1 100644 --- a/std/conv.d +++ b/std/conv.d @@ -5478,23 +5478,18 @@ pure nothrow @safe /* @nogc */ unittest void toTextRange(T, W)(T value, W writer) if (isIntegral!T && isOutputRange!(W, char)) { - char[value.sizeof * 4] buffer = void; - uint i = cast(uint) (buffer.length - 1); + import core.internal.string; - bool negative = value < 0; - Unqual!(Unsigned!T) v = negative ? -value : value; - - while (v >= 10) + if (value < 0) { - auto c = cast(uint) (v % 10); - v /= 10; - buffer[i--] = cast(char) (c + '0'); + SignedStringBuf buf = void; + put(writer, signedToTempString(value, buf, 10)); + } + else + { + UnsignedStringBuf buf = void; + put(writer, unsignedToTempString(value, buf, 10)); } - - buffer[i] = cast(char) (v + '0'); //hexDigits[cast(uint) v]; - if (negative) - buffer[--i] = '-'; - put(writer, buffer[i .. $]); } @safe unittest From fc3ab2bcfd2a26a71be63a8285c285a3ff4f16da Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 20 May 2017 16:13:42 -0700 Subject: [PATCH 168/262] reimplement toUTF8/16 in terms of encode() --- std/utf.d | 62 +++++++------------------------------------------------ 1 file changed, 7 insertions(+), 55 deletions(-) diff --git a/std/utf.d b/std/utf.d index 29609a42688..e2860745859 100644 --- a/std/utf.d +++ b/std/utf.d @@ -2144,7 +2144,7 @@ private dchar _utfException(UseReplacementDchar useReplacementDchar)(string msg, $(D UTFException) if $(D c) is not a valid UTF code point. +/ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref char[4] buf, dchar c) @safe pure + out char[4] buf, dchar c) @safe pure { if (c <= 0x7F) { @@ -2219,7 +2219,7 @@ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( /// Ditto size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref wchar[2] buf, dchar c) @safe pure + out wchar[2] buf, dchar c) @safe pure { if (c <= 0xFFFF) { @@ -2272,7 +2272,7 @@ size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( /// Ditto size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)( - ref dchar[1] buf, dchar c) @safe pure + out dchar[1] buf, dchar c) @safe pure { if ((0xD800 <= c && c <= 0xDFFF) || 0x10FFFF < c) c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-32", c); @@ -2710,42 +2710,8 @@ if (isSomeString!S) deprecated("To be removed November 2017. Please use std.utf.encode instead.") char[] toUTF8(return out char[4] buf, dchar c) nothrow @nogc @safe pure { - if (c <= 0x7F) - { - buf[0] = cast(char) c; - return buf[0 .. 1]; - } - else if (c <= 0x7FF) - { - buf[0] = cast(char)(0xC0 | (c >> 6)); - buf[1] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 2]; - } - else if (c <= 0xFFFF) - { - if (c >= 0xD800 && c <= 0xDFFF) - c = replacementDchar; - - L3: - buf[0] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[2] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 3]; - } - else - { - if (c > 0x10FFFF) - { - c = replacementDchar; - goto L3; - } - - buf[0] = cast(char)(0xF0 | (c >> 18)); - buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[3] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 4]; - } + const sz = encode!(Yes.useReplacementDchar)(buf, c); + return buf[0 .. sz]; } /** @@ -2792,23 +2758,9 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) //@@@DEPRECATED_2017-10@@@ deprecated("To be removed November 2017. Please use std.utf.encode instead.") wchar[] toUTF16(return ref wchar[2] buf, dchar c) nothrow @nogc @safe pure -in -{ - assert(isValidDchar(c)); -} -body { - if (c <= 0xFFFF) - { - buf[0] = cast(wchar) c; - return buf[0 .. 1]; - } - else - { - buf[0] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); - return buf[0 .. 2]; - } + const sz = encode!(Yes.useReplacementDchar)(buf, c); + return buf[0 .. sz]; } /** From 2ffe350c965512304db31169955c344aa43d121e Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Sun, 21 May 2017 18:52:21 +0300 Subject: [PATCH 169/262] Replace deprecated functions from std_conv and std_uri --- std/conv.d | 4 ++-- std/uri.d | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/std/conv.d b/std/conv.d index 7389b944cb1..ef729674418 100644 --- a/std/conv.d +++ b/std/conv.d @@ -2135,12 +2135,12 @@ Lerr: /// @safe pure unittest { - import std.string : munch; + import std.string : tr; string test = "123 \t 76.14"; auto a = parse!uint(test); assert(a == 123); assert(test == " \t 76.14"); // parse bumps string - munch(test, " \t\n\r"); // skip ws + test = tr(test, " \t\n\r", "", "d"); // skip ws assert(test == "76.14"); auto b = parse!double(test); assert(b == 76.14); diff --git a/std/uri.d b/std/uri.d index f05d7cc164a..0f03d03bae2 100644 --- a/std/uri.d +++ b/std/uri.d @@ -316,11 +316,12 @@ if (isSomeChar!Char) string decode(Char)(in Char[] encodedURI) if (isSomeChar!Char) { - // selective imports trigger wrong deprecation - // https://issues.dlang.org/show_bug.cgi?id=17193 - static import std.utf; + import std.utf : encode; + import std.algorithm.iteration : each; auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash); - return std.utf.toUTF8(s); + char[] r; + s.each!(c => encode(r, c)); + return r; } /******************************* @@ -331,11 +332,12 @@ if (isSomeChar!Char) string decodeComponent(Char)(in Char[] encodedURIComponent) if (isSomeChar!Char) { - // selective imports trigger wrong deprecation - // https://issues.dlang.org/show_bug.cgi?id=17193 - static import std.utf; + import std.utf : encode; + import std.algorithm.iteration : each; auto s = URI_Decode(encodedURIComponent, 0); - return std.utf.toUTF8(s); + char[] r; + s.each!(c => encode(r, c)); + return r; } /***************************** From d3e00ae03d7ecef2e05369e432bf139367a9fa60 Mon Sep 17 00:00:00 2001 From: Amaury SECHET Date: Wed, 18 Jan 2017 09:13:12 -0500 Subject: [PATCH 170/262] Add crc64 support --- std/digest/crc.d | 187 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 149 insertions(+), 38 deletions(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index 25ec75c9c96..d91d239a70f 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -100,13 +100,13 @@ version(unittest) hash = crc.finish(); } -private uint[256][8] genTables(uint polynomial) +private T[256][8] genTables(T)(T polynomial) { - uint[256][8] res = void; + T[256][8] res = void; foreach (i; 0 .. 0x100) { - uint crc = i; + T crc = i; foreach (_; 0 .. 8) crc = (crc >> 1) ^ (-int(crc & 1) & polynomial); res[0][i] = crc; @@ -117,7 +117,6 @@ private uint[256][8] genTables(uint polynomial) res[1][i] = (res[0][i] >> 8) ^ res[0][res[0][i] & 0xFF]; res[2][i] = (res[1][i] >> 8) ^ res[0][res[1][i] & 0xFF]; res[3][i] = (res[2][i] >> 8) ^ res[0][res[2][i] & 0xFF]; - res[4][i] = (res[3][i] >> 8) ^ res[0][res[3][i] & 0xFF]; res[5][i] = (res[4][i] >> 8) ^ res[0][res[4][i] & 0xFF]; res[6][i] = (res[5][i] >> 8) ^ res[0][res[5][i] & 0xFF]; @@ -127,6 +126,7 @@ private uint[256][8] genTables(uint polynomial) } private static immutable uint[256][8] crc32Tables = genTables(0xEDB88320); +private static immutable ulong[256][8] crc64Tables = genTables(0xC96C5795D7870F42); @system unittest { @@ -138,11 +138,36 @@ private static immutable uint[256][8] crc32Tables = genTables(0xEDB88320); * Template API CRC32 implementation. * See $(D std.digest.digest) for differences between template and OOP API. */ -struct CRC32 +alias CRC32 = CRC!32; + +/** + * Template API CRC64 implementation. + * See $(D std.digest.digest) for differences between template and OOP API. + */ +alias CRC64 = CRC!64; + +/** + * Generic Template API used for CRC32 and CRC64 implementations. + * See $(D std.digest.digest) for differences between template and OOP API. + */ +private struct CRC(uint N) if (N == 32 || N == 64) { private: + static if (N == 32) + { + alias T = uint; + alias tables = crc32Tables; + } + else + { + alias T = ulong; + alias tables = crc64Tables; + } + + alias R = ubyte[T.sizeof]; + // magic initialization constants - uint _state = uint.max; + T _state = T.max; public: /** @@ -152,7 +177,7 @@ struct CRC32 */ void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc { - uint crc = _state; + T crc = _state; // process eight bytes at once while (data.length >= 8) { @@ -169,36 +194,46 @@ struct CRC32 enum hasLittleEndianUnalignedReads = false; // leave decision to optimizer static if (hasLittleEndianUnalignedReads) { - uint one = (cast(uint*) data.ptr)[0] ^ crc; + uint one = (cast(uint*) data.ptr)[0]; uint two = (cast(uint*) data.ptr)[1]; } else { - uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]) ^ crc; + uint one = (data.ptr[3] << 24 | data.ptr[2] << 16 | data.ptr[1] << 8 | data.ptr[0]); uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]); } + static if (N == 32) + { + one ^= crc; + } + else + { + one ^= (crc & 0xffffffff); + two ^= (crc >> 32); + } + crc = - crc32Tables[0][two >> 24] ^ - crc32Tables[1][(two >> 16) & 0xFF] ^ - crc32Tables[2][(two >> 8) & 0xFF] ^ - crc32Tables[3][two & 0xFF] ^ - crc32Tables[4][one >> 24] ^ - crc32Tables[5][(one >> 16) & 0xFF] ^ - crc32Tables[6][(one >> 8) & 0xFF] ^ - crc32Tables[7][one & 0xFF]; + tables[0][two >> 24] ^ + tables[1][(two >> 16) & 0xFF] ^ + tables[2][(two >> 8) & 0xFF] ^ + tables[3][two & 0xFF] ^ + tables[4][one >> 24] ^ + tables[5][(one >> 16) & 0xFF] ^ + tables[6][(one >> 8) & 0xFF] ^ + tables[7][one & 0xFF]; data = data[8 .. $]; } // remaining 1 to 7 bytes foreach (d; data) - crc = (crc >> 8) ^ crc32Tables[0][(crc & 0xFF) ^ d]; + crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d]; _state = crc; } /// @safe unittest { - CRC32 dig; + CRC dig; dig.put(cast(ubyte) 0); //single ubyte dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic ubyte[10] buf; @@ -216,21 +251,21 @@ struct CRC32 */ void start() @safe pure nothrow @nogc { - this = CRC32.init; + this = CRC.init; } /// @safe unittest { - CRC32 digest; + CRC digest; //digest.start(); //Not necessary digest.put(0); } /** - * Returns the finished CRC32 hash. This also calls $(LREF start) to + * Returns the finished CRC hash. This also calls $(LREF start) to * reset the internal state. */ - ubyte[4] finish() @safe pure nothrow @nogc + R finish() @safe pure nothrow @nogc { auto tmp = peek(); start(); @@ -240,16 +275,16 @@ struct CRC32 @safe unittest { //Simple example - CRC32 hash; + CRC hash; hash.put(cast(ubyte) 0); - ubyte[4] result = hash.finish(); + R result = hash.finish(); } /** * Works like $(D finish) but does not reset the internal state, so it's possible - * to continue putting data into this CRC32 after a call to peek. + * to continue putting data into this CRC after a call to peek. */ - ubyte[4] peek() const @safe pure nothrow @nogc + R peek() const @safe pure nothrow @nogc { import std.bitmanip : nativeToLittleEndian; //Complement, LSB first / Little Endian, see http://rosettacode.org/wiki/CRC-32 @@ -261,20 +296,26 @@ struct CRC32 @safe unittest { //Simple example, hashing a string using crc32Of helper function - ubyte[4] hash = crc32Of("abc"); + ubyte[4] hash32 = crc32Of("abc"); //Let's get a hash string - assert(crcHexString(hash) == "352441C2"); + assert(crcHexString(hash32) == "352441C2"); + // Repeat for CRC64 + ubyte[8] hash64 = crc64Of("abc"); + assert(crcHexString(hash64) == "2CD8094A1A277627"); } /// @safe unittest { - //Using the basic API - CRC32 hash; ubyte[1024] data; + //Using the basic API + CRC32 hash32; + CRC64 hash64; //Initialize data here... - hash.put(data); - ubyte[4] result = hash.finish(); + hash32.put(data); + ubyte[4] result32 = hash32.finish(); + hash64.put(data); + ubyte[8] result64 = hash64.finish(); } /// @@ -287,15 +328,21 @@ struct CRC32 { hash.put(cast(ubyte) 0); } - CRC32 crc; - crc.start(); - doSomething(crc); - assert(crcHexString(crc.finish()) == "D202EF8D"); + CRC32 crc32; + crc32.start(); + doSomething(crc32); + assert(crcHexString(crc32.finish()) == "D202EF8D"); + // repeat for CRC64 + CRC64 crc64; + crc64.start(); + doSomething(crc64); + assert(crcHexString(crc64.finish()) == "1FADA17364673F59"); } @safe unittest { assert(isDigest!CRC32); + assert(isDigest!CRC64); } @system unittest @@ -337,6 +384,44 @@ struct CRC32 assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3"); } +unittest +{ + ubyte[8] digest; + + CRC64 crc; + crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); + assert(crc.peek() == cast(ubyte[])x"2f121b7575789626"); + crc.start(); + crc.put(cast(ubyte[])""); + assert(crc.finish() == cast(ubyte[])x"0000000000000000"); + digest = crc64Of(""); + assert(digest == cast(ubyte[])x"0000000000000000"); + + //Test vector from http://rosettacode.org/wiki/CRC-32 + assert(crcHexString(crc64Of("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4"); + + digest = crc64Of("a"); + assert(digest == cast(ubyte[])x"052b652e77840233"); + + digest = crc64Of("abc"); + assert(digest == cast(ubyte[])x"2776271a4a09d82c"); + + digest = crc64Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + assert(digest == cast(ubyte[])x"4b7cdce3746c449f"); + + digest = crc64Of("message digest"); + assert(digest == cast(ubyte[])x"6f9b8a3156c9bc5d"); + + digest = crc64Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + assert(digest == cast(ubyte[])x"2656b716e1bf0503"); + + digest = crc64Of("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + assert(digest == cast(ubyte[])x"bd3eb7765d0a22ae"); + + assert(crcHexString(cast(ubyte[8])x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); +} + /** * This is a convenience alias for $(REF digest, std,digest,digest) using the * CRC32 implementation. @@ -355,6 +440,24 @@ ubyte[4] crc32Of(T...)(T data) return digest!(CRC32, T)(data); } +/** + * This is a convenience alias for $(REF digest, std,digest,digest) using the + * CRC64 implementation. + * + * Params: + * data = $(D InputRange) of $(D ElementType) implicitly convertible to + * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays + * of any type. + * + * Returns: + * CRC64 of data + */ +//simple alias doesn't work here, hope this gets inlined... +ubyte[8] crc64Of(T...)(T data) +{ + return digest!(CRC64, T)(data); +} + /// @system unittest { @@ -380,7 +483,6 @@ public alias crcHexString = toHexString!(Order.decreasing); ///ditto public alias crcHexString = toHexString!(Order.decreasing, 16); - /** * OOP API CRC32 implementation. * See $(D std.digest.digest) for differences between template and OOP API. @@ -390,6 +492,15 @@ public alias crcHexString = toHexString!(Order.decreasing, 16); */ alias CRC32Digest = WrapperDigest!CRC32; +/** + * OOP API CRC64 implementation. + * See $(D std.digest.digest) for differences between template and OOP API. + * + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64), see + * there for more information. + */ +alias CRC64Digest = WrapperDigest!CRC64; + /// @safe unittest { From a4d9b57cdcfcda082203e2bd6ccca216922353dc Mon Sep 17 00:00:00 2001 From: Amaury SECHET Date: Thu, 19 Jan 2017 14:50:03 -0500 Subject: [PATCH 171/262] Add ECMA and ISO CRC64 variations --- std/digest/crc.d | 152 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 37 deletions(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index d91d239a70f..7772389d825 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -125,9 +125,6 @@ private T[256][8] genTables(T)(T polynomial) return res; } -private static immutable uint[256][8] crc32Tables = genTables(0xEDB88320); -private static immutable ulong[256][8] crc64Tables = genTables(0xC96C5795D7870F42); - @system unittest { auto tables = genTables(0xEDB88320); @@ -138,32 +135,38 @@ private static immutable ulong[256][8] crc64Tables = genTables(0xC96C5795D7870F4 * Template API CRC32 implementation. * See $(D std.digest.digest) for differences between template and OOP API. */ -alias CRC32 = CRC!32; +alias CRC32 = CRC!(32, 0xEDB88320); + +/** + * Template API CRC64-ECMA implementation. + * See $(D std.digest.digest) for differences between template and OOP API. + */ +alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42); /** - * Template API CRC64 implementation. + * Template API CRC64-ISO implementation. * See $(D std.digest.digest) for differences between template and OOP API. */ -alias CRC64 = CRC!64; +alias CRC64ISO = CRC!(64, 0xD800000000000000); /** * Generic Template API used for CRC32 and CRC64 implementations. * See $(D std.digest.digest) for differences between template and OOP API. */ -private struct CRC(uint N) if (N == 32 || N == 64) +private struct CRC(uint N, ulong P) if (N == 32 || N == 64) { private: static if (N == 32) { alias T = uint; - alias tables = crc32Tables; } else { alias T = ulong; - alias tables = crc64Tables; } + static immutable T[256][8] tables = genTables!T(P); + alias R = ubyte[T.sizeof]; // magic initialization constants @@ -300,8 +303,10 @@ private struct CRC(uint N) if (N == 32 || N == 64) //Let's get a hash string assert(crcHexString(hash32) == "352441C2"); // Repeat for CRC64 - ubyte[8] hash64 = crc64Of("abc"); - assert(crcHexString(hash64) == "2CD8094A1A277627"); + ubyte[8] hash64ecma = crc64ECMAOf("abc"); + assert(crcHexString(hash64ecma) == "2CD8094A1A277627"); + ubyte[8] hash64iso = crc64ISOOf("abc"); + assert(crcHexString(hash64iso) == "3776C42000000000"); } /// @@ -310,12 +315,15 @@ private struct CRC(uint N) if (N == 32 || N == 64) ubyte[1024] data; //Using the basic API CRC32 hash32; - CRC64 hash64; + CRC64ECMA hash64ecma; + CRC64ISO hash64iso; //Initialize data here... hash32.put(data); ubyte[4] result32 = hash32.finish(); - hash64.put(data); - ubyte[8] result64 = hash64.finish(); + hash64ecma.put(data); + ubyte[8] result64ecma = hash64ecma.finish(); + hash64iso.put(data); + ubyte[8] result64iso = hash64iso.finish(); } /// @@ -333,16 +341,21 @@ private struct CRC(uint N) if (N == 32 || N == 64) doSomething(crc32); assert(crcHexString(crc32.finish()) == "D202EF8D"); // repeat for CRC64 - CRC64 crc64; - crc64.start(); - doSomething(crc64); - assert(crcHexString(crc64.finish()) == "1FADA17364673F59"); + CRC64ECMA crc64ecma; + crc64ecma.start(); + doSomething(crc64ecma); + assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59"); + CRC64ISO crc64iso; + crc64iso.start(); + doSomething(crc64iso); + assert(crcHexString(crc64iso.finish()) == "6F90000000000000"); } @safe unittest { assert(isDigest!CRC32); - assert(isDigest!CRC64); + assert(isDigest!CRC64ECMA); + assert(isDigest!CRC64ISO); } @system unittest @@ -388,40 +401,78 @@ unittest { ubyte[8] digest; - CRC64 crc; + CRC64ECMA crc; crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); assert(crc.peek() == cast(ubyte[])x"2f121b7575789626"); crc.start(); crc.put(cast(ubyte[])""); assert(crc.finish() == cast(ubyte[])x"0000000000000000"); - digest = crc64Of(""); + digest = crc64ECMAOf(""); assert(digest == cast(ubyte[])x"0000000000000000"); //Test vector from http://rosettacode.org/wiki/CRC-32 - assert(crcHexString(crc64Of("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4"); + assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4"); - digest = crc64Of("a"); + digest = crc64ECMAOf("a"); assert(digest == cast(ubyte[])x"052b652e77840233"); - digest = crc64Of("abc"); + digest = crc64ECMAOf("abc"); assert(digest == cast(ubyte[])x"2776271a4a09d82c"); - digest = crc64Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); assert(digest == cast(ubyte[])x"4b7cdce3746c449f"); - digest = crc64Of("message digest"); + digest = crc64ECMAOf("message digest"); assert(digest == cast(ubyte[])x"6f9b8a3156c9bc5d"); - digest = crc64Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); assert(digest == cast(ubyte[])x"2656b716e1bf0503"); - digest = crc64Of("1234567890123456789012345678901234567890"~ - "1234567890123456789012345678901234567890"); + digest = crc64ECMAOf("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); assert(digest == cast(ubyte[])x"bd3eb7765d0a22ae"); assert(crcHexString(cast(ubyte[8])x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); } +unittest +{ + ubyte[8] digest; + + CRC64ISO crc; + crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); + assert(crc.peek() == cast(ubyte[])x"f0494ab780989b42"); + crc.start(); + crc.put(cast(ubyte[])""); + assert(crc.finish() == cast(ubyte[])x"0000000000000000"); + digest = crc64ISOOf(""); + assert(digest == cast(ubyte[])x"0000000000000000"); + + //Test vector from http://rosettacode.org/wiki/CRC-32 + assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E"); + + digest = crc64ISOOf("a"); + assert(digest == cast(ubyte[])x"0000000000002034"); + + digest = crc64ISOOf("abc"); + assert(digest == cast(ubyte[])x"0000000020c47637"); + + digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + assert(digest == cast(ubyte[])x"5173f717971365e5"); + + digest = crc64ISOOf("message digest"); + assert(digest == cast(ubyte[])x"a2c355bbc0b93f86"); + + digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + assert(digest == cast(ubyte[])x"598B258292E40084"); + + digest = crc64ISOOf("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + assert(digest == cast(ubyte[])x"760cd2d3588bf809"); + + assert(crcHexString(cast(ubyte[8])x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); +} + /** * This is a convenience alias for $(REF digest, std,digest,digest) using the * CRC32 implementation. @@ -442,7 +493,25 @@ ubyte[4] crc32Of(T...)(T data) /** * This is a convenience alias for $(REF digest, std,digest,digest) using the - * CRC64 implementation. + * CRC64-ECMA implementation. + * + * Params: + * data = $(D InputRange) of $(D ElementType) implicitly convertible to + * $(D ubyte), $(D ubyte[]) or $(D ubyte[num]) or one or more arrays + * of any type. + * + * Returns: + * CRC64-ECMA of data + */ +//simple alias doesn't work here, hope this gets inlined... +ubyte[8] crc64ECMAOf(T...)(T data) +{ + return digest!(CRC64ECMA, T)(data); +} + +/** + * This is a convenience alias for $(REF digest, std,digest,digest) using the + * CRC64-ISO implementation. * * Params: * data = $(D InputRange) of $(D ElementType) implicitly convertible to @@ -450,12 +519,12 @@ ubyte[4] crc32Of(T...)(T data) * of any type. * * Returns: - * CRC64 of data + * CRC64-ISO of data */ //simple alias doesn't work here, hope this gets inlined... -ubyte[8] crc64Of(T...)(T data) +ubyte[8] crc64ISOOf(T...)(T data) { - return digest!(CRC64, T)(data); + return digest!(CRC64ISO, T)(data); } /// @@ -493,13 +562,22 @@ public alias crcHexString = toHexString!(Order.decreasing, 16); alias CRC32Digest = WrapperDigest!CRC32; /** - * OOP API CRC64 implementation. + * OOP API CRC64-ECMA implementation. * See $(D std.digest.digest) for differences between template and OOP API. * - * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64), see - * there for more information. + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA), + * see there for more information. + */ +alias CRC64ECMADigest = WrapperDigest!CRC64ECMA; + +/** + * OOP API CRC64-ISO implementation. + * See $(D std.digest.digest) for differences between template and OOP API. + * + * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO), + * see there for more information. */ -alias CRC64Digest = WrapperDigest!CRC64; +alias CRC64ISODigest = WrapperDigest!CRC64ISO; /// @safe unittest From 0ea46ff1bdd2c4673a920cfff48d6099e6c78e1e Mon Sep 17 00:00:00 2001 From: Amaury SECHET Date: Mon, 23 Jan 2017 09:24:13 -0500 Subject: [PATCH 172/262] Make the CRC struct public. It seems like one can't alias a private symbol without making circle unhappy. --- std/digest/crc.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index 7772389d825..f1ada24d9ea 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -153,7 +153,7 @@ alias CRC64ISO = CRC!(64, 0xD800000000000000); * Generic Template API used for CRC32 and CRC64 implementations. * See $(D std.digest.digest) for differences between template and OOP API. */ -private struct CRC(uint N, ulong P) if (N == 32 || N == 64) +struct CRC(uint N, ulong P) if (N == 32 || N == 64) { private: static if (N == 32) From 6d4be23e2a4c74115634e79a087c853cbe892f68 Mon Sep 17 00:00:00 2001 From: Amaury SECHET Date: Wed, 8 Mar 2017 09:33:08 -0500 Subject: [PATCH 173/262] Add more doc --- std/digest/crc.d | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/std/digest/crc.d b/std/digest/crc.d index f1ada24d9ea..b68a576bb33 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -151,6 +151,13 @@ alias CRC64ISO = CRC!(64, 0xD800000000000000); /** * Generic Template API used for CRC32 and CRC64 implementations. + * + * The N parameter indicate the size of the hash in bits. + * The parameter P specify the polynomial to be used for reduction. + * + * You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases + * for convenience. + * * See $(D std.digest.digest) for differences between template and OOP API. */ struct CRC(uint N, ulong P) if (N == 32 || N == 64) @@ -167,6 +174,10 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64) static immutable T[256][8] tables = genTables!T(P); + /** + * Type of the finished CRC hash. + * ubyte[4] if N is 32, ubyte[8] if N is 64. + */ alias R = ubyte[T.sizeof]; // magic initialization constants From 05a4c060295a171d1ddb9ac01779e8dc733133a0 Mon Sep 17 00:00:00 2001 From: Amaury SECHET Date: Mon, 13 Mar 2017 20:26:13 -0400 Subject: [PATCH 174/262] Remove unittests which makes circle unhappy. --- std/digest/crc.d | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index b68a576bb33..7e76eb8f937 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -244,15 +244,6 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64) crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d]; _state = crc; } - /// - @safe unittest - { - CRC dig; - dig.put(cast(ubyte) 0); //single ubyte - dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic - ubyte[10] buf; - dig.put(buf); //buffer - } /** * Used to initialize the CRC32 digest. @@ -267,13 +258,6 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64) { this = CRC.init; } - /// - @safe unittest - { - CRC digest; - //digest.start(); //Not necessary - digest.put(0); - } /** * Returns the finished CRC hash. This also calls $(LREF start) to @@ -285,14 +269,6 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64) start(); return tmp; } - /// - @safe unittest - { - //Simple example - CRC hash; - hash.put(cast(ubyte) 0); - R result = hash.finish(); - } /** * Works like $(D finish) but does not reset the internal state, so it's possible From 308ad14249d841a3e27eab96d33e312972840d35 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 22 May 2017 09:51:21 -0400 Subject: [PATCH 175/262] Added Changelog Entry For 64 Bit CRC --- changelog/std-digest-crc64.dd | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelog/std-digest-crc64.dd diff --git a/changelog/std-digest-crc64.dd b/changelog/std-digest-crc64.dd new file mode 100644 index 00000000000..4879b4dfd86 --- /dev/null +++ b/changelog/std-digest-crc64.dd @@ -0,0 +1,16 @@ +Added support for 64 bit CRC + +Support for both ISO and ECMA 64 bit CRC was added to $(MREF std, digest, crc). + +------- +import std.digest.crc; + +void main() +{ + ubyte[8] hash64ecma = crc64ECMAOf("abc"); + assert(crcHexString(hash64ecma) == "2CD8094A1A277627"); + + ubyte[8] hash64iso = crc64ISOOf("abc"); + assert(crcHexString(hash64iso) == "3776C42000000000"); +} +------- From e0865f5131bb448da3a457e44bb252574ce8e036 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 22 May 2017 10:14:01 -0400 Subject: [PATCH 176/262] Optimized std.datetime.date.TimeOfDay.fromISOString --- std/datetime/date.d | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/std/datetime/date.d b/std/datetime/date.d index 8fde94edad6..3ff794cb6f1 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -8790,26 +8790,29 @@ public: static TimeOfDay fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : all; - import std.ascii : isDigit; - import std.conv : to; + import std.conv : to, text, ConvException; import std.exception : enforce; - import std.format : format; import std.string : strip; - auto dstr = to!dstring(strip(isoString)); - - enforce(dstr.length == 6, new DateTimeException(format("Invalid ISO String: %s", isoString))); + int hours, minutes, seconds; + auto str = strip(isoString); - auto hours = dstr[0 .. 2]; - auto minutes = dstr[2 .. 4]; - auto seconds = dstr[4 .. $]; + enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString)); - enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + try + { + // cast to int from uint is used because it checks for + // non digits without extra loops + hours = cast(int) to!uint(str[0 .. 2]); + minutes = cast(int) to!uint(str[2 .. 4]); + seconds = cast(int) to!uint(str[4 .. $]); + } + catch (ConvException) + { + throw new DateTimeException(text("Invalid ISO String: ", isoString)); + } - return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); + return TimeOfDay(hours, minutes, seconds); } /// From 36a02d393dd71fe2f01598428c68983c539e266c Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 22 May 2017 11:06:38 -0400 Subject: [PATCH 177/262] Moved ddoc unittest block back to its proper position in std.digest.crc --- std/digest/crc.d | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index 7e76eb8f937..c98fd28610a 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -478,6 +478,23 @@ ubyte[4] crc32Of(T...)(T data) return digest!(CRC32, T)(data); } +/// +@system unittest +{ + ubyte[] data = [4,5,7,25]; + assert(data.crc32Of == [167, 180, 199, 131]); + + import std.utf : byChar; + assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]); + + ubyte[4] hash = "abc".crc32Of(); + assert(hash == digest!CRC32("ab", "c")); + + import std.range : iota; + enum ubyte S = 5, F = 66; + assert(iota(S, F).crc32Of == [59, 140, 234, 154]); +} + /** * This is a convenience alias for $(REF digest, std,digest,digest) using the * CRC64-ECMA implementation. @@ -514,23 +531,6 @@ ubyte[8] crc64ISOOf(T...)(T data) return digest!(CRC64ISO, T)(data); } -/// -@system unittest -{ - ubyte[] data = [4,5,7,25]; - assert(data.crc32Of == [167, 180, 199, 131]); - - import std.utf : byChar; - assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]); - - ubyte[4] hash = "abc".crc32Of(); - assert(hash == digest!CRC32("ab", "c")); - - import std.range : iota; - enum ubyte S = 5, F = 66; - assert(iota(S, F).crc32Of == [59, 140, 234, 154]); -} - /** * This is a convenience alias for $(REF toHexString, std,digest,digest) * producing the usual CRC32 string output. From 4a2b94e18901da9ab15eed359f50f7b6f390f8ab Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 22 May 2017 19:16:55 +0200 Subject: [PATCH 178/262] Unmarkdownify the secureCompare changelog entry --- changelog/std-digest-digest-secureCompare.dd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/std-digest-digest-secureCompare.dd b/changelog/std-digest-digest-secureCompare.dd index 03d4026f3c6..8a86c7f41b0 100644 --- a/changelog/std-digest-digest-secureCompare.dd +++ b/changelog/std-digest-digest-secureCompare.dd @@ -6,7 +6,7 @@ constant time regardless of the equality of the two ranges in order to protect against timing attacks. For more information on the attack, please refer to the docs on $(REF secureEqual, std, digest, digest). -``` +----- import std.digest.digest : secureEqual, toHexString; import std.digest.hmac : hmac; import std.digest.sha : SHA1; @@ -26,4 +26,4 @@ void main() assert( secureEqual(hex1, hex2)); assert(!secureEqual(hex1, hex3)); } -``` \ No newline at end of file +----- From e7439425e8b646d12b4d9039df7b99a18d659ab6 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 22 May 2017 15:42:40 -0400 Subject: [PATCH 179/262] Added example to SysTime.isoWeek --- std/datetime/systime.d | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 8bc284eeffe..42275c1d6fb 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -7336,14 +7336,16 @@ public: return (cast(Date) this).isoWeek; } + /// @safe unittest { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + auto st = SysTime(Date(1999, 7, 6)); + const cst = SysTime(Date(2010, 5, 1)); + immutable ist = SysTime(Date(2015, 10, 10)); + assert(st.isoWeek == 27); - assert(cst.isoWeek == 27); - //assert(ist.isoWeek == 27); + assert(cst.isoWeek == 17); + assert(ist.isoWeek == 41); } From 9d6420ee31016960e7c4b0f457e14a37dff21ecc Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Mon, 22 May 2017 22:40:25 -0700 Subject: [PATCH 180/262] reimplement std.traits.functionLinkage using __traits --- std/traits.d | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/std/traits.d b/std/traits.d index ec9a9c356e7..5a237eb6ea3 100644 --- a/std/traits.d +++ b/std/traits.d @@ -1853,21 +1853,16 @@ template isUnsafe(alias func) /** -Returns the calling convention of function as a string. +Determine the linkage attribute of the function. +Params: + func = the function symbol, or the type of a function, delegate, or pointer to function +Returns: + one of the strings "D", "C", "Windows", "Pascal", or "Objective-C" */ template functionLinkage(func...) if (func.length == 1 && isCallable!func) { - alias Func = Unqual!(FunctionTypeOf!func); - - enum string functionLinkage = - [ - 'F': "D", - 'U': "C", - 'W': "Windows", - 'V': "Pascal", - 'R': "C++" - ][ mangledName!Func[0] ]; + enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func); } /// From 099fa37bb528b561d472c92ed5b5c244fce38229 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 23 May 2017 15:40:05 -0400 Subject: [PATCH 181/262] Get rid of static this for initializing std{in,out,err} --- index.d | 1 - posix.mak | 1 - std/stdio.d | 111 +++++++++++++++++++++++------------------------- std/stdiobase.d | 24 ----------- win32.mak | 2 - win64.mak | 1 - 6 files changed, 53 insertions(+), 87 deletions(-) delete mode 100644 std/stdiobase.d diff --git a/index.d b/index.d index b7f5341ebd5..905ebf3cbbf 100644 --- a/index.d +++ b/index.d @@ -470,7 +470,6 @@ $(COMMENT $(LINK2 std_regex_internal_parser.html, std.regex.internal.parser)$(BR) $(LINK2 std_regex_internal_tests.html, std.regex.internal.tests)$(BR) $(LINK2 std_regex_internal_thompson.html, std.regex.internal.thompson)$(BR) - $(LINK2 std_stdiobase.html, std.stdiobase)$(BR) ) $(TD Internal modules. diff --git a/posix.mak b/posix.mak index 2080712aa9c..bce3e08f29b 100644 --- a/posix.mak +++ b/posix.mak @@ -227,7 +227,6 @@ EXTRA_MODULES_INTERNAL := $(addprefix std/, \ processinit scopebuffer test/dummyrange \ $(addprefix unicode_, comp decomp grapheme norm tables) \ ) \ - stdiobase \ ) EXTRA_MODULES += $(EXTRA_DOCUMENTABLES) $(EXTRA_MODULES_INTERNAL) diff --git a/std/stdio.d b/std/stdio.d index 9da7656a4f9..dc723f40734 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -19,7 +19,6 @@ import std.algorithm.mutation; // copy import std.meta; // allSatisfy import std.range.primitives; // ElementEncodingType, empty, front, // isBidirectionalRange, isInputRange, put -import std.stdiobase; import std.traits; // isSomeChar, isSomeString, Unqual, isPointer import std.typecons; // Flag @@ -4522,70 +4521,66 @@ Initialize with a message and an error code. } } -extern(C) void std_stdio_static_this() +// Undocummented but public because std* handles are aliasing it +ref File makeGlobal(alias handle)() { - static import core.stdc.stdio; - //Bind stdin, stdout, stderr - __gshared File.Impl stdinImpl; - stdinImpl.handle = core.stdc.stdio.stdin; - .stdin._p = &stdinImpl; - // stdout - __gshared File.Impl stdoutImpl; - stdoutImpl.handle = core.stdc.stdio.stdout; - .stdout._p = &stdoutImpl; - // stderr - __gshared File.Impl stderrImpl; - stderrImpl.handle = core.stdc.stdio.stderr; - .stderr._p = &stderrImpl; + static __gshared File.Impl impl; + static __gshared File result; + static shared bool lilyWasHere; + import std.concurrency : initOnce; + initOnce!lilyWasHere({ + impl.handle = handle; + result._p = &impl; + return true; + }()); + return result; } -//--------- -__gshared +/** The standard input stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stdin` to a different `File` instance + than the default. +*/ +alias stdin = makeGlobal!(core.stdc.stdio.stdin); + +/// +@safe unittest { - /** The standard input stream. - Bugs: - Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), - it is thread un-safe to reassign `stdin` to a different `File` instance - than the default. - */ - File stdin; - /// - @safe unittest - { - // Read stdin, sort lines, write to stdout - import std.array : array; - import std.algorithm.sorting : sort; - import std.algorithm.mutation : copy; - import std.typecons : Yes; - - void main() { - stdin // read from stdin - .byLineCopy(Yes.keepTerminator) // copying each line - .array() // convert to array of lines - .sort() // sort the lines - .copy( // copy output of .sort to an OutputRange - stdout.lockingTextWriter()); // the OutputRange - } + // Read stdin, sort lines, write to stdout + import std.array : array; + import std.algorithm.sorting : sort; + import std.algorithm.mutation : copy; + import std.typecons : Yes; + + void main() { + stdin // read from stdin + .byLineCopy(Yes.keepTerminator) // copying each line + .array() // convert to array of lines + .sort() // sort the lines + .copy( // copy output of .sort to an OutputRange + stdout.lockingTextWriter()); // the OutputRange } - - /** - The standard output stream. - Bugs: - Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), - it is thread un-safe to reassign `stdout` to a different `File` instance - than the default. - */ - File stdout; - /** - The standard error stream. - Bugs: - Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), - it is thread un-safe to reassign `stderr` to a different `File` instance - than the default. - */ - File stderr; } +/** + The standard output stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stdout` to a different `File` instance + than the default. +*/ +alias stdout = makeGlobal!(core.stdc.stdio.stdout); + +/** + The standard error stream. + Bugs: + Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768), + it is thread un-safe to reassign `stderr` to a different `File` instance + than the default. +*/ +alias stderr = makeGlobal!(core.stdc.stdio.stderr); + @system unittest { static import std.file; diff --git a/std/stdiobase.d b/std/stdiobase.d deleted file mode 100644 index 4570a4c9c11..00000000000 --- a/std/stdiobase.d +++ /dev/null @@ -1,24 +0,0 @@ -// Written in the D programming language. - -/** - * The only purpose of this module is to do the static construction for - * std.stdio, to eliminate cyclic construction errors. - * - * Copyright: Copyright Andrei Alexandrescu 2008 - 2009. - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: $(HTTP erdani.org, Andrei Alexandrescu) - * Source: $(PHOBOSSRC std/_stdiobase.d) - */ -/* Copyright Andrei Alexandrescu 2008 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module std.stdiobase; - -extern(C) void std_stdio_static_this(); - -shared static this() -{ - std_stdio_static_this(); -} diff --git a/win32.mak b/win32.mak index 3a9f3b48d37..440bda2385b 100644 --- a/win32.mak +++ b/win32.mak @@ -108,7 +108,6 @@ SRC= \ # The separation is a workaround for bug 4904 (optlink bug 3372). SRC_STD_1= \ std\stdio.d \ - std\stdiobase.d \ std\string.d \ std\format.d \ std\file.d @@ -604,7 +603,6 @@ cov : $(SRC_TO_COMPILE) $(LIB) # cov del *.lst $(DMD) -conf= -cov=83 -unittest -main -run std\stdio.d - $(DMD) -conf= -cov=100 -unittest -main -run std\stdiobase.d $(DMD) -conf= -cov=95 -unittest -main -run std\string.d $(DMD) -conf= -cov=71 -unittest -main -run std\format.d $(DMD) -conf= -cov=83 -unittest -main -run std\file.d diff --git a/win64.mak b/win64.mak index 80bc8467ea0..99826d9c3c0 100644 --- a/win64.mak +++ b/win64.mak @@ -112,7 +112,6 @@ SRC= \ # The separation is a workaround for bug 4904 (optlink bug 3372). SRC_STD_1= \ std\stdio.d \ - std\stdiobase.d \ std\string.d \ std\format.d \ std\file.d From fa4d134ff969ba635e9c1240a2a54c841bfa7e2a Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Tue, 23 May 2017 14:35:21 -0700 Subject: [PATCH 182/262] reimplement std.traits.VariadicFunctionStyle() --- std/traits.d | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/std/traits.d b/std/traits.d index 5a237eb6ea3..d67a518380a 100644 --- a/std/traits.d +++ b/std/traits.d @@ -1897,11 +1897,16 @@ template functionLinkage(func...) /** Determines what kind of variadic parameters function has. +Params: + func = function symbol or type of function, delegate, or pointer to function +Returns: + enum Variadic */ enum Variadic { no, /// Function is not variadic. - c, /// Function is a _C-style variadic function. + c, /// Function is a _C-style variadic function, which uses + /// core.stdc.stdarg /// Function is a _D-style variadic function, which uses d, /// __argptr and __arguments. typesafe, /// Function is a typesafe variadic function. @@ -1911,21 +1916,12 @@ enum Variadic template variadicFunctionStyle(func...) if (func.length == 1 && isCallable!func) { - alias Func = Unqual!(FunctionTypeOf!func); - - // TypeFuncion --> CallConvention FuncAttrs Arguments ArgClose Type - enum callconv = functionLinkage!Func; - enum mfunc = mangledName!Func; - enum mtype = mangledName!(ReturnType!Func); - static assert(mfunc[$ - mtype.length .. $] == mtype, mfunc ~ "|" ~ mtype); - - enum argclose = mfunc[$ - mtype.length - 1]; - static assert(argclose >= 'X' && argclose <= 'Z'); - + enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func); enum Variadic variadicFunctionStyle = - argclose == 'X' ? Variadic.typesafe : - argclose == 'Y' ? (callconv == "C") ? Variadic.c : Variadic.d : - Variadic.no; // 'Z' + (varargs == "stdarg") ? Variadic.c : + (varargs == "argptr") ? Variadic.d : + (varargs == "typesafe") ? Variadic.typesafe : + (varargs == "none") ? Variadic.no : Variadic.no; } /// From 78b3328faafe51d78fcb3fa5aa50b3fedb729547 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 23 May 2017 16:40:45 -0400 Subject: [PATCH 183/262] Get rid of static this for initializing encoding --- posix.mak | 2 +- std/encoding.d | 31 ++++++++++++++++--------------- std/internal/encodinginit.d | 19 ------------------- win32.mak | 1 - win64.mak | 1 - 5 files changed, 17 insertions(+), 37 deletions(-) delete mode 100644 std/internal/encodinginit.d diff --git a/posix.mak b/posix.mak index 2080712aa9c..3d568a8ea2c 100644 --- a/posix.mak +++ b/posix.mak @@ -221,7 +221,7 @@ EXTRA_DOCUMENTABLES := $(EXTRA_MODULES_LINUX) $(EXTRA_MODULES_WIN32) $(EXTRA_MOD EXTRA_MODULES_INTERNAL := $(addprefix std/, \ algorithm/internal concurrencybase \ $(addprefix internal/, \ - cstring digest/sha_SSSE3 encodinginit \ + cstring digest/sha_SSSE3 \ $(addprefix math/, biguintcore biguintnoasm biguintx86 \ errorfunction gammafunction ) \ processinit scopebuffer test/dummyrange \ diff --git a/std/encoding.d b/std/encoding.d index 3acb4199e58..9e9da74725c 100644 --- a/std/encoding.d +++ b/std/encoding.d @@ -106,7 +106,6 @@ module std.encoding; import std.traits; import std.typecons; import std.range.primitives; -import std.internal.encodinginit; @system unittest { @@ -2461,6 +2460,22 @@ abstract class EncodingScheme */ static EncodingScheme create(string encodingName) { + static bool registerDefaultEncodings() + { + EncodingScheme.register!EncodingSchemeASCII; + EncodingScheme.register!EncodingSchemeLatin1; + EncodingScheme.register!EncodingSchemeLatin2; + EncodingScheme.register!EncodingSchemeWindows1250; + EncodingScheme.register!EncodingSchemeWindows1252; + EncodingScheme.register!EncodingSchemeUtf8; + EncodingScheme.register!EncodingSchemeUtf16Native; + EncodingScheme.register!EncodingSchemeUtf32Native; + return true; + } + + static shared bool initialized; + import std.concurrency : initOnce; + initOnce!initialized(registerDefaultEncodings()); encodingName = toLower(encodingName); if (auto p = encodingName in supported) @@ -3368,20 +3383,6 @@ class EncodingSchemeUtf32Native : EncodingScheme assert(ub.length == 8); } - -// shared static this() called from encodinginit to break ctor cycle -extern(C) void std_encoding_shared_static_this() -{ - EncodingScheme.register!EncodingSchemeASCII; - EncodingScheme.register!EncodingSchemeLatin1; - EncodingScheme.register!EncodingSchemeLatin2; - EncodingScheme.register!EncodingSchemeWindows1250; - EncodingScheme.register!EncodingSchemeWindows1252; - EncodingScheme.register!EncodingSchemeUtf8; - EncodingScheme.register!EncodingSchemeUtf16Native; - EncodingScheme.register!EncodingSchemeUtf32Native; -} - //============================================================================= diff --git a/std/internal/encodinginit.d b/std/internal/encodinginit.d deleted file mode 100644 index 837e4649287..00000000000 --- a/std/internal/encodinginit.d +++ /dev/null @@ -1,19 +0,0 @@ -// Written in the D programming language. - -/++ - The purpose of this module is to perform static construction away from the - normal modules to eliminate cyclic construction errors. - - Copyright: Copyright 2011 - 2016 - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Martin Nowak, Steven Schveighoffer - Source: $(PHOBOSSRC std/internal/_encodinginit.d) - +/ -module std.internal.encodinginit; - -extern(C) void std_encoding_shared_static_this(); - -shared static this() -{ - std_encoding_shared_static_this(); -} diff --git a/win32.mak b/win32.mak index 3a9f3b48d37..44657e2efce 100644 --- a/win32.mak +++ b/win32.mak @@ -271,7 +271,6 @@ SRC_STD_C_FREEBSD= \ SRC_STD_INTERNAL= \ std\internal\cstring.d \ - std\internal\encodinginit.d \ std\internal\processinit.d \ std\internal\unicode_tables.d \ std\internal\unicode_comp.d \ diff --git a/win64.mak b/win64.mak index 80bc8467ea0..b2fe50fab2e 100644 --- a/win64.mak +++ b/win64.mak @@ -296,7 +296,6 @@ SRC_STD_C_FREEBSD= \ SRC_STD_INTERNAL= \ std\internal\cstring.d \ - std\internal\encodinginit.d \ std\internal\processinit.d \ std\internal\unicode_tables.d \ std\internal\unicode_comp.d \ From a85409262293a70cee8cdcb5cae0bd4656b5948e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 23 May 2017 19:21:36 -0400 Subject: [PATCH 184/262] Make it less fun --- std/stdio.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index dc723f40734..8f193bd2b43 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4526,9 +4526,9 @@ ref File makeGlobal(alias handle)() { static __gshared File.Impl impl; static __gshared File result; - static shared bool lilyWasHere; + static shared bool initialized; import std.concurrency : initOnce; - initOnce!lilyWasHere({ + initOnce!initialized({ impl.handle = handle; result._p = &impl; return true; From 99659e6076af1cca496cffd19dfb67ab551c4729 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 23 May 2017 14:25:17 -0400 Subject: [PATCH 185/262] Removed unnessesary auto-decoding from the single argument version of startsWith --- std/algorithm/searching.d | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 85fe4e2f5c0..70f47bc5c4c 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -4192,9 +4192,24 @@ bool startsWith(alias pred = "a == b", R, E)(R doesThisStart, E withThis) if (isInputRange!R && is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool)) { - return doesThisStart.empty - ? false - : binaryFun!pred(doesThisStart.front, withThis); + if (doesThisStart.empty) + return false; + + alias predFunc = binaryFun!pred; + + // auto-decoding special case + static if (isNarrowString!R) + { + // specialize for ASCII as to not change previous behavior + if (withThis <= 0x7F) + return predFunc(doesThisStart[0], withThis); + else + return predFunc(doesThisStart.front, withThis); + } + else + { + return predFunc(doesThisStart.front, withThis); + } } /// Ditto From 825ad381834c15ca0b98676ddec5904d2934100a Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 23 May 2017 16:17:10 -0400 Subject: [PATCH 186/262] Removed auto-decoding from the single needle version of endsWith --- std/algorithm/searching.d | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 85fe4e2f5c0..5297fef8d05 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -1105,9 +1105,24 @@ bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis) if (isBidirectionalRange!R && is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool)) { - return doesThisEnd.empty - ? false - : binaryFun!pred(doesThisEnd.back, withThis); + if (doesThisEnd.empty) + return false; + + alias predFunc = binaryFun!pred; + + // auto-decoding special case + static if (isNarrowString!R) + { + // specialize for ASCII as to not change previous behavior + if (withThis <= 0x7F) + return predFunc(doesThisEnd[$ - 1], withThis); + else + return predFunc(doesThisEnd.back, withThis); + } + else + { + return predFunc(doesThisEnd.back, withThis); + } } /// Ditto From 38ebccd929888bf0d555936bdf598ff3acccc2db Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Wed, 24 May 2017 17:28:01 +0100 Subject: [PATCH 187/262] initOnce is slow, use double checking --- std/stdio.d | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 8f193bd2b43..25063e12d7f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4521,18 +4521,22 @@ Initialize with a message and an error code. } } -// Undocummented but public because std* handles are aliasing it +// Undocummnted but public because the std* handles are aliasing it. ref File makeGlobal(alias handle)() { static __gshared File.Impl impl; static __gshared File result; static shared bool initialized; - import std.concurrency : initOnce; - initOnce!initialized({ - impl.handle = handle; - result._p = &impl; - return true; - }()); + if (!initialized) + { + // Use double-checking because initOnce is inefficient. + import std.concurrency : initOnce; + initOnce!initialized({ + impl.handle = handle; + result._p = &impl; + return true; + }()); + } return result; } From ee318374bd7938e9156e2c29da53785d65958e36 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Thu, 25 May 2017 00:45:35 +0100 Subject: [PATCH 188/262] Alternative implementation that doesn't use initOnce --- std/stdio.d | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 25063e12d7f..132b47aca88 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4526,16 +4526,23 @@ ref File makeGlobal(alias handle)() { static __gshared File.Impl impl; static __gshared File result; - static shared bool initialized; + static shared uint initialized; if (!initialized) { - // Use double-checking because initOnce is inefficient. - import std.concurrency : initOnce; - initOnce!initialized({ - impl.handle = handle; - result._p = &impl; - return true; - }()); + for (;;) + { + if (initialized > uint.max / 2) + break; + import core.atomic; + if (atomicOp!"+="(initialized, 1) == 1) + { + impl.handle = handle; + result._p = &impl; + atomicOp!"+="(initialized, uint.max / 2); + break; + } + atomicOp!"-="(initialized, 1); + } } return result; } From 3f583249f8a315515918c92ddc513f464b9d27b5 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Thu, 25 May 2017 01:45:23 +0100 Subject: [PATCH 189/262] Use explicit atomic loading for the double-checked variable --- std/stdio.d | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 132b47aca88..f911f4b6cea 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4527,22 +4527,16 @@ ref File makeGlobal(alias handle)() static __gshared File.Impl impl; static __gshared File result; static shared uint initialized; - if (!initialized) - { - for (;;) - { - if (initialized > uint.max / 2) - break; - import core.atomic; - if (atomicOp!"+="(initialized, 1) == 1) - { - impl.handle = handle; - result._p = &impl; - atomicOp!"+="(initialized, uint.max / 2); - break; - } - atomicOp!"-="(initialized, 1); - } + import core.atomic; + if (!atomicLoad(initialized)) + { + // Use double-checking because initOnce is inefficient. + import std.concurrency : initOnce; + initOnce!initialized({ + impl.handle = handle; + result._p = &impl; + return true; + }()); } return result; } From d66e18a5d5ce9d7354b92fb625ef348138f791e9 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Thu, 25 May 2017 01:48:29 +0100 Subject: [PATCH 190/262] Add atomicLoad to the uint-based solution --- std/stdio.d | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index f911f4b6cea..d8bc234364b 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4530,13 +4530,19 @@ ref File makeGlobal(alias handle)() import core.atomic; if (!atomicLoad(initialized)) { - // Use double-checking because initOnce is inefficient. - import std.concurrency : initOnce; - initOnce!initialized({ - impl.handle = handle; - result._p = &impl; - return true; - }()); + for (;;) + { + if (atomicLoad(initialized) > uint.max / 2) + break; + if (atomicOp!"+="(initialized, 1) == 1) + { + impl.handle = handle; + result._p = &impl; + atomicOp!"+="(initialized, uint.max / 2); + break; + } + atomicOp!"-="(initialized, 1); + } } return result; } From 3d80936f0b1e56418d6826180a7a9dbb29e01b7f Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Wed, 24 May 2017 13:36:57 -0400 Subject: [PATCH 191/262] Fix Issue 16108: tostrings fails on struct with disabled postblit --- std/conv.d | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/std/conv.d b/std/conv.d index ef729674418..d91addd2377 100644 --- a/std/conv.d +++ b/std/conv.d @@ -207,6 +207,13 @@ template to(T) { return toImpl!T(arg); } + + // Fix issue 16108 + T to(S)(ref S arg) + if (isAggregateType!S && !isCopyable!S) + { + return toImpl!T(arg); + } } /** @@ -1006,6 +1013,51 @@ if (!(isImplicitlyConvertible!(S, T) && } } +/* + To string conversion for non copy-able structs + */ +private T toImpl(T, S)(ref S value) +if (!(isImplicitlyConvertible!(S, T) && + !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && + !isInfinite!S && isExactSomeString!T && !isCopyable!S) +{ + import std.format : FormatSpec, formatValue; + import std.array : appender; + + auto w = appender!T(); + FormatSpec!(ElementEncodingType!T) f; + formatValue(w, value, f); + return w.data; +} + +// Bugzilla 16108 +@system unittest +{ + static struct A + { + int val; + bool flag; + + string toString() { return text(val, ":", flag); } + + @disable this(this); + } + + auto a = A(); + assert(to!string(a) == "0:false"); + + static struct B + { + int val; + bool flag; + + @disable this(this); + } + + auto b = B(); + assert(to!string(b) == "B(0, false)"); +} + /* Check whether type $(D T) can be used in a switch statement. This is useful for compile-time generation of switch case statements. From 9e6759995a1502dbd92a05b4d0a8b662f1c6032b Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Sun, 7 May 2017 13:03:16 +0200 Subject: [PATCH 192/262] fix issue 15945, 16256, 17328 tab -> space --- std/experimental/logger/core.d | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index e8e8d4c0531..cdd19b85ebe 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -636,8 +636,6 @@ private struct MsgRange private Logger log; - private char[] buffer; - this(Logger log) @safe { this.log = log; @@ -652,8 +650,9 @@ private struct MsgRange void put(dchar elem) @safe { import std.utf : encode; - encode(this.buffer, elem); - log.logMsgPart(this.buffer); + char[4] buffer; + size_t len = encode(buffer, elem); + log.logMsgPart(buffer[0 .. len]); } } @@ -3105,3 +3104,43 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted tl.logf("%s", sts); assert(tl.msg == SystemToStringMsg); } + +// Issue 17328 +@safe unittest +{ + import std.format : format; + + ubyte[] data = [0]; + string s = format("%(%02x%)", data); // format 00 + assert(s == "00"); + + auto tl = new TestLogger(); + + tl.infof("%(%02x%)", data); // infof 000 + + size_t i; + string fs = tl.msg; + for (; i < s.length; ++i) + { + assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs); + } + assert(fs.length == 2); +} + +// Issue 15954 +@safe unittest +{ + import std.conv : to; + auto tl = new TestLogger(); + tl.log("123456789".to!wstring); + assert(tl.msg == "123456789"); +} + +// Issue 16256 +@safe unittest +{ + import std.conv : to; + auto tl = new TestLogger(); + tl.log("123456789"d); + assert(tl.msg == "123456789"); +} From 32e50bd7eb4e6a72a659089f558b4dfa76723047 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 26 May 2017 15:48:33 +0200 Subject: [PATCH 193/262] Reenable publictests checking on CircleCi --- circleci.sh | 7 +++++++ posix.mak | 6 +++--- std/datetime/systime.d | 8 +++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/circleci.sh b/circleci.sh index b8e9d89e34a..a9520081bad 100755 --- a/circleci.sh +++ b/circleci.sh @@ -127,9 +127,16 @@ coverage() sed -i 's/^ *[0-9]*\(|.*nocoverage.*\)$/ \1/' ./*.lst } +# extract publictests and run them independently +publictests() +{ + make -f posix.mak -j$N publictests DUB=$DUB +} + case $1 in install-deps) install_deps ;; setup-repos) setup_repos ;; coverage) coverage ;; + publictests) publictests ;; style) style ;; esac diff --git a/posix.mak b/posix.mak index 3d568a8ea2c..bd9c25da6fd 100644 --- a/posix.mak +++ b/posix.mak @@ -588,15 +588,15 @@ style_lint: ../dscanner/dsc $(LIB) ################################################################################ publictests: $(PUBLICTESTS) -$(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --compiler=$${PWD}/$(DMD) --root=$(TOOLS_DIR)/styles -c tests_extractor +$(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d $(LIB) + DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) build --force --compiler=$${PWD}/$(DMD) --root=$(TOOLS_DIR)/styles -c tests_extractor ################################################################################ # Extract public tests of a module and test them in an separate file (i.e. without its module) # This is done to check for potentially missing imports in the examples, e.g. # make -f posix.mak std/format.publictests ################################################################################ -%.publictests: %.d $(LIB) $(TEST_EXTRACTOR) +%.publictests: %.d $(LIB) $(TEST_EXTRACTOR) | $(PUBLICTESTS_DIR)/.directory @$(TEST_EXTRACTOR) --inputdir $< --outputdir $(PUBLICTESTS_DIR) @$(DMD) $(DFLAGS) -defaultlib= -debuglib= $(LIB) -main -unittest -run $(PUBLICTESTS_DIR)/$(subst /,_,$<) diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 42275c1d6fb..7ef14b44f60 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -7339,6 +7339,8 @@ public: /// @safe unittest { + import std.datetime.date : Date; + auto st = SysTime(Date(1999, 7, 6)); const cst = SysTime(Date(2010, 5, 1)); immutable ist = SysTime(Date(2015, 10, 10)); @@ -8308,7 +8310,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8557,7 +8559,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8783,7 +8785,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; From 3fd1bc6fc5bb3ecc2bad0ba56c86afd20a480b88 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 27 May 2017 02:59:45 +0100 Subject: [PATCH 194/262] std.stdio.makeGlobal: Fixup double-checked pattern --- std/stdio.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/stdio.d b/std/stdio.d index d8bc234364b..29c0b4da0c1 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4528,7 +4528,7 @@ ref File makeGlobal(alias handle)() static __gshared File result; static shared uint initialized; import core.atomic; - if (!atomicLoad(initialized)) + if (atomicLoad(initialized) <= uint.max / 2) { for (;;) { From 5a3b007409fffa6cec4787d03eb91b97973abf90 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 27 May 2017 03:11:50 +0100 Subject: [PATCH 195/262] std.stdio.makeGlobal: Add explanatory comment --- std/stdio.d | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 29c0b4da0c1..50e82408dba 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4521,27 +4521,32 @@ Initialize with a message and an error code. } } -// Undocummnted but public because the std* handles are aliasing it. +// Undocumented but public because the std* handles are aliasing it. ref File makeGlobal(alias handle)() { static __gshared File.Impl impl; static __gshared File result; - static shared uint initialized; + + // Use an inline spinlock to make sure the initializer is only run once. + // We assume there will be at most uint.max / 2 threads trying to initialize + // `handle` at once and steal the high bit to indicate that the globals have + // been initialized. + static shared uint spinlock; import core.atomic; - if (atomicLoad(initialized) <= uint.max / 2) + if (atomicLoad(spinlock) <= uint.max / 2) { for (;;) { - if (atomicLoad(initialized) > uint.max / 2) + if (atomicLoad(spinlock) > uint.max / 2) break; - if (atomicOp!"+="(initialized, 1) == 1) + if (atomicOp!"+="(spinlock, 1) == 1) { impl.handle = handle; result._p = &impl; - atomicOp!"+="(initialized, uint.max / 2); + atomicOp!"+="(spinlock, uint.max / 2); break; } - atomicOp!"-="(initialized, 1); + atomicOp!"-="(spinlock, 1); } } return result; From 18426550688a7b1e45885a5febda86659427268c Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 27 May 2017 03:12:55 +0100 Subject: [PATCH 196/262] std.stdio.makeGlobal: Optimize fast path, MemoryOrder.seq is overkill --- std/stdio.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index 50e82408dba..cf5eb48cd15 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4533,11 +4533,11 @@ ref File makeGlobal(alias handle)() // been initialized. static shared uint spinlock; import core.atomic; - if (atomicLoad(spinlock) <= uint.max / 2) + if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2) { for (;;) { - if (atomicLoad(spinlock) > uint.max / 2) + if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2) break; if (atomicOp!"+="(spinlock, 1) == 1) { From d3dcbc60b6dc59d8e6aa95bdd8ccc0e910b4e059 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 28 May 2017 16:33:08 -0600 Subject: [PATCH 197/262] Fixed circular reference bug in fullyQualifiedName. Note: unittest serves as a regression test to make sure the circular reference bug does not come back. --- std/traits.d | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/std/traits.d b/std/traits.d index 5a237eb6ea3..61e4d6e36de 100644 --- a/std/traits.d +++ b/std/traits.d @@ -543,7 +543,7 @@ private template fqnSym(alias T : X!A, alias X, A...) private template fqnSym(alias T) { - static if (__traits(compiles, __traits(parent, T))) + static if (__traits(compiles, __traits(parent, T)) && !__traits(isSame, T, __traits(parent, T))) enum parentPrefix = fqnSym!(__traits(parent, T)) ~ "."; else enum parentPrefix = null; @@ -585,6 +585,16 @@ private template fqnSym(alias T) static assert(fqn!Barrier == "core.sync.barrier.Barrier"); } +unittest +{ + struct TemplatedStruct() + { + enum foo = 0; + } + alias TemplatedStructAlias = TemplatedStruct; + assert("TemplatedStruct.foo" == fullyQualifiedName!(TemplatedStructAlias!().foo)); +} + private template fqnType(T, bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) { From 27b6122d942e17edeb7db6aafd148f6ab88a1f19 Mon Sep 17 00:00:00 2001 From: Robert burner Schadek Date: Sun, 7 May 2017 11:55:43 +0200 Subject: [PATCH 198/262] fix Issue 15517 was already fixed but had no fixture --- std/experimental/logger/core.d | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index cdd19b85ebe..b23e9452768 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -3144,3 +3144,51 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted tl.log("123456789"d); assert(tl.msg == "123456789"); } + +// Issue 15517 +@system unittest +{ + import std.stdio : File; + import std.string : indexOf; + import std.file : exists, remove; + + string fn = "logfile.log"; + if (exists(fn)) + { + remove(fn); + } + + auto oldShared = sharedLog; + scope(exit) + { + sharedLog = oldShared; + if (exists(fn)) + { + remove(fn); + } + } + + auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; + + auto fl = new FileLogger(fn); + sharedLog = fl; + assert(exists(fn)); + + foreach (t; ts) + { + log(t); + } + + auto f = File(fn); + auto l = f.byLine(); + assert(!l.empty); + size_t idx; + foreach (it; l) + { + assert(it.indexOf(ts[idx]) != -1, it); + ++idx; + } + + assert(exists(fn)); + fl.file.close(); +} From 4b440f742fcd844fa6ffff7e8dfd87e9bf278f96 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 29 May 2017 21:39:16 +0300 Subject: [PATCH 199/262] Track errors that may happen in forked process on Posix --- std/process.d | 114 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 12 deletions(-) diff --git a/std/process.d b/std/process.d index e0182cb40be..edd8acfc601 100644 --- a/std/process.d +++ b/std/process.d @@ -333,6 +333,14 @@ Pid spawnProcess(in char[] program, return spawnProcess((&program)[0 .. 1], env, config, workDir); } +version(Posix) private enum InternalError : ubyte +{ + noerror, + exec, + chdir, + getrlimit +} + /* Implementation of spawnProcess() for POSIX. @@ -406,9 +414,42 @@ private Pid spawnProcessImpl(in char[][] args, auto stdoutFD = getFD(stdout); auto stderrFD = getFD(stderr); + int[2] forkPipe; + if (core.sys.posix.unistd.pipe(forkPipe) == 0) + { + setCLOEXEC(forkPipe[1], true); + } + else + { + throw ProcessException.newFromErrno("Could not create pipe to check startup of child"); + } + scope(exit) close(forkPipe[0]); + + static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow + { + core.sys.posix.unistd.write(forkPipeOut, &errorType, errorType.sizeof); + core.sys.posix.unistd.write(forkPipeOut, &error, error.sizeof); + close(forkPipeOut); + core.sys.posix.unistd._exit(1); + assert(0); + } + + static void ignorePipeErrors() nothrow + { + import core.sys.posix.signal; + import core.stdc.string : memset; + sigaction_t ignoreAction; + memset(&ignoreAction, 0, sigaction_t.sizeof); + ignoreAction.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &ignoreAction, null); + } + auto id = core.sys.posix.unistd.fork(); if (id < 0) + { + close(forkPipe[1]); throw ProcessException.newFromErrno("Failed to spawn new process"); + } void forkChild() nothrow @nogc { @@ -417,6 +458,11 @@ private Pid spawnProcessImpl(in char[][] args, // Child process + ignorePipeErrors(); + // no need for the read end of pipe on child side + close(forkPipe[0]); + immutable forkPipeOut = forkPipe[1]; + // Set the working directory. if (workDirFD >= 0) { @@ -424,10 +470,7 @@ private Pid spawnProcessImpl(in char[][] args, { // Fail. It is dangerous to run a program // in an unexpected working directory. - core.sys.posix.stdio.perror("spawnProcess(): " ~ - "Failed to set working directory"); - core.sys.posix.unistd._exit(1); - assert(0); + abortOnError(forkPipeOut, InternalError.chdir, .errno); } close(workDirFD); } @@ -456,9 +499,7 @@ private Pid spawnProcessImpl(in char[][] args, rlimit r; if (getrlimit(RLIMIT_NOFILE, &r) != 0) { - core.sys.posix.stdio.perror("getrlimit"); - core.sys.posix.unistd._exit(1); - assert(0); + abortOnError(forkPipeOut, InternalError.getrlimit, .errno); } immutable maxDescriptors = cast(int) r.rlim_cur; @@ -477,6 +518,8 @@ private Pid spawnProcessImpl(in char[][] args, { foreach (i; 0 .. maxToClose) { + // don't close pipe write end + if (pfds[i].fd == forkPipeOut) continue; // POLLNVAL will be set if the file descriptor is invalid. if (!(pfds[i].revents & POLLNVAL)) close(pfds[i].fd); } @@ -484,7 +527,11 @@ private Pid spawnProcessImpl(in char[][] args, else { // Fall back to closing everything. - foreach (i; 3 .. maxDescriptors) close(i); + foreach (i; 3 .. maxDescriptors) + { + if (i == forkPipeOut) continue; + close(i); + } } } else @@ -501,8 +548,7 @@ private Pid spawnProcessImpl(in char[][] args, core.sys.posix.unistd.execve(argz[0], argz.ptr, envz); // If execution fails, exit as quickly as possible. - core.sys.posix.stdio.perror("spawnProcess(): Failed to execute program"); - core.sys.posix.unistd._exit(1); + abortOnError(forkPipeOut, InternalError.exec, .errno); } if (id == 0) @@ -512,6 +558,41 @@ private Pid spawnProcessImpl(in char[][] args, } else { + close(forkPipe[1]); + auto status = InternalError.noerror; + auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof); + if (readExecResult == -1) + throw ProcessException.newFromErrno("Could not read from pipe to get child status"); + + if (status != InternalError.noerror) + { + int error; + string errorMsg; + readExecResult = read(forkPipe[0], &error, error.sizeof); + + switch(status) + { + case InternalError.chdir: + errorMsg = "Failed to set working directory"; + break; + case InternalError.getrlimit: + errorMsg = "getrlimit failed"; + break; + case InternalError.exec: + errorMsg = "Failed to execute program"; + break; + default: + errorMsg = "Unknown error occured"; + break; + } + + if (readExecResult == error.sizeof) { + throw ProcessException.newFromErrno(error, errorMsg); + } else { + throw new ProcessException(errorMsg); + } + } + // Parent process: Close streams and return. if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO && stdinFD != getFD(std.stdio.stdin )) @@ -2329,18 +2410,27 @@ class ProcessException : Exception size_t line = __LINE__) { import core.stdc.errno : errno; + return newFromErrno(errno, customMsg, file, line); + } + + // ditto, but error number is provided by caller + static ProcessException newFromErrno(int error, + string customMsg = null, + string file = __FILE__, + size_t line = __LINE__) + { import std.conv : to; version (CRuntime_Glibc) { import core.stdc.string : strerror_r; char[1024] buf; auto errnoMsg = to!string( - core.stdc.string.strerror_r(errno, buf.ptr, buf.length)); + core.stdc.string.strerror_r(error, buf.ptr, buf.length)); } else { import core.stdc.string : strerror; - auto errnoMsg = to!string(strerror(errno)); + auto errnoMsg = to!string(strerror(error)); } auto msg = customMsg.empty ? errnoMsg : customMsg ~ " (" ~ errnoMsg ~ ')'; From e162658601c930748440957561aca47abda5fc41 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 29 May 2017 21:43:37 +0200 Subject: [PATCH 200/262] Fix Issue 17452 - Undefined references in std.container.array --- std/container/array.d | 477 +++++++++++++++++++++--------------------- 1 file changed, 237 insertions(+), 240 deletions(-) diff --git a/std/container/array.d b/std/container/array.d index a3b570f074f..b92a9c3e36b 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -724,34 +724,6 @@ if (!is(Unqual!T == bool)) return result; } - @nogc @system unittest - { - auto a = Array!int(0, 1, 2); - int[3] b = [3, 4, 5]; - short[3] ci = [0, 1, 0]; - auto c = Array!short(ci); - assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); - assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); - assert(Array!int(0, 1, 2, 3) == a ~ 3); - assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); - } - - @nogc @system unittest - { - auto a = Array!char('a', 'b'); - assert(Array!char("abc") == a ~ 'c'); - import std.utf : byCodeUnit; - assert(Array!char("abcd") == a ~ "cd".byCodeUnit); - } - - @nogc @system unittest - { - auto a = Array!dchar("ąćę"d); - assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); - wchar x = 'Ϣ'; - assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); - } - /** * Forwards to `insertBack`. */ @@ -1682,14 +1654,6 @@ if (is(Unqual!T == bool)) return !length; } - @system unittest - { - Array!bool a; - assert(a.empty); - a.insertBack(false); - assert(!a.empty); - } - /** * Returns: A duplicate of the array. * @@ -1702,16 +1666,6 @@ if (is(Unqual!T == bool)) return result; } - @system unittest - { - Array!bool a; - assert(a.empty); - auto b = a.dup; - assert(b.empty); - a.insertBack(true); - assert(b.empty); - } - /** * Returns the number of elements in the array. * @@ -1726,15 +1680,6 @@ if (is(Unqual!T == bool)) return length; } - @system unittest - { - import std.conv : to; - Array!bool a; - assert(a.length == 0); - a.insert(true); - assert(a.length == 1, to!string(a.length)); - } - /** * Returns: The maximum number of elements the array can store without * reallocating memory and invalidating iterators upon insertion. @@ -1748,18 +1693,6 @@ if (is(Unqual!T == bool)) : 0; } - @system unittest - { - import std.conv : to; - Array!bool a; - assert(a.capacity == 0); - foreach (i; 0 .. 100) - { - a.insert(true); - assert(a.capacity >= a.length, to!string(a.capacity)); - } - } - /** * Ensures sufficient capacity to accommodate `e` _elements. * If `e < capacity`, this method does nothing. @@ -1778,16 +1711,6 @@ if (is(Unqual!T == bool)) _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); } - @system unittest - { - Array!bool a; - assert(a.capacity == 0); - a.reserve(15657); - assert(a.capacity >= 15657); - a.reserve(100); - assert(a.capacity >= 15657); - } - /** * Returns: A range that iterates over all elements of the array in forward order. * @@ -1798,12 +1721,6 @@ if (is(Unqual!T == bool)) return Range(this, 0, length); } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[].length == 4); - } /** * Returns: A range that iterates the array between two specified positions. @@ -1816,13 +1733,6 @@ if (is(Unqual!T == bool)) return Range(this, a, b); } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[0 .. 2].length == 2); - } - /** * Returns: The first element of the array. * @@ -1846,15 +1756,6 @@ if (is(Unqual!T == bool)) else data.ptr[0] &= ~cast(size_t) 1; } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a.front); - a.front = false; - assert(!a.front); - } - /** * Returns: The last element of the array. * @@ -1885,15 +1786,6 @@ if (is(Unqual!T == bool)) } } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a.back); - a.back = false; - assert(!a.back); - } - /** * Indexing operators yielding or modifyng the value at the specified index. * @@ -1942,15 +1834,6 @@ if (is(Unqual!T == bool)) return this[i]; } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[0] && !a[1]); - a[0] &= a[1]; - assert(!a[0]); - } - /** * Returns: A new array which is a concatenation of `this` and its argument. * @@ -1974,21 +1857,6 @@ if (is(Unqual!T == bool)) return result; } - @system unittest - { - import std.algorithm.comparison : equal; - Array!bool a; - a.insertBack([true, false, true, true]); - Array!bool b; - b.insertBack([true, true, false, true]); - assert(equal((a ~ b)[], - [true, false, true, true, true, true, false, true])); - assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); - Array!bool c; - c.insertBack(true); - assert((c ~ false)[].equal([true, false])); - } - /** * Forwards to `insertBack`. */ @@ -2000,19 +1868,6 @@ if (is(Unqual!T == bool)) return this; } - @system unittest - { - import std.algorithm.comparison : equal; - Array!bool a; - a.insertBack([true, false, true, true]); - Array!bool b; - a.insertBack([false, true, false, true, true]); - a ~= b; - assert(equal( - a[], - [true, false, true, true, false, true, false, true, true])); - } - /** * Removes all the elements from the array and releases allocated memory. * @@ -2025,14 +1880,6 @@ if (is(Unqual!T == bool)) this = Array(); } - @system unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - a.clear(); - assert(a.capacity == 0); - } - /** * Sets the number of elements in the array to `newLength`. If `newLength` * is greater than `length`, the new elements are added to the end of the @@ -2054,23 +1901,6 @@ if (is(Unqual!T == bool)) _store._length = newLength; } - @system unittest - { - Array!bool a; - a.length = 1057; - assert(a.length == 1057); - assert(a.capacity >= a.length); - foreach (e; a) - { - assert(!e); - } - immutable cap = a.capacity; - a.length = 100; - assert(a.length == 100); - // do not realloc if length decreases - assert(a.capacity == cap); - } - /** * Removes the last element from the array and returns it. * Both stable and non-stable versions behave the same and guarantee @@ -2094,18 +1924,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableRemoveAny = removeAny; - @system unittest - { - Array!bool a; - a.length = 1057; - assert(!a.removeAny()); - assert(a.length == 1056); - foreach (e; a) - { - assert(!e); - } - } - /** * Inserts the specified elements at the back of the array. `stuff` can be * a value convertible to `bool` or a range of objects convertible to `bool`. @@ -2171,15 +1989,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableLinearInsert = insertBack; - @system unittest - { - Array!bool a; - for (int i = 0; i < 100; ++i) - a.insertBack(true); - foreach (e; a) - assert(e); - } - /** * Removes the value from the back of the array. Both stable and non-stable * versions behave the same and guarantee that ranges iterating over the @@ -2239,18 +2048,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableRemoveBack = removeBack; - @system unittest - { - Array!bool a; - a.length = 1057; - assert(a.removeBack(1000) == 1000); - assert(a.length == 57); - foreach (e; a) - { - assert(!e); - } - } - /** * Inserts `stuff` before, after, or instead range `r`, which must * be a valid range previously extracted from this array. `stuff` @@ -2277,23 +2074,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableInsertBefore = insertBefore; - @system unittest - { - import std.conv : to; - Array!bool a; - version (bugxxxx) - { - a._store.refCountedDebug = true; - } - a.insertBefore(a[], true); - assert(a.length == 1, to!string(a.length)); - a.insertBefore(a[], false); - assert(a.length == 2, to!string(a.length)); - a.insertBefore(a[1 .. $], true); - import std.algorithm.comparison : equal; - assert(a[].equal([false, true, true])); - } - /// ditto size_t insertAfter(Stuff)(Range r, Stuff stuff) { @@ -2310,16 +2090,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableInsertAfter = insertAfter; - @system unittest - { - import std.conv : to; - Array!bool a; - a.length = 10; - a.insertAfter(a[0 .. 5], true); - assert(a.length == 11, to!string(a.length)); - assert(a[5]); - } - /// ditto size_t replace(Stuff)(Range r, Stuff stuff) if (is(Stuff : bool)) @@ -2342,16 +2112,6 @@ if (is(Unqual!T == bool)) /// ditto alias stableReplace = replace; - @system unittest - { - import std.conv : to; - Array!bool a; - a.length = 10; - a.replace(a[3 .. 5], true); - assert(a.length == 9, to!string(a.length)); - assert(a[3]); - } - /** * Removes all elements belonging to `r`, which must be a range * obtained originally from this array. @@ -2403,3 +2163,240 @@ if (is(Unqual!T == bool)) double[] values = [double.nan, double.nan]; auto arr = Array!double(values); } + +@nogc @system unittest +{ + auto a = Array!int(0, 1, 2); + int[3] b = [3, 4, 5]; + short[3] ci = [0, 1, 0]; + auto c = Array!short(ci); + assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a); + assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b); + assert(Array!int(0, 1, 2, 3) == a ~ 3); + assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c); +} + +@nogc @system unittest +{ + auto a = Array!char('a', 'b'); + assert(Array!char("abc") == a ~ 'c'); + import std.utf : byCodeUnit; + assert(Array!char("abcd") == a ~ "cd".byCodeUnit); +} + +@nogc @system unittest +{ + auto a = Array!dchar("ąćę"d); + assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d); + wchar x = 'Ϣ'; + assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z'); +} + +@system unittest +{ + Array!bool a; + assert(a.empty); + a.insertBack(false); + assert(!a.empty); +} + +@system unittest +{ + Array!bool a; + assert(a.empty); + auto b = a.dup; + assert(b.empty); + a.insertBack(true); + assert(b.empty); +} + +@system unittest +{ + import std.conv : to; + Array!bool a; + assert(a.length == 0); + a.insert(true); + assert(a.length == 1, to!string(a.length)); +} + +@system unittest +{ + import std.conv : to; + Array!bool a; + assert(a.capacity == 0); + foreach (i; 0 .. 100) + { + a.insert(true); + assert(a.capacity >= a.length, to!string(a.capacity)); + } +} + +@system unittest +{ + Array!bool a; + assert(a.capacity == 0); + a.reserve(15657); + assert(a.capacity >= 15657); + a.reserve(100); + assert(a.capacity >= 15657); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[0 .. 2].length == 2); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[].length == 4); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a.front); + a.front = false; + assert(!a.front); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[].length == 4); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a.back); + a.back = false; + assert(!a.back); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[0] && !a[1]); + a[0] &= a[1]; + assert(!a[0]); +} + +@system unittest +{ + import std.algorithm.comparison : equal; + Array!bool a; + a.insertBack([true, false, true, true]); + Array!bool b; + b.insertBack([true, true, false, true]); + assert(equal((a ~ b)[], + [true, false, true, true, true, true, false, true])); + assert((a ~ [true, false])[].equal([true, false, true, true, true, false])); + Array!bool c; + c.insertBack(true); + assert((c ~ false)[].equal([true, false])); +} +@system unittest +{ + import std.algorithm.comparison : equal; + Array!bool a; + a.insertBack([true, false, true, true]); + Array!bool b; + a.insertBack([false, true, false, true, true]); + a ~= b; + assert(equal( + a[], + [true, false, true, true, false, true, false, true, true])); +} + +@system unittest +{ + Array!bool a; + a.insertBack([true, false, true, true]); + a.clear(); + assert(a.capacity == 0); +} + +@system unittest +{ + Array!bool a; + a.length = 1057; + assert(a.length == 1057); + assert(a.capacity >= a.length); + foreach (e; a) + { + assert(!e); + } + immutable cap = a.capacity; + a.length = 100; + assert(a.length == 100); + // do not realloc if length decreases + assert(a.capacity == cap); +} + +@system unittest +{ + Array!bool a; + a.length = 1057; + assert(!a.removeAny()); + assert(a.length == 1056); + foreach (e; a) + { + assert(!e); + } +} + +@system unittest +{ + Array!bool a; + for (int i = 0; i < 100; ++i) + a.insertBack(true); + foreach (e; a) + assert(e); +} + +@system unittest +{ + Array!bool a; + a.length = 1057; + assert(a.removeBack(1000) == 1000); + assert(a.length == 57); + foreach (e; a) + { + assert(!e); + } +} + +@system unittest +{ + import std.conv : to; + Array!bool a; + version (bugxxxx) + { + a._store.refCountedDebug = true; + } + a.insertBefore(a[], true); + assert(a.length == 1, to!string(a.length)); + a.insertBefore(a[], false); + assert(a.length == 2, to!string(a.length)); + a.insertBefore(a[1 .. $], true); + import std.algorithm.comparison : equal; + assert(a[].equal([false, true, true])); +} + +@system unittest +{ + import std.conv : to; + Array!bool a; + a.length = 10; + a.insertAfter(a[0 .. 5], true); + assert(a.length == 11, to!string(a.length)); + assert(a[5]); +} From 91877266e3bf076781085f99d66c52f71f6bd803 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Mon, 29 May 2017 22:52:34 +0300 Subject: [PATCH 201/262] Add unittest for using malformed executable in spawnProcess --- std/process.d | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/std/process.d b/std/process.d index edd8acfc601..ef07d1d7302 100644 --- a/std/process.d +++ b/std/process.d @@ -1053,6 +1053,19 @@ version (Posix) @system unittest import std.exception : assertThrown; assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf")); assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf")); + + // can't execute malformed file with executable permissions + version(Posix) + { + import std.path : buildPath; + import std.file : remove, write, setAttributes; + import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH; + string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID); + write(deleteme, ""); + scope(exit) remove(deleteme); + setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); + assertThrown!ProcessException(spawnProcess(deleteme)); + } } @system unittest // Specifying a working directory. From 44e584ee1834fda71935d12aa51ac37fdaa293c3 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Tue, 30 May 2017 14:26:11 +0300 Subject: [PATCH 202/262] Remove pipe errors ignoring in spawnProcess. Add assert(0) instead of unknown error --- std/process.d | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/std/process.d b/std/process.d index ef07d1d7302..10b29526e08 100644 --- a/std/process.d +++ b/std/process.d @@ -434,16 +434,6 @@ private Pid spawnProcessImpl(in char[][] args, assert(0); } - static void ignorePipeErrors() nothrow - { - import core.sys.posix.signal; - import core.stdc.string : memset; - sigaction_t ignoreAction; - memset(&ignoreAction, 0, sigaction_t.sizeof); - ignoreAction.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &ignoreAction, null); - } - auto id = core.sys.posix.unistd.fork(); if (id < 0) { @@ -458,7 +448,6 @@ private Pid spawnProcessImpl(in char[][] args, // Child process - ignorePipeErrors(); // no need for the read end of pipe on child side close(forkPipe[0]); immutable forkPipeOut = forkPipe[1]; @@ -570,7 +559,7 @@ private Pid spawnProcessImpl(in char[][] args, string errorMsg; readExecResult = read(forkPipe[0], &error, error.sizeof); - switch(status) + switch (status) { case InternalError.chdir: errorMsg = "Failed to set working directory"; @@ -582,8 +571,7 @@ private Pid spawnProcessImpl(in char[][] args, errorMsg = "Failed to execute program"; break; default: - errorMsg = "Unknown error occured"; - break; + assert(0); } if (readExecResult == error.sizeof) { From 8bc0ab643f0511203356a749b8d6b496f43047fa Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Tue, 30 May 2017 14:42:43 +0300 Subject: [PATCH 203/262] Improve coverage for failed chdir in spawnProcess --- std/process.d | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/std/process.d b/std/process.d index 10b29526e08..289f90af623 100644 --- a/std/process.d +++ b/std/process.d @@ -1081,6 +1081,17 @@ version (Posix) @system unittest std.file.write(directory, "foo"); scope(exit) remove(directory); assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); + + // can't run in directory if user does not have search permission on this directory + version(Posix) + { + import core.sys.posix.sys.stat : S_IRUSR; + auto directoryNoSearch = uniqueTempPath(); + mkdir(directoryNoSearch); + scope(exit) rmdirRecurse(directoryNoSearch); + setAttributes(directoryNoSearch, S_IRUSR); + assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch)); + } } @system unittest // Specifying empty working directory. From a2e63b0bf26db31eee30635702c60ff9d4e5ac22 Mon Sep 17 00:00:00 2001 From: Roman Chistokhodov Date: Tue, 30 May 2017 15:12:44 +0300 Subject: [PATCH 204/262] final switch on spawnProcess child errors --- std/process.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/process.d b/std/process.d index 289f90af623..51ddaf6276b 100644 --- a/std/process.d +++ b/std/process.d @@ -559,7 +559,7 @@ private Pid spawnProcessImpl(in char[][] args, string errorMsg; readExecResult = read(forkPipe[0], &error, error.sizeof); - switch (status) + final switch (status) { case InternalError.chdir: errorMsg = "Failed to set working directory"; @@ -570,8 +570,8 @@ private Pid spawnProcessImpl(in char[][] args, case InternalError.exec: errorMsg = "Failed to execute program"; break; - default: - assert(0); + case InternalError.noerror: + assert(false); } if (readExecResult == error.sizeof) { From a5263c30b498e4a09cf360e6335dcac9197ec674 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Tue, 30 May 2017 14:03:32 -0400 Subject: [PATCH 205/262] Removed extra string allocation in std.conv.to --- std/conv.d | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/std/conv.d b/std/conv.d index ef729674418..aa1631f0f05 100644 --- a/std/conv.d +++ b/std/conv.d @@ -956,9 +956,7 @@ if (!(isImplicitlyConvertible!(S, T) && //Default case, delegate to format //Note: we don't call toStr directly, to avoid duplicate work. auto app = appender!T(); - app.put("cast("); - app.put(S.stringof); - app.put(')'); + app.put("cast(" ~ S.stringof ~ ")"); FormatSpec!char f; formatValue(app, cast(OriginalType!S) value, f); return app.data; From 81f1c582da1862d446a75763017aeb5670d69c23 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 31 May 2017 09:49:14 +0100 Subject: [PATCH 206/262] document std.complex .im 0 initialization --- std/complex.d | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/std/complex.d b/std/complex.d index 1ce874dbd24..923250dd381 100644 --- a/std/complex.d +++ b/std/complex.d @@ -22,7 +22,8 @@ import std.traits; Params: R = (template parameter) type of real part of complex number - I = (template parameter) type of imaginary part of complex number + I = (template parameter) type of imaginary part of complex number, + 0 if omitted. re = real part of complex number to be constructed im = (optional) imaginary part of complex number @@ -162,18 +163,25 @@ if (isFloatingPoint!T) @safe pure nothrow @nogc: + /** Construct a complex number with the specified real and + imaginary parts. In the case where a single argument is passed + that is not complex, the imaginary part of the result will be + zero. + */ this(R : T)(Complex!R z) { re = z.re; im = z.im; } + /// ditto this(Rx : T, Ry : T)(Rx x, Ry y) { re = x; im = y; } + /// ditto this(R : T)(R r) { re = r; From 7eafb6d53969c9d5f4c2488fa8711c94ec8a1816 Mon Sep 17 00:00:00 2001 From: John Colvin Date: Wed, 31 May 2017 11:22:45 +0100 Subject: [PATCH 207/262] fix im 0 init placement info for complex --- std/complex.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/complex.d b/std/complex.d index 923250dd381..225e0d48755 100644 --- a/std/complex.d +++ b/std/complex.d @@ -22,11 +22,10 @@ import std.traits; Params: R = (template parameter) type of real part of complex number - I = (template parameter) type of imaginary part of complex number, - 0 if omitted. + I = (template parameter) type of imaginary part of complex number re = real part of complex number to be constructed - im = (optional) imaginary part of complex number + im = (optional) imaginary part of complex number, 0 if omitted. Returns: $(D Complex) instance with real and imaginary parts set From 23c4f0f9a958efe4ab6522d2ef1e2fbb30883c57 Mon Sep 17 00:00:00 2001 From: Andrey Kabylin Date: Thu, 1 Jun 2017 18:13:10 +0700 Subject: [PATCH 208/262] errors with getSymbolsByUDA to get a private members (#5344) * Remove the ability for getSymbolsByUDA to get a private method from another module --- std/traits.d | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/std/traits.d b/std/traits.d index 1b90e338e71..0056a35912b 100644 --- a/std/traits.d +++ b/std/traits.d @@ -7670,12 +7670,16 @@ template getSymbolsByUDA(alias symbol, alias attribute) { .format(names[0])); } - // filtering out nested class context - enum noThisMember(string name) = (name != "this"); - alias membersWithoutNestedCC = Filter!(noThisMember, __traits(allMembers, symbol)); + // filtering inaccessible members + enum isAccessibleMember(string name) = __traits(compiles, __traits(getMember, symbol, name)); + alias accessibleMembers = Filter!(isAccessibleMember, __traits(allMembers, symbol)); - enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol.%s, attribute)".format(name)); - alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, membersWithoutNestedCC)); + // filtering not compiled members such as alias of basic types + enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol." ~ name ~ ", attribute)"); + enum isCorrectMember(string name) = __traits(compiles, hasSpecificUDA!(name)); + + alias correctMembers = Filter!(isCorrectMember, accessibleMembers); + alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, correctMembers)); // if the symbol itself has the UDA, tack it on to the front of the list static if (hasUDA!(symbol, attribute)) @@ -7754,9 +7758,30 @@ template getSymbolsByUDA(alias symbol, alias attribute) { { // HasPrivateMembers has, well, private members, one of which has a UDA. import std.internal.test.uda : Attr, HasPrivateMembers; - static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 2); + // Trying access to private member from another file therefore we do not have access + // for this otherwise we get deprecation warning - not visible from module + static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1); static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr)); - static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[1], Attr)); +} + +/// +@safe unittest +{ + enum Attr; + struct A + { + alias int INT; + alias void function(INT) SomeFunction; + @Attr int a; + int b; + @Attr private int c; + private int d; + } + + // Here everything is fine, we have access to private member c + static assert(getSymbolsByUDA!(A, Attr).length == 2); + static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr)); + static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr)); } // #16387: getSymbolsByUDA works with structs but fails with classes From b5ffc889caadf9484a49888dea06b2549fae5ee0 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Thu, 1 Jun 2017 11:13:04 -0400 Subject: [PATCH 209/262] Remove debug writelns --- std/algorithm/comparison.d | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index 25da1c94d76..4642c86f810 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -554,8 +554,6 @@ body @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int a = 1; short b = 6; double c = 2; @@ -868,9 +866,6 @@ range of range (of range...) comparisons. import std.internal.test.dummyrange : ReferenceForwardRange, ReferenceInputRange, ReferenceInfiniteForwardRange; - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - // various strings assert(equal("æøå", "æøå")); //UTF8 vs UTF8 assert(!equal("???", "æøå")); //UTF8 vs UTF8 @@ -1335,8 +1330,6 @@ if (isForwardRange!(Range1) && isForwardRange!(Range2)) @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); assert(levenshteinDistance("a", "a") == 0); assert(levenshteinDistance("a", "b") == 1); assert(levenshteinDistance("aa", "ab") == 1); @@ -1422,8 +1415,6 @@ if (T.length >= 2) @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); int a = 5; short b = 6; double c = 2; @@ -1586,9 +1577,6 @@ if (isInputRange!(Range1) && isInputRange!(Range2)) @safe unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 3 ]; int[] b = [ 1, 2, 4, 5 ]; auto mm = mismatch(a, b); From f2ff3527a8059842066638fab0118e4b48577649 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sat, 3 Jun 2017 13:22:10 -0400 Subject: [PATCH 210/262] opChecked: use "overflow" consistently in code & dox --- std/experimental/checkedint.d | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d index a1701a8419e..3be83b4069c 100644 --- a/std/experimental/checkedint.d +++ b/std/experimental/checkedint.d @@ -2263,13 +2263,13 @@ Params: x = The binary operator involved, e.g. `/` lhs = The left-hand side of the operator rhs = The right-hand side of the operator -error = The error indicator (assigned `true` in case there's an error) +overflow = The overflow indicator (assigned `true` in case there's an error) Returns: The result of the operation, which is the same as the built-in operator */ typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()"))) -opChecked(string x, L, R)(const L lhs, const R rhs, ref bool error) +opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow) if (isIntegral!L && isIntegral!R) { static if (x == "cmp") @@ -2304,7 +2304,7 @@ if (isIntegral!L && isIntegral!R) if (lhs >= 0) return true; } - error = true; + overflow = true; return true; } } @@ -2317,12 +2317,12 @@ if (isIntegral!L && isIntegral!R) static assert(isUnsigned!L != isUnsigned!R); if (!isUnsigned!L && lhs < 0) { - error = true; + overflow = true; return -1; } if (!isUnsigned!R && rhs < 0) { - error = true; + overflow = true; return 1; } } @@ -2344,7 +2344,7 @@ if (isIntegral!L && isIntegral!R) else static if (x == "^^") { // Exponentiation is weird, handle separately - return pow(lhs, rhs, error); + return pow(lhs, rhs, overflow); } else static if (valueConvertible!(L, Result) && valueConvertible!(R, Result)) @@ -2359,13 +2359,13 @@ if (isIntegral!L && isIntegral!R) { static if (isUnsigned!Result) alias impl = addu; else alias impl = adds; - return impl(Result(lhs), Result(rhs), error); + return impl(Result(lhs), Result(rhs), overflow); } else static if (x == "-") { static if (isUnsigned!Result) alias impl = subu; else alias impl = subs; - return impl(Result(lhs), Result(rhs), error); + return impl(Result(lhs), Result(rhs), overflow); } else static if (x == "*") { @@ -2376,7 +2376,7 @@ if (isIntegral!L && isIntegral!R) } static if (isUnsigned!Result) alias impl = mulu; else alias impl = muls; - return impl(Result(lhs), Result(rhs), error); + return impl(Result(lhs), Result(rhs), overflow); } else static if (x == "/" || x == "%") { @@ -2399,14 +2399,14 @@ if (isIntegral!L && isIntegral!R) static if (!isUnsigned!L) { if (lhs < 0) - return subu(Result(rhs), Result(-lhs), error); + return subu(Result(rhs), Result(-lhs), overflow); } else static if (!isUnsigned!R) { if (rhs < 0) - return subu(Result(lhs), Result(-rhs), error); + return subu(Result(lhs), Result(-rhs), overflow); } - return addu(Result(lhs), Result(rhs), error); + return addu(Result(lhs), Result(rhs), overflow); } else static if (x == "-") { @@ -2417,9 +2417,9 @@ if (isIntegral!L && isIntegral!R) else static if (!isUnsigned!R) { if (rhs < 0) - return addu(Result(lhs), Result(-rhs), error); + return addu(Result(lhs), Result(-rhs), overflow); } - return subu(Result(lhs), Result(rhs), error); + return subu(Result(lhs), Result(rhs), overflow); } else static if (x == "*") { @@ -2431,7 +2431,7 @@ if (isIntegral!L && isIntegral!R) { if (rhs < 0) goto fail; } - return mulu(Result(lhs), Result(rhs), error); + return mulu(Result(lhs), Result(rhs), overflow); } else static if (x == "/" || x == "%") { @@ -2449,7 +2449,7 @@ if (isIntegral!L && isIntegral!R) } debug assert(false); fail: - error = true; + overflow = true; return Result(0); } From 825a974912b345a53213f2e7d364b2c030972758 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 6 Jun 2017 01:42:18 +0100 Subject: [PATCH 211/262] ieeeMean: Fix weird indentation --- std/math.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/math.d b/std/math.d index 8c394ab1730..30fd3ee51ba 100644 --- a/std/math.d +++ b/std/math.d @@ -6823,8 +6823,8 @@ body ulong *yl = cast(ulong *)&y; ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1; - m |= ((*xl) & 0x8000_0000_0000_0000L); - *ul = m; + m |= ((*xl) & 0x8000_0000_0000_0000L); + *ul = m; } else static if (F.realFormat == RealFormat.ieeeSingle) { From ec15b75f2fb7a696723926c4667f953365b039c9 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 6 Jun 2017 01:42:54 +0100 Subject: [PATCH 212/262] ieeeMean: Fix for 128 bit reals The implementation previously failed the existing tests. Verified on LDC/AArch64. --- std/math.d | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/std/math.d b/std/math.d index 30fd3ee51ba..71ef9817801 100644 --- a/std/math.d +++ b/std/math.d @@ -6800,21 +6800,15 @@ body ulong *yl = cast(ulong *)&y; // Multi-byte add, then multi-byte right shift. - ulong mh = ((xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) - + (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL)); + import core.checkedint : addu; + bool carry; + ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry); - // Discard the lowest bit (to avoid overflow) - ulong ml = (xl[MANTISSA_LSB]>>>1) + (yl[MANTISSA_LSB]>>>1); + ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) + + (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL); - // add the lowest bit back in, if necessary. - if (xl[MANTISSA_LSB] & yl[MANTISSA_LSB] & 1) - { - ++ml; - if (ml == 0) ++mh; - } - mh >>>=1; - ul[MANTISSA_MSB] = mh | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000); - ul[MANTISSA_LSB] = ml; + ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000); + ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63; } else static if (F.realFormat == RealFormat.ieeeDouble) { From 3cc37a24bd367bbea61de3447aa789079447ac10 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 6 Jun 2017 03:05:32 +0100 Subject: [PATCH 213/262] std.math.nextUp: Fix 128 bit real implementation Also minimally uncrustify code. Verified on LDC/AArch64. --- std/math.d | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/std/math.d b/std/math.d index 8c394ab1730..48fb0f1f0cd 100644 --- a/std/math.d +++ b/std/math.d @@ -5782,25 +5782,22 @@ real nextUp(real x) @trusted pure nothrow @nogc { // NaN or Infinity if (x == -real.infinity) return -real.max; - return x; // +Inf and NaN are unchanged. } - ulong* ps = cast(ulong *)&e; - if (ps[MANTISSA_LSB] & 0x8000_0000_0000_0000) + auto ps = cast(ulong *)&x; + if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000) { // Negative number - - if (ps[MANTISSA_LSB] == 0 - && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000) + if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000) { // it was negative zero, change to smallest subnormal - ps[MANTISSA_LSB] = 0x0000_0000_0000_0001; + ps[MANTISSA_LSB] = 1; ps[MANTISSA_MSB] = 0; return x; } - --*ps; if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB]; + --ps[MANTISSA_LSB]; } else { @@ -5809,7 +5806,6 @@ real nextUp(real x) @trusted pure nothrow @nogc if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB]; } return x; - } else static if (F.realFormat == RealFormat.ieeeExtended) { From e401ca1e271c0db8ca461d9e41e9808b8f38246b Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 6 Jun 2017 03:47:49 +0100 Subject: [PATCH 214/262] std.math: Fix frexp() for 128 bit reals by aligning EXPBIAS with other formats Also replace other uses of the constant with EXPBIAS. Verified on LDC/AArch64. --- std/math.d | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/std/math.d b/std/math.d index 8c394ab1730..6591f458ff8 100644 --- a/std/math.d +++ b/std/math.d @@ -255,6 +255,7 @@ template floatTraits(T) { // EXPMASK is a ushort mask to select the exponent portion (without sign) // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort + // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1). // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal @@ -345,7 +346,7 @@ template floatTraits(T) // Quadruple precision float enum ushort EXPMASK = 0x7FFF; enum ushort EXPSHIFT = 0; - enum ushort EXPBIAS = 0x3FFF; + enum ushort EXPBIAS = 0x3FFE; enum realFormat = RealFormat.ieeeQuadruple; version(LittleEndian) { @@ -2451,9 +2452,10 @@ if (isFloatingPoint!T) if (ex) // If exponent is non-zero { if (ex == F.EXPMASK) - { // infinity or NaN + { + // infinity or NaN if (vl[MANTISSA_LSB] | - ( vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN + (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN { // convert NaNS to NaNQ vl[MANTISSA_MSB] |= 0x0000_8000_0000_0000; @@ -2463,17 +2465,15 @@ if (isFloatingPoint!T) exp = int.min; else // positive infinity exp = int.max; - } else { exp = ex - F.EXPBIAS; - vu[F.EXPPOS_SHORT] = - cast(ushort)((0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE); + vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]); } } - else if ((vl[MANTISSA_LSB] - |(vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0) + else if ((vl[MANTISSA_LSB] | + (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0) { // vf is +-0.0 exp = 0; @@ -2484,8 +2484,7 @@ if (isFloatingPoint!T) vf *= F.RECIP_EPSILON; ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; exp = ex - F.EXPBIAS - T.mant_dig + 1; - vu[F.EXPPOS_SHORT] = - cast(ushort)((~F.EXPMASK & vu[F.EXPPOS_SHORT]) | 0x3FFE); + vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]); } return vf; } From c35287dd00da95e05faecc5961fac97d459efd65 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Tue, 6 Jun 2017 03:14:16 +0100 Subject: [PATCH 215/262] std.math.lrint: Implement for 128 bit reals Verified on LDC/AArch64. --- std/math.d | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/std/math.d b/std/math.d index 8c394ab1730..8b7da5fc062 100644 --- a/std/math.d +++ b/std/math.d @@ -4240,9 +4240,45 @@ long lrint(real x) @trusted pure nothrow @nogc return sign ? -result : result; } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + const vu = cast(ushort*)(&x); + + // Find the exponent and sign + const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1; + if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63) + { + // The result is left implementation defined when the number is + // too large to fit in a 64 bit long. + return cast(long) x; + } + + // Force rounding of lower bits according to current rounding + // mode by adding ±2^-112 and subtracting it again. + enum OF = 5.19229685853482762853049632922009600E33L; + const j = sign ? -OF : OF; + x = (j + x) - j; + + const implicitOne = 1UL << 48; + auto vl = cast(ulong*)(&x); + vl[MANTISSA_MSB] &= implicitOne - 1; + vl[MANTISSA_MSB] |= implicitOne; + + long result; + + const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1); + if (exp < 0) + result = 0; + else if (exp <= 48) + result = vl[MANTISSA_MSB] >> (48 - exp); + else + result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp)); + + return sign ? -result : result; + } else { - static assert(false, "Only 64-bit and 80-bit reals are supported by lrint()"); + static assert(false, "real type not supported by lrint()"); } } } @@ -4261,6 +4297,17 @@ long lrint(real x) @trusted pure nothrow @nogc assert(lrint(int.min + 0.5) == -2147483648L); } +static if (real.mant_dig >= long.sizeof * 8) +{ + @safe pure nothrow @nogc unittest + { + assert(lrint(long.max - 1.5L) == long.max - 1); + assert(lrint(long.max - 0.5L) == long.max - 1); + assert(lrint(long.min + 0.5L) == long.min); + assert(lrint(long.min + 1.5L) == long.min + 2); + } +} + /******************************************* * Return the value of x rounded to the nearest integer. * If the fractional part of x is exactly 0.5, the return value is From 9a09fbc8005074aaabdccdd6d09f166bdb05067a Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Tue, 6 Jun 2017 21:01:25 -0400 Subject: [PATCH 216/262] Eliminate redundant `static` in `static __gshared` cc @MartinNowak --- std/stdio.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/stdio.d b/std/stdio.d index cf5eb48cd15..0446f1135a4 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4524,8 +4524,8 @@ Initialize with a message and an error code. // Undocumented but public because the std* handles are aliasing it. ref File makeGlobal(alias handle)() { - static __gshared File.Impl impl; - static __gshared File result; + __gshared File.Impl impl; + __gshared File result; // Use an inline spinlock to make sure the initializer is only run once. // We assume there will be at most uint.max / 2 threads trying to initialize From 09e5a3ccc77bf39adeec4138d5a654afdcc1f02d Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Wed, 7 Jun 2017 10:31:55 -0700 Subject: [PATCH 217/262] Fix bracing style. --- std/traits.d | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/std/traits.d b/std/traits.d index 0056a35912b..de3aa5da170 100644 --- a/std/traits.d +++ b/std/traits.d @@ -7656,13 +7656,15 @@ template getUDAs(alias symbol, alias attribute) * This is not recursive; it will not search for symbols within symbols such as * nested structs or unions. */ -template getSymbolsByUDA(alias symbol, alias attribute) { +template getSymbolsByUDA(alias symbol, alias attribute) +{ import std.format : format; import std.meta : AliasSeq, Filter; // translate a list of strings into symbols. mixing in the entire alias // avoids trying to access the symbol, which could cause a privacy violation - template toSymbols(names...) { + template toSymbols(names...) + { static if (names.length == 0) alias toSymbols = AliasSeq!(); else From 79cf4ab9139e60114bbd0c995b47bea2fcc59173 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Fri, 19 May 2017 18:29:47 +0300 Subject: [PATCH 218/262] Use OpaquePointer as a safe support for region based allocators --- changelog/std-experimental-allocator.dd | 34 +++++ .../building_blocks/affix_allocator.d | 4 +- .../building_blocks/bitmapped_block.d | 140 ++++++++++-------- .../allocator/building_blocks/free_list.d | 97 +++++++++--- .../building_blocks/kernighan_ritchie.d | 126 +++++++++++----- .../allocator/building_blocks/region.d | 55 +++++-- std/experimental/allocator/common.d | 13 +- std/experimental/allocator/package.d | 25 +++- 8 files changed, 347 insertions(+), 147 deletions(-) create mode 100644 changelog/std-experimental-allocator.dd diff --git a/changelog/std-experimental-allocator.dd b/changelog/std-experimental-allocator.dd new file mode 100644 index 00000000000..b7aa696b8d1 --- /dev/null +++ b/changelog/std-experimental-allocator.dd @@ -0,0 +1,34 @@ +Add `OpaquePointer` to the `std.experimental.allocator` package + +`OpaquePointer` should be used to provide a memory buffer to allocators that +manage a buffer, such as $(REF Region,std,experimental,allocator,building_blocks,region). +Using `OpaquePointer` will ensure that the garbage collector will mark the +memory for scanning. + +The following allocators have a `safe` ctor when provided with an +$(REF OpaquePointer,std,experimental,allocator) memory buffer: + +$(UL + $(LI $(REF BitmappedBlock,std,experimental,allocator,building_blocks,bitmapped_block)) + $(LI $(REF FreeList,std,experimental,allocator,building_blocks,free_list)) + $(LI $(REF KRRegion,std,experimental,allocator,building_blocks,kernighan_ritchie)) + $(LI $(REF Region,std,experimental,allocator,building_blocks,region)) +) + +Note: the responsibility of allocating the buffer properly falls on the caller. + +------- +import std.experimental.allocator : OpaquePointer; +import std.experimental.allocator.building_blocks.region : Region; + +void main() +{ + // Create a 64 KB region + auto reg = Region!()(new OpaquePointer[1024 * 64 / OpaquePointer.sizeof]); + const b = reg.allocate(101); + assert(b.length == 101); + assert(reg.deallocateAll()); + auto c = reg.allocateAll(); + assert(c.length == 1024 * 64); +} +------- diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index 761840dc54a..4c22e5964ed 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -395,9 +395,11 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; import std.experimental.allocator.common : testAllocator; + import std.experimental.allocator : OpaquePointer; testAllocator!({ auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) - (BitmappedBlock!128(new ubyte[128 * 4096])); + (BitmappedBlock!128( + new OpaquePointer[128 * 4096 / OpaquePointer.sizeof])); return a; }); } diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index a0ded14ab68..331c1180069 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -49,12 +49,13 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment import std.traits : hasMember; import std.conv : text; import std.typecons : Ternary; + import std.experimental.allocator : OpaquePointer; @system unittest { import std.experimental.allocator.mallocator : AlignedMallocator; import std.algorithm.comparison : max; - auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + auto m = cast(OpaquePointer[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, max(theAlignment, cast(uint) size_t.sizeof))); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlock(m)); @@ -142,18 +143,24 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment in bytes. $(UL - $(LI If $(D ParentAllocator) is $(D NullAllocator), only the constructor - taking $(D data) is defined and the user is responsible for freeing $(D - data) if desired.) - $(LI Otherwise, both constructors are defined. The $(D data)-based - constructor assumes memory has been allocated with the parent allocator. - The $(D capacity)-based constructor uses $(D ParentAllocator) to allocate + $(LI If `ParentAllocator` is `NullAllocator`, only the constructor taking + `data` is defined and the user is responsible for freeing `data` if desired.) + $(LI Otherwise, both constructors are defined. The `data`-based constructor + assumes memory has been allocated with the parent allocator. The underlying + `data` must be of $(LREF OpaquePointer) type for a safe allocation of objects + that may contain pointers, and thus for a safe allocator. + The `capacity`-based constructor uses `ParentAllocator` to allocate an appropriate contiguous hunk of memory. Regardless of the constructor - used, the destructor releases the memory by using $(D - ParentAllocator.deallocate).) + used, the destructor releases the memory by using `ParentAllocator.deallocate`.) ) */ - this(ubyte[] data) + @trusted this(OpaquePointer[] data) + { + this(cast(ubyte[]) data); + } + + /// Ditto + @system this(ubyte[] data) { immutable a = data.ptr.effectiveAlignment; assert(a >= size_t.alignof || !data.ptr, @@ -169,7 +176,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment // blocks found. for (; _blocks; --_blocks) { - immutable controlWords = _blocks.divideRoundUp(64); + immutable controlWords = _blocks.divideRoundUp(ulong.sizeof * 8); auto payload = data[controlWords * 8 .. $].roundStartToMultipleOf( alignment); if (payload.length < _blocks * blockSize) @@ -188,8 +195,9 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment static if (!is(ParentAllocator == NullAllocator)) this(size_t capacity) { - size_t toAllocate = totalAllocation(capacity); - auto data = cast(ubyte[])(parent.allocate(toAllocate)); + size_t toAllocate = totalAllocation(capacity) + .roundUpToAlignment(OpaquePointer.alignof); + auto data = cast(OpaquePointer[])(parent.allocate(toAllocate)); this(data); assert(_blocks * blockSize >= capacity); } @@ -694,9 +702,10 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { // Create a block allocator on top of a 10KB stack region. import std.experimental.allocator.building_blocks.region : InSituRegion; + import std.experimental.allocator : OpaquePointer; import std.traits : hasMember; InSituRegion!(10_240, 64) r; - auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll())); + auto a = BitmappedBlock!(64, 64)(cast(OpaquePointer[])(r.allocateAll())); static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); const b = a.allocate(100); assert(b.length == 100); @@ -710,14 +719,15 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment @system unittest { - static void testAllocateAll(size_t bs)(uint blocks, uint blocksAtATime) + static void testAllocateAll(size_t bs, T)(uint blocks, uint blocksAtATime) { import std.algorithm.comparison : min; assert(bs); import std.experimental.allocator.gc_allocator : GCAllocator; + + auto nb = ((blocks * bs * 8 + blocks) / 8).roundUpToAlignment(T.alignof); auto a = BitmappedBlock!(bs, min(bs, platformAlignment))( - cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + - blocks) / 8)) + cast(T[])(GCAllocator.instance.allocate(nb)) // this many bytes ); import std.conv : text; assert(blocks >= a._blocks, text(blocks, " < ", a._blocks)); @@ -801,26 +811,32 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment } } - testAllocateAll!(1)(0, 1); - testAllocateAll!(1)(8, 1); - testAllocateAll!(4096)(128, 1); + import std.experimental.allocator : OpaquePointer; + import std.meta : AliasSeq; - testAllocateAll!(1)(0, 2); - testAllocateAll!(1)(128, 2); - testAllocateAll!(4096)(128, 2); - - testAllocateAll!(1)(0, 4); - testAllocateAll!(1)(128, 4); - testAllocateAll!(4096)(128, 4); - - testAllocateAll!(1)(0, 3); - testAllocateAll!(1)(24, 3); - testAllocateAll!(3008)(100, 1); - testAllocateAll!(3008)(100, 3); - - testAllocateAll!(1)(0, 128); - testAllocateAll!(1)(128 * 1, 128); - testAllocateAll!(128 * 20)(13 * 128, 128); + foreach(T; AliasSeq!(ubyte, OpaquePointer)) + { + testAllocateAll!(1, T)(0, 1); + testAllocateAll!(1, T)(8, 1); + testAllocateAll!(4096, T)(128, 1); + + testAllocateAll!(1, T)(0, 2); + testAllocateAll!(1, T)(128, 2); + testAllocateAll!(4096, T)(128, 2); + + testAllocateAll!(1, T)(0, 4); + testAllocateAll!(1, T)(128, 4); + testAllocateAll!(4096, T)(128, 4); + + testAllocateAll!(1, T)(0, 3); + testAllocateAll!(1, T)(24, 3); + testAllocateAll!(3008, T)(100, 1); + testAllocateAll!(3008, T)(100, 3); + + testAllocateAll!(1, T)(0, 128); + testAllocateAll!(1, T)(128 * 1, 128); + testAllocateAll!(128 * 20, T)(13 * 128, 128); + } } // Test totalAllocation @@ -861,10 +877,11 @@ struct BitmappedBlockWithInternalPointers( { import std.conv : text; import std.typecons : Ternary; + import std.experimental.allocator : OpaquePointer; @system unittest { import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + auto m = cast(OpaquePointer[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, theAlignment)); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlockWithInternalPointers(m)); @@ -879,7 +896,8 @@ struct BitmappedBlockWithInternalPointers( Constructors accepting desired capacity or a preallocated buffer, similar in semantics to those of $(D BitmappedBlock). */ - this(ubyte[] data) + this(T)(T[] data) + if (is (T == ubyte) || is (T == OpaquePointer)) { _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); } @@ -1064,8 +1082,10 @@ struct BitmappedBlockWithInternalPointers( @system unittest { import std.typecons : Ternary; + import std.experimental.allocator : OpaquePointer; - auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); + auto h = BitmappedBlockWithInternalPointers!(4096)(new OpaquePointer[4096 * + 1024 / OpaquePointer.sizeof]); auto b = h.allocate(123); assert(b.length == 123); @@ -1095,7 +1115,7 @@ struct BitmappedBlockWithInternalPointers( Returns the number of most significant ones before a zero can be found in $(D x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64. */ -private uint leadingOnes(ulong x) +private @safe uint leadingOnes(ulong x) { uint result = 0; while (cast(long) x < 0) @@ -1106,7 +1126,7 @@ private uint leadingOnes(ulong x) return result; } -@system unittest +@safe unittest { assert(leadingOnes(0) == 0); assert(leadingOnes(~0UL) == 64); @@ -1120,7 +1140,7 @@ private uint leadingOnes(ulong x) /** Finds a run of contiguous ones in $(D x) of length at least $(D n). */ -private uint findContigOnes(ulong x, uint n) +private @safe uint findContigOnes(ulong x, uint n) { while (n > 1) { @@ -1131,7 +1151,7 @@ private uint findContigOnes(ulong x, uint n) return leadingOnes(~x); } -@system unittest +@safe unittest { assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54); @@ -1148,14 +1168,14 @@ private uint findContigOnes(ulong x, uint n) /* Unconditionally sets the bits from lsb through msb in w to zero. */ -private void setBits(ref ulong w, uint lsb, uint msb) +private @safe void setBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); w |= mask; } -@system unittest +@safe unittest { ulong w; w = 0; setBits(w, 0, 63); assert(w == ulong.max); @@ -1167,7 +1187,7 @@ private void setBits(ref ulong w, uint lsb, uint msb) /* Are bits from lsb through msb in w zero? If so, make then 1 and return the resulting w. Otherwise, just return 0. */ -private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) +private @safe bool setBitsIfZero(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); @@ -1177,7 +1197,7 @@ private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) } // Assigns bits in w from lsb through msb to zero. -private void resetBits(ref ulong w, uint lsb, uint msb) +private @safe void resetBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); @@ -1191,13 +1211,13 @@ private struct BitVector { ulong[] _rep; - auto rep() { return _rep; } + @safe auto rep() { return _rep; } - this(ulong[] data) { _rep = data; } + @safe this(ulong[] data) { _rep = data; } - void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } + @safe void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } - void opSliceAssign(bool b, ulong x, ulong y) + @safe void opSliceAssign(bool b, ulong x, ulong y) { assert(x <= y && y <= _rep.length * 64); if (x == y) return; @@ -1228,14 +1248,14 @@ private struct BitVector } } - bool opIndex(ulong x) + @safe bool opIndex(ulong x) { assert(x < length); return (_rep[cast(size_t) (x / 64)] & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; } - void opIndexAssign(bool b, ulong x) + @safe void opIndexAssign(bool b, ulong x) { assert(x / 64 <= size_t.max); immutable i = cast(size_t) (x / 64); @@ -1244,7 +1264,7 @@ private struct BitVector else _rep[i] &= ~j; } - ulong length() const + @safe ulong length() const { return _rep.length * 64; } @@ -1252,7 +1272,7 @@ private struct BitVector /* Returns the index of the first 1 to the right of i (including i itself), or length if not found. */ - ulong find1(ulong i) + @safe ulong find1(ulong i) { assert(i < length); assert(i / 64 <= size_t.max); @@ -1279,7 +1299,7 @@ private struct BitVector /* Returns the index of the first 1 to the left of i (including i itself), or ulong.max if not found. */ - ulong find1Backward(ulong i) + @safe ulong find1Backward(ulong i) { assert(i < length); auto w = cast(size_t) (i / 64); @@ -1304,20 +1324,20 @@ private struct BitVector } /// Are all bits zero? - bool allAre0() const + @safe bool allAre0() const { foreach (w; _rep) if (w) return false; return true; } /// Are all bits one? - bool allAre1() const + @safe bool allAre1() const { foreach (w; _rep) if (w != ulong.max) return false; return true; } - ulong findZeros(immutable size_t howMany, ulong start) + @safe ulong findZeros(immutable size_t howMany, ulong start) { assert(start < length); assert(howMany > 64); @@ -1353,7 +1373,7 @@ private struct BitVector } } -@system unittest +@safe unittest { auto v = BitVector(new ulong[10]); assert(v.length == 640); diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 136e9b44db4..337b4cacff4 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -430,6 +430,7 @@ struct ContiguousFreeList(ParentAllocator, : NullAllocator; import std.experimental.allocator.building_blocks.stats_collector : StatsCollector, Options; + import std.experimental.allocator : OpaquePointer; import std.traits : hasMember; import std.typecons : Ternary; @@ -477,13 +478,20 @@ struct ContiguousFreeList(ParentAllocator, } } + private @trusted void initialize(OpaquePointer[] buffer, size_t itemSize = fl.max) + { + initialize(cast(ubyte[]) buffer, itemSize); + } + /** Constructors setting up the memory structured as a free list. Params: - buffer = Buffer to structure as a free list. If $(D ParentAllocator) is not - $(D NullAllocator), the buffer is assumed to be allocated by $(D parent) - and will be freed in the destructor. + buffer = Buffer to structure as a free list. If `ParentAllocator` is not + `NullAllocator`, the buffer is assumed to be allocated by `parent` and will + be freed in the destructor. The buffer must be of $(LREF OpaquePointer) + type for a safe allocation of objects that may contain pointers, and thus + for a safe allocator. parent = Parent allocator. For construction from stateless allocators, use their `instance` static member. bytes = Bytes (not items) to be allocated for the free list. Memory will be @@ -493,18 +501,20 @@ struct ContiguousFreeList(ParentAllocator, == unbounded). min = Minimum size eligible for freelisting. Construction with this parameter is defined only if $(D minSize == chooseAtRuntime). If this - condition is met and no $(D min) parameter is present, $(D min) is - initialized with $(D max). + condition is met and no `min` parameter is present, `min` is initialized + with `max`. */ static if (!stateSize!ParentAllocator) - this(ubyte[] buffer) + this(T)(T[] buffer) + if(is(T == ubyte) || is(T == OpaquePointer)) { initialize(buffer); } /// ditto static if (stateSize!ParentAllocator) - this(ParentAllocator parent, ubyte[] buffer) + this(T)(ParentAllocator parent, T[] buffer) + if(is(T == ubyte) || is(T == OpaquePointer)) { initialize(buffer); this.parent = SParent(parent); @@ -514,14 +524,16 @@ struct ContiguousFreeList(ParentAllocator, static if (!stateSize!ParentAllocator) this(size_t bytes) { - initialize(cast(ubyte[])(ParentAllocator.instance.allocate(bytes))); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(ParentAllocator.instance.allocate(nb))); } /// ditto static if (stateSize!ParentAllocator) this(ParentAllocator parent, size_t bytes) { - initialize(cast(ubyte[])(parent.allocate(bytes))); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(parent.allocate(nb))); this.parent = SParent(parent); } @@ -532,7 +544,8 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); } /// ditto @@ -542,7 +555,8 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); this.parent = SParent(parent); } @@ -554,7 +568,8 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } @@ -567,11 +582,23 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - initialize(cast(ubyte[])(parent.allocate(bytes)), max); + auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); + initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } + /** + If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D + deallocate), the destructor is defined to deallocate the block held. + */ + static if (!is(ParentAllocator == NullAllocator) + && hasMember!(ParentAllocator, "deallocate")) + ~this() + { + parent.deallocate(support); + } + /** If $(D n) is eligible for freelisting, returns $(D max). Otherwise, returns $(D parent.goodAllocSize(n)). @@ -686,20 +713,14 @@ struct ContiguousFreeList(ParentAllocator, @system unittest { - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.common : unbounded; import std.typecons : Ternary; - alias A = ContiguousFreeList!(NullAllocator, 0, 64); - auto a = A(new ubyte[1024]); - assert(a.empty == Ternary.yes); - - assert(a.goodAllocSize(15) == 64); - assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65)); + auto a = ContiguousFreeList!(GCAllocator, 0, unbounded)(4096, 8); auto b = a.allocate(100); - assert(a.empty == Ternary.yes); - assert(b.length == 0); + assert(b.length == 100); a.deallocate(b); b = a.allocate(64); assert(a.empty == Ternary.no); @@ -709,6 +730,36 @@ struct ContiguousFreeList(ParentAllocator, a.deallocate(b); } +@system unittest +{ + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; + import std.experimental.allocator : OpaquePointer; + import std.typecons : Ternary; + import std.meta : AliasSeq; + + alias A = ContiguousFreeList!(NullAllocator, 0, 64); + foreach(T; AliasSeq!(ubyte, OpaquePointer)) + { + auto a = A(new T[1024]); + assert(a.empty == Ternary.yes); + + assert(a.goodAllocSize(15) == 64); + assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65)); + + auto b = a.allocate(100); + assert(a.empty == Ternary.yes); + assert(b.length == 0); + a.deallocate(b); + b = a.allocate(64); + assert(a.empty == Ternary.no); + assert(b.length == 64); + assert(a.owns(b) == Ternary.yes); + assert(a.owns(null) == Ternary.no); + a.deallocate(b); + } +} + @system unittest { import std.experimental.allocator.building_blocks.region : Region; diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index f331fba00b2..225da95ca16 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -95,7 +95,9 @@ information is available in client code at deallocation time.) */ struct KRRegion(ParentAllocator = NullAllocator) { - import std.experimental.allocator.common : stateSize, alignedAt; + import std.experimental.allocator.common : stateSize, alignedAt, + roundUpToAlignment; + import std.experimental.allocator : OpaquePointer; import std.traits : hasMember; import std.typecons : Ternary; @@ -302,16 +304,18 @@ struct KRRegion(ParentAllocator = NullAllocator) } /** - Create a $(D KRRegion). If $(D ParentAllocator) is not $(D NullAllocator), - $(D KRRegion)'s destructor will call $(D parent.deallocate). + Create a `KRRegion`. If `ParentAllocator` is not `NullAllocator`, `KRRegion`'s + destructor will call $(D parent.deallocate). Params: b = Block of memory to serve as support for the allocator. Memory must be - larger than two words and word-aligned. - n = Capacity desired. This constructor is defined only if $(D - ParentAllocator) is not $(D NullAllocator). + larger than two words and word-aligned. The memory block must be of + $(LREF OpaquePointer) type for a safe allocation of objects that may + contain pointers, and thus for a safe allocator. + n = Capacity desired. This constructor is defined only if `ParentAllocator` + is not `NullAllocator`. */ - this(ubyte[] b) + @system this(ubyte[] b) { if (b.length < Node.sizeof) { @@ -333,12 +337,19 @@ struct KRRegion(ParentAllocator = NullAllocator) b.ptr, b.length); } + /// ditto + @trusted this(OpaquePointer[] b) + { + this(cast(ubyte[]) b); + } + /// Ditto static if (!is(ParentAllocator == NullAllocator)) this(size_t n) { assert(n > Node.sizeof); - this(cast(ubyte[])(parent.allocate(n))); + auto nb = n.roundUpToAlignment(OpaquePointer.alignof); + this(cast(OpaquePointer[])(parent.allocate(nb))); } /// Ditto @@ -618,13 +629,18 @@ fronting the GC allocator. import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.fallback_allocator : fallbackAllocator; + import std.experimental.allocator : OpaquePointer; import std.typecons : Ternary; + import std.meta : AliasSeq; // KRRegion fronting a general-purpose allocator - ubyte[1024 * 128] buf; - auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); - auto b = alloc.allocate(100); - assert(b.length == 100); - assert(alloc.primary.owns(b) == Ternary.yes); + foreach(T; AliasSeq!(ubyte, OpaquePointer)) + { + T[1024 * 128] buf; + auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); + auto b = alloc.allocate(100); + assert(b.length == 100); + assert(alloc.primary.owns(b) == Ternary.yes); + } } /** @@ -750,47 +766,75 @@ it actually returns memory to the operating system when possible. @system unittest { import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator : OpaquePointer; import std.typecons : Ternary; - auto alloc = KRRegion!()( - cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); - const store = alloc.allocate(KRRegion!().sizeof); - auto p = cast(KRRegion!()* ) store.ptr; import std.conv : emplace; import std.algorithm.mutation : move; import core.stdc.string : memcpy; + import std.meta : AliasSeq; - memcpy(p, &alloc, alloc.sizeof); - emplace(&alloc); - - void[][100] array; - foreach (i; 0 .. array.length) + foreach(T; AliasSeq!(ubyte, OpaquePointer)) { - auto length = 100 * i + 1; - array[i] = p.allocate(length); - assert(array[i].length == length, text(array[i].length)); - assert(p.owns(array[i]) == Ternary.yes); + auto alloc = KRRegion!()( + cast(T[])(GCAllocator.instance.allocate(1024 * 1024))); + const store = alloc.allocate(KRRegion!().sizeof); + auto p = cast(KRRegion!()* ) store.ptr; + + memcpy(p, &alloc, alloc.sizeof); + emplace(&alloc); + + void[][100] array; + foreach (i; 0 .. array.length) + { + auto length = 100 * i + 1; + array[i] = p.allocate(length); + assert(array[i].length == length, text(array[i].length)); + assert(p.owns(array[i]) == Ternary.yes); + } + import std.random : randomShuffle; + randomShuffle(array[]); + foreach (i; 0 .. array.length) + { + assert(p.owns(array[i]) == Ternary.yes); + p.deallocate(array[i]); + } + auto b = p.allocateAll(); + assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); } - import std.random : randomShuffle; - randomShuffle(array[]); - foreach (i; 0 .. array.length) +} + +@system unittest +{ + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator : OpaquePointer; + import std.meta : AliasSeq; + + foreach(T; AliasSeq!(ubyte, OpaquePointer)) { - assert(p.owns(array[i]) == Ternary.yes); - p.deallocate(array[i]); + auto alloc = KRRegion!()( + cast(T[])(GCAllocator.instance.allocate(1024 * 1024))); + auto p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); + alloc.deallocateAll(); + p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); } - auto b = p.allocateAll(); - assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); } @system unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; - auto alloc = KRRegion!()( - cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); - auto p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); - alloc.deallocateAll(); - p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); + import std.experimental.allocator : OpaquePointer; + import std.meta : AliasSeq; + + foreach(T; AliasSeq!(ubyte, OpaquePointer)) + { + T[] emptyBuf; + auto alloc = KRRegion!()(emptyBuf); + auto p = alloc.allocateAll(); + assert(p.length == 0 && p is null); + assert(alloc.deallocateAll()); + assert(alloc.allocate(1) is null); + } } @system unittest diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index 3809b9dd7e4..d1119ed98db 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -35,6 +35,7 @@ struct Region(ParentAllocator = NullAllocator, import std.traits : hasMember; import std.typecons : Ternary; + import std.experimental.allocator : OpaquePointer; // state /** @@ -53,21 +54,23 @@ struct Region(ParentAllocator = NullAllocator, private void* _current, _begin, _end; /** - Constructs a region backed by a user-provided store. Assumes $(D store) is - aligned at $(D minAlign). Also assumes the memory was allocated with $(D - ParentAllocator) (if different from $(D NullAllocator)). + Constructs a region backed by a user-provided store. Assumes `store` is + aligned at `minAlign`. Also assumes the memory was allocated with + `ParentAllocator` (if different from `NullAllocator`). Params: - store = User-provided store backing up the region. $(D store) must be - aligned at $(D minAlign) (enforced with $(D assert)). If $(D - ParentAllocator) is different from $(D NullAllocator), memory is assumed to - have been allocated with $(D ParentAllocator). - n = Bytes to allocate using $(D ParentAllocator). This constructor is only - defined If $(D ParentAllocator) is different from $(D NullAllocator). If - $(D parent.allocate(n)) returns $(D null), the region will be initialized + store = User-provided store backing up the region. `store` must be aligned + at `minAlign` (enforced with `assert`). If `ParentAllocator` is different + from `NullAllocator`, memory is assumed to have been allocated with + `ParentAllocator`. + The underlying `store` must be of $(LREF OpaquePointer) type for a safe + allocation of objects that may contain pointers, and thus for a safe allocator. + n = Bytes to allocate using `ParentAllocator`. This constructor is only + defined if `ParentAllocator` is different from `NullAllocator`. If + $(D parent.allocate(n)) returns `null`, the region will be initialized as empty (correctly initialized but unable to allocate). */ - this(ubyte[] store) + @system this(ubyte[] store) { store = cast(ubyte[])(store.roundUpToAlignment(alignment)); store = store[0 .. $.roundDownToAlignment(alignment)]; @@ -81,11 +84,19 @@ struct Region(ParentAllocator = NullAllocator, _current = store.ptr; } + /// ditto + @trusted this(OpaquePointer[] store) + { + this(cast(ubyte[]) store); + } + /// Ditto static if (!is(ParentAllocator == NullAllocator)) this(size_t n) { - this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment)))); + auto nb = n.roundUpToAlignment(alignment) + .roundUpToAlignment(OpaquePointer.alignof); + this(cast(OpaquePointer[])(parent.allocate(nb))); } /* @@ -354,7 +365,23 @@ struct Region(ParentAllocator = NullAllocator, Yes.growDownwards)(1024 * 64); const b = reg.allocate(101); assert(b.length == 101); - // Destructor will free the memory +} + +@system unittest +{ + import std.experimental.allocator : OpaquePointer; + import std.meta : AliasSeq; + + foreach (T; AliasSeq!(ubyte, OpaquePointer)) + { + // Create a 64 KB region + auto reg = Region!()(new T[1024 * 64 / T.sizeof]); + const b = reg.allocate(101); + assert(b.length == 101); + assert(reg.deallocateAll()); + auto c = reg.allocateAll(); + assert(c.length == 1024 * 64); + } } /** @@ -543,6 +570,8 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; + import std.experimental.allocator : OpaquePointer; + FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; const a2 = r2.allocate(102); assert(a2.length == 102); diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index c1d2540b0c8..d2c5bb1a00d 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -188,25 +188,26 @@ package size_t divideRoundUp(size_t a, size_t b) } /** -Returns `s` rounded up to a multiple of `base`. +Returns `s` rounded up to a multiple of `base` or `null` if the resulted round +up is out of the bounds of `s`. */ -@nogc nothrow pure +@nogc nothrow pure @trusted package void[] roundStartToMultipleOf(void[] s, uint base) { assert(base); - auto p = cast(void*) roundUpToMultipleOf( - cast(size_t) s.ptr, base); + auto p = cast(void*) roundUpToMultipleOf(cast(size_t) s.ptr, base); auto end = s.ptr + s.length; - return p[0 .. end - p]; + return cast(size_t) end > cast(size_t) p ? p[0 .. end - p] : null; } nothrow pure -@system unittest +@safe unittest { void[] p; assert(roundStartToMultipleOf(p, 16) is null); p = new ulong[10]; assert(roundStartToMultipleOf(p, 16) is p); + assert(roundStartToMultipleOf(p, 1 << 31) is null); } /** diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index d4724830490..7b243f4a927 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -249,9 +249,11 @@ public import std.experimental.allocator.common, 2048, Bucketizer!(FList, 1025, 2048, 256), 3584, Bucketizer!(FList, 2049, 3584, 512), 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)( - cast(ubyte[])(GCAllocator.instance.allocate( - max(n, 4072 * 1024))))), + (n) => BitmappedBlock!(4096)( + cast(OpaquePointer[])(GCAllocator.instance.allocate( + max(n, 4072 * 1024 / OpaquePointer.sizeof) + .roundUpToAlignment(OpaquePointer.alignof) + )))), GCAllocator ); A tuMalloc; @@ -262,6 +264,9 @@ public import std.experimental.allocator.common, assert(tuMalloc.expand(c, 14)); tuMalloc.deallocate(b); tuMalloc.deallocate(c); + auto d = tuMalloc.allocate(4072 * 100); + assert(d.length == 4072 * 100); + tuMalloc.deallocate(d); } import std.range.primitives; @@ -2515,6 +2520,20 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) theAllocator.dispose(arr); } +/** +`OpaquePointer` should be used to provide a memory buffer to allocators that +manage a buffer, such as $(LREF Region). Using `OpaquePointer` will ensure that +the garbage collector will mark the memory for scanning. + +Note: the responsibility of allocating the buffer properly falls on the caller. +*/ + +struct OpaquePointer +{ + private void* pointer; + @disable this(void*); +} + __EOF__ /** From 5cc9d8b1a8cdcc6fd9af6a1e2396412569d62e8e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 4 Jun 2017 10:27:45 -0400 Subject: [PATCH 219/262] Eliminate failing assert on 32 bit platforms --- std/experimental/allocator/common.d | 1 - 1 file changed, 1 deletion(-) diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index d2c5bb1a00d..7663adf291a 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -207,7 +207,6 @@ nothrow pure assert(roundStartToMultipleOf(p, 16) is null); p = new ulong[10]; assert(roundStartToMultipleOf(p, 16) is p); - assert(roundStartToMultipleOf(p, 1 << 31) is null); } /** From dfe45f3d0e1185dd9b6c111ae61659d4eb16605e Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 6 Jun 2017 21:05:15 +0300 Subject: [PATCH 220/262] Remove free_list faulty dtor --- .../allocator/building_blocks/free_list.d | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 337b4cacff4..57e5991d410 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -588,17 +588,6 @@ struct ContiguousFreeList(ParentAllocator, this.parent = SParent(parent); } - /** - If $(D ParentAllocator) is not $(D NullAllocator) and defines $(D - deallocate), the destructor is defined to deallocate the block held. - */ - static if (!is(ParentAllocator == NullAllocator) - && hasMember!(ParentAllocator, "deallocate")) - ~this() - { - parent.deallocate(support); - } - /** If $(D n) is eligible for freelisting, returns $(D max). Otherwise, returns $(D parent.goodAllocSize(n)). From 71ca4c5b1428e6b4a9b2d89586cd8851c48619a6 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Sat, 4 Feb 2017 01:17:15 +0200 Subject: [PATCH 221/262] Partial Fix For Issue 16615 - convert os pid to std.process Pid --- std/process.d | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/std/process.d b/std/process.d index e0182cb40be..34aa40d75a2 100644 --- a/std/process.d +++ b/std/process.d @@ -1266,6 +1266,62 @@ final class Pid return _processID; } + version (Windows) + { + /** + A simple factory that returns a $(LREF Pid) handle when provided with a + global `pid`. This handle can be used to kill processes that haven't been + spawned by this process. + Note: this does not check whether the provided `pid` is valid and + corresponds to a running process within the operating system. + + Params: + pid = a process id + desiredAccess = (Windows specific, optional) the desired access to the + process object + + Returns: an instance to a `Pid` that corresponds to the pid within the + operating system. + + Throws: $(LREF ProcessException) if opening the process failed; this is + Windows specific. + */ + static Pid fromSystemPid(int pid, DWORD desiredAccess = PROCESS_ALL_ACCESS) + { + import core.sys.windows.winbase : OpenProcess; + import std.conv : text; + HANDLE _handle = OpenProcess(desiredAccess, false, pid); + if (_handle is null) + { + throw new ProcessException(text("Failed to open process with pid: ", pid)); + } + return new Pid(pid, _handle); + } + } + else version (Posix) + { + /** + A simple factory that returns a $(LREF Pid) handle when provided with a + global `pid`. This handle can be used to kill processes that haven't been + spawned by this process. + Note: this does not check whether the provided `pid` is valid and + corresponds to a running process within the operating system. + + Params: + pid = a process id + + Returns: an instance to a `Pid` that corresponds to the pid within the + operating system. + + Throws: $(LREF ProcessException) if opening the process failed; this is + Windows specific. + */ + static Pid fromSystemPid(int pid) + { + return new Pid(pid); + } + } + private: /* Pid.performWait() does the dirty work for wait() and nonBlockingWait(). @@ -1443,6 +1499,33 @@ int wait(Pid pid) @safe else version (Posix) assert(pid.osHandle < 0); } +@system unittest // Pid.fromSystemPid() +{ + version (Windows) TestScript prog = "do { timeout 1 } while (1)"; + else version (Posix) TestScript prog = "while true; do sleep 1; done"; + auto pid = spawnProcess([prog.path, "10"]); + + assert(pid.processID > 0); + auto fromSystemPid = Pid.fromSystemPid(pid.processID); + + assert(fromSystemPid.processID == pid.processID); + version (Windows) assert(fromSystemPid.osHandle != INVALID_HANDLE_VALUE); + else version (Posix) assert(fromSystemPid.osHandle == fromSystemPid.processID); + + // Send SIGKILL + kill(fromSystemPid, 9); + version (Windows) assert(wait(pid) == 9); + else version (Posix) assert(wait(pid) == -9); // Negative on Posix + + version (Windows) + { + import std.exception : assertThrown; + // If the specified process is the System Process (0x00000000), the + // function fails (text from the OpenProcess function API description) + assertThrown!ProcessException(Pid.fromSystemPid(0)); + } +} + /** A non-blocking version of $(LREF wait). From 1e7f7a1ba2b11ee9d3c07671c564136f4299fb8a Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Thu, 8 Jun 2017 09:45:31 +0200 Subject: [PATCH 222/262] Revert "Use OpaquePointer as a safe support for region based allocators" --- changelog/std-experimental-allocator.dd | 34 ----- .../building_blocks/affix_allocator.d | 4 +- .../building_blocks/bitmapped_block.d | 140 ++++++++---------- .../allocator/building_blocks/free_list.d | 86 +++-------- .../building_blocks/kernighan_ritchie.d | 126 +++++----------- .../allocator/building_blocks/region.d | 55 ++----- std/experimental/allocator/common.d | 12 +- std/experimental/allocator/package.d | 25 +--- 8 files changed, 147 insertions(+), 335 deletions(-) delete mode 100644 changelog/std-experimental-allocator.dd diff --git a/changelog/std-experimental-allocator.dd b/changelog/std-experimental-allocator.dd deleted file mode 100644 index b7aa696b8d1..00000000000 --- a/changelog/std-experimental-allocator.dd +++ /dev/null @@ -1,34 +0,0 @@ -Add `OpaquePointer` to the `std.experimental.allocator` package - -`OpaquePointer` should be used to provide a memory buffer to allocators that -manage a buffer, such as $(REF Region,std,experimental,allocator,building_blocks,region). -Using `OpaquePointer` will ensure that the garbage collector will mark the -memory for scanning. - -The following allocators have a `safe` ctor when provided with an -$(REF OpaquePointer,std,experimental,allocator) memory buffer: - -$(UL - $(LI $(REF BitmappedBlock,std,experimental,allocator,building_blocks,bitmapped_block)) - $(LI $(REF FreeList,std,experimental,allocator,building_blocks,free_list)) - $(LI $(REF KRRegion,std,experimental,allocator,building_blocks,kernighan_ritchie)) - $(LI $(REF Region,std,experimental,allocator,building_blocks,region)) -) - -Note: the responsibility of allocating the buffer properly falls on the caller. - -------- -import std.experimental.allocator : OpaquePointer; -import std.experimental.allocator.building_blocks.region : Region; - -void main() -{ - // Create a 64 KB region - auto reg = Region!()(new OpaquePointer[1024 * 64 / OpaquePointer.sizeof]); - const b = reg.allocate(101); - assert(b.length == 101); - assert(reg.deallocateAll()); - auto c = reg.allocateAll(); - assert(c.length == 1024 * 64); -} -------- diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index 4c22e5964ed..761840dc54a 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -395,11 +395,9 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; import std.experimental.allocator.common : testAllocator; - import std.experimental.allocator : OpaquePointer; testAllocator!({ auto a = AffixAllocator!(BitmappedBlock!128, ulong, ulong) - (BitmappedBlock!128( - new OpaquePointer[128 * 4096 / OpaquePointer.sizeof])); + (BitmappedBlock!128(new ubyte[128 * 4096])); return a; }); } diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index 331c1180069..a0ded14ab68 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -49,13 +49,12 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment import std.traits : hasMember; import std.conv : text; import std.typecons : Ternary; - import std.experimental.allocator : OpaquePointer; @system unittest { import std.experimental.allocator.mallocator : AlignedMallocator; import std.algorithm.comparison : max; - auto m = cast(OpaquePointer[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, max(theAlignment, cast(uint) size_t.sizeof))); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlock(m)); @@ -143,24 +142,18 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment in bytes. $(UL - $(LI If `ParentAllocator` is `NullAllocator`, only the constructor taking - `data` is defined and the user is responsible for freeing `data` if desired.) - $(LI Otherwise, both constructors are defined. The `data`-based constructor - assumes memory has been allocated with the parent allocator. The underlying - `data` must be of $(LREF OpaquePointer) type for a safe allocation of objects - that may contain pointers, and thus for a safe allocator. - The `capacity`-based constructor uses `ParentAllocator` to allocate + $(LI If $(D ParentAllocator) is $(D NullAllocator), only the constructor + taking $(D data) is defined and the user is responsible for freeing $(D + data) if desired.) + $(LI Otherwise, both constructors are defined. The $(D data)-based + constructor assumes memory has been allocated with the parent allocator. + The $(D capacity)-based constructor uses $(D ParentAllocator) to allocate an appropriate contiguous hunk of memory. Regardless of the constructor - used, the destructor releases the memory by using `ParentAllocator.deallocate`.) + used, the destructor releases the memory by using $(D + ParentAllocator.deallocate).) ) */ - @trusted this(OpaquePointer[] data) - { - this(cast(ubyte[]) data); - } - - /// Ditto - @system this(ubyte[] data) + this(ubyte[] data) { immutable a = data.ptr.effectiveAlignment; assert(a >= size_t.alignof || !data.ptr, @@ -176,7 +169,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment // blocks found. for (; _blocks; --_blocks) { - immutable controlWords = _blocks.divideRoundUp(ulong.sizeof * 8); + immutable controlWords = _blocks.divideRoundUp(64); auto payload = data[controlWords * 8 .. $].roundStartToMultipleOf( alignment); if (payload.length < _blocks * blockSize) @@ -195,9 +188,8 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment static if (!is(ParentAllocator == NullAllocator)) this(size_t capacity) { - size_t toAllocate = totalAllocation(capacity) - .roundUpToAlignment(OpaquePointer.alignof); - auto data = cast(OpaquePointer[])(parent.allocate(toAllocate)); + size_t toAllocate = totalAllocation(capacity); + auto data = cast(ubyte[])(parent.allocate(toAllocate)); this(data); assert(_blocks * blockSize >= capacity); } @@ -702,10 +694,9 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment { // Create a block allocator on top of a 10KB stack region. import std.experimental.allocator.building_blocks.region : InSituRegion; - import std.experimental.allocator : OpaquePointer; import std.traits : hasMember; InSituRegion!(10_240, 64) r; - auto a = BitmappedBlock!(64, 64)(cast(OpaquePointer[])(r.allocateAll())); + auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll())); static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll")); const b = a.allocate(100); assert(b.length == 100); @@ -719,15 +710,14 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment @system unittest { - static void testAllocateAll(size_t bs, T)(uint blocks, uint blocksAtATime) + static void testAllocateAll(size_t bs)(uint blocks, uint blocksAtATime) { import std.algorithm.comparison : min; assert(bs); import std.experimental.allocator.gc_allocator : GCAllocator; - - auto nb = ((blocks * bs * 8 + blocks) / 8).roundUpToAlignment(T.alignof); auto a = BitmappedBlock!(bs, min(bs, platformAlignment))( - cast(T[])(GCAllocator.instance.allocate(nb)) // this many bytes + cast(ubyte[])(GCAllocator.instance.allocate((blocks * bs * 8 + + blocks) / 8)) ); import std.conv : text; assert(blocks >= a._blocks, text(blocks, " < ", a._blocks)); @@ -811,32 +801,26 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment } } - import std.experimental.allocator : OpaquePointer; - import std.meta : AliasSeq; + testAllocateAll!(1)(0, 1); + testAllocateAll!(1)(8, 1); + testAllocateAll!(4096)(128, 1); - foreach(T; AliasSeq!(ubyte, OpaquePointer)) - { - testAllocateAll!(1, T)(0, 1); - testAllocateAll!(1, T)(8, 1); - testAllocateAll!(4096, T)(128, 1); - - testAllocateAll!(1, T)(0, 2); - testAllocateAll!(1, T)(128, 2); - testAllocateAll!(4096, T)(128, 2); - - testAllocateAll!(1, T)(0, 4); - testAllocateAll!(1, T)(128, 4); - testAllocateAll!(4096, T)(128, 4); - - testAllocateAll!(1, T)(0, 3); - testAllocateAll!(1, T)(24, 3); - testAllocateAll!(3008, T)(100, 1); - testAllocateAll!(3008, T)(100, 3); - - testAllocateAll!(1, T)(0, 128); - testAllocateAll!(1, T)(128 * 1, 128); - testAllocateAll!(128 * 20, T)(13 * 128, 128); - } + testAllocateAll!(1)(0, 2); + testAllocateAll!(1)(128, 2); + testAllocateAll!(4096)(128, 2); + + testAllocateAll!(1)(0, 4); + testAllocateAll!(1)(128, 4); + testAllocateAll!(4096)(128, 4); + + testAllocateAll!(1)(0, 3); + testAllocateAll!(1)(24, 3); + testAllocateAll!(3008)(100, 1); + testAllocateAll!(3008)(100, 3); + + testAllocateAll!(1)(0, 128); + testAllocateAll!(1)(128 * 1, 128); + testAllocateAll!(128 * 20)(13 * 128, 128); } // Test totalAllocation @@ -877,11 +861,10 @@ struct BitmappedBlockWithInternalPointers( { import std.conv : text; import std.typecons : Ternary; - import std.experimental.allocator : OpaquePointer; @system unittest { import std.experimental.allocator.mallocator : AlignedMallocator; - auto m = cast(OpaquePointer[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, + auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, theAlignment)); scope(exit) AlignedMallocator.instance.deallocate(m); testAllocator!(() => BitmappedBlockWithInternalPointers(m)); @@ -896,8 +879,7 @@ struct BitmappedBlockWithInternalPointers( Constructors accepting desired capacity or a preallocated buffer, similar in semantics to those of $(D BitmappedBlock). */ - this(T)(T[] data) - if (is (T == ubyte) || is (T == OpaquePointer)) + this(ubyte[] data) { _heap = BitmappedBlock!(theBlockSize, theAlignment, ParentAllocator)(data); } @@ -1082,10 +1064,8 @@ struct BitmappedBlockWithInternalPointers( @system unittest { import std.typecons : Ternary; - import std.experimental.allocator : OpaquePointer; - auto h = BitmappedBlockWithInternalPointers!(4096)(new OpaquePointer[4096 * - 1024 / OpaquePointer.sizeof]); + auto h = BitmappedBlockWithInternalPointers!(4096)(new ubyte[4096 * 1024]); auto b = h.allocate(123); assert(b.length == 123); @@ -1115,7 +1095,7 @@ struct BitmappedBlockWithInternalPointers( Returns the number of most significant ones before a zero can be found in $(D x). If $(D x) contains no zeros (i.e. is equal to $(D ulong.max)), returns 64. */ -private @safe uint leadingOnes(ulong x) +private uint leadingOnes(ulong x) { uint result = 0; while (cast(long) x < 0) @@ -1126,7 +1106,7 @@ private @safe uint leadingOnes(ulong x) return result; } -@safe unittest +@system unittest { assert(leadingOnes(0) == 0); assert(leadingOnes(~0UL) == 64); @@ -1140,7 +1120,7 @@ private @safe uint leadingOnes(ulong x) /** Finds a run of contiguous ones in $(D x) of length at least $(D n). */ -private @safe uint findContigOnes(ulong x, uint n) +private uint findContigOnes(ulong x, uint n) { while (n > 1) { @@ -1151,7 +1131,7 @@ private @safe uint findContigOnes(ulong x, uint n) return leadingOnes(~x); } -@safe unittest +@system unittest { assert(findContigOnes(0x0000_0000_0000_0300, 2) == 54); @@ -1168,14 +1148,14 @@ private @safe uint findContigOnes(ulong x, uint n) /* Unconditionally sets the bits from lsb through msb in w to zero. */ -private @safe void setBits(ref ulong w, uint lsb, uint msb) +private void setBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); w |= mask; } -@safe unittest +@system unittest { ulong w; w = 0; setBits(w, 0, 63); assert(w == ulong.max); @@ -1187,7 +1167,7 @@ private @safe void setBits(ref ulong w, uint lsb, uint msb) /* Are bits from lsb through msb in w zero? If so, make then 1 and return the resulting w. Otherwise, just return 0. */ -private @safe bool setBitsIfZero(ref ulong w, uint lsb, uint msb) +private bool setBitsIfZero(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); @@ -1197,7 +1177,7 @@ private @safe bool setBitsIfZero(ref ulong w, uint lsb, uint msb) } // Assigns bits in w from lsb through msb to zero. -private @safe void resetBits(ref ulong w, uint lsb, uint msb) +private void resetBits(ref ulong w, uint lsb, uint msb) { assert(lsb <= msb && msb < 64); const mask = (ulong.max << lsb) & (ulong.max >> (63 - msb)); @@ -1211,13 +1191,13 @@ private struct BitVector { ulong[] _rep; - @safe auto rep() { return _rep; } + auto rep() { return _rep; } - @safe this(ulong[] data) { _rep = data; } + this(ulong[] data) { _rep = data; } - @safe void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } + void opSliceAssign(bool b) { _rep[] = b ? ulong.max : 0; } - @safe void opSliceAssign(bool b, ulong x, ulong y) + void opSliceAssign(bool b, ulong x, ulong y) { assert(x <= y && y <= _rep.length * 64); if (x == y) return; @@ -1248,14 +1228,14 @@ private struct BitVector } } - @safe bool opIndex(ulong x) + bool opIndex(ulong x) { assert(x < length); return (_rep[cast(size_t) (x / 64)] & (0x8000_0000_0000_0000UL >> (x % 64))) != 0; } - @safe void opIndexAssign(bool b, ulong x) + void opIndexAssign(bool b, ulong x) { assert(x / 64 <= size_t.max); immutable i = cast(size_t) (x / 64); @@ -1264,7 +1244,7 @@ private struct BitVector else _rep[i] &= ~j; } - @safe ulong length() const + ulong length() const { return _rep.length * 64; } @@ -1272,7 +1252,7 @@ private struct BitVector /* Returns the index of the first 1 to the right of i (including i itself), or length if not found. */ - @safe ulong find1(ulong i) + ulong find1(ulong i) { assert(i < length); assert(i / 64 <= size_t.max); @@ -1299,7 +1279,7 @@ private struct BitVector /* Returns the index of the first 1 to the left of i (including i itself), or ulong.max if not found. */ - @safe ulong find1Backward(ulong i) + ulong find1Backward(ulong i) { assert(i < length); auto w = cast(size_t) (i / 64); @@ -1324,20 +1304,20 @@ private struct BitVector } /// Are all bits zero? - @safe bool allAre0() const + bool allAre0() const { foreach (w; _rep) if (w) return false; return true; } /// Are all bits one? - @safe bool allAre1() const + bool allAre1() const { foreach (w; _rep) if (w != ulong.max) return false; return true; } - @safe ulong findZeros(immutable size_t howMany, ulong start) + ulong findZeros(immutable size_t howMany, ulong start) { assert(start < length); assert(howMany > 64); @@ -1373,7 +1353,7 @@ private struct BitVector } } -@safe unittest +@system unittest { auto v = BitVector(new ulong[10]); assert(v.length == 640); diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 57e5991d410..136e9b44db4 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -430,7 +430,6 @@ struct ContiguousFreeList(ParentAllocator, : NullAllocator; import std.experimental.allocator.building_blocks.stats_collector : StatsCollector, Options; - import std.experimental.allocator : OpaquePointer; import std.traits : hasMember; import std.typecons : Ternary; @@ -478,20 +477,13 @@ struct ContiguousFreeList(ParentAllocator, } } - private @trusted void initialize(OpaquePointer[] buffer, size_t itemSize = fl.max) - { - initialize(cast(ubyte[]) buffer, itemSize); - } - /** Constructors setting up the memory structured as a free list. Params: - buffer = Buffer to structure as a free list. If `ParentAllocator` is not - `NullAllocator`, the buffer is assumed to be allocated by `parent` and will - be freed in the destructor. The buffer must be of $(LREF OpaquePointer) - type for a safe allocation of objects that may contain pointers, and thus - for a safe allocator. + buffer = Buffer to structure as a free list. If $(D ParentAllocator) is not + $(D NullAllocator), the buffer is assumed to be allocated by $(D parent) + and will be freed in the destructor. parent = Parent allocator. For construction from stateless allocators, use their `instance` static member. bytes = Bytes (not items) to be allocated for the free list. Memory will be @@ -501,20 +493,18 @@ struct ContiguousFreeList(ParentAllocator, == unbounded). min = Minimum size eligible for freelisting. Construction with this parameter is defined only if $(D minSize == chooseAtRuntime). If this - condition is met and no `min` parameter is present, `min` is initialized - with `max`. + condition is met and no $(D min) parameter is present, $(D min) is + initialized with $(D max). */ static if (!stateSize!ParentAllocator) - this(T)(T[] buffer) - if(is(T == ubyte) || is(T == OpaquePointer)) + this(ubyte[] buffer) { initialize(buffer); } /// ditto static if (stateSize!ParentAllocator) - this(T)(ParentAllocator parent, T[] buffer) - if(is(T == ubyte) || is(T == OpaquePointer)) + this(ParentAllocator parent, ubyte[] buffer) { initialize(buffer); this.parent = SParent(parent); @@ -524,16 +514,14 @@ struct ContiguousFreeList(ParentAllocator, static if (!stateSize!ParentAllocator) this(size_t bytes) { - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(ParentAllocator.instance.allocate(nb))); + initialize(cast(ubyte[])(ParentAllocator.instance.allocate(bytes))); } /// ditto static if (stateSize!ParentAllocator) this(ParentAllocator parent, size_t bytes) { - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(parent.allocate(nb))); + initialize(cast(ubyte[])(parent.allocate(bytes))); this.parent = SParent(parent); } @@ -544,8 +532,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); } /// ditto @@ -555,8 +542,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; static if (minSize == chooseAtRuntime) fl.min = max; - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); this.parent = SParent(parent); } @@ -568,8 +554,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } @@ -582,8 +567,7 @@ struct ContiguousFreeList(ParentAllocator, { static if (maxSize == chooseAtRuntime) fl.max = max; fl.min = min; - auto nb = bytes.roundUpToAlignment(OpaquePointer.alignof); - initialize(cast(OpaquePointer[])(parent.allocate(nb)), max); + initialize(cast(ubyte[])(parent.allocate(bytes)), max); static if (stateSize!ParentAllocator) this.parent = SParent(parent); } @@ -702,14 +686,20 @@ struct ContiguousFreeList(ParentAllocator, @system unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.common : unbounded; + import std.experimental.allocator.building_blocks.null_allocator + : NullAllocator; import std.typecons : Ternary; + alias A = ContiguousFreeList!(NullAllocator, 0, 64); + auto a = A(new ubyte[1024]); + + assert(a.empty == Ternary.yes); - auto a = ContiguousFreeList!(GCAllocator, 0, unbounded)(4096, 8); + assert(a.goodAllocSize(15) == 64); + assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65)); auto b = a.allocate(100); - assert(b.length == 100); + assert(a.empty == Ternary.yes); + assert(b.length == 0); a.deallocate(b); b = a.allocate(64); assert(a.empty == Ternary.no); @@ -719,36 +709,6 @@ struct ContiguousFreeList(ParentAllocator, a.deallocate(b); } -@system unittest -{ - import std.experimental.allocator.building_blocks.null_allocator - : NullAllocator; - import std.experimental.allocator : OpaquePointer; - import std.typecons : Ternary; - import std.meta : AliasSeq; - - alias A = ContiguousFreeList!(NullAllocator, 0, 64); - foreach(T; AliasSeq!(ubyte, OpaquePointer)) - { - auto a = A(new T[1024]); - assert(a.empty == Ternary.yes); - - assert(a.goodAllocSize(15) == 64); - assert(a.goodAllocSize(65) == NullAllocator.instance.goodAllocSize(65)); - - auto b = a.allocate(100); - assert(a.empty == Ternary.yes); - assert(b.length == 0); - a.deallocate(b); - b = a.allocate(64); - assert(a.empty == Ternary.no); - assert(b.length == 64); - assert(a.owns(b) == Ternary.yes); - assert(a.owns(null) == Ternary.no); - a.deallocate(b); - } -} - @system unittest { import std.experimental.allocator.building_blocks.region : Region; diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 225da95ca16..f331fba00b2 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -95,9 +95,7 @@ information is available in client code at deallocation time.) */ struct KRRegion(ParentAllocator = NullAllocator) { - import std.experimental.allocator.common : stateSize, alignedAt, - roundUpToAlignment; - import std.experimental.allocator : OpaquePointer; + import std.experimental.allocator.common : stateSize, alignedAt; import std.traits : hasMember; import std.typecons : Ternary; @@ -304,18 +302,16 @@ struct KRRegion(ParentAllocator = NullAllocator) } /** - Create a `KRRegion`. If `ParentAllocator` is not `NullAllocator`, `KRRegion`'s - destructor will call $(D parent.deallocate). + Create a $(D KRRegion). If $(D ParentAllocator) is not $(D NullAllocator), + $(D KRRegion)'s destructor will call $(D parent.deallocate). Params: b = Block of memory to serve as support for the allocator. Memory must be - larger than two words and word-aligned. The memory block must be of - $(LREF OpaquePointer) type for a safe allocation of objects that may - contain pointers, and thus for a safe allocator. - n = Capacity desired. This constructor is defined only if `ParentAllocator` - is not `NullAllocator`. + larger than two words and word-aligned. + n = Capacity desired. This constructor is defined only if $(D + ParentAllocator) is not $(D NullAllocator). */ - @system this(ubyte[] b) + this(ubyte[] b) { if (b.length < Node.sizeof) { @@ -337,19 +333,12 @@ struct KRRegion(ParentAllocator = NullAllocator) b.ptr, b.length); } - /// ditto - @trusted this(OpaquePointer[] b) - { - this(cast(ubyte[]) b); - } - /// Ditto static if (!is(ParentAllocator == NullAllocator)) this(size_t n) { assert(n > Node.sizeof); - auto nb = n.roundUpToAlignment(OpaquePointer.alignof); - this(cast(OpaquePointer[])(parent.allocate(nb))); + this(cast(ubyte[])(parent.allocate(n))); } /// Ditto @@ -629,18 +618,13 @@ fronting the GC allocator. import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.fallback_allocator : fallbackAllocator; - import std.experimental.allocator : OpaquePointer; import std.typecons : Ternary; - import std.meta : AliasSeq; // KRRegion fronting a general-purpose allocator - foreach(T; AliasSeq!(ubyte, OpaquePointer)) - { - T[1024 * 128] buf; - auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); - auto b = alloc.allocate(100); - assert(b.length == 100); - assert(alloc.primary.owns(b) == Ternary.yes); - } + ubyte[1024 * 128] buf; + auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance); + auto b = alloc.allocate(100); + assert(b.length == 100); + assert(alloc.primary.owns(b) == Ternary.yes); } /** @@ -766,75 +750,47 @@ it actually returns memory to the operating system when possible. @system unittest { import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator : OpaquePointer; import std.typecons : Ternary; + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); + const store = alloc.allocate(KRRegion!().sizeof); + auto p = cast(KRRegion!()* ) store.ptr; import std.conv : emplace; import std.algorithm.mutation : move; import core.stdc.string : memcpy; - import std.meta : AliasSeq; - - foreach(T; AliasSeq!(ubyte, OpaquePointer)) - { - auto alloc = KRRegion!()( - cast(T[])(GCAllocator.instance.allocate(1024 * 1024))); - const store = alloc.allocate(KRRegion!().sizeof); - auto p = cast(KRRegion!()* ) store.ptr; - memcpy(p, &alloc, alloc.sizeof); - emplace(&alloc); + memcpy(p, &alloc, alloc.sizeof); + emplace(&alloc); - void[][100] array; - foreach (i; 0 .. array.length) - { - auto length = 100 * i + 1; - array[i] = p.allocate(length); - assert(array[i].length == length, text(array[i].length)); - assert(p.owns(array[i]) == Ternary.yes); - } - import std.random : randomShuffle; - randomShuffle(array[]); - foreach (i; 0 .. array.length) - { - assert(p.owns(array[i]) == Ternary.yes); - p.deallocate(array[i]); - } - auto b = p.allocateAll(); - assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); + void[][100] array; + foreach (i; 0 .. array.length) + { + auto length = 100 * i + 1; + array[i] = p.allocate(length); + assert(array[i].length == length, text(array[i].length)); + assert(p.owns(array[i]) == Ternary.yes); } -} - -@system unittest -{ - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator : OpaquePointer; - import std.meta : AliasSeq; - - foreach(T; AliasSeq!(ubyte, OpaquePointer)) + import std.random : randomShuffle; + randomShuffle(array[]); + foreach (i; 0 .. array.length) { - auto alloc = KRRegion!()( - cast(T[])(GCAllocator.instance.allocate(1024 * 1024))); - auto p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); - alloc.deallocateAll(); - p = alloc.allocateAll(); - assert(p.length == 1024 * 1024); + assert(p.owns(array[i]) == Ternary.yes); + p.deallocate(array[i]); } + auto b = p.allocateAll(); + assert(b.length == 1024 * 1024 - KRRegion!().sizeof, text(b.length)); } @system unittest { - import std.experimental.allocator : OpaquePointer; - import std.meta : AliasSeq; - - foreach(T; AliasSeq!(ubyte, OpaquePointer)) - { - T[] emptyBuf; - auto alloc = KRRegion!()(emptyBuf); - auto p = alloc.allocateAll(); - assert(p.length == 0 && p is null); - assert(alloc.deallocateAll()); - assert(alloc.allocate(1) is null); - } + import std.experimental.allocator.gc_allocator : GCAllocator; + auto alloc = KRRegion!()( + cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); + auto p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); + alloc.deallocateAll(); + p = alloc.allocateAll(); + assert(p.length == 1024 * 1024); } @system unittest diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index d1119ed98db..3809b9dd7e4 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -35,7 +35,6 @@ struct Region(ParentAllocator = NullAllocator, import std.traits : hasMember; import std.typecons : Ternary; - import std.experimental.allocator : OpaquePointer; // state /** @@ -54,23 +53,21 @@ struct Region(ParentAllocator = NullAllocator, private void* _current, _begin, _end; /** - Constructs a region backed by a user-provided store. Assumes `store` is - aligned at `minAlign`. Also assumes the memory was allocated with - `ParentAllocator` (if different from `NullAllocator`). + Constructs a region backed by a user-provided store. Assumes $(D store) is + aligned at $(D minAlign). Also assumes the memory was allocated with $(D + ParentAllocator) (if different from $(D NullAllocator)). Params: - store = User-provided store backing up the region. `store` must be aligned - at `minAlign` (enforced with `assert`). If `ParentAllocator` is different - from `NullAllocator`, memory is assumed to have been allocated with - `ParentAllocator`. - The underlying `store` must be of $(LREF OpaquePointer) type for a safe - allocation of objects that may contain pointers, and thus for a safe allocator. - n = Bytes to allocate using `ParentAllocator`. This constructor is only - defined if `ParentAllocator` is different from `NullAllocator`. If - $(D parent.allocate(n)) returns `null`, the region will be initialized + store = User-provided store backing up the region. $(D store) must be + aligned at $(D minAlign) (enforced with $(D assert)). If $(D + ParentAllocator) is different from $(D NullAllocator), memory is assumed to + have been allocated with $(D ParentAllocator). + n = Bytes to allocate using $(D ParentAllocator). This constructor is only + defined If $(D ParentAllocator) is different from $(D NullAllocator). If + $(D parent.allocate(n)) returns $(D null), the region will be initialized as empty (correctly initialized but unable to allocate). */ - @system this(ubyte[] store) + this(ubyte[] store) { store = cast(ubyte[])(store.roundUpToAlignment(alignment)); store = store[0 .. $.roundDownToAlignment(alignment)]; @@ -84,19 +81,11 @@ struct Region(ParentAllocator = NullAllocator, _current = store.ptr; } - /// ditto - @trusted this(OpaquePointer[] store) - { - this(cast(ubyte[]) store); - } - /// Ditto static if (!is(ParentAllocator == NullAllocator)) this(size_t n) { - auto nb = n.roundUpToAlignment(alignment) - .roundUpToAlignment(OpaquePointer.alignof); - this(cast(OpaquePointer[])(parent.allocate(nb))); + this(cast(ubyte[])(parent.allocate(n.roundUpToAlignment(alignment)))); } /* @@ -365,23 +354,7 @@ struct Region(ParentAllocator = NullAllocator, Yes.growDownwards)(1024 * 64); const b = reg.allocate(101); assert(b.length == 101); -} - -@system unittest -{ - import std.experimental.allocator : OpaquePointer; - import std.meta : AliasSeq; - - foreach (T; AliasSeq!(ubyte, OpaquePointer)) - { - // Create a 64 KB region - auto reg = Region!()(new T[1024 * 64 / T.sizeof]); - const b = reg.allocate(101); - assert(b.length == 101); - assert(reg.deallocateAll()); - auto c = reg.allocateAll(); - assert(c.length == 1024 * 64); - } + // Destructor will free the memory } /** @@ -570,8 +543,6 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; - import std.experimental.allocator : OpaquePointer; - FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; const a2 = r2.allocate(102); assert(a2.length == 102); diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index 7663adf291a..c1d2540b0c8 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -188,20 +188,20 @@ package size_t divideRoundUp(size_t a, size_t b) } /** -Returns `s` rounded up to a multiple of `base` or `null` if the resulted round -up is out of the bounds of `s`. +Returns `s` rounded up to a multiple of `base`. */ -@nogc nothrow pure @trusted +@nogc nothrow pure package void[] roundStartToMultipleOf(void[] s, uint base) { assert(base); - auto p = cast(void*) roundUpToMultipleOf(cast(size_t) s.ptr, base); + auto p = cast(void*) roundUpToMultipleOf( + cast(size_t) s.ptr, base); auto end = s.ptr + s.length; - return cast(size_t) end > cast(size_t) p ? p[0 .. end - p] : null; + return p[0 .. end - p]; } nothrow pure -@safe unittest +@system unittest { void[] p; assert(roundStartToMultipleOf(p, 16) is null); diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 7b243f4a927..d4724830490 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -249,11 +249,9 @@ public import std.experimental.allocator.common, 2048, Bucketizer!(FList, 1025, 2048, 256), 3584, Bucketizer!(FList, 2049, 3584, 512), 4072 * 1024, AllocatorList!( - (n) => BitmappedBlock!(4096)( - cast(OpaquePointer[])(GCAllocator.instance.allocate( - max(n, 4072 * 1024 / OpaquePointer.sizeof) - .roundUpToAlignment(OpaquePointer.alignof) - )))), + (n) => BitmappedBlock!(4096)( + cast(ubyte[])(GCAllocator.instance.allocate( + max(n, 4072 * 1024))))), GCAllocator ); A tuMalloc; @@ -264,9 +262,6 @@ public import std.experimental.allocator.common, assert(tuMalloc.expand(c, 14)); tuMalloc.deallocate(b); tuMalloc.deallocate(c); - auto d = tuMalloc.allocate(4072 * 100); - assert(d.length == 4072 * 100); - tuMalloc.deallocate(d); } import std.range.primitives; @@ -2520,20 +2515,6 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect) theAllocator.dispose(arr); } -/** -`OpaquePointer` should be used to provide a memory buffer to allocators that -manage a buffer, such as $(LREF Region). Using `OpaquePointer` will ensure that -the garbage collector will mark the memory for scanning. - -Note: the responsibility of allocating the buffer properly falls on the caller. -*/ - -struct OpaquePointer -{ - private void* pointer; - @disable this(void*); -} - __EOF__ /** From c2fbe42a027ef9b3bc6169a8b2ae618afa762cf9 Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Thu, 8 Jun 2017 09:33:25 -0700 Subject: [PATCH 223/262] Fix wrong parens in std.range ddoc. --- std/range/package.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/package.d b/std/range/package.d index 83a93a2bd19..57a126557e1 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -165,7 +165,7 @@ $(BOOKTABLE , $(TD Creates a _range that returns a fixed-size sliding window over the original _range. Unlike chunks, it advances a configurable number of items at a time, - not one chunk at a time). + not one chunk at a time. )) $(TR $(TD $(LREF stride)) $(TD Iterates a _range with stride $(I n). From 442cd24c1515213c872120ed3b939463e24ab00c Mon Sep 17 00:00:00 2001 From: "H. S. Teoh" Date: Thu, 8 Jun 2017 09:37:47 -0700 Subject: [PATCH 224/262] Hide bugfix unittest from ddoc. This particular unittest does not give any additional information to the user, and the bugzilla note only serves to confuse. So it should not be part of the ddoc'd unittest. --- std/meta.d | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std/meta.d b/std/meta.d index 9e7a061f887..6b946e1cb17 100644 --- a/std/meta.d +++ b/std/meta.d @@ -503,7 +503,10 @@ template NoDuplicates(TList...) alias TL = NoDuplicates!(Types); static assert(is(TL == AliasSeq!(int, long, float))); +} +@safe unittest +{ // Bugzilla 14561: huge enums alias LongList = Repeat!(1500, int); static assert(NoDuplicates!LongList.length == 1); From 3caa34f219cebaf37b75ce7384a3a40dbb87fe1e Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Fri, 9 Jun 2017 14:24:11 +0200 Subject: [PATCH 225/262] fix Issue 17472 - typeof(stdin) is no longer a File --- std/stdio.d | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/std/stdio.d b/std/stdio.d index 0446f1135a4..b08c3883152 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4522,7 +4522,7 @@ Initialize with a message and an error code. } // Undocumented but public because the std* handles are aliasing it. -ref File makeGlobal(alias handle)() +@property ref File makeGlobal(alias handle)() { __gshared File.Impl impl; __gshared File result; @@ -4621,6 +4621,15 @@ alias stderr = makeGlobal!(core.stdc.stdio.stderr); } } +unittest +{ + // Retain backwards compatibility + // https://issues.dlang.org/show_bug.cgi?id=17472 + static assert(is(typeof(stdin) == File)); + static assert(is(typeof(stdout) == File)); + static assert(is(typeof(stderr) == File)); +} + // roll our own appender, but with "safe" arrays private struct ReadlnAppender { From 07602da6334fa250032d302c777b75d17b0e4379 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 9 Jun 2017 15:51:34 +0000 Subject: [PATCH 226/262] Revert "Partial Fix Issue 16615 - convert os pid to std.process Pid" --- std/process.d | 83 --------------------------------------------------- 1 file changed, 83 deletions(-) diff --git a/std/process.d b/std/process.d index 34aa40d75a2..e0182cb40be 100644 --- a/std/process.d +++ b/std/process.d @@ -1266,62 +1266,6 @@ final class Pid return _processID; } - version (Windows) - { - /** - A simple factory that returns a $(LREF Pid) handle when provided with a - global `pid`. This handle can be used to kill processes that haven't been - spawned by this process. - Note: this does not check whether the provided `pid` is valid and - corresponds to a running process within the operating system. - - Params: - pid = a process id - desiredAccess = (Windows specific, optional) the desired access to the - process object - - Returns: an instance to a `Pid` that corresponds to the pid within the - operating system. - - Throws: $(LREF ProcessException) if opening the process failed; this is - Windows specific. - */ - static Pid fromSystemPid(int pid, DWORD desiredAccess = PROCESS_ALL_ACCESS) - { - import core.sys.windows.winbase : OpenProcess; - import std.conv : text; - HANDLE _handle = OpenProcess(desiredAccess, false, pid); - if (_handle is null) - { - throw new ProcessException(text("Failed to open process with pid: ", pid)); - } - return new Pid(pid, _handle); - } - } - else version (Posix) - { - /** - A simple factory that returns a $(LREF Pid) handle when provided with a - global `pid`. This handle can be used to kill processes that haven't been - spawned by this process. - Note: this does not check whether the provided `pid` is valid and - corresponds to a running process within the operating system. - - Params: - pid = a process id - - Returns: an instance to a `Pid` that corresponds to the pid within the - operating system. - - Throws: $(LREF ProcessException) if opening the process failed; this is - Windows specific. - */ - static Pid fromSystemPid(int pid) - { - return new Pid(pid); - } - } - private: /* Pid.performWait() does the dirty work for wait() and nonBlockingWait(). @@ -1499,33 +1443,6 @@ int wait(Pid pid) @safe else version (Posix) assert(pid.osHandle < 0); } -@system unittest // Pid.fromSystemPid() -{ - version (Windows) TestScript prog = "do { timeout 1 } while (1)"; - else version (Posix) TestScript prog = "while true; do sleep 1; done"; - auto pid = spawnProcess([prog.path, "10"]); - - assert(pid.processID > 0); - auto fromSystemPid = Pid.fromSystemPid(pid.processID); - - assert(fromSystemPid.processID == pid.processID); - version (Windows) assert(fromSystemPid.osHandle != INVALID_HANDLE_VALUE); - else version (Posix) assert(fromSystemPid.osHandle == fromSystemPid.processID); - - // Send SIGKILL - kill(fromSystemPid, 9); - version (Windows) assert(wait(pid) == 9); - else version (Posix) assert(wait(pid) == -9); // Negative on Posix - - version (Windows) - { - import std.exception : assertThrown; - // If the specified process is the System Process (0x00000000), the - // function fails (text from the OpenProcess function API description) - assertThrown!ProcessException(Pid.fromSystemPid(0)); - } -} - /** A non-blocking version of $(LREF wait). From 0415721e05f27504627c455f198ff9f6122ee5a4 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Fri, 9 Jun 2017 19:24:38 +0300 Subject: [PATCH 227/262] DList: popFirstOf and popLastOf --- std/container/dlist.d | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/std/container/dlist.d b/std/container/dlist.d index d2e90782794..3609c6ce7e6 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -629,6 +629,54 @@ Complexity: $(BIGOH 1) return remove(r); } +/** +Removes first element of $(D r), wich must be a range obrained originally +from this container. + +Returns: A range spanning the remaining elements in the container that +initially were right after the first element of $(D r). + +Compexity: $(BIGOH 1) + */ + Range popFirstOf(Range r) + { + if (r.empty) + return r; + + assert(_root !is null, "Cannot remove from an un-initialized List"); + assert(r._first, "Remove: Range is empty"); + BaseNode.connect(r._first._prev, r._first._next); + auto after = r._first._next; + if (after is _root) + return Range(null, null); + else + return Range(after, _last); + } + +/** +Removes last element of $(D r), wich must be a range obrained originally +from this container. + +Returns: A range spanning the remaining elements in the container that +initially were right before the last element of $(D r). + +Compexity: $(BIGOH 1) + */ + Range popLastOf(Range r) + { + if (r.empty) + return r; + + assert(_root !is null, "Cannot remove from an un-initialized List"); + assert(r._last, "Remove: Range is empty"); + BaseNode.connect(r._last._prev, r._last._next); + auto before = r._last._prev; + if (before is _root) + return Range(null, null); + else + return Range(_first, before); + } + /** $(D linearRemove) functions as $(D remove), but also accepts ranges that are result the of a $(D take) operation. This is a convenient way to remove a @@ -812,6 +860,20 @@ private: assert(equal(list[],[0,3])); } +@safe unittest +{ + import std.algorithm.comparison : equal; + + auto dl = DList!int([1, 2, 3, 4, 5]); + auto r = dl[]; + r.popFront(); + dl.popFirstOf(r); + assert(equal(dl[], [1, 3, 4, 5])); + r.popBack(); + dl.popLastOf(r); + assert(equal(dl[], [1, 3, 5])); +} + @safe unittest { import std.algorithm.comparison : equal; From 2c79fbf8e5920cd4a84e38a61d14cf354e20cc0d Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Fri, 9 Jun 2017 20:52:20 +0300 Subject: [PATCH 228/262] extended moduletests coverage --- std/container/dlist.d | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/std/container/dlist.d b/std/container/dlist.d index 3609c6ce7e6..155bba02108 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -872,6 +872,19 @@ private: r.popBack(); dl.popLastOf(r); assert(equal(dl[], [1, 3, 5])); + DList!int empty_list; + dl.popFirstOf(empty_list[]); + assert(equal(dl[], [1, 3, 5])); + dl.popLastOf(empty_list[]); + assert(equal(dl[], [1, 3, 5])); + dl = DList!int([0]); + r = dl[]; + dl.popFirstOf(r); + assert(dl.empty); + dl = DList!int([0]); + r = dl[]; + dl.popLastOf(r); + assert(dl.empty); } @safe unittest From 1757660223f48b54002604be38909c08afb70c82 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Thu, 25 May 2017 20:55:11 -0700 Subject: [PATCH 229/262] reimplement std.traits.ParameterStorageClassTuple() --- std/traits.d | 81 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/std/traits.d b/std/traits.d index 5a237eb6ea3..bc56d9fe5e1 100644 --- a/std/traits.d +++ b/std/traits.d @@ -1000,8 +1000,11 @@ template arity(alias func) } /** -Returns a tuple consisting of the storage classes of the parameters of a -function $(D func). +Get tuple, one per function parameter, of the storage classes of the parameters. +Params: + func = function symbol or type of function, delegate, or pointer to function +Returns: + a tuple of ParameterStorageClass bits */ enum ParameterStorageClass : uint { @@ -1021,39 +1024,28 @@ enum ParameterStorageClass : uint template ParameterStorageClassTuple(func...) if (func.length == 1 && isCallable!func) { - alias Func = Unqual!(FunctionTypeOf!func); + alias Func = FunctionTypeOf!func; - /* - * TypeFuncion: - * CallConvention FuncAttrs Arguments ArgClose Type - */ - alias Params = Parameters!Func; - - // chop off CallConvention and FuncAttrs - enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest; - - // demangle Arguments and store parameter storage classes in a tuple - template demangleNextParameter(string margs, size_t i = 0) + static if (is(Func PT == __parameters)) { - static if (i < Params.length) - { - enum demang = demangleParameterStorageClass(margs); - enum skip = mangledName!(Params[i]).length; // for bypassing Type - enum rest = demang.rest; - - alias demangleNextParameter = - TypeTuple!( - demang.value + 0, // workaround: "not evaluatable at ..." - demangleNextParameter!(rest[skip .. $], i + 1) - ); - } - else // went thru all the parameters + template StorageClass(size_t i) { - alias demangleNextParameter = TypeTuple!(); + static if (i < PT.length) + { + alias StorageClass = TypeTuple!( + extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)), + StorageClass!(i + 1)); + } + else + alias StorageClass = TypeTuple!(); } + alias ParameterStorageClassTuple = StorageClass!0; + } + else + { + static assert(0, func[0].stringof ~ " is not a function"); + alias ParameterStorageClassTuple = TypeTuple!(); } - - alias ParameterStorageClassTuple = demangleNextParameter!margs; } /// @@ -1071,6 +1063,35 @@ template ParameterStorageClassTuple(func...) static assert(pstc[2] == STC.none); } +/***************** + * Convert string tuple Attribs to ParameterStorageClass bits + * Params: + * Attribs = string tuple + * Returns: + * ParameterStorageClass bits + */ +ParameterStorageClass extractParameterStorageClassFlags(Attribs...)() +{ + auto result = ParameterStorageClass.none; + foreach (attrib; Attribs) + { + final switch (attrib) with (ParameterStorageClass) + { + case "scope": result |= scope_; break; + case "out": result |= out_; break; + case "ref": result |= ref_; break; + case "lazy": result |= lazy_; break; + case "return": result |= return_; break; + } + } + /* Mimic behavor of original version of ParameterStorageClassTuple() + * to avoid breaking existing code. + */ + if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_)) + result = ParameterStorageClass.return_; + return result; +} + @safe unittest { alias STC = ParameterStorageClass; From afd0135547516389ba685e5da9f1c2090244ea15 Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Fri, 12 May 2017 07:20:39 -0700 Subject: [PATCH 230/262] std.xml: make work with -dip1000 --- std/xml.d | 118 +++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/std/xml.d b/std/xml.d index a193471cf54..1143dfa6d20 100644 --- a/std/xml.d +++ b/std/xml.d @@ -589,11 +589,11 @@ class Document : Element * if (d1 == d2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const doc = toType!(const Document)(o); return prolog == doc.prolog - && (cast() this).Element.opEquals(cast() doc) + && (cast(const) this).Element.opEquals(cast(const) doc) && epilog == doc.epilog; } @@ -609,12 +609,12 @@ class Document : Element * if (d1 < d2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const doc = toType!(const Document)(o); if (prolog != doc.prolog) return prolog < doc.prolog ? -1 : 1; - if (int cmp = (cast() this).Element.opCmp(cast() doc)) + if (int cmp = this.Element.opCmp(doc)) return cmp; if (epilog != doc.epilog) return epilog < doc.epilog ? -1 : 1; @@ -627,7 +627,7 @@ class Document : Element * You should rarely need to call this function. It exists so that * Documents can be used as associative array keys. */ - override size_t toHash() @trusted + override size_t toHash() scope const @trusted { return hash(prolog, hash(epilog, (cast() this).Element.toHash())); } @@ -636,7 +636,7 @@ class Document : Element * Returns the string representation of a Document. (That is, the * complete XML of a document). */ - override string toString() @safe + override string toString() scope const @safe { return prolog ~ super.toString() ~ epilog; } @@ -835,14 +835,14 @@ class Element : Item * if (e1 == e2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const element = toType!(const Element)(o); immutable len = items.length; if (len != element.items.length) return false; foreach (i; 0 .. len) { - if (!items[i].opEquals(cast() element.items[i])) return false; + if (!items[i].opEquals(element.items[i])) return false; } return true; } @@ -859,7 +859,7 @@ class Element : Item * if (e1 < e2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) @safe const { const element = toType!(const Element)(o); for (uint i=0; ; ++i) @@ -867,8 +867,8 @@ class Element : Item if (i == items.length && i == element.items.length) return 0; if (i == items.length) return -1; if (i == element.items.length) return 1; - if (items[i] != element.items[i]) - return items[i].opCmp(cast() element.items[i]); + if (!items[i].opEquals(element.items[i])) + return items[i].opCmp(element.items[i]); } } @@ -878,7 +878,7 @@ class Element : Item * You should rarely need to call this function. It exists so that Elements * can be used as associative array keys. */ - override size_t toHash() const @safe + override size_t toHash() scope const @safe { size_t hash = tag.toHash(); foreach (item;items) hash += item.toHash(); @@ -918,7 +918,7 @@ class Element : Item * indent = (optional) number of spaces by which to indent this * element. Defaults to 2. */ - override string[] pretty(uint indent=2) + override string[] pretty(uint indent=2) scope { import std.algorithm.searching : count; import std.string : rightJustify; @@ -927,7 +927,7 @@ class Element : Item if (items.length == 1) { - Text t = cast(Text)(items[0]); + auto t = cast(const(Text))(items[0]); if (t !is null) { return [tag.toStartString() ~ t.toString() ~ tag.toEndString()]; @@ -956,7 +956,7 @@ class Element : Item * writefln(element.toString()); // writes "
" * -------------- */ - override string toString() @safe + override string toString() scope @safe { if (isEmptyXML) return tag.toEmptyString(); @@ -966,7 +966,7 @@ class Element : Item return buffer; } - override @property @safe pure @nogc nothrow bool isEmptyXML() { return items.length == 0; } + override @property @safe pure @nogc nothrow bool isEmptyXML() const scope { return items.length == 0; } } } @@ -1123,7 +1123,7 @@ class Tag * if (tag1 == tag2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope Object o) { const tag = toType!(const Tag)(o); return @@ -1270,10 +1270,10 @@ class Comment : Item * if (item1 == item2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const item = toType!(const Item)(o); - const t = cast(Comment) item; + const t = cast(const Comment) item; return t !is null && content == t.content; } @@ -1289,10 +1289,10 @@ class Comment : Item * if (item1 < item2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const item = toType!(const Item)(o); - const t = cast(Comment) item; + const t = cast(const Comment) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1303,14 +1303,14 @@ class Comment : Item * You should rarely need to call this function. It exists so that Comments * can be used as associative array keys. */ - override size_t toHash() const nothrow { return hash(content); } + override size_t toHash() scope const nothrow { return hash(content); } /** * Returns a string representation of this comment */ - override string toString() const @safe pure nothrow { return ""; } + override string toString() scope const @safe pure nothrow { return ""; } - override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always } @safe unittest // issue 16241 @@ -1358,10 +1358,10 @@ class CData : Item * if (item1 == item2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const item = toType!(const Item)(o); - const t = cast(CData) item; + const t = cast(const CData) item; return t !is null && content == t.content; } @@ -1377,10 +1377,10 @@ class CData : Item * if (item1 < item2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const item = toType!(const Item)(o); - const t = cast(CData) item; + const t = cast(const CData) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1391,14 +1391,14 @@ class CData : Item * You should rarely need to call this function. It exists so that CDatas * can be used as associative array keys. */ - override size_t toHash() const nothrow { return hash(content); } + override size_t toHash() scope const nothrow { return hash(content); } /** * Returns a string representation of this CData section */ - override string toString() const @safe pure nothrow { return cdata ~ content ~ "]]>"; } + override string toString() scope const @safe pure nothrow { return cdata ~ content ~ "]]>"; } - override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always } /** @@ -1435,10 +1435,10 @@ class Text : Item * if (item1 == item2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const item = toType!(const Item)(o); - const t = cast(Text) item; + const t = cast(const Text) item; return t !is null && content == t.content; } @@ -1454,10 +1454,10 @@ class Text : Item * if (item1 < item2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const item = toType!(const Item)(o); - const t = cast(Text) item; + const t = cast(const Text) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1468,17 +1468,17 @@ class Text : Item * You should rarely need to call this function. It exists so that Texts * can be used as associative array keys. */ - override size_t toHash() const nothrow { return hash(content); } + override size_t toHash() scope const nothrow { return hash(content); } /** * Returns a string representation of this Text section */ - override string toString() const @safe @nogc pure nothrow { return content; } + override string toString() scope const @safe @nogc pure nothrow { return content; } /** * Returns true if the content is the empty string */ - override @property @safe @nogc pure nothrow bool isEmptyXML() const { return content.length == 0; } + override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return content.length == 0; } } /** @@ -1518,10 +1518,10 @@ class XMLInstruction : Item * if (item1 == item2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const item = toType!(const Item)(o); - const t = cast(XMLInstruction) item; + const t = cast(const XMLInstruction) item; return t !is null && content == t.content; } @@ -1537,10 +1537,10 @@ class XMLInstruction : Item * if (item1 < item2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const item = toType!(const Item)(o); - const t = cast(XMLInstruction) item; + const t = cast(const XMLInstruction) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1551,14 +1551,14 @@ class XMLInstruction : Item * You should rarely need to call this function. It exists so that * XmlInstructions can be used as associative array keys. */ - override size_t toHash() const nothrow { return hash(content); } + override size_t toHash() scope const nothrow { return hash(content); } /** * Returns a string representation of this XmlInstruction */ - override string toString() const @safe pure nothrow { return ""; } + override string toString() scope const @safe pure nothrow { return ""; } - override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always } /** @@ -1598,10 +1598,10 @@ class ProcessingInstruction : Item * if (item1 == item2) { } * -------------- */ - override bool opEquals(Object o) + override bool opEquals(scope const Object o) const { const item = toType!(const Item)(o); - const t = cast(ProcessingInstruction) item; + const t = cast(const ProcessingInstruction) item; return t !is null && content == t.content; } @@ -1617,10 +1617,10 @@ class ProcessingInstruction : Item * if (item1 < item2) { } * -------------- */ - override int opCmp(Object o) + override int opCmp(scope const Object o) scope const { const item = toType!(const Item)(o); - const t = cast(ProcessingInstruction) item; + const t = cast(const ProcessingInstruction) item; return t !is null && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 ); } @@ -1631,14 +1631,14 @@ class ProcessingInstruction : Item * You should rarely need to call this function. It exists so that * ProcessingInstructions can be used as associative array keys. */ - override size_t toHash() const nothrow { return hash(content); } + override size_t toHash() scope const nothrow { return hash(content); } /** * Returns a string representation of this ProcessingInstruction */ - override string toString() const @safe pure nothrow { return ""; } + override string toString() scope const @safe pure nothrow { return ""; } - override @property @safe @nogc pure nothrow bool isEmptyXML() const { return false; } /// Returns false always + override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } /// Returns false always } /** @@ -1647,16 +1647,16 @@ class ProcessingInstruction : Item abstract class Item { /// Compares with another Item of same type for equality - abstract override bool opEquals(Object o); + abstract override bool opEquals(scope const Object o) @safe const; /// Compares with another Item of same type - abstract override int opCmp(Object o); + abstract override int opCmp(scope const Object o) @safe const; /// Returns the hash of this item - abstract override size_t toHash() const; + abstract override size_t toHash() @safe scope const; /// Returns a string representation of this item - abstract override string toString() @safe const; + abstract override string toString() @safe scope const; /** * Returns an indented string representation of this item @@ -1664,7 +1664,7 @@ abstract class Item * Params: * indent = number of spaces by which to indent child elements */ - string[] pretty(uint indent) const + string[] pretty(uint indent) @safe scope const { import std.string : strip; string s = strip(toString()); @@ -1672,7 +1672,7 @@ abstract class Item } /// Returns true if the item represents empty XML text - abstract @property @safe @nogc pure nothrow bool isEmptyXML() const; + abstract @property @safe @nogc pure nothrow bool isEmptyXML() scope const; } /** @@ -2938,7 +2938,7 @@ private alias Err = CheckException; private { - T toType(T)(Object o) + inout(T) toType(T)(inout Object o) { T t = cast(T)(o); if (t is null) From 3662b6ff5590d16c289abf466cb45f75a72ba205 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 10 Jun 2017 23:04:31 +0100 Subject: [PATCH 231/262] std.mathspecial: erf*() for quad-precision reals Like the 80 bit real implementation, this is a translation of CEPHES code. Verified on LDC/AArch64. --- std/internal/math/errorfunction.d | 791 +++++++++++++++++++++++++++--- std/mathspecial.d | 2 + 2 files changed, 726 insertions(+), 67 deletions(-) diff --git a/std/internal/math/errorfunction.d b/std/internal/math/errorfunction.d index 910c7471415..4012e64181f 100644 --- a/std/internal/math/errorfunction.d +++ b/std/internal/math/errorfunction.d @@ -4,7 +4,7 @@ * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). * Copyright: Based on the CEPHES math library, which is * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com). - * Authors: Stephen L. Moshier, ported to D by Don Clugston + * Authors: Stephen L. Moshier, ported to D by Don Clugston and David Nadlinger */ /** * Macros: @@ -31,8 +31,8 @@ nothrow: @nogc: private { -immutable real EXP_2 = 0.13533528323661269189L; /* exp(-2) */ -enum real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) +immutable real EXP_2 = 0.135335283236612691893999494972484403L; /* exp(-2) */ +enum real SQRT2PI = 2.50662827463100050241576528481104525L; // sqrt(2pi) enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max) @@ -50,44 +50,582 @@ private { /* erfc(x) = exp(-x^2) P(1/x)/Q(1/x) 1/8 <= 1/x <= 1 Peak relative error 5.8e-21 */ -immutable real [10] P = [ -0x1.30dfa809b3cc6676p-17, 0x1.38637cd0913c0288p+18, +immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17, 0x1.38637cd0913c0288p+18, 0x1.2f015e047b4476bp+22, 0x1.24726f46aa9ab08p+25, 0x1.64b13c6395dc9c26p+27, 0x1.294c93046ad55b5p+29, 0x1.5962a82f92576dap+30, 0x1.11a709299faba04ap+31, 0x1.11028065b087be46p+31, 0x1.0d8ef40735b097ep+30 ]; -immutable real [11] Q = [ 0x1.14d8e2a72dec49f4p+19, 0x1.0c880ff467626e1p+23, +immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19, 0x1.0c880ff467626e1p+23, 0x1.04417ef060b58996p+26, 0x1.404e61ba86df4ebap+28, 0x1.0f81887bc82b873ap+30, 0x1.4552a5e39fb49322p+31, 0x1.11779a0ceb2a01cep+32, 0x1.3544dd691b5b1d5cp+32, 0x1.a91781f12251f02ep+31, 0x1.0d8ef3da605a1c86p+30, 1.0 ]; - -/* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) - 1/128 <= 1/x < 1/8 - Peak relative error 1.9e-21 */ -immutable real [5] R = [ 0x1.b9f6d8b78e22459ep-6, 0x1.1b84686b0a4ea43ap-1, - 0x1.b8f6aebe96000c2ap+1, 0x1.cb1dbedac27c8ec2p+2, 0x1.cf885f8f572a4c14p+1 -]; - -immutable real [6] S = [ - 0x1.87ae3cae5f65eb5ep-5, 0x1.01616f266f306d08p+0, 0x1.a4abe0411eed6c22p+2, - 0x1.eac9ce3da600abaap+3, 0x1.5752a9ac2faebbccp+3, 1.0 -]; - -/* erf(x) = x P(x^2)/Q(x^2) - 0 <= x <= 1 - Peak relative error 7.6e-23 */ -immutable real [7] T = [ 0x1.0da01654d757888cp+20, 0x1.2eb7497bc8b4f4acp+17, - 0x1.79078c19530f72a8p+15, 0x1.4eaf2126c0b2c23p+11, 0x1.1f2ea81c9d272a2ep+8, - 0x1.59ca6e2d866e625p+2, 0x1.c188e0b67435faf4p-4 -]; - -immutable real [7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18, - 0x1.8465900e88b6903ap+16, 0x1.855877093959ffdp+13, 0x1.e5c44395625ee358p+9, - 0x1.6a0fed103f1c68a6p+5, 1.0 -]; - +// For 128 bit quadruple-precision floats, we use a higher-precision implementation +// with more polynomial segments. +enum isIEEEQuadruple = floatTraits!real.realFormat == RealFormat.ieeeQuadruple; +static if (isIEEEQuadruple) +{ + // erfc(x + 0.25) = erfc(0.25) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 1.4e-35 + immutable real[9] RNr13 = [ + -2.353707097641280550282633036456457014829E3L, + 3.871159656228743599994116143079870279866E2L, + -3.888105134258266192210485617504098426679E2L, + -2.129998539120061668038806696199343094971E1L, + -8.125462263594034672468446317145384108734E1L, + 8.151549093983505810118308635926270319660E0L, + -5.033362032729207310462422357772568553670E0L, + -4.253956621135136090295893547735851168471E-2L, + -8.098602878463854789780108161581050357814E-2L + ]; + immutable real[9] RDr13 = [ + 2.220448796306693503549505450626652881752E3L, + 1.899133258779578688791041599040951431383E2L, + 1.061906712284961110196427571557149268454E3L, + 7.497086072306967965180978101974566760042E1L, + 2.146796115662672795876463568170441327274E2L, + 1.120156008362573736664338015952284925592E1L, + 2.211014952075052616409845051695042741074E1L, + 6.469655675326150785692908453094054988938E-1L, + 1.0 + ]; + + // erfc(0.25) = C13a + C13b to extra precision. + immutable real C13a = 0.723663330078125L; + immutable real C13b = 1.0279753638067014931732235184287934646022E-5L; + + // erfc(x + 0.375) = erfc(0.375) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 1.2e-35 + immutable real[9] RNr14 = [ + -2.446164016404426277577283038988918202456E3L, + 6.718753324496563913392217011618096698140E2L, + -4.581631138049836157425391886957389240794E2L, + -2.382844088987092233033215402335026078208E1L, + -7.119237852400600507927038680970936336458E1L, + 1.313609646108420136332418282286454287146E1L, + -6.188608702082264389155862490056401365834E0L, + -2.787116601106678287277373011101132659279E-2L, + -2.230395570574153963203348263549700967918E-2L + ]; + immutable real[9] RDr14 = [ + 2.495187439241869732696223349840963702875E3L, + 2.503549449872925580011284635695738412162E2L, + 1.159033560988895481698051531263861842461E3L, + 9.493751466542304491261487998684383688622E1L, + 2.276214929562354328261422263078480321204E2L, + 1.367697521219069280358984081407807931847E1L, + 2.276988395995528495055594829206582732682E1L, + 7.647745753648996559837591812375456641163E-1L, + 1.0 + ]; + + // erfc(0.375) = C14a + C14b to extra precision. + immutable real C14a = 0.5958709716796875L; + immutable real C14b = 1.2118885490201676174914080878232469565953E-5L; + + // erfc(x + 0.5) = erfc(0.5) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 4.7e-36 + immutable real[9] RNr15 = [ + -2.624212418011181487924855581955853461925E3L, + 8.473828904647825181073831556439301342756E2L, + -5.286207458628380765099405359607331669027E2L, + -3.895781234155315729088407259045269652318E1L, + -6.200857908065163618041240848728398496256E1L, + 1.469324610346924001393137895116129204737E1L, + -6.961356525370658572800674953305625578903E0L, + 5.145724386641163809595512876629030548495E-3L, + 1.990253655948179713415957791776180406812E-2L + ]; + immutable real[9] RDr15 = [ + 2.986190760847974943034021764693341524962E3L, + 5.288262758961073066335410218650047725985E2L, + 1.363649178071006978355113026427856008978E3L, + 1.921707975649915894241864988942255320833E2L, + 2.588651100651029023069013885900085533226E2L, + 2.628752920321455606558942309396855629459E1L, + 2.455649035885114308978333741080991380610E1L, + 1.378826653595128464383127836412100939126E0L, + 1.0 + ]; + // erfc(0.5) = C15a + C15b to extra precision. + immutable real C15a = 0.4794921875L; + immutable real C15b = 7.9346869534623172533461080354712635484242E-6L; + + // erfc(x + 0.625) = erfc(0.625) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 5.1e-36 + immutable real[9] RNr16 = [ + -2.347887943200680563784690094002722906820E3L, + 8.008590660692105004780722726421020136482E2L, + -5.257363310384119728760181252132311447963E2L, + -4.471737717857801230450290232600243795637E1L, + -4.849540386452573306708795324759300320304E1L, + 1.140885264677134679275986782978655952843E1L, + -6.731591085460269447926746876983786152300E0L, + 1.370831653033047440345050025876085121231E-1L, + 2.022958279982138755020825717073966576670E-2L, + ]; + immutable real[9] RDr16 = [ + 3.075166170024837215399323264868308087281E3L, + 8.730468942160798031608053127270430036627E2L, + 1.458472799166340479742581949088453244767E3L, + 3.230423687568019709453130785873540386217E2L, + 2.804009872719893612081109617983169474655E2L, + 4.465334221323222943418085830026979293091E1L, + 2.612723259683205928103787842214809134746E1L, + 2.341526751185244109722204018543276124997E0L, + 1.0 + ]; + // erfc(0.625) = C16a + C16b to extra precision. + immutable real C16a = 0.3767547607421875L; + immutable real C16b = 4.3570693945275513594941232097252997287766E-6L; + + // erfc(x + 0.75) = erfc(0.75) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 1.7e-35 + immutable real[9] RNr17 = [ + -1.767068734220277728233364375724380366826E3L, + 6.693746645665242832426891888805363898707E2L, + -4.746224241837275958126060307406616817753E2L, + -2.274160637728782675145666064841883803196E1L, + -3.541232266140939050094370552538987982637E1L, + 6.988950514747052676394491563585179503865E0L, + -5.807687216836540830881352383529281215100E0L, + 3.631915988567346438830283503729569443642E-1L, + -1.488945487149634820537348176770282391202E-2L + ]; + immutable real[9] RDr17 = [ + 2.748457523498150741964464942246913394647E3L, + 1.020213390713477686776037331757871252652E3L, + 1.388857635935432621972601695296561952738E3L, + 3.903363681143817750895999579637315491087E2L, + 2.784568344378139499217928969529219886578E2L, + 5.555800830216764702779238020065345401144E1L, + 2.646215470959050279430447295801291168941E1L, + 2.984905282103517497081766758550112011265E0L, + 1.0 + ]; + // erfc(0.75) = C17a + C17b to extra precision. + immutable real C17a = 0.2888336181640625L; + immutable real C17b = 1.0748182422368401062165408589222625794046E-5L; + + + // erfc(x + 0.875) = erfc(0.875) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 2.2e-35 + immutable real[9] RNr18 = [ + -1.342044899087593397419622771847219619588E3L, + 6.127221294229172997509252330961641850598E2L, + -4.519821356522291185621206350470820610727E2L, + 1.223275177825128732497510264197915160235E1L, + -2.730789571382971355625020710543532867692E1L, + 4.045181204921538886880171727755445395862E0L, + -4.925146477876592723401384464691452700539E0L, + 5.933878036611279244654299924101068088582E-1L, + -5.557645435858916025452563379795159124753E-2L + ]; + immutable real[9] RDr18 = [ + 2.557518000661700588758505116291983092951E3L, + 1.070171433382888994954602511991940418588E3L, + 1.344842834423493081054489613250688918709E3L, + 4.161144478449381901208660598266288188426E2L, + 2.763670252219855198052378138756906980422E2L, + 5.998153487868943708236273854747564557632E1L, + 2.657695108438628847733050476209037025318E1L, + 3.252140524394421868923289114410336976512E0L, + 1.0 + ]; + + // erfc(0.875) = C18a + C18b to extra precision. + immutable real C18a = 0.215911865234375L; + immutable real C18b = 1.3073705765341685464282101150637224028267E-5L; + + // erfc(x + 1.0) = erfc(1.0) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 1.6e-35 + immutable real[9] RNr19 = [ + -1.139180936454157193495882956565663294826E3L, + 6.134903129086899737514712477207945973616E2L, + -4.628909024715329562325555164720732868263E2L, + 4.165702387210732352564932347500364010833E1L, + -2.286979913515229747204101330405771801610E1L, + 1.870695256449872743066783202326943667722E0L, + -4.177486601273105752879868187237000032364E0L, + 7.533980372789646140112424811291782526263E-1L, + -8.629945436917752003058064731308767664446E-2L + ]; + immutable real[9] RDr19 = [ + 2.744303447981132701432716278363418643778E3L, + 1.266396359526187065222528050591302171471E3L, + 1.466739461422073351497972255511919814273E3L, + 4.868710570759693955597496520298058147162E2L, + 2.993694301559756046478189634131722579643E2L, + 6.868976819510254139741559102693828237440E1L, + 2.801505816247677193480190483913753613630E1L, + 3.604439909194350263552750347742663954481E0L, + 1.0 + ]; + + // erfc(1.0) = C19a + C19b to extra precision. + immutable real C19a = 0.15728759765625L; + immutable real C19b = 1.1609394035130658779364917390740703933002E-5L; + + // erfc(x + 1.125) = erfc(1.125) + x R(x) + // 0 <= x < 0.125 + // Peak relative error 3.6e-36 + immutable real[9] RNr20 = [ + -9.652706916457973956366721379612508047640E2L, + 5.577066396050932776683469951773643880634E2L, + -4.406335508848496713572223098693575485978E2L, + 5.202893466490242733570232680736966655434E1L, + -1.931311847665757913322495948705563937159E1L, + -9.364318268748287664267341457164918090611E-2L, + -3.306390351286352764891355375882586201069E0L, + 7.573806045289044647727613003096916516475E-1L, + -9.611744011489092894027478899545635991213E-2L + ]; + immutable real[9] RDr20 = [ + 3.032829629520142564106649167182428189014E3L, + 1.659648470721967719961167083684972196891E3L, + 1.703545128657284619402511356932569292535E3L, + 6.393465677731598872500200253155257708763E2L, + 3.489131397281030947405287112726059221934E2L, + 8.848641738570783406484348434387611713070E1L, + 3.132269062552392974833215844236160958502E1L, + 4.430131663290563523933419966185230513168E0L, + 1.0 + ]; + + // erfc(1.125) = C20a + C20b to extra precision. + immutable real C20a = 0.111602783203125L; + immutable real C20b = 8.9850951672359304215530728365232161564636E-6L; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 7/8 <= 1/x < 1 + // Peak relative error 1.4e-35 + immutable real[10] RNr8 = [ + 3.587451489255356250759834295199296936784E1L, + 5.406249749087340431871378009874875889602E2L, + 2.931301290625250886238822286506381194157E3L, + 7.359254185241795584113047248898753470923E3L, + 9.201031849810636104112101947312492532314E3L, + 5.749697096193191467751650366613289284777E3L, + 1.710415234419860825710780802678697889231E3L, + 2.150753982543378580859546706243022719599E2L, + 8.740953582272147335100537849981160931197E0L, + 4.876422978828717219629814794707963640913E-2L + ]; + immutable real[10] RDr8 = [ + 6.358593134096908350929496535931630140282E1L, + 9.900253816552450073757174323424051765523E2L, + 5.642928777856801020545245437089490805186E3L, + 1.524195375199570868195152698617273739609E4L, + 2.113829644500006749947332935305800887345E4L, + 1.526438562626465706267943737310282977138E4L, + 5.561370922149241457131421914140039411782E3L, + 9.394035530179705051609070428036834496942E2L, + 6.147019596150394577984175188032707343615E1L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 3/4 <= 1/x < 7/8 + // Peak relative error 1.7e-36 + immutable real[10] RNr7 = [ + 1.293515364043117601705535485785956592493E2L, + 2.474534371269189867053251150063459055230E3L, + 1.756537563959875738809491329250457486510E4L, + 5.977479535376639763773153344676726091607E4L, + 1.054313816139671870123172936972055385049E5L, + 9.754699773487726957401038094714603033904E4L, + 4.579131242577671038339922925213209214880E4L, + 1.000710322164510887997115157797717324370E4L, + 8.496863250712471449526805271633794700452E2L, + 1.797349831386892396933210199236530557333E1L + ]; + immutable real[11] RDr7 = [ + 2.292696320307033494820399866075534515002E2L, + 4.500632608295626968062258401895610053116E3L, + 3.321218723485498111535866988511716659339E4L, + 1.196084512221845156596781258490840961462E5L, + 2.287033883912529843927983406878910939930E5L, + 2.370223495794642027268482075021298394425E5L, + 1.305173734022437154610938308944995159199E5L, + 3.589386258485887630236490009835928559621E4L, + 4.339996864041074149726360516336773136101E3L, + 1.753135522665469574605384979152863899099E2L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 5/8 <= 1/x < 3/4 + // Peak relative error 1.6e-35 + immutable real[10] RNr6 = [ + 1.423313561367394923305025174137639124533E1L, + 3.244462503273003837514629113846075327206E2L, + 2.784937282403293364911673341412846781934E3L, + 1.163060685810874867196849890286455473382E4L, + 2.554141394931962276102205517358731053756E4L, + 2.982733782500729530503336931258698708782E4L, + 1.789683564523810605328169719436374742840E4L, + 5.056032142227470121262177112822018882754E3L, + 5.605349942234782054561269306895707034586E2L, + 1.561652599080729507274832243665726064881E1L + ]; + immutable real[11] RDr6 = [ + 2.522757606611479946069351519410222913326E1L, + 5.876797910931896554014229647006604017806E2L, + 5.211092128250480712011248211246144751074E3L, + 2.282679910855404599271496827409168580797E4L, + 5.371245819205596609986320599133109262447E4L, + 6.926186102106400355114925675028888924445E4L, + 4.794366033363621432575096487724913414473E4L, + 1.673190682734065914573814938835674963896E4L, + 2.589544846151313120096957014256536236242E3L, + 1.349438432583208276883323156200117027433E2L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 1/2 <= 1/x < 5/8 + // Peak relative error 4.3e-36 + immutable real[11] RNr5 = [ + 6.743447478279267339131211137241149796763E-2L, + 2.031339015932962998168472743355874796350E0L, + 2.369234815713196480221800940201367484379E1L, + 1.387819614016107433603101545594790875922E2L, + 4.435600256936515839937720907171966121786E2L, + 7.881577949936817507981170892417739733047E2L, + 7.615749099291545976179905281851765734680E2L, + 3.752484528663442467089606663006771157777E2L, + 8.279644286027145214308303292537009564726E1L, + 6.201462983413738162709722770960040042647E0L, + 6.649631608720062333043506249503378282697E-2L + ]; + immutable real[11] RDr5 = [ + 1.195244945161889822018178270706903972343E-1L, + 3.660216908153253021384862427197665991311E0L, + 4.373405883243078019655721779021995159854E1L, + 2.653305963056235008916733402786877121865E2L, + 8.921329790491152046318422124415895506335E2L, + 1.705552231555600759729260640562363304312E3L, + 1.832711109606893446763174603477244625325E3L, + 1.056823953275835649973998168744261083316E3L, + 2.975561981792909722126456997074344895584E2L, + 3.393149095158232521894537008472203487436E1L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 3/8 <= 1/x < 1/2 + // Peak relative error 1.8e-36 + immutable real[11] RNr4 = [ + 3.558685919236420073872459554885612994007E-2L, + 1.460223642496950651561817195253277924528E0L, + 2.379856746555189546876720308841066577268E1L, + 2.005205521246554860334064698817220160117E2L, + 9.533374928664989955629120027419490517596E2L, + 2.623024576994438336130421711314560425373E3L, + 4.126446434603735586340585027628851620886E3L, + 3.540675861596687801829655387867654030013E3L, + 1.506037084891064572653273761987617394259E3L, + 2.630715699182706745867272452228891752353E2L, + 1.202476629652900619635409242749750364878E1L + ]; + immutable real[12] RDr4 = [ + 6.307606561714590590399683184410336583739E-2L, + 2.619717051134271249293056836082721776665E0L, + 4.344441402681725017630451522968410844608E1L, + 3.752891116408399440953195184301023399176E2L, + 1.849305988804654653921972804388006355502E3L, + 5.358505261991675891835885654499883449403E3L, + 9.091890995405251314631428721090705475825E3L, + 8.731418313949291797856351745278287516416E3L, + 4.420211285043270337492325400764271868740E3L, + 1.031487363021856106882306509107923200832E3L, + 8.387036084846046121805145056040429461783E1L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 1/4 <= 1/x < 3/8 + // Peak relative error 8.1e-37 + immutable real[12] RNr3 = [ + 4.584481542956275354582319313040418316755E-5L, + 2.674923158288848442110883948437930349128E-3L, + 6.344232532055212248017211243740466847311E-2L, + 7.985145965992002744933550450451513513963E-1L, + 5.845078061888281665064746347663123946270E0L, + 2.566625318816866587482759497608029522596E1L, + 6.736225182343446605268837827950856640948E1L, + 1.021796460139598089409347761712730512053E2L, + 8.344336615515430530929955615400706619764E1L, + 3.207749011528249356283897356277376306967E1L, + 4.386185123863412086856423971695142026036E0L, + 8.971576448581208351826868348023528863856E-2L + ]; + immutable real[12] RDr3 = [ + 8.125781965218112303281657065320409661370E-5L, + 4.781806762611504685247817818428945295520E-3L, + 1.147785538413798317790357996845767614561E-1L, + 1.469285552007088106614218996464752307606E0L, + 1.101712261349880339221039938999124077650E1L, + 5.008507527095093413713171655268276861071E1L, + 1.383058691613468970486425146336829447433E2L, + 2.264114250278912520501010108736339599752E2L, + 2.081377197698598680576330179979996940039E2L, + 9.724438129690013609440151781601781137944E1L, + 1.907905050771832372089975877589291760121E1L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 1/8 <= 1/x < 1/4 + // Peak relative error 1.5e-36 + immutable real[12] RNr2 = [ + 6.928615158005256885698045840589513728399E-7L, + 5.616245938942075826026382337922413007879E-5L, + 1.871624980715261794832358438894219696113E-3L, + 3.349922063795792371642023765253747563009E-2L, + 3.531865233974349943956345502463135695834E-1L, + 2.264714157625072773976468825160906342360E0L, + 8.810720294489253776747319730638214883026E0L, + 2.014056685571655833019183248931442888437E1L, + 2.524586947657190747039554310814128743320E1L, + 1.520656940937208886246188940244581671609E1L, + 3.334145500790963675035841482334493680498E0L, + 1.122108380007109245896534245151140632457E-1L + ]; + immutable real[12] RDr2 = [ + 1.228065061824874795984937092427781089256E-6L, + 1.001593999520159167559129042893802235969E-4L, + 3.366527555699367241421450749821030974446E-3L, + 6.098626947195865254152265585991861150369E-2L, + 6.541547922508613985813189387198804660235E-1L, + 4.301130233305371976727117480925676583204E0L, + 1.737155892350891711527711121692994762909E1L, + 4.206892112110558214680649401236873828801E1L, + 5.787487996025016843403524261574779631219E1L, + 4.094047148590822715163181507813774861621E1L, + 1.230603930056944875836549716515643997094E1L, + 1.0L + ]; + + // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + // 1/128 <= 1/x < 1/8 + // Peak relative error 2.2e-36 + immutable real[10] RNr1 = [ + 1.293111801138199795250035229383033322539E-6L, + 9.785224720880980456171438759161161816706E-5L, + 2.932474396233212166056331430621176065943E-3L, + 4.496429595719847083917435337780697436921E-2L, + 3.805989855927720844877478869846718877846E-1L, + 1.789745532222426292126781724570152590071E0L, + 4.465737379634389318903237306594171764628E0L, + 5.268535822258082278401240171488850433767E0L, + 2.258357766807433839494276681092713991651E0L, + 1.504459334078750002966538036652860809497E-1L + ]; + immutable real[10] RDr1 = [ + 2.291980991578770070179177302906728371406E-6L, + 1.745845828808028552029674694534934620384E-4L, + 5.283248841982102317072923869576785278019E-3L, + 8.221212297078141470944454807434634848018E-2L, + 7.120500674861902950423510939060230945621E-1L, + 3.475435367560809622183983439133664598155E0L, + 9.243253391989233533874386043611304387113E0L, + 1.227894792475280941511758877318903197188E1L, + 6.789361410398841316638617624392719077724E0L, + 1.0L + ]; + + // erf(z+1) = erfConst + P(z)/Q(z) + // -.125 <= z <= 0 + // Peak relative error 7.3e-36 + immutable real erfConst = 0.845062911510467529296875L; + immutable real[9] TN2 = [ + -4.088889697077485301010486931817357000235E1L, + 7.157046430681808553842307502826960051036E3L, + -2.191561912574409865550015485451373731780E3L, + 2.180174916555316874988981177654057337219E3L, + 2.848578658049670668231333682379720943455E2L, + 1.630362490952512836762810462174798925274E2L, + 6.317712353961866974143739396865293596895E0L, + 2.450441034183492434655586496522857578066E1L, + 5.127662277706787664956025545897050896203E-1L + ]; + immutable real[10] TD2 = [ + 1.731026445926834008273768924015161048885E4L, + 1.209682239007990370796112604286048173750E4L, + 1.160950290217993641320602282462976163857E4L, + 5.394294645127126577825507169061355698157E3L, + 2.791239340533632669442158497532521776093E3L, + 8.989365571337319032943005387378993827684E2L, + 2.974016493766349409725385710897298069677E2L, + 6.148192754590376378740261072533527271947E1L, + 1.178502892490738445655468927408440847480E1L, + 1.0L + ]; + + // erf(x) = x + x P(x^2)/Q(x^2) + // 0 <= x <= 7/8 + // Peak relative error 1.8e-35 + immutable real[9] TN1 = [ + -3.858252324254637124543172907442106422373E10L, + 9.580319248590464682316366876952214879858E10L, + 1.302170519734879977595901236693040544854E10L, + 2.922956950426397417800321486727032845006E9L, + 1.764317520783319397868923218385468729799E8L, + 1.573436014601118630105796794840834145120E7L, + 4.028077380105721388745632295157816229289E5L, + 1.644056806467289066852135096352853491530E4L, + 3.390868480059991640235675479463287886081E1L + ]; + immutable real[10] TD1 = [ + -3.005357030696532927149885530689529032152E11L, + -1.342602283126282827411658673839982164042E11L, + -2.777153893355340961288511024443668743399E10L, + -3.483826391033531996955620074072768276974E9L, + -2.906321047071299585682722511260895227921E8L, + -1.653347985722154162439387878512427542691E7L, + -6.245520581562848778466500301865173123136E5L, + -1.402124304177498828590239373389110545142E4L, + -1.209368072473510674493129989468348633579E2L, + 1.0L + ]; +} +else +{ + /* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2) + 1/128 <= 1/x < 1/8 + Peak relative error 1.9e-21 */ + immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6, 0x1.1b84686b0a4ea43ap-1, + 0x1.b8f6aebe96000c2ap+1, 0x1.cb1dbedac27c8ec2p+2, 0x1.cf885f8f572a4c14p+1 + ]; + + immutable real[6] S = [ + 0x1.87ae3cae5f65eb5ep-5, 0x1.01616f266f306d08p+0, 0x1.a4abe0411eed6c22p+2, + 0x1.eac9ce3da600abaap+3, 0x1.5752a9ac2faebbccp+3, 1.0 + ]; + + /* erf(x) = x P(x^2)/Q(x^2) + 0 <= x <= 1 + Peak relative error 7.6e-23 */ + immutable real[7] T = [ 0x1.0da01654d757888cp+20, 0x1.2eb7497bc8b4f4acp+17, + 0x1.79078c19530f72a8p+15, 0x1.4eaf2126c0b2c23p+11, 0x1.1f2ea81c9d272a2ep+8, + 0x1.59ca6e2d866e625p+2, 0x1.c188e0b67435faf4p-4 + ]; + + immutable real[7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18, + 0x1.8465900e88b6903ap+16, 0x1.855877093959ffdp+13, 0x1.e5c44395625ee358p+9, + 0x1.6a0fed103f1c68a6p+5, 1.0 + ]; +} } /** @@ -113,39 +651,95 @@ real erfc(real a) if (a == -real.infinity) return 2.0; - real x; + immutable x = (a < 0.0) ? -a : a; - if (a < 0.0L ) - x = -a; - else - x = a; - if (x < 1.0) + if (x < (isIEEEQuadruple ? 0.25 : 1.0)) return 1.0 - erf(a); - real z = -a * a; + static if (isIEEEQuadruple) + { + if (x < 1.25) + { + real y; + final switch (cast(int)(8.0 * x)) + { + case 2: + const z = x - 0.25; + y = C13b + z * rationalPoly(z, RNr13, RDr13); + y += C13a; + break; + case 3: + const z = x - 0.375; + y = C14b + z * rationalPoly(z, RNr14, RDr14); + y += C14a; + break; + case 4: + const z = x - 0.5; + y = C15b + z * rationalPoly(z, RNr15, RDr15); + y += C15a; + break; + case 5: + const z = x - 0.625; + y = C16b + z * rationalPoly(z, RNr16, RDr16); + y += C16a; + break; + case 6: + const z = x - 0.75; + y = C17b + z * rationalPoly(z, RNr17, RDr17); + y += C17a; + break; + case 7: + const z = x - 0.875; + y = C18b + z * rationalPoly(z, RNr18, RDr18); + y += C18a; + break; + case 8: + const z = x - 1.0; + y = C19b + z * rationalPoly(z, RNr19, RDr19); + y += C19a; + break; + case 9: + const z = x - 1.125; + y = C20b + z * rationalPoly(z, RNr20, RDr20); + y += C20a; + break; + } + if (a < 0.0) + y = 2.0 - y; + return y; + } + } - if (z < -MAXLOG) + if (-a * a < -MAXLOG) { -// mtherr( "erfcl", UNDERFLOW ); - if (a < 0) return 2.0; + // underflow + if (a < 0.0) return 2.0; else return 0.0; } - /* Compute z = exp(z). */ - z = expx2(a, -1); - real y = 1.0/x; - + real y; + immutable z = expx2(a, -1); - if ( x < 8.0 ) y = z * rationalPoly(y, P, Q); - else y = z * y * rationalPoly(y * y, R, S); + static if (isIEEEQuadruple) + { + y = z * erfce(x); + } + else + { + y = 1.0 / x; + if (x < 8.0) + y = z * rationalPoly(y, P, Q); + else + y = z * y * rationalPoly(y * y, R, S); + } - if (a < 0.0L) - y = 2.0L - y; + if (a < 0.0) + y = 2.0 - y; if (y == 0.0) { -// mtherr( "erfcl", UNDERFLOW ); - if (a < 0) return 2.0; + // underflow + if (a < 0.0) return 2.0; else return 0.0; } @@ -158,21 +752,60 @@ private { exp(x^2) erfc(x) valid for x > 1. Use with normalDistribution and expx2. */ - -real erfce(real x) +static if (isIEEEQuadruple) { - real y = 1.0/x; - - if (x < 8.0) + real erfce(real x) { - return rationalPoly( y, P, Q); + immutable z = 1.0L / (x * x); + + real p; + switch (cast(int)(8.0 / x)) + { + default: + case 0: + p = rationalPoly(z, RNr1, RDr1); + break; + case 1: + p = rationalPoly(z, RNr2, RDr2); + break; + case 2: + p = rationalPoly(z, RNr3, RDr3); + break; + case 3: + p = rationalPoly(z, RNr4, RDr4); + break; + case 4: + p = rationalPoly(z, RNr5, RDr5); + break; + case 5: + p = rationalPoly(z, RNr6, RDr6); + break; + case 6: + p = rationalPoly(z, RNr7, RDr7); + break; + case 7: + p = rationalPoly(z, RNr8, RDr8); + break; + } + return p / x; } - else +} +else +{ + real erfce(real x) { - return y * rationalPoly(y*y, R, S); + real y = 1.0/x; + + if (x < 8.0) + { + return rationalPoly(y, P, Q); + } + else + { + return y * rationalPoly(y * y, R, S); + } } } - } /** @@ -202,16 +835,38 @@ real erf(real x) return -1.0; if (x == real.infinity) return 1.0; - if (abs(x) > 1.0L) + immutable ax = abs(x); + if (ax > 1.0L) return 1.0L - erfc(x); - real z = x * x; - return x * rationalPoly(z, T, U); + static if (isIEEEQuadruple) + { + immutable z = x * x; + + real y; + if (ax < 0.875) + { + y = ax + ax * rationalPoly(x * x, TN1, TD1); + } + else + { + y = erfConst + rationalPoly(ax - 1.0L, TN2, TD2); + } + + if (x < 0) + y = -y; + return y; + } + else + { + real z = x * x; + return x * rationalPoly(x * x, T, U); + } } @safe unittest { - // High resolution test points. + // High resolution test points. enum real erfc0_250 = 0.723663330078125 + 1.0279753638067014931732235184287934646022E-5; enum real erfc0_375 = 0.5958709716796875 + 1.2118885490201676174914080878232469565953E-5; enum real erfc0_500 = 0.4794921875 + 7.9346869534623172533461080354712635484242E-6; @@ -237,8 +892,9 @@ real erf(real x) assert(feqrel(erfc(1.000L), erfc1_000 )>=real.mant_dig-2); assert(feqrel(erfc(1.125L), erfc1_125 )>=real.mant_dig-2); assert(feqrel(erf(0.875L), erf0_875 )>=real.mant_dig-1); - // The DMC implementation of erfc() fails this next test (just) - assert(feqrel(erfc(4.1L),0.67000276540848983727e-8L)>=real.mant_dig-5); + // The DMC implementation of erfc() fails this next test (just). + // Test point from Mathematica 11.0. + assert(feqrel(erfc(4.1L), 6.70002765408489837272673380763418472e-9L) >= real.mant_dig-5); assert(isIdentical(erf(0.0),0.0)); assert(isIdentical(erf(-0.0),-0.0)); @@ -361,8 +1017,9 @@ assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325))); * z = sqrt( -2 log(p) ); then the approximation is * x = z - log(z)/z - (1/z) P(1/z) / Q(1/z) . * For larger arguments, x/sqrt(2 pi) = w + w^3 R(w^2)/S(w^2)) , - * where w = p - 0.5 . + * where w = p - 0.5. */ +// TODO: isIEEEQuadruple (128 bit) real implementation; not available from CEPHES. real normalDistributionInvImpl(real p) in { assert(p >= 0.0L && p <= 1.0L, "Domain error"); diff --git a/std/mathspecial.d b/std/mathspecial.d index 896b035c12b..b0f6e3ec962 100644 --- a/std/mathspecial.d +++ b/std/mathspecial.d @@ -348,6 +348,8 @@ real normalDistribution(real x) * Returns the argument, x, for which the area under the * Normal probability density function (integrated from * minus infinity to x) is equal to p. + * + * Note: This function is only implemented to 80 bit precision. */ real normalDistributionInverse(real p) in { From 0c870f03ce76a108a0bba961859a0d2a08ffc6f9 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sat, 10 Jun 2017 23:17:16 +0100 Subject: [PATCH 232/262] std.math: Implement transcendental functions for quad-precision reals Also merged some arrays for log expansion coefficients, which were used multiple times. Like the 80 bit real implementation, these are translations of CEPHES code. Verified on LDC/AArch64. --- std/math.d | 518 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 335 insertions(+), 183 deletions(-) diff --git a/std/math.d b/std/math.d index 08b77552890..43c18187f5d 100644 --- a/std/math.d +++ b/std/math.d @@ -116,7 +116,7 @@ $(TR $(TDNW Hardware Control) $(TD * the following terms: * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston, - * Conversion of CEPHES math library to D by Iain Buclaw + * Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger * Source: $(PHOBOSSRC std/_math.d) */ module std.math; @@ -854,24 +854,53 @@ Lret: {} } else { - // Coefficients for tan(x) - static immutable real[3] P = [ - -1.7956525197648487798769E7L, - 1.1535166483858741613983E6L, - -1.3093693918138377764608E4L, - ]; - static immutable real[5] Q = [ - -5.3869575592945462988123E7L, - 2.5008380182335791583922E7L, - -1.3208923444021096744731E6L, - 1.3681296347069295467845E4L, - 1.0000000000000000000000E0L, - ]; + // Coefficients for tan(x) and PI/4 split into three parts. + static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + { + static immutable real[6] P = [ + 2.883414728874239697964612246732416606301E10L, + -2.307030822693734879744223131873392503321E9L, + 5.160188250214037865511600561074819366815E7L, + -4.249691853501233575668486667664718192660E5L, + 1.272297782199996882828849455156962260810E3L, + -9.889929415807650724957118893791829849557E-1L + ]; + static immutable real[7] Q = [ + 8.650244186622719093893836740197250197602E10L + -4.152206921457208101480801635640958361612E10L, + 2.758476078803232151774723646710890525496E9L, + -5.733709132766856723608447733926138506824E7L, + 4.529422062441341616231663543669583527923E5L, + -1.317243702830553658702531997959756728291E3L, + 1.0 + ]; + + enum real DP1 = + 7.853981633974483067550664827649598009884357452392578125E-1L; + enum real DP2 = + 2.8605943630549158983813312792950660807511260829685741796657E-18L; + enum real DP3 = + 2.1679525325309452561992610065108379921905808E-35L; + } + else + { + static immutable real[3] P = [ + -1.7956525197648487798769E7L, + 1.1535166483858741613983E6L, + -1.3093693918138377764608E4L, + ]; + static immutable real[5] Q = [ + -5.3869575592945462988123E7L, + 2.5008380182335791583922E7L, + -1.3208923444021096744731E6L, + 1.3681296347069295467845E4L, + 1.0000000000000000000000E0L, + ]; - // PI/4 split into three parts. - enum real P1 = 7.853981554508209228515625E-1L; - enum real P2 = 7.946627356147928367136046290398E-9L; - enum real P3 = 3.061616997868382943065164830688E-17L; + enum real P1 = 7.853981554508209228515625E-1L; + enum real P2 = 7.946627356147928367136046290398E-9L; + enum real P3 = 3.061616997868382943065164830688E-17L; + } // Special cases. if (x == 0.0 || isNaN(x)) @@ -1051,26 +1080,54 @@ real atan(real x) @safe pure nothrow @nogc else { // Coefficients for atan(x) - static immutable real[5] P = [ - -5.0894116899623603312185E1L, - -9.9988763777265819915721E1L, - -6.3976888655834347413154E1L, - -1.4683508633175792446076E1L, - -8.6863818178092187535440E-1L, - ]; - static immutable real[6] Q = [ - 1.5268235069887081006606E2L, - 3.9157570175111990631099E2L, - 3.6144079386152023162701E2L, - 1.4399096122250781605352E2L, - 2.2981886733594175366172E1L, - 1.0000000000000000000000E0L, - ]; + static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + { + static immutable real[9] P = [ + -6.880597774405940432145577545328795037141E2L, + -2.514829758941713674909996882101723647996E3L, + -3.696264445691821235400930243493001671932E3L, + -2.792272753241044941703278827346430350236E3L, + -1.148164399808514330375280133523543970854E3L, + -2.497759878476618348858065206895055957104E2L, + -2.548067867495502632615671450650071218995E1L, + -8.768423468036849091777415076702113400070E-1L, + -6.635810778635296712545011270011752799963E-4L + ]; + static immutable real[9] Q = [ + 2.064179332321782129643673263598686441900E3L, + 8.782996876218210302516194604424986107121E3L, + 1.547394317752562611786521896296215170819E4L, + 1.458510242529987155225086911411015961174E4L, + 7.928572347062145288093560392463784743935E3L, + 2.494680540950601626662048893678584497900E3L, + 4.308348370818927353321556740027020068897E2L, + 3.566239794444800849656497338030115886153E1L, + 1.0 + ]; + } + else + { + static immutable real[5] P = [ + -5.0894116899623603312185E1L, + -9.9988763777265819915721E1L, + -6.3976888655834347413154E1L, + -1.4683508633175792446076E1L, + -8.6863818178092187535440E-1L, + ]; + static immutable real[6] Q = [ + 1.5268235069887081006606E2L, + 3.9157570175111990631099E2L, + 3.6144079386152023162701E2L, + 1.4399096122250781605352E2L, + 2.2981886733594175366172E1L, + 1.0000000000000000000000E0L, + ]; + } // tan(PI/8) - enum real TAN_PI_8 = 4.1421356237309504880169e-1L; + enum real TAN_PI_8 = 0.414213562373095048801688724209698078569672L; // tan(3 * PI/8) - enum real TAN3_PI_8 = 2.41421356237309504880169L; + enum real TAN3_PI_8 = 2.414213562373095048801688724209698078569672L; // Special cases. if (x == 0.0) @@ -1662,6 +1719,33 @@ real exp(real x) @trusted pure nothrow @nogc enum real OF = 1.1356523406294143949492E4L; // ln((1-2^-64) * 2^16384) enum real UF = -1.13994985314888605586758E4L; // ln(2^-16446) } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + // Coefficients for exp(x) - 1 + static immutable real[5] P = [ + 9.999999999999999999999999999999999998502E-1L, + 3.508710990737834361215404761139478627390E-2L, + 2.708775201978218837374512615596512792224E-4L, + 6.141506007208645008909088812338454698548E-7L, + 3.279723985560247033712687707263393506266E-10L + ]; + static immutable real[6] Q = [ + 2.000000000000000000000000000000000000150E0, + 2.368408864814233538909747618894558968880E-1L, + 3.611828913847589925056132680618007270344E-3L, + 1.504792651814944826817779302637284053660E-5L, + 1.771372078166251484503904874657985291164E-8L, + 2.980756652081995192255342779918052538681E-12L + ]; + + // C1 + C2 = LN2. + enum real C1 = 6.93145751953125E-1L; + enum real C2 = 1.428606820309417232121458176568075500134E-6L; + + // Overflow and Underflow limits. + enum real OF = 1.135583025911358400418251384584930671458833e4L; + enum real UF = -1.143276959615573793352782661133116431383730e4L; + } else static assert(0, "Not implemented for this architecture"); @@ -1896,30 +1980,61 @@ L_largenegative: } else { - // Coefficients for exp(x) - 1 - static immutable real[5] P = [ - -1.586135578666346600772998894928250240826E4L, - 2.642771505685952966904660652518429479531E3L, - -3.423199068835684263987132888286791620673E2L, - 1.800826371455042224581246202420972737840E1L, - -5.238523121205561042771939008061958820811E-1L, - ]; - static immutable real[6] Q = [ - -9.516813471998079611319047060563358064497E4L, - 3.964866271411091674556850458227710004570E4L, - -7.207678383830091850230366618190187434796E3L, - 7.206038318724600171970199625081491823079E2L, - -4.002027679107076077238836622982900945173E1L, - 1.000000000000000000000000000000000000000E0L, - ]; + // Coefficients for exp(x) - 1 and overflow/underflow limits. + static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + { + static immutable real[8] P = [ + 2.943520915569954073888921213330863757240E8L, + -5.722847283900608941516165725053359168840E7L, + 8.944630806357575461578107295909719817253E6L, + -7.212432713558031519943281748462837065308E5L, + 4.578962475841642634225390068461943438441E4L, + -1.716772506388927649032068540558788106762E3L, + 4.401308817383362136048032038528753151144E1L, + -4.888737542888633647784737721812546636240E-1L + ]; + + static immutable real[9] Q = [ + 1.766112549341972444333352727998584753865E9L, + -7.848989743695296475743081255027098295771E8L, + 1.615869009634292424463780387327037251069E8L, + -2.019684072836541751428967854947019415698E7L, + 1.682912729190313538934190635536631941751E6L, + -9.615511549171441430850103489315371768998E4L, + 3.697714952261803935521187272204485251835E3L, + -8.802340681794263968892934703309274564037E1L, + 1.0 + ]; + + enum real OF = 1.1356523406294143949491931077970764891253E4L; + enum real UF = -1.143276959615573793352782661133116431383730e4L; + } + else + { + static immutable real[5] P = [ + -1.586135578666346600772998894928250240826E4L, + 2.642771505685952966904660652518429479531E3L, + -3.423199068835684263987132888286791620673E2L, + 1.800826371455042224581246202420972737840E1L, + -5.238523121205561042771939008061958820811E-1L, + ]; + static immutable real[6] Q = [ + -9.516813471998079611319047060563358064497E4L, + 3.964866271411091674556850458227710004570E4L, + -7.207678383830091850230366618190187434796E3L, + 7.206038318724600171970199625081491823079E2L, + -4.002027679107076077238836622982900945173E1L, + 1.0 + ]; + + enum real OF = 1.1356523406294143949492E4L; + enum real UF = -4.5054566736396445112120088E1L; + } + // C1 + C2 = LN2. enum real C1 = 6.9314575195312500000000E-1L; - enum real C2 = 1.4286068203094172321215E-6L; - - // Overflow and Underflow limits. - enum real OF = 1.1356523406294143949492E4L; - enum real UF = -4.5054566736396445112120088E1L; + enum real C2 = 1.428606820309417232121458176568075500134E-6L; // Special cases. Raises an overflow flag, except in the case // for CTFE, where there are no hardware controls. @@ -2160,17 +2275,39 @@ L_was_nan: else { // Coefficients for exp2(x) - static immutable real[3] P = [ - 2.0803843631901852422887E6L, - 3.0286971917562792508623E4L, - 6.0614853552242266094567E1L, - ]; - static immutable real[4] Q = [ - 6.0027204078348487957118E6L, - 3.2772515434906797273099E5L, - 1.7492876999891839021063E3L, - 1.0000000000000000000000E0L, - ]; + static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + { + static immutable real[5] P = [ + 9.079594442980146270952372234833529694788E12L, + 1.530625323728429161131811299626419117557E11L, + 5.677513871931844661829755443994214173883E8L, + 6.185032670011643762127954396427045467506E5L, + 1.587171580015525194694938306936721666031E2L + ]; + + static immutable real[6] Q = [ + 2.619817175234089411411070339065679229869E13L, + 1.490560994263653042761789432690793026977E12L, + 1.092141473886177435056423606755843616331E10L, + 2.186249607051644894762167991800811827835E7L, + 1.236602014442099053716561665053645270207E4L, + 1.0 + ]; + } + else + { + static immutable real[3] P = [ + 2.0803843631901852422887E6L, + 3.0286971917562792508623E4L, + 6.0614853552242266094567E1L, + ]; + static immutable real[4] Q = [ + 6.0027204078348487957118E6L, + 3.2772515434906797273099E5L, + 1.7492876999891839021063E3L, + 1.0000000000000000000000E0L, + ]; + } // Overflow and Underflow limits. enum real OF = 16_384.0L; @@ -2238,7 +2375,26 @@ L_was_nan: ctrl.disableExceptions(FloatingPointControl.allExceptions); ctrl.rounding = FloatingPointControl.roundToNearest; - static if (real.mant_dig == 64) // 80-bit reals + static if (real.mant_dig == 113) + { + static immutable real[2][] exptestpoints = + [ // x exp(x) + [ 1.0L, E ], + [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ], + [ 3.0L, E*E*E ], + [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow + [ 0x1.7p+13L, real.infinity ], // close overflow + [ 0x1p+80L, real.infinity ], // far overflow + [ real.infinity, real.infinity ], + [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow + [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto + [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal + [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto + [-0x1.655p+13L, 0 ], // close underflow + [-0x1p+30L, 0 ], // far underflow + ]; + } + else static if (real.mant_dig == 64) // 80-bit reals { static immutable real[2][] exptestpoints = [ // x exp(x) @@ -2327,8 +2483,8 @@ L_was_nan: x = exp(NaN(0x123)); assert(isIdentical(x, NaN(0x123))); - // High resolution test - assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6D_33Fp+0L); + // High resolution test (verified against GNU MPFR/Mathematica). + assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L); } @@ -3018,6 +3174,104 @@ typed_allocator.d assert(pldexp != null); } +private +{ + version (INLINE_YL2X) {} else + { + static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple) + { + // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x) + static immutable real[13] logCoeffsP = [ + 1.313572404063446165910279910527789794488E4L, + 7.771154681358524243729929227226708890930E4L, + 2.014652742082537582487669938141683759923E5L, + 3.007007295140399532324943111654767187848E5L, + 2.854829159639697837788887080758954924001E5L, + 1.797628303815655343403735250238293741397E5L, + 7.594356839258970405033155585486712125861E4L, + 2.128857716871515081352991964243375186031E4L, + 3.824952356185897735160588078446136783779E3L, + 4.114517881637811823002128927449878962058E2L, + 2.321125933898420063925789532045674660756E1L, + 4.998469661968096229986658302195402690910E-1L, + 1.538612243596254322971797716843006400388E-6L + ]; + static immutable real[13] logCoeffsQ = [ + 3.940717212190338497730839731583397586124E4L, + 2.626900195321832660448791748036714883242E5L, + 7.777690340007566932935753241556479363645E5L, + 1.347518538384329112529391120390701166528E6L, + 1.514882452993549494932585972882995548426E6L, + 1.158019977462989115839826904108208787040E6L, + 6.132189329546557743179177159925690841200E5L, + 2.248234257620569139969141618556349415120E5L, + 5.605842085972455027590989944010492125825E4L, + 9.147150349299596453976674231612674085381E3L, + 9.104928120962988414618126155557301584078E2L, + 4.839208193348159620282142911143429644326E1L, + 1.0 + ]; + + // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2) + // where z = 2(x-1)/(x+1) + static immutable real[6] logCoeffsR = [ + -8.828896441624934385266096344596648080902E-1L, + 8.057002716646055371965756206836056074715E1L, + -2.024301798136027039250415126250455056397E3L, + 2.048819892795278657810231591630928516206E4L, + -8.977257995689735303686582344659576526998E4L, + 1.418134209872192732479751274970992665513E5L + ]; + static immutable real[6] logCoeffsS = [ + 1.701761051846631278975701529965589676574E6L + -1.332535117259762928288745111081235577029E6L, + 4.001557694070773974936904547424676279307E5L, + -5.748542087379434595104154610899551484314E4L, + 3.998526750980007367835804959888064681098E3L, + -1.186359407982897997337150403816839480438E2L, + 1.0 + ]; + } + else + { + // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x) + static immutable real[7] logCoeffsP = [ + 2.0039553499201281259648E1L, + 5.7112963590585538103336E1L, + 6.0949667980987787057556E1L, + 2.9911919328553073277375E1L, + 6.5787325942061044846969E0L, + 4.9854102823193375972212E-1L, + 4.5270000862445199635215E-5L, + ]; + static immutable real[7] logCoeffsQ = [ + 6.0118660497603843919306E1L, + 2.1642788614495947685003E2L, + 3.0909872225312059774938E2L, + 2.2176239823732856465394E2L, + 8.3047565967967209469434E1L, + 1.5062909083469192043167E1L, + 1.0000000000000000000000E0L, + ]; + + // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2) + // where z = 2(x-1)/(x+1) + static immutable real[4] logCoeffsR = [ + -3.5717684488096787370998E1L, + 1.0777257190312272158094E1L, + -7.1990767473014147232598E-1L, + 1.9757429581415468984296E-3L, + ]; + static immutable real[4] logCoeffsS = [ + -4.2861221385716144629696E2L, + 1.9361891836232102174846E2L, + -2.6201045551331104417768E1L, + 1.0000000000000000000000E0L, + ]; + } + } +} + /************************************** * Calculate the natural logarithm of x. * @@ -3034,43 +3288,9 @@ real log(real x) @safe pure nothrow @nogc return core.math.yl2x(x, LN2); else { - // Coefficients for log(1 + x) - static immutable real[7] P = [ - 2.0039553499201281259648E1L, - 5.7112963590585538103336E1L, - 6.0949667980987787057556E1L, - 2.9911919328553073277375E1L, - 6.5787325942061044846969E0L, - 4.9854102823193375972212E-1L, - 4.5270000862445199635215E-5L, - ]; - static immutable real[7] Q = [ - 6.0118660497603843919306E1L, - 2.1642788614495947685003E2L, - 3.0909872225312059774938E2L, - 2.2176239823732856465394E2L, - 8.3047565967967209469434E1L, - 1.5062909083469192043167E1L, - 1.0000000000000000000000E0L, - ]; - - // Coefficients for log(x) - static immutable real[4] R = [ - -3.5717684488096787370998E1L, - 1.0777257190312272158094E1L, - -7.1990767473014147232598E-1L, - 1.9757429581415468984296E-3L, - ]; - static immutable real[4] S = [ - -4.2861221385716144629696E2L, - 1.9361891836232102174846E2L, - -2.6201045551331104417768E1L, - 1.0000000000000000000000E0L, - ]; - // C1 + C2 = LN2. - enum real C1 = 6.9314575195312500000000E-1L; - enum real C2 = 1.4286068203094172321215E-6L; + enum real C1 = 6.93145751953125E-1L; + enum real C2 = 1.428606820309417232121458176568075500134E-6L; // Special cases. if (isNaN(x)) @@ -3089,7 +3309,7 @@ real log(real x) @safe pure nothrow @nogc x = frexp(x, exp); - // Logarithm using log(x) = z + z^^3 P(z) / Q(z), + // Logarithm using log(x) = z + z^^3 R(z) / S(z), // where z = 2(x - 1)/(x + 1) if ((exp > 2) || (exp < -2)) { @@ -3107,7 +3327,7 @@ real log(real x) @safe pure nothrow @nogc } x = z / y; z = x * x; - z = x * (z * poly(z, R) / poly(z, S)); + z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); z += exp * C2; z += x; z += exp * C1; @@ -3126,7 +3346,7 @@ real log(real x) @safe pure nothrow @nogc x = x - 1.0; } z = x * x; - y = x * (z * poly(x, P) / poly(x, Q)); + y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); y += exp * C2; z = y - ldexp(z, -1); @@ -3161,40 +3381,6 @@ real log10(real x) @safe pure nothrow @nogc return core.math.yl2x(x, LOG2); else { - // Coefficients for log(1 + x) - static immutable real[7] P = [ - 2.0039553499201281259648E1L, - 5.7112963590585538103336E1L, - 6.0949667980987787057556E1L, - 2.9911919328553073277375E1L, - 6.5787325942061044846969E0L, - 4.9854102823193375972212E-1L, - 4.5270000862445199635215E-5L, - ]; - static immutable real[7] Q = [ - 6.0118660497603843919306E1L, - 2.1642788614495947685003E2L, - 3.0909872225312059774938E2L, - 2.2176239823732856465394E2L, - 8.3047565967967209469434E1L, - 1.5062909083469192043167E1L, - 1.0000000000000000000000E0L, - ]; - - // Coefficients for log(x) - static immutable real[4] R = [ - -3.5717684488096787370998E1L, - 1.0777257190312272158094E1L, - -7.1990767473014147232598E-1L, - 1.9757429581415468984296E-3L, - ]; - static immutable real[4] S = [ - -4.2861221385716144629696E2L, - 1.9361891836232102174846E2L, - -2.6201045551331104417768E1L, - 1.0000000000000000000000E0L, - ]; - // log10(2) split into two parts. enum real L102A = 0.3125L; enum real L102B = -1.14700043360188047862611052755069732318101185E-2L; @@ -3220,7 +3406,7 @@ real log10(real x) @safe pure nothrow @nogc x = frexp(x, exp); - // Logarithm using log(x) = z + z^^3 P(z) / Q(z), + // Logarithm using log(x) = z + z^^3 R(z) / S(z), // where z = 2(x - 1)/(x + 1) if ((exp > 2) || (exp < -2)) { @@ -3238,7 +3424,7 @@ real log10(real x) @safe pure nothrow @nogc } x = z / y; z = x * x; - y = x * (z * poly(z, R) / poly(z, S)); + y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); goto Ldone; } @@ -3252,7 +3438,7 @@ real log10(real x) @safe pure nothrow @nogc x = x - 1.0; z = x * x; - y = x * (z * poly(x, P) / poly(x, Q)); + y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); y = y - ldexp(z, -1); // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). @@ -3331,40 +3517,6 @@ real log2(real x) @safe pure nothrow @nogc return core.math.yl2x(x, 1); else { - // Coefficients for log(1 + x) - static immutable real[7] P = [ - 2.0039553499201281259648E1L, - 5.7112963590585538103336E1L, - 6.0949667980987787057556E1L, - 2.9911919328553073277375E1L, - 6.5787325942061044846969E0L, - 4.9854102823193375972212E-1L, - 4.5270000862445199635215E-5L, - ]; - static immutable real[7] Q = [ - 6.0118660497603843919306E1L, - 2.1642788614495947685003E2L, - 3.0909872225312059774938E2L, - 2.2176239823732856465394E2L, - 8.3047565967967209469434E1L, - 1.5062909083469192043167E1L, - 1.0000000000000000000000E0L, - ]; - - // Coefficients for log(x) - static immutable real[4] R = [ - -3.5717684488096787370998E1L, - 1.0777257190312272158094E1L, - -7.1990767473014147232598E-1L, - 1.9757429581415468984296E-3L, - ]; - static immutable real[4] S = [ - -4.2861221385716144629696E2L, - 1.9361891836232102174846E2L, - -2.6201045551331104417768E1L, - 1.0000000000000000000000E0L, - ]; - // Special cases are the same as for log. if (isNaN(x)) return x; @@ -3382,7 +3534,7 @@ real log2(real x) @safe pure nothrow @nogc x = frexp(x, exp); - // Logarithm using log(x) = z + z^^3 P(z) / Q(z), + // Logarithm using log(x) = z + z^^3 R(z) / S(z), // where z = 2(x - 1)/(x + 1) if ((exp > 2) || (exp < -2)) { @@ -3400,7 +3552,7 @@ real log2(real x) @safe pure nothrow @nogc } x = z / y; z = x * x; - y = x * (z * poly(z, R) / poly(z, S)); + y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS)); goto Ldone; } @@ -3414,7 +3566,7 @@ real log2(real x) @safe pure nothrow @nogc x = x - 1.0; z = x * x; - y = x * (z * poly(x, P) / poly(x, Q)); + y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ)); y = y - ldexp(z, -1); // Multiply log of fraction by log10(e) and base 2 exponent by log10(2). From 44a3be48dbeaeb033e484c128b970578567e55f1 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 11:01:08 -0400 Subject: [PATCH 233/262] Remove std.concurrencybase --- index.d | 1 - posix.mak | 2 +- std/concurrency.d | 8 ++++---- std/concurrencybase.d | 20 -------------------- win32.mak | 4 +--- win64.mak | 3 +-- 6 files changed, 7 insertions(+), 31 deletions(-) delete mode 100644 std/concurrencybase.d diff --git a/index.d b/index.d index 905ebf3cbbf..52fed13c351 100644 --- a/index.d +++ b/index.d @@ -461,7 +461,6 @@ $(COMMENT $(TR $(TDNW $(LINK2 core_sync_config.html, core.sync.config)$(BR) - $(LINK2 std_concurrencybase.html, std.concurrencybase)$(BR) $(LINK2 std_container_util.html, std.container.util)$(BR) $(LINK2 std_regex_internal_backtracking.html, std.regex.internal.backtracking)$(BR) $(LINK2 std_regex_internal_generator.html, std.regex.internal.generator)$(BR) diff --git a/posix.mak b/posix.mak index 4e7e5eab1dc..bb58c4e873e 100644 --- a/posix.mak +++ b/posix.mak @@ -219,7 +219,7 @@ EXTRA_MODULES_COMMON := $(addprefix etc/c/,curl odbc/sql odbc/sqlext \ EXTRA_DOCUMENTABLES := $(EXTRA_MODULES_LINUX) $(EXTRA_MODULES_WIN32) $(EXTRA_MODULES_COMMON) EXTRA_MODULES_INTERNAL := $(addprefix std/, \ - algorithm/internal concurrencybase \ + algorithm/internal \ $(addprefix internal/, \ cstring digest/sha_SSSE3 \ $(addprefix math/, biguintcore biguintnoasm biguintx86 \ diff --git a/std/concurrency.d b/std/concurrency.d index 44b9d8f8285..d02bcd16844 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -69,7 +69,6 @@ import core.atomic; import core.sync.condition; import core.sync.mutex; import core.thread; -import std.concurrencybase; import std.range.primitives; import std.traits; @@ -910,12 +909,13 @@ private { __gshared Tid[string] tidByName; __gshared string[][Tid] namesByTid; - __gshared Mutex registryLock; } -extern (C) void std_concurrency_static_this() +private @property Mutex registryLock() { - registryLock = new Mutex; + __gshared Mutex impl; + initOnce!impl(new Mutex); + return impl; } private void unregisterMe() diff --git a/std/concurrencybase.d b/std/concurrencybase.d deleted file mode 100644 index 2d26d8e4009..00000000000 --- a/std/concurrencybase.d +++ /dev/null @@ -1,20 +0,0 @@ -// Written in the D programming language. - -/** - * The only purpose of this module is to do the static construction for - * std.concurrency, to eliminate cyclic construction errors. - * - * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Source: $(PHOBOSSRC std/_concurrencybase.d) - */ -module std.concurrencybase; - -import core.sync.mutex; - -extern(C) void std_concurrency_static_this(); - -shared static this() -{ - std_concurrency_static_this(); -} - diff --git a/win32.mak b/win32.mak index 854ed9a6a6d..ee7716a177f 100644 --- a/win32.mak +++ b/win32.mak @@ -146,8 +146,7 @@ SRC_STD_3a= \ std\exception.d \ std\compiler.d \ std\system.d \ - std\concurrency.d \ - std\concurrencybase.d + std\concurrency.d SRC_STD_4= \ std\uuid.d @@ -634,7 +633,6 @@ cov : $(SRC_TO_COMPILE) $(LIB) $(DMD) -conf= -cov=79 -unittest -main -run std\random.d $(DMD) -conf= -cov=92 -unittest -main -run std\exception.d $(DMD) -conf= -cov=73 -unittest -main -run std\concurrency.d - $(DMD) -conf= -cov=100 -unittest -main -run std\concurrencybase.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\date.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\interval.d $(DMD) -conf= -cov=95 -unittest -main -run std\datetime\package.d diff --git a/win64.mak b/win64.mak index 9e59883561d..90fa699b830 100644 --- a/win64.mak +++ b/win64.mak @@ -152,8 +152,7 @@ SRC_STD_3c= \ std\exception.d \ std\compiler.d \ std\system.d \ - std\concurrency.d \ - std\concurrencybase.d + std\concurrency.d SRC_STD_3d= \ std\bitmanip.d \ From 1cdd07b60d6cc2dda8a4880d8c857fb45aadfcde Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 11:28:48 -0400 Subject: [PATCH 234/262] Eliminate shared this for allocators --- std/experimental/allocator/package.d | 42 +++++++++++++--------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index d4724830490..55ace6a47aa 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -459,86 +459,79 @@ interface ISharedAllocator Ternary empty() shared; } -shared ISharedAllocator _processAllocator; +private shared ISharedAllocator _processAllocator; IAllocator _threadAllocator; -shared static this() -{ - assert(!_processAllocator); - import std.experimental.allocator.gc_allocator : GCAllocator; - _processAllocator = sharedAllocatorObject(GCAllocator.instance); -} - static this() { /* - Forwards the `_threadAllocator` calls to the `_processAllocator` + Forwards the `_threadAllocator` calls to the `processAllocator` */ static class ThreadAllocator : IAllocator { override @property uint alignment() { - return _processAllocator.alignment(); + return processAllocator.alignment(); } override size_t goodAllocSize(size_t s) { - return _processAllocator.goodAllocSize(s); + return processAllocator.goodAllocSize(s); } override void[] allocate(size_t n, TypeInfo ti = null) { - return _processAllocator.allocate(n, ti); + return processAllocator.allocate(n, ti); } override void[] alignedAllocate(size_t n, uint a) { - return _processAllocator.alignedAllocate(n, a); + return processAllocator.alignedAllocate(n, a); } override void[] allocateAll() { - return _processAllocator.allocateAll(); + return processAllocator.allocateAll(); } override bool expand(ref void[] b, size_t size) { - return _processAllocator.expand(b, size); + return processAllocator.expand(b, size); } override bool reallocate(ref void[] b, size_t size) { - return _processAllocator.reallocate(b, size); + return processAllocator.reallocate(b, size); } override bool alignedReallocate(ref void[] b, size_t size, uint alignment) { - return _processAllocator.alignedReallocate(b, size, alignment); + return processAllocator.alignedReallocate(b, size, alignment); } override Ternary owns(void[] b) { - return _processAllocator.owns(b); + return processAllocator.owns(b); } override Ternary resolveInternalPointer(const void* p, ref void[] result) { - return _processAllocator.resolveInternalPointer(p, result); + return processAllocator.resolveInternalPointer(p, result); } override bool deallocate(void[] b) { - return _processAllocator.deallocate(b); + return processAllocator.deallocate(b); } override bool deallocateAll() { - return _processAllocator.deallocateAll(); + return processAllocator.deallocateAll(); } override Ternary empty() { - return _processAllocator.empty(); + return processAllocator.empty(); } } @@ -589,7 +582,10 @@ allocator can be cast to $(D shared). */ @property shared(ISharedAllocator) processAllocator() { - return _processAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.concurrency : initOnce; + return initOnce!_processAllocator( + sharedAllocatorObject(GCAllocator.instance)); } /// Ditto From 739d08b6da1ec4e69afe3ee65bd8d9ae4415408e Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 12:26:26 -0400 Subject: [PATCH 235/262] Eliminate shared static this() from std.experimental.logger --- std/experimental/logger/core.d | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index b23e9452768..1d4fee6e8e0 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -8,11 +8,6 @@ import core.sync.mutex : Mutex; import std.experimental.logger.filelogger; -shared static this() -{ - stdSharedLoggerMutex = new Mutex; -} - /** This template evaluates if the passed $(D LogLevel) is active. The previously described version statements are used to decide if the $(D LogLevel) is active. The version statements only influence the compile @@ -1617,7 +1612,6 @@ abstract class Logger // Thread Global -private __gshared Mutex stdSharedLoggerMutex; private __gshared Logger stdSharedDefaultLogger; private shared Logger stdSharedLogger; private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; @@ -1632,15 +1626,12 @@ private @property Logger defaultSharedLoggerImpl() @trusted static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer; - synchronized (stdSharedLoggerMutex) - { + import std.concurrency : initOnce; + initOnce!stdSharedDefaultLogger({ auto buffer = cast(ubyte[]) _buffer; + return emplace!FileLogger(buffer, stderr, LogLevel.all); + }()); - if (stdSharedDefaultLogger is null) - { - stdSharedDefaultLogger = emplace!FileLogger(buffer, stderr, LogLevel.all); - } - } return stdSharedDefaultLogger; } From dba38b5640f120b92e39c7a9eafdb7aef4c20533 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 14:39:41 -0400 Subject: [PATCH 236/262] Revert "reimplement std.traits.ParameterStorageClassTuple()" --- std/traits.d | 81 +++++++++++++++++++--------------------------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/std/traits.d b/std/traits.d index 91b4d015b21..506dd3ffeaf 100644 --- a/std/traits.d +++ b/std/traits.d @@ -1010,11 +1010,8 @@ template arity(alias func) } /** -Get tuple, one per function parameter, of the storage classes of the parameters. -Params: - func = function symbol or type of function, delegate, or pointer to function -Returns: - a tuple of ParameterStorageClass bits +Returns a tuple consisting of the storage classes of the parameters of a +function $(D func). */ enum ParameterStorageClass : uint { @@ -1034,28 +1031,39 @@ enum ParameterStorageClass : uint template ParameterStorageClassTuple(func...) if (func.length == 1 && isCallable!func) { - alias Func = FunctionTypeOf!func; + alias Func = Unqual!(FunctionTypeOf!func); - static if (is(Func PT == __parameters)) + /* + * TypeFuncion: + * CallConvention FuncAttrs Arguments ArgClose Type + */ + alias Params = Parameters!Func; + + // chop off CallConvention and FuncAttrs + enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest; + + // demangle Arguments and store parameter storage classes in a tuple + template demangleNextParameter(string margs, size_t i = 0) { - template StorageClass(size_t i) + static if (i < Params.length) { - static if (i < PT.length) - { - alias StorageClass = TypeTuple!( - extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)), - StorageClass!(i + 1)); - } - else - alias StorageClass = TypeTuple!(); + enum demang = demangleParameterStorageClass(margs); + enum skip = mangledName!(Params[i]).length; // for bypassing Type + enum rest = demang.rest; + + alias demangleNextParameter = + TypeTuple!( + demang.value + 0, // workaround: "not evaluatable at ..." + demangleNextParameter!(rest[skip .. $], i + 1) + ); + } + else // went thru all the parameters + { + alias demangleNextParameter = TypeTuple!(); } - alias ParameterStorageClassTuple = StorageClass!0; - } - else - { - static assert(0, func[0].stringof ~ " is not a function"); - alias ParameterStorageClassTuple = TypeTuple!(); } + + alias ParameterStorageClassTuple = demangleNextParameter!margs; } /// @@ -1073,35 +1081,6 @@ template ParameterStorageClassTuple(func...) static assert(pstc[2] == STC.none); } -/***************** - * Convert string tuple Attribs to ParameterStorageClass bits - * Params: - * Attribs = string tuple - * Returns: - * ParameterStorageClass bits - */ -ParameterStorageClass extractParameterStorageClassFlags(Attribs...)() -{ - auto result = ParameterStorageClass.none; - foreach (attrib; Attribs) - { - final switch (attrib) with (ParameterStorageClass) - { - case "scope": result |= scope_; break; - case "out": result |= out_; break; - case "ref": result |= ref_; break; - case "lazy": result |= lazy_; break; - case "return": result |= return_; break; - } - } - /* Mimic behavor of original version of ParameterStorageClassTuple() - * to avoid breaking existing code. - */ - if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_)) - result = ParameterStorageClass.return_; - return result; -} - @safe unittest { alias STC = ParameterStorageClass; From 994dbce1343f5f8d9a9bb7a05756e9bba315d585 Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 15:13:31 -0400 Subject: [PATCH 237/262] Eliminate redundant __gshared, immutable data is shared already --- std/range/primitives.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index aeedf605601..761dd5443f0 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -2113,7 +2113,7 @@ if (isNarrowString!(C[])) static if (is(Unqual!C == char)) { - __gshared static immutable ubyte[] charWidthTab = [ + static immutable ubyte[] charWidthTab = [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, From c42e5941b4466a8acda4bc6b36b5b923cb3c133d Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:36:55 +0200 Subject: [PATCH 238/262] Re-enable linting tests at CircleCi --- circleci.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/circleci.sh b/circleci.sh index a9520081bad..80e528b5ac4 100755 --- a/circleci.sh +++ b/circleci.sh @@ -95,7 +95,7 @@ setup_repos() } # verify style guide -style() +style_lint() { # dscanner needs a more up-to-date DMD version source "$(CURL_USER_AGENT=\"$CURL_USER_AGENT\" bash ~/dlang/install.sh dmd-$DSCANNER_DMD_VER --activate)" @@ -105,7 +105,7 @@ style() # fix to a specific version of https://github.com/dlang/tools/tree/master/styles git -C ../tools checkout 60583c8363ff25d00017dffdb18c7ee7e7d9a343 - make -f posix.mak style DUB=$DUB + make -f posix.mak style_lint DUB=$DUB } # run unittest with coverage @@ -133,10 +133,18 @@ publictests() make -f posix.mak -j$N publictests DUB=$DUB } +# check modules for public unittests +has_public_example() +{ + make -f posix.mak -j$N has_public_example DUB=$DUB +} + case $1 in install-deps) install_deps ;; setup-repos) setup_repos ;; coverage) coverage ;; publictests) publictests ;; - style) style ;; + has_public_example) has_public_example;; + style_lint) style_lint ;; + *) echo "Unknown command"; exit 1;; esac From 052fcda9177013952882ce4d3dfb9dc6c0b3f3b2 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:43:07 +0200 Subject: [PATCH 239/262] Style fix: no space after assert( --- std/range/package.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 57a126557e1..6a9939c64e1 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -8332,7 +8332,7 @@ unittest Range r; r.arr = 10.iota.array; // for clarity - static assert (isForwardRange!Range); + static assert(isForwardRange!Range); enum hasSliceToEnd = hasSlicing!Range && is(typeof(Range.init[0 .. $]) == Range); assert(r.slide(2)[0].equal([0, 1])); @@ -8356,8 +8356,8 @@ unittest foreach (Range; AliasSeq!SliceableDummyRangesWithoutInfinity) { - static assert (hasSlicing!Range); - static assert (hasLength!Range); + static assert(hasSlicing!Range); + static assert(hasLength!Range); Range r; r.arr = 10.iota.array; // for clarity From 9392b4d9c08ac713e96872fd3128e9c40ff6dd35 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:44:30 +0200 Subject: [PATCH 240/262] Style fix: space after cast(..) --- std/digest/crc.d | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/std/digest/crc.d b/std/digest/crc.d index c98fd28610a..a82ced072de 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -390,36 +390,36 @@ unittest CRC64ECMA crc; crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[])x"2f121b7575789626"); + assert(crc.peek() == cast(ubyte[]) x"2f121b7575789626"); crc.start(); crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[])x"0000000000000000"); + assert(crc.finish() == cast(ubyte[]) x"0000000000000000"); digest = crc64ECMAOf(""); - assert(digest == cast(ubyte[])x"0000000000000000"); + assert(digest == cast(ubyte[]) x"0000000000000000"); //Test vector from http://rosettacode.org/wiki/CRC-32 assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4"); digest = crc64ECMAOf("a"); - assert(digest == cast(ubyte[])x"052b652e77840233"); + assert(digest == cast(ubyte[]) x"052b652e77840233"); digest = crc64ECMAOf("abc"); - assert(digest == cast(ubyte[])x"2776271a4a09d82c"); + assert(digest == cast(ubyte[]) x"2776271a4a09d82c"); digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"4b7cdce3746c449f"); + assert(digest == cast(ubyte[]) x"4b7cdce3746c449f"); digest = crc64ECMAOf("message digest"); - assert(digest == cast(ubyte[])x"6f9b8a3156c9bc5d"); + assert(digest == cast(ubyte[]) x"6f9b8a3156c9bc5d"); digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"2656b716e1bf0503"); + assert(digest == cast(ubyte[]) x"2656b716e1bf0503"); digest = crc64ECMAOf("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"bd3eb7765d0a22ae"); + assert(digest == cast(ubyte[]) x"bd3eb7765d0a22ae"); - assert(crcHexString(cast(ubyte[8])x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); + assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); } unittest @@ -428,36 +428,36 @@ unittest CRC64ISO crc; crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz"); - assert(crc.peek() == cast(ubyte[])x"f0494ab780989b42"); + assert(crc.peek() == cast(ubyte[]) x"f0494ab780989b42"); crc.start(); crc.put(cast(ubyte[])""); - assert(crc.finish() == cast(ubyte[])x"0000000000000000"); + assert(crc.finish() == cast(ubyte[]) x"0000000000000000"); digest = crc64ISOOf(""); - assert(digest == cast(ubyte[])x"0000000000000000"); + assert(digest == cast(ubyte[]) x"0000000000000000"); //Test vector from http://rosettacode.org/wiki/CRC-32 assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E"); digest = crc64ISOOf("a"); - assert(digest == cast(ubyte[])x"0000000000002034"); + assert(digest == cast(ubyte[]) x"0000000000002034"); digest = crc64ISOOf("abc"); - assert(digest == cast(ubyte[])x"0000000020c47637"); + assert(digest == cast(ubyte[]) x"0000000020c47637"); digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); - assert(digest == cast(ubyte[])x"5173f717971365e5"); + assert(digest == cast(ubyte[]) x"5173f717971365e5"); digest = crc64ISOOf("message digest"); - assert(digest == cast(ubyte[])x"a2c355bbc0b93f86"); + assert(digest == cast(ubyte[]) x"a2c355bbc0b93f86"); digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - assert(digest == cast(ubyte[])x"598B258292E40084"); + assert(digest == cast(ubyte[]) x"598B258292E40084"); digest = crc64ISOOf("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); - assert(digest == cast(ubyte[])x"760cd2d3588bf809"); + assert(digest == cast(ubyte[]) x"760cd2d3588bf809"); - assert(crcHexString(cast(ubyte[8])x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); + assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); } /** From d68692938538ac368b6efc6cbe4cad0bcbb033e3 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:46:10 +0200 Subject: [PATCH 241/262] Style fix: use space between a .. b --- std/range/package.d | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 6a9939c64e1..29044a50a4b 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7638,14 +7638,14 @@ public: * Notice that we only need to move for windowSize - 1 to the right: * source = [0, 1, 2, 3] (length: 4) * - source.slide(2) -> s = [[0, 1], [1, 2], [2, 3]] - * right pos for s[0..3]: 3 (upper) + 2 (windowSize) - 1 = 4 + * right pos for s[0 .. 3]: 3 (upper) + 2 (windowSize) - 1 = 4 * * - source.slide(3) -> s = [[0, 1, 2], [1, 2, 3]] - * right pos for s[0..2]: 2 (upper) + 3 (windowSize) - 1 = 4 + * right pos for s[0 .. 2]: 2 (upper) + 3 (windowSize) - 1 = 4 * * source = [0, 1, 2, 3, 4] (length: 5) * - source.slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] - * right pos for s[0..2]: 2 (upper) + 4 (windowSize) - 1 = 5 + * right pos for s[0 .. 2]: 2 (upper) + 4 (windowSize) - 1 = 5 */ return typeof(this) (_source[min(lower, len) .. min(upper + _windowSize - 1, len)], @@ -8250,17 +8250,17 @@ unittest )); // front = back - foreach (windowSize; 1..10) - foreach (stepSize; 1..10) + foreach (windowSize; 1 .. 10) + foreach (stepSize; 1 .. 10) { auto slider = r.slide(windowSize, stepSize); assert(slider.retro.retro.equal!equal(slider)); } } - assert(iota(1, 12).slide(2, 4)[0..3].equal!equal([[1, 2], [5, 6], [9, 10]])); - assert(iota(1, 12).slide(2, 4)[0..$].equal!equal([[1, 2], [5, 6], [9, 10]])); - assert(iota(1, 12).slide(2, 4)[$/2..$].equal!equal([[5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[0 .. 3].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[0 .. $].equal!equal([[1, 2], [5, 6], [9, 10]])); + assert(iota(1, 12).slide(2, 4)[$/2 .. $].equal!equal([[5, 6], [9, 10]])); // reverse assert(iota(1, 12).slide(2, 4).retro.equal!equal([[9, 10], [5, 6], [1, 2]])); From c5fde07c13c726c5b1a54e8e812f660af0cb74e5 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:47:34 +0200 Subject: [PATCH 242/262] Fix Ddoc warnings --- std/range/package.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 29044a50a4b..fa412552187 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -6918,7 +6918,7 @@ divisible by $(D chunkSize), the back element of this range will contain fewer than $(D chunkSize) elements. Params: - r = Range from which the chunks will be selected + source = Range from which the chunks will be selected chunkSize = Chunk size See_Also: $(LREF slide) @@ -7429,7 +7429,7 @@ Params: elements than `windowSize`. This can only happen if the initial range contains less elements than `windowSize`. In this case if `No.withFewerElements` an empty range will be returned. - r = Range from which the slide will be selected + source = Range from which the slide will be selected windowSize = Sliding window size stepSize = Steps between the windows (by default 1) From 3afbd28c436edb6dd068d8caeb677d1416e69f02 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 03:54:59 +0200 Subject: [PATCH 243/262] Fix Dscanner errors --- std/conv.d | 5 +++-- std/digest/crc.d | 4 ++-- std/range/package.d | 2 +- std/stdio.d | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/std/conv.d b/std/conv.d index ed6ab6e9549..4a3e458357e 100644 --- a/std/conv.d +++ b/std/conv.d @@ -5534,7 +5534,8 @@ pure nothrow @safe /* @nogc */ unittest void toTextRange(T, W)(T value, W writer) if (isIntegral!T && isOutputRange!(W, char)) { - import core.internal.string; + import core.internal.string : SignedStringBuf, signedToTempString, + UnsignedStringBuf, unsignedToTempString; if (value < 0) { @@ -5712,7 +5713,7 @@ OriginalType!E asOriginalType(E)(E value) if (is(E == enum)) } /// -unittest +@safe unittest { enum A { a = 42 } static assert(is(typeof(A.a.asOriginalType) == int)); diff --git a/std/digest/crc.d b/std/digest/crc.d index a82ced072de..242c24aab63 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -384,7 +384,7 @@ struct CRC(uint N, ulong P) if (N == 32 || N == 64) assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3"); } -unittest +@system unittest { ubyte[8] digest; @@ -422,7 +422,7 @@ unittest assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3"); } -unittest +@system unittest { ubyte[8] digest; diff --git a/std/range/package.d b/std/range/package.d index fa412552187..ec8280f6af2 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7888,7 +7888,7 @@ public: assert(iota(1, 4).slide(3).equal!equal([[1, 2, 3]])); } -unittest +@safe unittest { import std.algorithm.comparison : equal; diff --git a/std/stdio.d b/std/stdio.d index b08c3883152..06dc977cda3 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -4532,7 +4532,7 @@ Initialize with a message and an error code. // `handle` at once and steal the high bit to indicate that the globals have // been initialized. static shared uint spinlock; - import core.atomic; + import core.atomic : atomicLoad, atomicOp, MemoryOrder; if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2) { for (;;) @@ -4621,7 +4621,7 @@ alias stderr = makeGlobal!(core.stdc.stdio.stderr); } } -unittest +@safe unittest { // Retain backwards compatibility // https://issues.dlang.org/show_bug.cgi?id=17472 From 39647a80c0c80ce4486dbe04f65253c9a370b4d8 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 04:03:08 +0200 Subject: [PATCH 244/262] has_public_examples: Fix Makefile target --- posix.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posix.mak b/posix.mak index 4e7e5eab1dc..c0107ed2c0f 100644 --- a/posix.mak +++ b/posix.mak @@ -602,7 +602,7 @@ $(TEST_EXTRACTOR): $(TOOLS_DIR)/styles/tests_extractor.d $(LIB) has_public_example: $(LIB) # checks whether public function have public examples (for now some modules are excluded) rm -rf ./out - DFLAGS="$(DFLAGS) $(LIB) -defaultlib= -debuglib= $(LINKDL)" $(DUB) --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir . --ignore "etc,array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/stopwatch.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" + DFLAGS="$(DFLAGS) $(LIB) $(LINKDL)" $(DUB) -v --compiler=$${PWD}/$(DMD) --root=../tools/styles -c has_public_example -- --inputdir std --ignore "array.d,allocator,base64.d,bitmanip.d,concurrency.d,conv.d,csv.d,datetime/date.d,datetime/interval.d,datetime/package.d,datetime/stopwatch.d,datetime/systime.d,datetime/timezone.d,demangle.d,digest/hmac.d,digest/sha.d,encoding.d,exception.d,file.d,format.d,getopt.d,index.d,internal,isemail.d,json.d,logger/core.d,logger/nulllogger.d,math.d,mathspecial.d,net/curl.d,numeric.d,parallelism.d,path.d,process.d,random.d,range,regex/package.d,socket.d,stdio.d,string.d,traits.d,typecons.d,uni.d,unittest.d,uri.d,utf.d,uuid.d,xml.d,zlib.d" .PHONY : auto-tester-build auto-tester-build: all checkwhitespace From d47f9f110de8e11218e360ff754ae51894b0a691 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 04:16:16 +0200 Subject: [PATCH 245/262] has_public_example: Add missing public examples to std/digest/crc.d --- std/digest/crc.d | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/std/digest/crc.d b/std/digest/crc.d index 242c24aab63..7aaff655193 100644 --- a/std/digest/crc.d +++ b/std/digest/crc.d @@ -513,6 +513,24 @@ ubyte[8] crc64ECMAOf(T...)(T data) return digest!(CRC64ECMA, T)(data); } +/// +@system unittest +{ + ubyte[] data = [4,5,7,25]; + assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]); + + import std.utf : byChar; + assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]); + + ubyte[8] hash = "abc".crc64ECMAOf(); + assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]); + assert(hash == digest!CRC64ECMA("ab", "c")); + + import std.range : iota; + enum ubyte S = 5, F = 66; + assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]); +} + /** * This is a convenience alias for $(REF digest, std,digest,digest) using the * CRC64-ISO implementation. @@ -531,6 +549,25 @@ ubyte[8] crc64ISOOf(T...)(T data) return digest!(CRC64ISO, T)(data); } +/// +@system unittest +{ + ubyte[] data = [4,5,7,25]; + assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]); + + import std.utf : byChar; + assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]); + + ubyte[8] hash = "abc".crc64ISOOf(); + assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]); + assert(hash == digest!CRC64ISO("ab", "c")); + + import std.range : iota; + enum ubyte S = 5, F = 66; + + assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]); +} + /** * This is a convenience alias for $(REF toHexString, std,digest,digest) * producing the usual CRC32 string output. From 61717ecc7d748b6b8a7c817c42db9bb84995e162 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 07:46:40 +0200 Subject: [PATCH 246/262] Sort imports --- etc/c/curl.d | 2 +- std/algorithm/comparison.d | 10 ++--- std/algorithm/iteration.d | 26 +++++------ std/algorithm/mutation.d | 6 +-- std/algorithm/package.d | 2 +- std/algorithm/searching.d | 8 ++-- std/algorithm/setops.d | 2 +- std/algorithm/sorting.d | 24 +++++----- std/array.d | 36 +++++++-------- std/base64.d | 2 +- std/bigint.d | 12 ++--- std/bitmanip.d | 6 +-- std/c/freebsd/socket.d | 2 +- std/c/linux/linux.d | 6 +-- std/c/linux/socket.d | 2 +- std/c/osx/socket.d | 2 +- std/complex.d | 6 +-- std/concurrency.d | 2 +- std/container/array.d | 4 +- std/container/binaryheap.d | 6 +-- std/container/dlist.d | 2 +- std/container/rbtree.d | 6 +-- std/container/slist.d | 2 +- std/container/util.d | 10 ++--- std/conv.d | 12 ++--- std/csv.d | 2 +- std/datetime/package.d | 2 +- std/datetime/systime.d | 2 +- std/datetime/timezone.d | 4 +- std/digest/digest.d | 8 ++-- std/digest/hmac.d | 2 +- std/digest/sha.d | 2 +- std/encoding.d | 2 +- std/exception.d | 8 ++-- .../building_blocks/affix_allocator.d | 8 ++-- .../building_blocks/allocator_list.d | 10 ++--- .../building_blocks/bitmapped_block.d | 8 ++-- .../allocator/building_blocks/bucketizer.d | 6 +-- .../building_blocks/fallback_allocator.d | 2 +- .../allocator/building_blocks/free_list.d | 12 ++--- .../building_blocks/kernighan_ritchie.d | 28 ++++++------ .../allocator/building_blocks/quantizer.d | 2 +- .../allocator/building_blocks/region.d | 8 ++-- .../allocator/building_blocks/segregator.d | 4 +- .../building_blocks/stats_collector.d | 12 ++--- std/experimental/allocator/common.d | 4 +- std/experimental/allocator/package.d | 16 +++---- std/experimental/allocator/typed.d | 8 ++-- std/experimental/checkedint.d | 2 +- std/experimental/logger/core.d | 6 +-- std/experimental/logger/filelogger.d | 10 ++--- std/experimental/logger/multilogger.d | 2 +- std/experimental/logger/package.d | 2 +- std/experimental/typecons.d | 2 +- std/file.d | 14 +++--- std/format.d | 24 +++++----- std/functional.d | 4 +- std/getopt.d | 16 +++---- std/internal/cstring.d | 4 +- std/internal/math/biguintcore.d | 4 +- std/internal/scopebuffer.d | 2 +- std/internal/test/dummyrange.d | 4 +- std/json.d | 4 +- std/mathspecial.d | 4 +- std/meta.d | 2 +- std/mmfile.d | 12 ++--- std/net/curl.d | 2 +- std/net/isemail.d | 2 +- std/numeric.d | 10 ++--- std/outbuffer.d | 2 +- std/parallelism.d | 4 +- std/path.d | 10 ++--- std/process.d | 30 ++++++------- std/random.d | 18 ++++---- std/range/interfaces.d | 2 +- std/range/package.d | 16 +++---- std/range/primitives.d | 2 +- std/regex/internal/backtracking.d | 2 +- std/regex/internal/generator.d | 4 +- std/regex/internal/ir.d | 6 +-- std/regex/internal/kickstart.d | 4 +- std/regex/internal/parser.d | 4 +- std/regex/internal/tests.d | 10 ++--- std/regex/internal/thompson.d | 2 +- std/regex/package.d | 10 ++--- std/signals.d | 6 +-- std/socket.d | 14 +++--- std/stdio.d | 44 +++++++++---------- std/string.d | 6 +-- std/uni.d | 6 +-- std/uri.d | 4 +- std/utf.d | 34 +++++++------- std/uuid.d | 6 +-- std/variant.d | 2 +- std/windows/charset.d | 6 +-- std/windows/registry.d | 12 ++--- std/windows/syserror.d | 8 ++-- std/zip.d | 4 +- std/zlib.d | 2 +- 99 files changed, 385 insertions(+), 385 deletions(-) diff --git a/etc/c/curl.d b/etc/c/curl.d index 7ac597c9fc0..f5c709f0c07 100644 --- a/etc/c/curl.d +++ b/etc/c/curl.d @@ -35,8 +35,8 @@ module etc.c.curl; -import core.stdc.time; import core.stdc.config; +import core.stdc.time; import std.socket; // linux diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index 4642c86f810..566b686b16a 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -63,8 +63,8 @@ import std.functional; // : unaryFun, binaryFun; import std.range.primitives; import std.traits; // FIXME -import std.typecons; // : tuple, Tuple, Flag, Yes; import std.meta : allSatisfy; +import std.typecons; // : tuple, Tuple, Flag, Yes; /** Find $(D value) _among $(D values), returning the 1-based index @@ -825,8 +825,8 @@ template equal(alias pred = "a == b") /// @safe unittest { - import std.math : approxEqual; import std.algorithm.comparison : equal; + import std.math : approxEqual; int[] a = [ 1, 2, 4, 3 ]; assert(!equal(a, a[1..$])); @@ -851,8 +851,8 @@ range of range (of range...) comparisons. +/ @safe unittest { - import std.range : iota, chunks; import std.algorithm.comparison : equal; + import std.range : iota, chunks; assert(equal!(equal!equal)( [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], iota(0, 8).chunks(2).chunks(2) @@ -862,9 +862,9 @@ range of range (of range...) comparisons. @safe unittest { import std.algorithm.iteration : map; - import std.math : approxEqual; import std.internal.test.dummyrange : ReferenceForwardRange, ReferenceInputRange, ReferenceInfiniteForwardRange; + import std.math : approxEqual; // various strings assert(equal("æøå", "æøå")); //UTF8 vs UTF8 @@ -1069,8 +1069,8 @@ private: cols = c; if (_matrix.length < rc) { - import core.stdc.stdlib : realloc; import core.exception : onOutOfMemoryError; + import core.stdc.stdlib : realloc; const nbytes = mulu(rc, _matrix[0].sizeof, overflow); if (overflow) assert(0); auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 46284815e6b..ff832ab221a 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -682,8 +682,8 @@ private struct MapResult(alias fun, Range) @safe unittest { import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; import std.ascii : toUpper; + import std.internal.test.dummyrange; import std.range; import std.typecons : tuple; @@ -1959,8 +1959,8 @@ version(none) // this example requires support for non-equivalence relations /* FIXME: pure @safe nothrow*/ @system unittest { import std.algorithm.comparison : equal; - import std.typecons : tuple; import std.range.primitives; + import std.typecons : tuple; // Grouping by particular attribute of each element: auto range = @@ -2296,8 +2296,8 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR) @system unittest { import std.algorithm.comparison : equal; - import std.range.primitives; import std.range.interfaces; + import std.range.primitives; // joiner() should work for non-forward ranges too. auto r = inputRangeObject(["abc", "def"]); assert(equal(joiner(r, "xyz"), "abcxyzdef")); @@ -2607,8 +2607,8 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) @safe unittest { - import std.algorithm.internal : algoFormat; import std.algorithm.comparison : equal; + import std.algorithm.internal : algoFormat; struct TransientRange { @@ -2657,8 +2657,8 @@ if (isInputRange!RoR && isInputRange!(ElementType!RoR)) // Issue 8061 @system unittest { - import std.range.interfaces; import std.conv : to; + import std.range.interfaces; auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]); assert(isForwardRange!(typeof(r))); @@ -3848,9 +3848,9 @@ if (is(typeof(binaryFun!pred(r.front, s)) : bool) @safe unittest { - import std.internal.test.dummyrange; import std.algorithm; import std.array : array; + import std.internal.test.dummyrange; import std.range : retro; assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); @@ -4095,8 +4095,8 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) @safe unittest { import std.algorithm.comparison : equal; - import std.conv : text; import std.array : split; + import std.conv : text; auto s = ",abc, de, fg,hi,"; auto sp0 = splitter(s, ','); @@ -4360,8 +4360,8 @@ private struct SplitterResult(alias isTerminator, Range) @safe unittest { - import std.algorithm.internal : algoFormat; import std.algorithm.comparison : equal; + import std.algorithm.internal : algoFormat; import std.internal.test.dummyrange; void compare(string sentence, string[] witness) @@ -4395,8 +4395,8 @@ private struct SplitterResult(alias isTerminator, Range) @safe unittest { - import std.algorithm.internal : algoFormat; import std.algorithm.comparison : equal; + import std.algorithm.internal : algoFormat; import std.range; struct Entry @@ -4560,10 +4560,10 @@ if (isSomeChar!C) @safe unittest { - import std.algorithm.internal : algoFormat; import std.algorithm.comparison : equal; - import std.conv : text; + import std.algorithm.internal : algoFormat; import std.array : split; + import std.conv : text; // Check consistency: // All flavors of split should produce the same results @@ -4953,8 +4953,8 @@ if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) /// @safe unittest { - import std.algorithm.mutation : copy; import std.algorithm.comparison : equal; + import std.algorithm.mutation : copy; int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); @@ -5125,8 +5125,8 @@ if (isRandomAccessRange!Range && hasLength!Range) /// this(Range r) { - import std.range : iota; import std.array : array; + import std.range : iota; this._r = r; _state = r.length ? new size_t[r.length-1] : null; diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index f1aa9ee4769..49f3c850c7f 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -148,8 +148,8 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange) private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back) if (isInputRange!InputRange && isForwardRange!ForwardRange) { - import std.range : take, Take; import std.array : sameHead; + import std.range : take, Take; enum bool sameHeadExists = is(typeof(front.sameHead(back))); size_t result; @@ -1095,8 +1095,8 @@ pure nothrow @safe @nogc unittest @safe unittest { - import std.traits; import std.exception : assertCTFEable; + import std.traits; assertCTFEable!((){ Object obj1 = new Object; @@ -1209,8 +1209,8 @@ private T moveImpl(T)(ref T source) @safe unittest { - import std.traits; import std.exception : assertCTFEable; + import std.traits; assertCTFEable!((){ Object obj1 = new Object; diff --git a/std/algorithm/package.d b/std/algorithm/package.d index fab759773df..d357a4f0ef2 100644 --- a/std/algorithm/package.d +++ b/std/algorithm/package.d @@ -190,8 +190,8 @@ module std.algorithm; public import std.algorithm.comparison; public import std.algorithm.iteration; public import std.algorithm.mutation; -public import std.algorithm.setops; public import std.algorithm.searching; +public import std.algorithm.setops; public import std.algorithm.sorting; static import std.functional; diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 79f79597ab1..72a6f341d39 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -1164,8 +1164,8 @@ if (isInputRange!R && @safe unittest { import std.algorithm.iteration : filterBidirectional; - import std.meta : AliasSeq; import std.conv : to; + import std.meta : AliasSeq; foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) { @@ -1631,8 +1631,8 @@ if (isInputRange!InputRange && { import std.algorithm.comparison : equal; import std.container : SList; - import std.range.primitives : empty; import std.range; + import std.range.primitives : empty; auto arr = assumeSorted!"a < b"([1, 2, 4, 4, 4, 4, 5, 6, 9]); assert(find(arr, 4) == assumeSorted!"a < b"([4, 4, 4, 4, 5, 6, 9])); @@ -1956,8 +1956,8 @@ if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRa // of the first element of the needle in haystack. // When it is found O(walklength(needle)) steps are performed. // 8829 enhancement - import std.range : SortedRange; import std.algorithm.comparison : mismatch; + import std.range : SortedRange; static if (is(R1 == R2) && is(R1 : SortedRange!TT, TT) && pred == "a == b") @@ -2383,8 +2383,8 @@ if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) @safe unittest { - import std.algorithm.internal : rndstuff; import std.algorithm.comparison : equal; + import std.algorithm.internal : rndstuff; import std.meta : AliasSeq; import std.range : retro; diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index 4b23446d4e3..f948cb0635c 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -247,9 +247,9 @@ if (!allSatisfy!(isForwardRange, R1, R2) || @safe unittest { - import std.algorithm.searching : canFind; import std.algorithm.comparison : equal; import std.algorithm.iteration : map; + import std.algorithm.searching : canFind; import std.typecons : tuple; import std.range; diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 384a9c00ec8..9bc5bf311be 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -75,14 +75,14 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) */ module std.algorithm.sorting; -import std.typecons : Flag; import std.algorithm.mutation : SwapStrategy; import std.functional; // : unaryFun, binaryFun; import std.range.primitives; +import std.typecons : Flag; // FIXME +import std.meta; // : allSatisfy; import std.range; // : SortedRange; import std.traits; -import std.meta; // : allSatisfy; /** Specifies whether the output of certain algorithm is desired in sorted @@ -518,10 +518,10 @@ if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Rang /// @safe unittest { + import std.algorithm.mutation : SwapStrategy; import std.algorithm.searching : count, find; import std.conv : text; import std.range.primitives : empty; - import std.algorithm.mutation : SwapStrategy; auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; auto arr = Arr.dup; @@ -746,8 +746,8 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range) assert(pivot == 0 || pivot == 1); assert(a == [ 42, 42 ]); - import std.random; import std.algorithm.iteration : map; + import std.random; import std.stdio; auto s = unpredictableSeed; auto g = Random(s); @@ -830,8 +830,8 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range // The algorithm is described in "Engineering a sort function" by // Jon Bentley et al, pp 1257. - import std.algorithm.mutation : swap, swapAt, swapRanges; import std.algorithm.comparison : min; + import std.algorithm.mutation : swap, swapAt, swapRanges; import std.typecons : tuple; alias lessFun = binaryFun!less; @@ -986,8 +986,8 @@ if (isRandomAccessRange!Range && !isInfinite!Range && isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex && isIntegral!(ElementType!RangeIndex)) { - import std.exception : enforce; import std.conv : to; + import std.exception : enforce; alias IndexType = Unqual!(ElementType!RangeIndex); enforce(r.length == index.length, @@ -1443,8 +1443,8 @@ template multiSort(less...) //if (less.length > 1) auto multiSort(Range)(Range r) if (validPredicates!(ElementType!Range, less)) { - import std.range : assumeSorted; import std.meta : AliasSeq; + import std.range : assumeSorted; static if (is(typeof(less[$ - 1]) == SwapStrategy)) { enum ss = less[$ - 1]; @@ -1899,8 +1899,8 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || // Sorting floating-point numbers in presence of NaN double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan]; - import std.math : cmp, isIdentical; import std.algorithm.comparison : equal; + import std.math : cmp, isIdentical; sort!((a, b) => cmp(a, b) < 0)(numbers); @@ -2038,8 +2038,8 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || private void quickSortImpl(alias less, Range)(Range r, size_t depth) { - import std.algorithm.mutation : swap, swapAt; import std.algorithm.comparison : min, max; + import std.algorithm.mutation : swap, swapAt; alias Elem = ElementType!(Range); enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof); @@ -2893,14 +2893,14 @@ schwartzSort(alias transform, alias less = "a < b", if (isRandomAccessRange!R && hasLength!R) { import std.conv : emplace; - import std.string : representation; import std.range : zip, SortedRange; + import std.string : representation; alias T = typeof(unaryFun!transform(r.front)); static trustedMalloc(size_t len) @trusted { - import core.stdc.stdlib : malloc; import core.checkedint : mulu; + import core.stdc.stdlib : malloc; bool overflow; const nbytes = mulu(len, T.sizeof, overflow); if (overflow) assert(0); @@ -3436,8 +3436,8 @@ private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)( size_t maxSize = 1000, T minValue = 0, T maxValue = 255) { - import std.random : unpredictableSeed, Random, uniform; import std.algorithm.iteration : map; + import std.random : unpredictableSeed, Random, uniform; auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize); return iota(0, size).map!(_ => uniform(minValue, maxValue)).array; } diff --git a/std/array.d b/std/array.d index 4d923776be4..e3d94ad17ae 100644 --- a/std/array.d +++ b/std/array.d @@ -76,10 +76,10 @@ Source: $(PHOBOSSRC std/_array.d) */ module std.array; +static import std.algorithm.iteration; // FIXME, remove with alias of splitter +import std.functional; import std.meta; import std.traits; -import std.functional; -static import std.algorithm.iteration; // FIXME, remove with alias of splitter import std.range.primitives; public import std.range.primitives : save, empty, popFront, popBack, front, back; @@ -316,8 +316,8 @@ if (isNarrowString!String) // Bugzilla 10220 @safe unittest { - import std.exception; import std.algorithm.comparison : equal; + import std.exception; import std.range : repeat; static struct S @@ -418,8 +418,8 @@ of Tuple's of key and value pairs from the given associative array. */ auto byPair(Key, Value)(Value[Key] aa) { - import std.typecons : tuple; import std.algorithm.iteration : map; + import std.typecons : tuple; return aa.byKeyValue.map!(pair => tuple(pair.key, pair.value)); } @@ -427,8 +427,8 @@ auto byPair(Key, Value)(Value[Key] aa) /// @system unittest { - import std.typecons : tuple, Tuple; import std.algorithm.sorting : sort; + import std.typecons : tuple, Tuple; auto aa = ["a": 1, "b": 2, "c": 3]; Tuple!(string, int)[] pairs; @@ -641,8 +641,8 @@ private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow } else { - import core.stdc.string : memset; import core.memory : GC; + import core.stdc.string : memset; import core.checkedint : mulu; bool overflow; @@ -1031,9 +1031,9 @@ private template isInputRangeOrConvertible(E) @system unittest { // @system due to insertInPlace + import core.exception; import std.algorithm.comparison : equal; import std.algorithm.iteration : filter; - import core.exception; import std.conv : to; import std.exception; @@ -1511,8 +1511,8 @@ if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(range.front)))) @safe unittest { - import std.conv; import std.algorithm.comparison : cmp; + import std.conv; debug(std_array) printf("array.split\n"); foreach (S; AliasSeq!(string, wstring, dstring, @@ -1841,8 +1841,8 @@ if (isInputRange!RoR && @system unittest { - import std.conv : to; import std.algorithm; + import std.conv : to; import std.range; debug(std_array) printf("array.join.unittest\n"); @@ -2065,8 +2065,8 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) @safe unittest { - import std.conv : to; import std.algorithm.comparison : cmp; + import std.conv : to; debug(std_array) printf("array.replace.unittest\n"); @@ -2098,8 +2098,8 @@ if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) @safe unittest { - import std.conv : to; import std.algorithm.searching : skipOver; + import std.conv : to; struct CheckOutput(C) { @@ -2172,9 +2172,9 @@ if (isInputRange!Range && @system unittest { import core.exception; + import std.algorithm.iteration : filter; import std.conv : to; import std.exception; - import std.algorithm.iteration : filter; auto a = [ 1, 2, 3, 4 ]; @@ -2333,8 +2333,8 @@ if (is(typeof(replace(array, from, to, stuff)))) void testStringReplaceInPlace(T, U)() { - import std.conv; import std.algorithm.comparison : equal; + import std.conv; auto a = unicoded.to!(U[]); auto b = unicodedLong.to!(U[]); @@ -2376,9 +2376,9 @@ if (is(typeof(replace(array, from, to, stuff)))) @system unittest { + import core.exception; import std.algorithm.comparison : equal; import std.algorithm.iteration : filter; - import core.exception; import std.conv : to; import std.exception; @@ -2509,8 +2509,8 @@ if (isDynamicArray!(E[]) && @safe unittest { - import std.conv : to; import std.algorithm.comparison : cmp; + import std.conv : to; debug(std_array) printf("array.replaceFirst.unittest\n"); @@ -2624,8 +2624,8 @@ if (isDynamicArray!(E[]) && @safe unittest { - import std.conv : to; import std.algorithm.comparison : cmp; + import std.conv : to; debug(std_array) printf("array.replaceLast.unittest\n"); @@ -3359,8 +3359,8 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) @safe unittest { - import std.typecons; import std.algorithm; + import std.typecons; //10690 [tuple(1)].filter!(t => true).array; // No error [tuple("A")].filter!(t => true).array; // error @@ -3489,8 +3489,8 @@ Appender!(E[]) appender(A : E[], E)(auto ref A array) @safe unittest //Test large allocations (for GC.extend) { - import std.range; import std.algorithm.comparison : equal; + import std.range; Appender!(char[]) app; app.reserve(1); //cover reserve on non-initialized foreach (_; 0 .. 100_000) diff --git a/std/base64.d b/std/base64.d index 63c00a6ed09..59c941e27b4 100644 --- a/std/base64.d +++ b/std/base64.d @@ -1737,8 +1737,8 @@ class Base64Exception : Exception @system unittest { - import std.algorithm.sorting : sort; import std.algorithm.comparison : equal; + import std.algorithm.sorting : sort; import std.conv; import std.file; import std.stdio; diff --git a/std/bigint.d b/std/bigint.d index 35bb2e993d0..861df64b1ce 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -27,10 +27,10 @@ module std.bigint; import std.conv : ConvException; -private import std.internal.math.biguintcore; private import std.format : FormatSpec, FormatException; -private import std.traits; +private import std.internal.math.biguintcore; private import std.range.primitives; +private import std.traits; /** A struct representing an arbitrary precision integer. * @@ -69,9 +69,9 @@ public: { import std.algorithm.iteration : filterBidirectional; import std.algorithm.searching : startsWith; - import std.utf : byChar; - import std.exception : enforce; import std.conv : ConvException; + import std.exception : enforce; + import std.utf : byChar; enforce!ConvException(!s.empty, "Can't initialize BigInt with an empty range"); @@ -125,8 +125,8 @@ public: @system unittest { // system because of the dummy ranges eventually call std.array!string - import std.internal.test.dummyrange; import std.exception : assertThrown; + import std.internal.test.dummyrange; auto r1 = new ReferenceBidirectionalRange!dchar("101"); auto big1 = BigInt(r1); @@ -1503,8 +1503,8 @@ unittest assert(__traits(compiles, foo(cbi))); assert(__traits(compiles, foo(ibi))); - import std.meta : AliasSeq; import std.conv : to; + import std.meta : AliasSeq; foreach (T1; AliasSeq!(BigInt, const(BigInt), immutable(BigInt))) { diff --git a/std/bitmanip.d b/std/bitmanip.d index b2e4be33b8a..c38a2a8cec4 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -632,9 +632,9 @@ unittest // Issue 12477 @system unittest { + import core.exception : AssertError; import std.algorithm.searching : canFind; import std.bitmanip : bitfields; - import core.exception : AssertError; static struct S { @@ -772,8 +772,8 @@ struct BitArray { private: - import std.format : FormatSpec; import core.bitop : bts, btr, bsf, bt; + import std.format : FormatSpec; size_t _len; size_t* _ptr; @@ -3779,8 +3779,8 @@ if (canSwapEndianness!T && isOutputRange!(R, ubyte)) @system unittest { - import std.format : format; import std.array; + import std.format : format; import std.meta; foreach (endianness; AliasSeq!(Endian.bigEndian, Endian.littleEndian)) { diff --git a/std/c/freebsd/socket.d b/std/c/freebsd/socket.d index 5a73d47572c..35fa4c7810f 100644 --- a/std/c/freebsd/socket.d +++ b/std/c/freebsd/socket.d @@ -11,7 +11,7 @@ module std.c.freebsd.socket; version (FreeBSD): public import core.sys.posix.netdb; -public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, SOCK_RDM, MSG_NOSIGNAL; public import core.sys.posix.netinet.in_ : IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP, IPPROTO_ND, IPPROTO_MAX, INADDR_LOOPBACK, INADDR_NONE; +public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, SOCK_RDM, MSG_NOSIGNAL; diff --git a/std/c/linux/linux.d b/std/c/linux/linux.d index 165068a1034..a11c474eabd 100644 --- a/std/c/linux/linux.d +++ b/std/c/linux/linux.d @@ -60,11 +60,11 @@ public import core.sys.posix.dirent; public import core.sys.posix.dlfcn; public import core.sys.posix.fcntl; public import core.sys.posix.pwd; -public import core.sys.posix.time; -public import core.sys.posix.unistd; -public import core.sys.posix.utime; public import core.sys.posix.sys.mman; public import core.sys.posix.sys.stat; public import core.sys.posix.sys.time; public import core.sys.posix.sys.types; public import core.sys.posix.sys.wait; +public import core.sys.posix.time; +public import core.sys.posix.unistd; +public import core.sys.posix.utime; diff --git a/std/c/linux/socket.d b/std/c/linux/socket.d index 59c85c1c470..fcc577d1cd2 100644 --- a/std/c/linux/socket.d +++ b/std/c/linux/socket.d @@ -16,8 +16,8 @@ version (linux): private import core.stdc.stdint; public import core.sys.posix.arpa.inet; public import core.sys.posix.netdb; -public import core.sys.posix.netinet.tcp; public import core.sys.posix.netinet.in_; +public import core.sys.posix.netinet.tcp; public import core.sys.posix.sys.select; public import core.sys.posix.sys.socket; diff --git a/std/c/osx/socket.d b/std/c/osx/socket.d index 835ed276cd2..167af36a88e 100644 --- a/std/c/osx/socket.d +++ b/std/c/osx/socket.d @@ -16,8 +16,8 @@ version (OSX): private import core.stdc.stdint; public import core.sys.posix.arpa.inet; public import core.sys.posix.netdb; -public import core.sys.posix.netinet.tcp; public import core.sys.posix.netinet.in_; +public import core.sys.posix.netinet.tcp; public import core.sys.posix.sys.select; public import core.sys.posix.sys.socket; diff --git a/std/complex.d b/std/complex.d index 225e0d48755..77638229220 100644 --- a/std/complex.d +++ b/std/complex.d @@ -150,8 +150,8 @@ if (isFloatingPoint!T) FormatSpec!Char formatSpec) const if (isOutputRange!(Writer, const(Char)[])) { - import std.math : signbit; import std.format : formatValue; + import std.math : signbit; import std.range.primitives : put; formatValue(w, re, formatSpec); if (signbit(im) == 0) @@ -440,8 +440,8 @@ if (isFloatingPoint!T) @safe pure nothrow unittest { - import std.math; import std.complex; + import std.math; enum EPS = double.epsilon; auto c1 = complex(1.0, 1.0); @@ -849,8 +849,8 @@ Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc /// @safe pure nothrow unittest { - import std.math; import std.complex; + import std.math; assert(cos(complex(0.0)) == 1.0); assert(cos(complex(1.3L)) == std.math.cos(1.3L)); assert(cos(complex(0, 5.2L)) == cosh(5.2L)); diff --git a/std/concurrency.d b/std/concurrency.d index 44b9d8f8285..2fbcd308bae 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -975,8 +975,8 @@ bool register(string name, Tid tid) */ bool unregister(string name) { - import std.algorithm.searching : countUntil; import std.algorithm.mutation : remove, SwapStrategy; + import std.algorithm.searching : countUntil; synchronized (registryLock) { diff --git a/std/container/array.d b/std/container/array.d index b92a9c3e36b..af01deff11e 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -18,9 +18,9 @@ */ module std.container.array; +import core.exception : RangeError; import std.range.primitives; import std.traits; -import core.exception : RangeError; public import std.container.util; @@ -419,8 +419,8 @@ if (!is(Unqual!T == bool)) this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) { - import std.conv : emplace; import core.checkedint : mulu; + import std.conv : emplace; bool overflow; const nbytes = mulu(values.length, T.sizeof, overflow); if (overflow) assert(0); diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 4ce3953ab0d..4adf6045436 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -65,11 +65,11 @@ container. struct BinaryHeap(Store, alias less = "a < b") if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) { - import std.functional : binaryFun; - import std.exception : enforce; import std.algorithm.comparison : min; import std.algorithm.mutation : move, swapAt; import std.algorithm.sorting : HeapOps; + import std.exception : enforce; + import std.functional : binaryFun; import std.typecons : RefCounted, RefCountedAutoInitialize; static if (isRandomAccessRange!Store) @@ -565,8 +565,8 @@ BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, @system unittest { - import std.internal.test.dummyrange; import std.algorithm.comparison : equal; + import std.internal.test.dummyrange; alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random); diff --git a/std/container/dlist.d b/std/container/dlist.d index d2e90782794..d7af3609c9c 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -21,8 +21,8 @@ module std.container.dlist; /// @safe unittest { - import std.container : DList; import std.algorithm.comparison : equal; + import std.container : DList; auto s = DList!int(1, 2, 3); assert(equal(s[], [1, 2, 3])); diff --git a/std/container/rbtree.d b/std/container/rbtree.d index 524282e1764..5342b1692e4 100644 --- a/std/container/rbtree.d +++ b/std/container/rbtree.d @@ -19,8 +19,8 @@ module std.container.rbtree; /// @safe pure unittest { - import std.container.rbtree; import std.algorithm.comparison : equal; + import std.container.rbtree; auto rbt = redBlackTree(3, 1, 4, 2, 5); assert(rbt.front == 1); @@ -56,8 +56,8 @@ module std.container.rbtree; assert(equal(ubt[], [0, 0, 1, 1])); } -import std.functional : binaryFun; import std.format; +import std.functional : binaryFun; public import std.container.util; @@ -740,8 +740,8 @@ final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) if (is(typeof(binaryFun!less(T.init, T.init)))) { import std.meta : allSatisfy; - import std.range.primitives : isInputRange, walkLength; import std.range : Take; + import std.range.primitives : isInputRange, walkLength; import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible; alias _less = binaryFun!less; diff --git a/std/container/slist.d b/std/container/slist.d index 3cd67a81ff1..820b0bbac45 100644 --- a/std/container/slist.d +++ b/std/container/slist.d @@ -21,8 +21,8 @@ module std.container.slist; /// @safe unittest { - import std.container : SList; import std.algorithm.comparison : equal; + import std.container : SList; auto s = SList!int(1, 2, 3); assert(equal(s[], [1, 2, 3])); diff --git a/std/container/util.d b/std/container/util.d index aed1f1aa680..5be9e7d5470 100644 --- a/std/container/util.d +++ b/std/container/util.d @@ -54,8 +54,8 @@ if (is(T == struct) || is(T == class)) /// @system unittest { - import std.container; import std.algorithm.comparison : equal; + import std.container; auto arr = make!(Array!int)([4, 2, 3, 1]); assert(equal(arr[], [4, 2, 3, 1])); @@ -70,8 +70,8 @@ if (is(T == struct) || is(T == class)) @system unittest { - import std.container; import std.algorithm.comparison : equal; + import std.container; auto arr1 = make!(Array!dchar)(); assert(arr1.empty); @@ -87,8 +87,8 @@ if (is(T == struct) || is(T == class)) // Issue 8895 @safe unittest { - import std.container; import std.algorithm.comparison : equal; + import std.container; auto a = make!(DList!int)(1,2,3,4); auto b = make!(DList!int)(1,2,3,4); @@ -135,9 +135,9 @@ if (!is(Container)) /// @system unittest { + import std.algorithm.comparison : equal; import std.container.array, std.container.rbtree, std.container.slist; import std.range : iota; - import std.algorithm.comparison : equal; auto arr = make!Array(iota(5)); assert(equal(arr[], [0, 1, 2, 3, 4])); @@ -155,8 +155,8 @@ if (!is(Container)) @safe unittest { - import std.container.rbtree; import std.algorithm.comparison : equal; + import std.container.rbtree; auto rbtmin = make!(RedBlackTree, "a < b", false)(3, 2, 2, 1); assert(equal(rbtmin[], [1, 2, 3])); diff --git a/std/conv.d b/std/conv.d index 4a3e458357e..85a62cfdc08 100644 --- a/std/conv.d +++ b/std/conv.d @@ -129,8 +129,8 @@ private } else { - import std.format : FormatSpec, formatValue; import std.array : appender; + import std.format : FormatSpec, formatValue; auto w = appender!T(); FormatSpec!(ElementEncodingType!T) f; @@ -957,8 +957,8 @@ if (!(isImplicitlyConvertible!(S, T) && } } - import std.format : FormatSpec, formatValue; import std.array : appender; + import std.format : FormatSpec, formatValue; //Default case, delegate to format //Note: we don't call toStr directly, to avoid duplicate work. @@ -1019,8 +1019,8 @@ if (!(isImplicitlyConvertible!(S, T) && !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) && !isInfinite!S && isExactSomeString!T && !isCopyable!S) { - import std.format : FormatSpec, formatValue; import std.array : appender; + import std.format : FormatSpec, formatValue; auto w = appender!T(); FormatSpec!(ElementEncodingType!T) f; @@ -2021,8 +2021,8 @@ Lerr: @safe unittest { - import std.exception; import std.algorithm.comparison : equal; + import std.exception; struct InputString { string _s; @@ -2637,9 +2637,9 @@ Target parse(Target, Source)(ref Source source) if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) && isFloatingPoint!Target && !is(Target == enum)) { + import core.stdc.math : HUGE_VAL; import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; import std.exception : enforce; - import core.stdc.math : HUGE_VAL; static if (isNarrowString!Source) { @@ -5251,8 +5251,8 @@ version(unittest) @safe unittest //@@@9559@@@ { import std.algorithm.iteration : map; - import std.typecons : Nullable; import std.array : array; + import std.typecons : Nullable; alias I = Nullable!int; auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); auto asArray = array(ints); diff --git a/std/csv.d b/std/csv.d index be286c9a7e7..a896159b5b0 100644 --- a/std/csv.d +++ b/std/csv.d @@ -92,9 +92,9 @@ module std.csv; import std.conv; +import std.exception; // basicExceptionCtors import std.range.primitives; import std.traits; -import std.exception; // basicExceptionCtors /** * Exception containing the row and column for when an exception was thrown. diff --git a/std/datetime/package.d b/std/datetime/package.d index 9395590a002..976d06ddb79 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -116,9 +116,9 @@ public import std.datetime.systime; public import std.datetime.timezone; import core.exception : AssertError; -import std.typecons : Flag, Yes, No; import std.functional : unaryFun; import std.traits; +import std.typecons : Flag, Yes, No; // Verify module example. diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 7ef14b44f60..46eee5c8325 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -10,8 +10,8 @@ module std.datetime.systime; import core.time; import std.datetime.date; import std.datetime.timezone; -import std.format : format; import std.exception : enforce; +import std.format : format; import std.range.primitives; import std.traits : isIntegral, isSigned, isSomeString, Unqual; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 17841bbb2f3..0b3bfa74e2d 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -2081,10 +2081,10 @@ public: static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted { import std.algorithm.sorting : sort; - import std.range : retro; + import std.conv : to; import std.format : format; import std.path : asNormalizedPath, chainPath; - import std.conv : to; + import std.range : retro; name = strip(name); diff --git a/std/digest/digest.d b/std/digest/digest.d index 48cbe35da43..9114cb3b2b1 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -63,10 +63,10 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi */ module std.digest.digest; +public import std.ascii : LetterCase; import std.meta : allSatisfy; -import std.traits; import std.range.primitives; -public import std.ascii : LetterCase; +import std.traits; /// @@ -260,8 +260,8 @@ version(ExampleDigest) { //Using the OutputRange feature import std.algorithm.mutation : copy; - import std.range : repeat; import std.digest.md; + import std.range : repeat; auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); auto ctx = makeDigest!MD5(); @@ -629,8 +629,8 @@ interface Digest { //Using the OutputRange feature import std.algorithm.mutation : copy; - import std.range : repeat; import std.digest.md; + import std.range : repeat; auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); auto ctx = new MD5Digest(); diff --git a/std/digest/hmac.d b/std/digest/hmac.d index 0e6158e0b59..63fffdd5004 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -268,9 +268,9 @@ if (isDigest!H) /// @system pure nothrow @nogc unittest { + import std.algorithm.iteration : map; import std.digest.sha, std.digest.hmac; import std.string : representation; - import std.algorithm.iteration : map; string data = "Hello, world"; auto digest = data.representation .map!(a => cast(ubyte)(a+1)) diff --git a/std/digest/sha.d b/std/digest/sha.d index 5494e7b3186..4f661f7c2b9 100644 --- a/std/digest/sha.d +++ b/std/digest/sha.d @@ -882,8 +882,8 @@ alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte @system unittest { - import std.range; import std.conv : hexString; + import std.range; ubyte[20] digest; ubyte[28] digest224; diff --git a/std/encoding.d b/std/encoding.d index 9e9da74725c..33249e1fb48 100644 --- a/std/encoding.d +++ b/std/encoding.d @@ -103,9 +103,9 @@ Distributed under the Boost Software License, Version 1.0. */ module std.encoding; +import std.range.primitives; import std.traits; import std.typecons; -import std.range.primitives; @system unittest { diff --git a/std/exception.d b/std/exception.d index 44854b87599..5767e8710a0 100644 --- a/std/exception.d +++ b/std/exception.d @@ -631,8 +631,8 @@ if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LI @system unittest { - import std.array : empty; import core.exception : OutOfMemoryError; + import std.array : empty; assertNotThrown(enforceEx!Exception(true)); assertNotThrown(enforceEx!Exception(true, "blah")); assertNotThrown(enforceEx!OutOfMemoryError(true)); @@ -1658,8 +1658,8 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate //Verify Examples @system unittest { - import std.string; import std.conv; + import std.string; //Revert to a default value upon an error: assert("x".to!int().ifThrown(0) == 0); @@ -1690,9 +1690,9 @@ CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate @system unittest { - import std.string; - import std.conv; import core.exception; + import std.conv; + import std.string; //Basic behaviour - all versions. assert("1".to!int().ifThrown(0) == 1); assert("x".to!int().ifThrown(0) == 0); diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index 761840dc54a..cfeae0cdb77 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -19,15 +19,15 @@ The following methods are defined if $(D Allocator) defines them, and forward to */ struct AffixAllocator(Allocator, Prefix, Suffix = void) { + import std.algorithm.comparison : min; import std.conv : emplace; + import std.experimental.allocator : IAllocator, theAllocator; import std.experimental.allocator.common : stateSize, forwardToMember, roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf, hasStaticallyKnownAlignment; - import std.experimental.allocator : IAllocator, theAllocator; + import std.math : isPowerOf2; import std.traits : hasMember; - import std.algorithm.comparison : min; import std.typecons : Ternary; - import std.math : isPowerOf2; static if (hasStaticallyKnownAlignment!Allocator) { @@ -419,8 +419,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) @system unittest { - import std.experimental.allocator.gc_allocator; import std.experimental.allocator; + import std.experimental.allocator.gc_allocator; import std.typecons : Ternary; alias MyAllocator = AffixAllocator!(GCAllocator, uint); auto a = MyAllocator.instance.makeArray!(shared int)(100); diff --git a/std/experimental/allocator/building_blocks/allocator_list.d b/std/experimental/allocator/building_blocks/allocator_list.d index bd212fbb4f2..1ea27a602fd 100644 --- a/std/experimental/allocator/building_blocks/allocator_list.d +++ b/std/experimental/allocator/building_blocks/allocator_list.d @@ -1,8 +1,8 @@ /// module std.experimental.allocator.building_blocks.allocator_list; -import std.experimental.allocator.common; import std.experimental.allocator.building_blocks.null_allocator; +import std.experimental.allocator.common; import std.experimental.allocator.gc_allocator; version(unittest) import std.stdio; @@ -64,11 +64,11 @@ called `factory`. */ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator) { - import std.traits : hasMember; import std.conv : emplace; - import std.typecons : Ternary; import std.experimental.allocator.building_blocks.stats_collector : StatsCollector, Options; + import std.traits : hasMember; + import std.typecons : Ternary; private enum ouroboros = is(BookkeepingAllocator == NullAllocator); @@ -536,10 +536,10 @@ template AllocatorList(alias factoryFunction, version(Posix) @system unittest { import std.algorithm.comparison : max; - import std.experimental.allocator.building_blocks.region : Region; import std.experimental.allocator.building_blocks.free_list : ContiguousFreeList; - import std.experimental.allocator.building_blocks.segregator : Segregator; import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; + import std.experimental.allocator.building_blocks.region : Region; + import std.experimental.allocator.building_blocks.segregator : Segregator; import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.mmap_allocator : MmapAllocator; diff --git a/std/experimental/allocator/building_blocks/bitmapped_block.d b/std/experimental/allocator/building_blocks/bitmapped_block.d index a0ded14ab68..24e27a9c5ed 100644 --- a/std/experimental/allocator/building_blocks/bitmapped_block.d +++ b/std/experimental/allocator/building_blocks/bitmapped_block.d @@ -1,8 +1,8 @@ /// module std.experimental.allocator.building_blocks.bitmapped_block; -import std.experimental.allocator.common; import std.experimental.allocator.building_blocks.null_allocator; +import std.experimental.allocator.common; /** @@ -45,15 +45,15 @@ block size to the constructor. struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment, ParentAllocator = NullAllocator) { - import std.typecons : tuple, Tuple; - import std.traits : hasMember; import std.conv : text; + import std.traits : hasMember; import std.typecons : Ternary; + import std.typecons : tuple, Tuple; @system unittest { - import std.experimental.allocator.mallocator : AlignedMallocator; import std.algorithm.comparison : max; + import std.experimental.allocator.mallocator : AlignedMallocator; auto m = cast(ubyte[])(AlignedMallocator.instance.alignedAllocate(1024 * 64, max(theAlignment, cast(uint) size_t.sizeof))); scope(exit) AlignedMallocator.instance.deallocate(m); diff --git a/std/experimental/allocator/building_blocks/bucketizer.d b/std/experimental/allocator/building_blocks/bucketizer.d index e63c9274c59..64067ddc0ea 100644 --- a/std/experimental/allocator/building_blocks/bucketizer.d +++ b/std/experimental/allocator/building_blocks/bucketizer.d @@ -17,8 +17,8 @@ for $(D Bucketizer). To handle them separately, $(D Segregator) may be of use. */ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) { - import std.traits : hasMember; import common = std.experimental.allocator.common : roundUpToMultipleOf; + import std.traits : hasMember; import std.typecons : Ternary; static assert((max - (min - 1)) % step == 0, @@ -220,13 +220,13 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step) /// @system unittest { + import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; import std.experimental.allocator.building_blocks.free_list : FreeList; import std.experimental.allocator.building_blocks.region : Region; - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.common : unbounded; + import std.experimental.allocator.mallocator : Mallocator; import std.typecons : Ternary; - import std.algorithm.comparison : max; Bucketizer!( FreeList!( AllocatorList!( diff --git a/std/experimental/allocator/building_blocks/fallback_allocator.d b/std/experimental/allocator/building_blocks/fallback_allocator.d index f3f099dfaf0..ca7961b9274 100644 --- a/std/experimental/allocator/building_blocks/fallback_allocator.d +++ b/std/experimental/allocator/building_blocks/fallback_allocator.d @@ -256,9 +256,9 @@ struct FallbackAllocator(Primary, Fallback) @system unittest { + import std.conv : text; import std.experimental.allocator.building_blocks.region : InSituRegion; import std.experimental.allocator.gc_allocator : GCAllocator; - import std.conv : text; import std.typecons : Ternary; FallbackAllocator!(InSituRegion!16_384, GCAllocator) a; // This allocation uses the stack diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 136e9b44db4..ba47f6fc791 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -117,8 +117,8 @@ struct FreeList(ParentAllocator, /// @safe unittest { - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.common : chooseAtRuntime; + import std.experimental.allocator.mallocator : Mallocator; FreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; a.min = 64; @@ -673,9 +673,9 @@ struct ContiguousFreeList(ParentAllocator, /// @safe unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.common : unbounded; @@ -896,8 +896,8 @@ struct SharedFreeList(ParentAllocator, /// @safe unittest { - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.common : chooseAtRuntime; + import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a; // Set the maxSize first so setting the minSize doesn't throw @@ -917,8 +917,8 @@ struct SharedFreeList(ParentAllocator, /// @safe unittest { - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.common : chooseAtRuntime; + import std.experimental.allocator.mallocator : Mallocator; shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a; // Set the maxSize first so setting the minSize doesn't throw @@ -1049,10 +1049,10 @@ struct SharedFreeList(ParentAllocator, @system unittest { + import core.thread : ThreadGroup; import std.algorithm.comparison : equal; - import std.range : repeat; import std.experimental.allocator.mallocator : Mallocator; - import core.thread : ThreadGroup; + import std.range : repeat; static shared SharedFreeList!(Mallocator, 64, 128, 10) a; diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index f331fba00b2..38c53848bae 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -3,8 +3,8 @@ module std.experimental.allocator.building_blocks.kernighan_ritchie; import std.experimental.allocator.building_blocks.null_allocator; //debug = KRRegion; -debug(KRRegion) import std.stdio; version(unittest) import std.conv : text; +debug(KRRegion) import std.stdio; // KRRegion /** @@ -615,9 +615,9 @@ fronting the GC allocator. */ @system unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.fallback_allocator : fallbackAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; import std.typecons : Ternary; // KRRegion fronting a general-purpose allocator ubyte[1024 * 128] buf; @@ -640,21 +640,21 @@ it actually returns memory to the operating system when possible. @system unittest { import std.algorithm.comparison : max; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.mmap_allocator : MmapAllocator; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc; } @system unittest { import std.algorithm.comparison : max; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; + import std.typecons : Ternary; /* Create a scalable allocator consisting of 1 MB (or larger) blocks fetched from the garbage-collected heap. Each block is organized as a KR-style @@ -683,11 +683,11 @@ it actually returns memory to the operating system when possible. @system unittest { import std.algorithm.comparison : max; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.typecons : Ternary; - import std.experimental.allocator.mmap_allocator : MmapAllocator; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; + import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mmap_allocator : MmapAllocator; + import std.typecons : Ternary; /* Create a scalable allocator consisting of 1 MB (or larger) blocks fetched from the garbage-collected heap. Each block is organized as a KR-style @@ -720,11 +720,11 @@ it actually returns memory to the operating system when possible. @system unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; + import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.algorithm.comparison : max; import std.experimental.allocator.common : testAllocator; + import std.experimental.allocator.gc_allocator : GCAllocator; testAllocator!(() => AllocatorList!( n => KRRegion!GCAllocator(max(n * 16, 1024 * 1024)))()); } @@ -755,9 +755,9 @@ it actually returns memory to the operating system when possible. cast(ubyte[])(GCAllocator.instance.allocate(1024 * 1024))); const store = alloc.allocate(KRRegion!().sizeof); auto p = cast(KRRegion!()* ) store.ptr; - import std.conv : emplace; - import std.algorithm.mutation : move; import core.stdc.string : memcpy; + import std.algorithm.mutation : move; + import std.conv : emplace; memcpy(p, &alloc, alloc.sizeof); emplace(&alloc); diff --git a/std/experimental/allocator/building_blocks/quantizer.d b/std/experimental/allocator/building_blocks/quantizer.d index 38a061d7454..b3f205dcc61 100644 --- a/std/experimental/allocator/building_blocks/quantizer.d +++ b/std/experimental/allocator/building_blocks/quantizer.d @@ -212,8 +212,8 @@ struct Quantizer(ParentAllocator, alias roundingFunction) @system unittest { import std.experimental.allocator.building_blocks.free_tree : FreeTree; - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.common : roundUpToMultipleOf; + import std.experimental.allocator.gc_allocator : GCAllocator; // Quantize small allocations to a multiple of cache line, large ones to a // multiple of page size diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index 3809b9dd7e4..0cc60b9dd58 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -1,8 +1,8 @@ /// module std.experimental.allocator.building_blocks.region; -import std.experimental.allocator.common; import std.experimental.allocator.building_blocks.null_allocator; +import std.experimental.allocator.common; import std.typecons : Flag, Yes, No; /** @@ -329,10 +329,10 @@ struct Region(ParentAllocator = NullAllocator, /// @system unittest { - import std.experimental.allocator.mallocator : Mallocator; + import std.algorithm.comparison : max; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; - import std.algorithm.comparison : max; + import std.experimental.allocator.mallocator : Mallocator; // Create a scalable list of regions. Each gets at least 1MB at a time by // using malloc. auto batchAllocator = AllocatorList!( @@ -540,9 +540,9 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment) : FallbackAllocator; import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; + import std.experimental.allocator.gc_allocator : GCAllocator; FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2; const a2 = r2.allocate(102); assert(a2.length == 102); diff --git a/std/experimental/allocator/building_blocks/segregator.d b/std/experimental/allocator/building_blocks/segregator.d index 2d8ecffb2c8..83520117935 100644 --- a/std/experimental/allocator/building_blocks/segregator.d +++ b/std/experimental/allocator/building_blocks/segregator.d @@ -277,8 +277,8 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator) @system unittest { import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; alias A = Segregator!( 1024 * 4, @@ -345,8 +345,8 @@ if (Args.length > 3) @system unittest { import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.mallocator : Mallocator; import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator.mallocator : Mallocator; alias A = Segregator!( 128, FreeList!(Mallocator, 0, 128), diff --git a/std/experimental/allocator/building_blocks/stats_collector.d b/std/experimental/allocator/building_blocks/stats_collector.d index 327c76b3618..aba084e8129 100644 --- a/std/experimental/allocator/building_blocks/stats_collector.d +++ b/std/experimental/allocator/building_blocks/stats_collector.d @@ -529,8 +529,8 @@ public: */ void reportStatistics(R)(auto ref R output) { - import std.traits : EnumMembers; import std.conv : to; + import std.traits : EnumMembers; foreach (e; EnumMembers!Options) { static if ((flags & e) && e != Options.numAll @@ -611,8 +611,8 @@ public: private PerCallStatistics* statsAt(string f, uint n, opts...)() { - import std.range : repeat; import std.array : array; + import std.range : repeat; static PerCallStatistics s = { f, n, [ opts ], repeat(0UL, opts.length).array }; @@ -656,8 +656,8 @@ public: /// @system unittest { - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; alias Allocator = StatsCollector!(GCAllocator, Options.all, Options.all); Allocator alloc; @@ -666,8 +666,8 @@ public: alloc.deallocate(b); import std.file : deleteme, remove; - import std.stdio : File; import std.range : walkLength; + import std.stdio : File; auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt"; scope(exit) remove(f); @@ -705,8 +705,8 @@ public: assert(a.bytesUsed == 0); } - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; test!(StatsCollector!(GCAllocator, Options.all, Options.all)); test!(StatsCollector!(FreeList!(GCAllocator, 128), Options.all, Options.all)); @@ -729,7 +729,7 @@ public: a.deallocate(b1); a.deallocate(b3); } - import std.experimental.allocator.gc_allocator : GCAllocator; import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.gc_allocator : GCAllocator; test!(StatsCollector!(GCAllocator, 0, 0)); } diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index c1d2540b0c8..0b0bde6a507 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -424,8 +424,8 @@ version(unittest) package void testAllocator(alias make)() { import std.conv : text; - import std.stdio : writeln, stderr; import std.math : isPowerOf2; + import std.stdio : writeln, stderr; import std.typecons : Ternary; alias A = typeof(make()); scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); @@ -550,8 +550,8 @@ version(unittest) || is (AllocInterface : shared ISharedAllocator)) { import std.conv : text; - import std.stdio : writeln, stderr; import std.math : isPowerOf2; + import std.stdio : writeln, stderr; import std.typecons : Ternary; scope(failure) stderr.writeln("testAllocatorObject failed for ", AllocInterface.stringof); diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index d4724830490..2e971c8d7e1 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -230,14 +230,14 @@ public import std.experimental.allocator.common, @system unittest { import std.algorithm.comparison : min, max; - import std.experimental.allocator.building_blocks.free_list : FreeList; - import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator.building_blocks.segregator : Segregator; - import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock; + import std.experimental.allocator.building_blocks.bucketizer : Bucketizer; + import std.experimental.allocator.building_blocks.free_list : FreeList; + import std.experimental.allocator.building_blocks.segregator : Segregator; + import std.experimental.allocator.gc_allocator : GCAllocator; alias FList = FreeList!(GCAllocator, 0, unbounded); alias A = Segregator!( @@ -601,10 +601,10 @@ allocator can be cast to $(D shared). @system unittest { - import std.experimental.allocator.mallocator : Mallocator; - import std.experimental.allocator.building_blocks.free_list : SharedFreeList; - import std.exception : assertThrown; import core.exception : AssertError; + import std.exception : assertThrown; + import std.experimental.allocator.building_blocks.free_list : SharedFreeList; + import std.experimental.allocator.mallocator : Mallocator; assert(processAllocator); assert(theAllocator); @@ -1320,8 +1320,8 @@ if (isInputRange!R && !isInfinite!R) /*pure*/ nothrow @safe unittest { import std.algorithm.comparison : equal; - import std.internal.test.dummyrange; import std.experimental.allocator.gc_allocator : GCAllocator; + import std.internal.test.dummyrange; import std.range : iota; foreach (DummyType; AllDummyRanges) { diff --git a/std/experimental/allocator/typed.d b/std/experimental/allocator/typed.d index 754c9359f0e..92828f3879e 100644 --- a/std/experimental/allocator/typed.d +++ b/std/experimental/allocator/typed.d @@ -13,10 +13,10 @@ module std.experimental.allocator.typed; import std.experimental.allocator; import std.experimental.allocator.common; -import std.traits : isPointer, hasElaborateDestructor; -import std.typecons : Flag, Yes, No; import std.range : isInputRange, isForwardRange, walkLength, save, empty, front, popFront; +import std.traits : isPointer, hasElaborateDestructor; +import std.typecons : Flag, Yes, No; /** Allocation-related flags dictated by type characteristics. `TypedAllocator` @@ -122,9 +122,9 @@ type. */ struct TypedAllocator(PrimaryAllocator, Policies...) { - import std.typecons : Tuple; - import std.meta : AliasSeq; import std.algorithm.sorting : isSorted; + import std.meta : AliasSeq; + import std.typecons : Tuple; static assert(Policies.length == 0 || isSorted([Stride2!Policies])); diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d index 3be83b4069c..96dc4f282cf 100644 --- a/std/experimental/checkedint.d +++ b/std/experimental/checkedint.d @@ -214,8 +214,8 @@ struct Checked(T, Hook = Abort) if (isIntegral!T || is(T == Checked!(U, H), U, H)) { import std.algorithm.comparison : among; - import std.traits : hasMember; import std.experimental.allocator.common : stateSize; + import std.traits : hasMember; /** The type of the integral subject to checking. diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 1d4fee6e8e0..782dd5885e4 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -1,10 +1,10 @@ /// module std.experimental.logger.core; +import core.sync.mutex : Mutex; import std.datetime; import std.range.primitives; import std.traits; -import core.sync.mutex : Mutex; import std.experimental.logger.filelogger; @@ -2174,8 +2174,8 @@ version(unittest) private void testFuncNames(Logger logger) @safe @safe unittest { import std.conv : to; - import std.string : indexOf; import std.format : format; + import std.string : indexOf; auto oldunspecificLogger = sharedLog; @@ -3139,9 +3139,9 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted // Issue 15517 @system unittest { + import std.file : exists, remove; import std.stdio : File; import std.string : indexOf; - import std.file : exists, remove; string fn = "logfile.log"; if (exists(fn)) diff --git a/std/experimental/logger/filelogger.d b/std/experimental/logger/filelogger.d index 79d5baaab43..3b6e5ddddd7 100644 --- a/std/experimental/logger/filelogger.d +++ b/std/experimental/logger/filelogger.d @@ -1,8 +1,8 @@ /// module std.experimental.logger.filelogger; -import std.stdio; import std.experimental.logger.core; +import std.stdio; /** This $(D Logger) implementation writes log messages to the associated file. The name of the file has to be passed on construction time. If the file @@ -10,9 +10,9 @@ is already present new log messages will be append at its end. */ class FileLogger : Logger { - import std.format : formattedWrite; - import std.datetime : SysTime; import std.concurrency : Tid; + import std.datetime : SysTime; + import std.format : formattedWrite; /** A constructor for the $(D FileLogger) Logger. @@ -131,8 +131,8 @@ class FileLogger : Logger @system unittest { - import std.file : deleteme, remove; import std.array : empty; + import std.file : deleteme, remove; import std.string : indexOf; string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; @@ -160,8 +160,8 @@ class FileLogger : Logger @system unittest { - import std.file : deleteme, remove; import std.array : empty; + import std.file : deleteme, remove; import std.string : indexOf; string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; diff --git a/std/experimental/logger/multilogger.d b/std/experimental/logger/multilogger.d index 58ac051bcc1..ed9cfd9b1ef 100644 --- a/std/experimental/logger/multilogger.d +++ b/std/experimental/logger/multilogger.d @@ -105,8 +105,8 @@ class MultiLogger : Logger @safe unittest { - import std.experimental.logger.nulllogger; import std.exception : assertThrown; + import std.experimental.logger.nulllogger; auto a = new MultiLogger; auto n0 = new NullLogger(); auto n1 = new NullLogger(); diff --git a/std/experimental/logger/package.d b/std/experimental/logger/package.d index d0d141d1dba..b9a075c9f9f 100644 --- a/std/experimental/logger/package.d +++ b/std/experimental/logger/package.d @@ -181,5 +181,5 @@ module std.experimental.logger; public import std.experimental.logger.core; public import std.experimental.logger.filelogger; -public import std.experimental.logger.nulllogger; public import std.experimental.logger.multilogger; +public import std.experimental.logger.nulllogger; diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 6b965439bc7..13a21a9223c 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -1075,4 +1075,4 @@ pure nothrow @system unittest static assert(!__traits(compiles, arr.ptr++ )); -} \ No newline at end of file +} diff --git a/std/file.d b/std/file.d index 09d611a8141..f796d5c041c 100644 --- a/std/file.d +++ b/std/file.d @@ -81,11 +81,11 @@ module std.file; import core.stdc.stdlib, core.stdc.string, core.stdc.errno; import std.datetime; +import std.internal.cstring; import std.meta; import std.range.primitives; import std.traits; import std.typecons; -import std.internal.cstring; version (Windows) { @@ -330,9 +330,9 @@ if (isConvertibleToString!R) version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @trusted { + import core.memory : GC; import std.algorithm.comparison : min; import std.array : uninitializedArray; - import core.memory : GC; import std.conv : to; // A few internal configuration parameters { @@ -379,9 +379,9 @@ version (Posix) private void[] readImpl(const(char)[] name, const(FSChar)* namez version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe { + import core.memory : GC; import std.algorithm.comparison : min; import std.array : uninitializedArray; - import core.memory : GC; static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode, SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted @@ -3970,10 +3970,10 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true) { string[] listdir(string pathname) { - import std.file; - import std.path; import std.algorithm; import std.array; + import std.file; + import std.path; return std.file.dirEntries(pathname, SpanMode.shallow) .filter!(a => a.isFile) @@ -4199,11 +4199,11 @@ auto dirEntries(string path, string pattern, SpanMode mode, Select!(Types.length == 1, Types[0][], Tuple!(Types)[]) slurp(Types...)(string filename, in char[] format) { - import std.stdio : File; - import std.format : formattedRead; import std.array : appender; import std.conv : text; import std.exception : enforce; + import std.format : formattedRead; + import std.stdio : File; auto app = appender!(typeof(return))(); ElementType!(typeof(return)) toAdd; diff --git a/std/format.d b/std/format.d index a746008d640..e155372fc77 100644 --- a/std/format.d +++ b/std/format.d @@ -1003,8 +1003,8 @@ if (!is(Unqual!Char == Char)) struct FormatSpec(Char) if (is(Unqual!Char == Char)) { - import std.ascii : isDigit, isPunctuation, isAlpha; import std.algorithm.searching : startsWith; + import std.ascii : isDigit, isPunctuation, isAlpha; import std.conv : parse, text, to; /** @@ -2134,8 +2134,8 @@ Params: void formatValue(Writer, T, Char)(Writer w, T obj, const ref FormatSpec!Char f) if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { - import std.algorithm.searching : find; import std.algorithm.comparison : min; + import std.algorithm.searching : find; import std.string : indexOf, indexOfAny, indexOfNeither; FormatSpec!Char fs = f; // fs is copy for change its values. @@ -3076,8 +3076,8 @@ private void formatChar(Writer)(Writer w, in dchar c, in char quote) void formatElement(Writer, T, Char)(Writer w, T val, const ref FormatSpec!Char f) if (is(StringTypeOf!T) && !is(T == enum)) { - import std.utf : UTFException; import std.array : appender; + import std.utf : UTFException; StringTypeOf!T str = val; // bug 8015 @@ -3517,8 +3517,8 @@ if (is(T == class) && !is(T == enum)) +/ @safe pure unittest { - import std.format; import std.array : appender; + import std.format; auto writer1 = appender!string(); writer1.formattedWrite("%08b", 42); @@ -3653,8 +3653,8 @@ if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is // Issue 11175 version (Windows) { - import core.sys.windows.windows : HRESULT; import core.sys.windows.com : IUnknown, IID; + import core.sys.windows.windows : HRESULT; interface IUnknown2 : IUnknown { } @@ -4146,8 +4146,8 @@ version(unittest) void formatTest(T)(T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) { import core.exception : AssertError; - import std.conv : text; import std.array : appender; + import std.conv : text; FormatSpec!char f; auto w = appender!string(); formatValue(w, val, f); @@ -4164,8 +4164,8 @@ version(unittest) void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, string fn = __FILE__) @safe { import core.exception : AssertError; - import std.conv : text; import std.array : appender; + import std.conv : text; auto w = appender!string(); formattedWrite(w, fmt, val); foreach (cur; expected) @@ -4248,10 +4248,10 @@ void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, s @safe unittest { - import std.conv : text, octal; + import core.stdc.string : strlen; import std.array : appender; + import std.conv : text, octal; import std.c.stdio : snprintf; - import core.stdc.string : strlen; debug(format) printf("std.format.format.unittest\n"); @@ -5797,8 +5797,8 @@ if (isSomeString!(typeof(fmt))) immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char) { - import std.format : formattedWrite, FormatException; import std.array : appender; + import std.format : formattedWrite, FormatException; auto w = appender!(immutable(Char)[]); auto n = formattedWrite(w, fmt, args); version (all) @@ -5815,9 +5815,9 @@ if (isSomeChar!Char) @safe pure unittest { - import std.format; import core.exception; import std.exception; + import std.format; assertCTFEable!( { // assert(format(null) == ""); @@ -5874,8 +5874,8 @@ if (isSomeString!(typeof(fmt))) char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { import core.exception : RangeError; - import std.utf : encode; import std.format : formattedWrite, FormatException; + import std.utf : encode; size_t i; diff --git a/std/functional.d b/std/functional.d index a0f4c096c0c..d52e6cf8692 100644 --- a/std/functional.d +++ b/std/functional.d @@ -105,8 +105,8 @@ template unaryFun(alias fun, string parmName = "a") { static if (!fun._ctfeMatchUnary(parmName)) { - import std.traits, std.typecons, std.meta; import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; + import std.traits, std.typecons, std.meta; } auto unaryFun(ElementType)(auto ref ElementType __a) { @@ -190,8 +190,8 @@ template binaryFun(alias fun, string parm1Name = "a", { static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { - import std.traits, std.typecons, std.meta; import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; + import std.traits, std.typecons, std.meta; } auto binaryFun(ElementType1, ElementType2) (auto ref ElementType1 __a, auto ref ElementType2 __b) diff --git a/std/getopt.d b/std/getopt.d index 98d4eb62ebd..890a3c3f233 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -28,8 +28,8 @@ Distributed under the Boost Software License, Version 1.0. */ module std.getopt; -import std.traits; import std.exception; // basicExceptionCtors +import std.traits; /** Thrown on one of the following conditions: @@ -560,8 +560,8 @@ follow this pattern: */ private template optionValidator(A...) { - import std.typecons : staticIota; import std.format : format; + import std.typecons : staticIota; enum fmt = "getopt validator: %s (at position %d)"; enum isReceiver(T) = isPointer!T || (is(T == function)) || (is(T == delegate)); @@ -941,8 +941,8 @@ private bool handleOption(R)(string option, R receiver, ref string[] args, alias V = typeof(receiver.values[0]); import std.range : only; - import std.typecons : Tuple, tuple; import std.string : indexOf; + import std.typecons : Tuple, tuple; static Tuple!(K, V) getter(string input) { @@ -1078,9 +1078,9 @@ private struct configuration private bool optMatch(string arg, string optPattern, ref string value, configuration cfg) @safe { - import std.uni : toUpper; - import std.string : indexOf; import std.array : split; + import std.string : indexOf; + import std.uni : toUpper; //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value); //scope(success) writeln("optMatch result: ", value); if (!arg.length || arg[0] != optionChar) return false; @@ -1428,8 +1428,8 @@ private void setConfig(ref configuration cfg, config option) @safe pure nothrow @system unittest // 5228 { - import std.exception; import std.conv; + import std.exception; auto args = ["prog", "--foo=bar"]; int abc; @@ -1614,8 +1614,8 @@ Params: */ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) { - import std.format : formattedWrite; import std.algorithm.comparison : min, max; + import std.format : formattedWrite; output.formattedWrite("%s\n", text); @@ -1669,9 +1669,9 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) @system unittest { + import std.array ; import std.conv; import std.string; - import std.array ; bool a; auto args = ["prog", "--foo"]; auto t = getopt(args, config.required, "foo|f", "Help", &a); diff --git a/std/internal/cstring.d b/std/internal/cstring.d index 626ea9a0f7f..08b75e4f143 100644 --- a/std/internal/cstring.d +++ b/std/internal/cstring.d @@ -38,8 +38,8 @@ module std.internal.cstring; } } -import std.traits; import std.range; +import std.traits; version(unittest) @property inout(C)[] asArray(C)(inout C* cstr) pure nothrow @nogc @trusted @@ -159,8 +159,8 @@ if (isSomeChar!To && (isInputRange!From || isSomeString!From) && pragma(inline, false); // because it's rarely called import core.exception : onOutOfMemoryError; - import core.stdc.string : memcpy; import core.stdc.stdlib : malloc, realloc; + import core.stdc.string : memcpy; if (res_is_onstack) { diff --git a/std/internal/math/biguintcore.d b/std/internal/math/biguintcore.d index 7591a7ea8ac..6391a70a9f2 100644 --- a/std/internal/math/biguintcore.d +++ b/std/internal/math/biguintcore.d @@ -47,9 +47,9 @@ alias multibyteSub = multibyteAddSub!('-'); private import core.cpuid; -private import std.traits; -private import std.range.primitives; public import std.ascii : LetterCase; +private import std.range.primitives; +private import std.traits; shared static this() { diff --git a/std/internal/scopebuffer.d b/std/internal/scopebuffer.d index 007e277e352..8d7548a6eb2 100644 --- a/std/internal/scopebuffer.d +++ b/std/internal/scopebuffer.d @@ -98,8 +98,8 @@ if (isAssignable!T && !hasElaborateCopyConstructor!T && !hasElaborateAssign!T) { - import core.stdc.string : memcpy; import core.exception : onOutOfMemoryError; + import core.stdc.string : memcpy; /************************** diff --git a/std/internal/test/dummyrange.d b/std/internal/test/dummyrange.d index 6a6b08cb99b..a6bce0ada23 100644 --- a/std/internal/test/dummyrange.d +++ b/std/internal/test/dummyrange.d @@ -5,8 +5,8 @@ Used with the dummy ranges for testing higher order ranges. module std.internal.test.dummyrange; import std.meta; -import std.typecons; import std.range.primitives; +import std.typecons; enum RangeType { @@ -390,8 +390,8 @@ if (is(T == TestFoo)) static auto iota(size_t low = 1, size_t high = 11) { - import std.range : iota; import std.algorithm.iteration : map; + import std.range : iota; return iota(cast(int) low, cast(int) high).map!(a => TestFoo(a)); } diff --git a/std/json.d b/std/json.d index 7e9a62e41e8..008870aa341 100644 --- a/std/json.d +++ b/std/json.d @@ -17,9 +17,9 @@ Distributed under the Boost Software License, Version 1.0. */ module std.json; +import std.array; import std.conv; import std.range.primitives; -import std.array; import std.traits; /// @@ -1637,8 +1637,8 @@ EOF"; // handling of special float values (NaN, Inf, -Inf) @safe unittest { - import std.math : isNaN, isInfinity; import std.exception : assertThrown; + import std.math : isNaN, isInfinity; // expected representations of NaN and Inf enum { diff --git a/std/mathspecial.d b/std/mathspecial.d index b0f6e3ec962..4b85189e94c 100644 --- a/std/mathspecial.d +++ b/std/mathspecial.d @@ -53,9 +53,9 @@ * Source: $(PHOBOSSRC std/_mathspecial.d) */ module std.mathspecial; -public import std.math; -private import std.internal.math.gammafunction; private import std.internal.math.errorfunction; +private import std.internal.math.gammafunction; +public import std.math; /* *********************************************** * GAMMA AND RELATED FUNCTIONS * diff --git a/std/meta.d b/std/meta.d index 6b946e1cb17..77ed211d6e0 100644 --- a/std/meta.d +++ b/std/meta.d @@ -1164,8 +1164,8 @@ template aliasSeqOf(alias range) @safe unittest { - import std.range : iota; import std.conv : to, octal; + import std.range : iota; //Testing compile time octal foreach (I2; aliasSeqOf!(iota(0, 8))) foreach (I1; aliasSeqOf!(iota(0, 8))) diff --git a/std/mmfile.d b/std/mmfile.d index 4313b01bcd5..627abb37629 100644 --- a/std/mmfile.d +++ b/std/mmfile.d @@ -17,13 +17,13 @@ */ module std.mmfile; -private import std.file; +private import core.stdc.errno; private import core.stdc.stdio; private import core.stdc.stdlib; -private import core.stdc.errno; +import std.conv, std.exception, std.stdio; +private import std.file; private import std.path; private import std.string; -import std.conv, std.exception, std.stdio; import std.internal.cstring; @@ -38,9 +38,9 @@ version (Windows) else version (Posix) { private import core.sys.posix.fcntl; - private import core.sys.posix.unistd; private import core.sys.posix.sys.mman; private import core.sys.posix.sys.stat; + private import core.sys.posix.unistd; } else { @@ -635,8 +635,8 @@ private: @system unittest { - import std.file : deleteme; import core.memory : GC; + import std.file : deleteme; const size_t K = 1024; size_t win = 64*K; // assume the page size is 64K @@ -681,8 +681,8 @@ private: version(linux) @system unittest // Issue 14868 { - import std.typecons : scoped; import std.file : deleteme; + import std.typecons : scoped; // Test retaining ownership of File/fd diff --git a/std/net/curl.d b/std/net/curl.d index 711c49819c6..e8212735363 100644 --- a/std/net/curl.d +++ b/std/net/curl.d @@ -175,8 +175,8 @@ version(unittest) { // Run unit test with the PHOBOS_TEST_ALLOW_NET=1 set in order to // allow net traffic - import std.stdio; import std.range; + import std.stdio; import std.socket : Address, INADDR_LOOPBACK, Socket, TcpSocket; diff --git a/std/net/isemail.d b/std/net/isemail.d index c2c80c11f77..d37093db375 100644 --- a/std/net/isemail.d +++ b/std/net/isemail.d @@ -64,9 +64,9 @@ if (isSomeChar!(Char)) { import std.algorithm.iteration : uniq, filter, map; import std.algorithm.searching : canFind, maxElement; - import std.exception : enforce; import std.array : array, split; import std.conv : to; + import std.exception : enforce; import std.string : indexOf, lastIndexOf; import std.uni : isNumber; diff --git a/std/numeric.d b/std/numeric.d index c6cea6fc608..da287290f68 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -2187,10 +2187,10 @@ F gapWeightedSimilarity(alias comp = "a == b", R1, R2, F)(R1 s, R2 t, F lambda) if (isRandomAccessRange!(R1) && hasLength!(R1) && isRandomAccessRange!(R2) && hasLength!(R2)) { - import std.functional : binaryFun; - import std.algorithm.mutation : swap; - import core.stdc.stdlib : malloc, free; import core.exception : onOutOfMemoryError; + import core.stdc.stdlib : malloc, free; + import std.algorithm.mutation : swap; + import std.functional : binaryFun; if (s.length < t.length) return gapWeightedSimilarity(t, s, lambda); if (!t.length) return 0; @@ -2765,8 +2765,8 @@ private alias lookup_t = float; */ final class Fft { - import std.algorithm.iteration : map; import core.bitop : bsf; + import std.algorithm.iteration : map; import std.array : uninitializedArray; private: @@ -3255,8 +3255,8 @@ void inverseFft(Ret, R)(R range, Ret buf) @system unittest { import std.algorithm; - import std.range; import std.conv; + import std.range; // Test values from R and Octave. auto arr = [1,2,3,4,5,6,7,8]; auto fft1 = fft(arr); diff --git a/std/outbuffer.d b/std/outbuffer.d index f136c8f42b1..913a29881fd 100644 --- a/std/outbuffer.d +++ b/std/outbuffer.d @@ -241,9 +241,9 @@ class OutBuffer void vprintf(string format, va_list args) @trusted nothrow { - import std.string : toStringz; import core.stdc.stdio : vsnprintf; import core.stdc.stdlib : alloca; + import std.string : toStringz; version (unittest) char[3] buffer = void; // trigger reallocation diff --git a/std/parallelism.d b/std/parallelism.d index a079458422c..51d322172d0 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -44,9 +44,9 @@ module std.parallelism; @system unittest { import std.algorithm.iteration : map; - import std.range : iota; import std.math : approxEqual; import std.parallelism : taskPool; + import std.range : iota; // Parallel reduce can be combined with // std.algorithm.iteration.map to interesting effect. @@ -3931,8 +3931,8 @@ version(unittest) // These are the tests that should be run every time Phobos is compiled. @system unittest { - import std.algorithm.iteration : filter, map, reduce; import std.algorithm.comparison : equal, min, max; + import std.algorithm.iteration : filter, map, reduce; import std.array : split; import std.conv : text; import std.exception : assertThrown; diff --git a/std/path.d b/std/path.d index 1aafe469062..a9f0bd8015d 100644 --- a/std/path.d +++ b/std/path.d @@ -98,9 +98,9 @@ module std.path; // FIXME import std.file; //: getcwd; +static import std.meta; import std.range.primitives; import std.traits; -static import std.meta; version (unittest) { @@ -2395,8 +2395,8 @@ if (isConvertibleToString!R) { // equal2 verifies that the range is the same both ways, i.e. // through front/popFront and back/popBack. - import std.range; import std.algorithm; + import std.range; bool equal2(R1, R2)(R1 r1, R2 r2) { static assert(isBidirectionalRange!R1); @@ -2903,11 +2903,11 @@ if ((isNarrowString!R1 || basePS.popFront(); pathPS.popFront(); - import std.range.primitives : walkLength; - import std.range : repeat, chain, choose; import std.algorithm.comparison : mismatch; import std.algorithm.iteration : joiner; import std.array : array; + import std.range.primitives : walkLength; + import std.range : repeat, chain, choose; import std.utf : byCodeUnit, byChar; // Remove matching prefix from basePS and pathPS @@ -3849,9 +3849,9 @@ string expandTilde(string inputPath) nothrow { version(Posix) { - import core.stdc.stdlib : malloc, free, realloc; import core.exception : onOutOfMemoryError; import core.stdc.errno : errno, ERANGE; + import core.stdc.stdlib : malloc, free, realloc; /* Joins a path from a C string to the remainder of path. diff --git a/std/process.d b/std/process.d index e0182cb40be..4063442c08f 100644 --- a/std/process.d +++ b/std/process.d @@ -87,8 +87,8 @@ module std.process; version (Posix) { - import core.sys.posix.unistd; import core.sys.posix.sys.wait; + import core.sys.posix.unistd; } version (Windows) { @@ -98,10 +98,10 @@ version (Windows) import std.windows.syserror; } +import std.internal.cstring; +import std.internal.processinit; import std.range.primitives; import std.stdio; -import std.internal.processinit; -import std.internal.cstring; // When the DMC runtime is used, we have to use some custom functions @@ -350,9 +350,9 @@ private Pid spawnProcessImpl(in char[][] args, @trusted // TODO: Should be @safe { import core.exception : RangeError; + import std.algorithm.searching : any; import std.conv : text; import std.path : isDirSeparator; - import std.algorithm.searching : any; import std.string : toStringz; if (args.empty) throw new RangeError(); @@ -448,9 +448,9 @@ private Pid spawnProcessImpl(in char[][] args, setCLOEXEC(STDERR_FILENO, false); if (!(config & Config.inheritFDs)) { + import core.stdc.stdlib : malloc; import core.sys.posix.poll : pollfd, poll, POLLNVAL; import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; - import core.stdc.stdlib : malloc; // Get the maximum number of file descriptors that could be open. rlimit r; @@ -739,8 +739,8 @@ version (Posix) private string searchPathFor(in char[] executable) @trusted //TODO: @safe nothrow { - import std.conv : to; import std.algorithm.iteration : splitter; + import std.conv : to; import std.path : buildPath; auto pathz = core.stdc.stdlib.getenv("PATH"); @@ -813,12 +813,12 @@ version (Posix) @system unittest { import core.sys.posix.fcntl : open, O_RDONLY; import core.sys.posix.unistd : close; - import std.path : buildPath; import std.algorithm.searching : canFind, findSplitBefore; - import std.functional : reverseArgs; import std.array : split; import std.conv : to; static import std.file; + import std.functional : reverseArgs; + import std.path : buildPath; auto directory = uniqueTempPath(); std.file.mkdir(directory); @@ -926,8 +926,8 @@ version (Posix) @system unittest @system unittest // Stream redirection in spawnProcess(). { - import std.string; import std.path : buildPath; + import std.string; version (Windows) TestScript prog = "set /p INPUT= echo %INPUT% output %~1 @@ -2236,9 +2236,9 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( in char[] workDir = null, ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init) { - import std.typecons : Tuple; - import std.array : appender; import std.algorithm.comparison : min; + import std.array : appender; + import std.typecons : Tuple; auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout, env, config, workDir, extraArgs); @@ -2806,10 +2806,10 @@ if (is(typeof(allocator(size_t.init)[0] = char.init))) version(Windows) version(unittest) { - import core.sys.windows.shellapi : CommandLineToArgvW; - import core.sys.windows.windows; import core.stdc.stddef; import core.stdc.wchar_ : wcslen; + import core.sys.windows.shellapi : CommandLineToArgvW; + import core.sys.windows.windows; import std.array; string[] parseCommandLine(string line) @@ -3469,10 +3469,10 @@ Distributed under the Boost Software License, Version 1.0. */ -import core.stdc.stdlib; import core.stdc.errno; -import core.thread; +import core.stdc.stdlib; import core.stdc.string; +import core.thread; version (Windows) { diff --git a/std/random.d b/std/random.d index 5148ac86f5f..20f88b1be5d 100644 --- a/std/random.d +++ b/std/random.d @@ -917,9 +917,9 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31, @safe unittest { + import std.algorithm; import std.exception; import std.range; - import std.algorithm; Mt19937 gen; @@ -1424,8 +1424,8 @@ auto uniform(string boundaries = "[)", (T1 a, T2 b, ref UniformRandomNumberGenerator urng) if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGenerator) { - import std.exception : enforce; import std.conv : text; + import std.exception : enforce; alias NumberType = Unqual!(CommonType!(T1, T2)); static if (boundaries[0] == '(') { @@ -1523,8 +1523,8 @@ auto uniform(string boundaries = "[)", T1, T2, RandomGen) if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && isUniformRNG!RandomGen) { - import std.exception : enforce; import std.conv : text, unsigned; + import std.exception : enforce; alias ResultType = Unqual!(CommonType!(T1, T2)); static if (boundaries[0] == '(') { @@ -1966,8 +1966,8 @@ if (isFloatingPoint!F) @safe unittest { - import std.math; import std.algorithm; + import std.math; static assert(is(CommonType!(double, int) == double)); auto a = uniformDistribution(5); assert(a.length == 5); @@ -2048,8 +2048,8 @@ if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) @system unittest { - import std.algorithm.searching : canFind; import std.algorithm.iteration : map; + import std.algorithm.searching : canFind; auto array = [1, 2, 3, 4, 5]; auto elemAddr = &choice(array); @@ -2123,8 +2123,8 @@ Params: void partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen) if (isRandomAccessRange!Range && isUniformRNG!RandomGen) { - import std.exception : enforce; import std.algorithm.mutation : swapAt; + import std.exception : enforce; enforce(n <= r.length, "n must be <= r.length for partialShuffle."); foreach (i; 0 .. n) { @@ -2242,8 +2242,8 @@ in } body { - import std.exception : enforce; import std.algorithm.iteration : reduce; + import std.exception : enforce; double sum = reduce!"a + b"(0.0, proportions.save); enforce(sum > 0, "Proportions in a dice cannot sum to zero"); immutable point = uniform(0.0, sum, rng); @@ -2659,8 +2659,8 @@ if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void))) private void initialize(size_t howMany, size_t total) { - import std.exception : enforce; import std.conv : text; + import std.exception : enforce; _available = total; _toSelect = howMany; enforce(_toSelect <= _available, @@ -3011,9 +3011,9 @@ if (isInputRange!Range && hasLength!Range && isUniformRNG!UniformRNG) @system unittest { // @system because it takes the address of a local + import std.conv : text; import std.exception; import std.range; - import std.conv : text; // For test purposes, an infinite input range struct TestInputRange { diff --git a/std/range/interfaces.d b/std/range/interfaces.d index e519a667e90..76d00e38ff8 100644 --- a/std/range/interfaces.d +++ b/std/range/interfaces.d @@ -519,9 +519,9 @@ template outputRangeObject(E...) { @system unittest { - import std.internal.test.dummyrange; import std.algorithm.comparison : equal; import std.array; + import std.internal.test.dummyrange; static void testEquality(R)(iInputRange r1, R r2) { assert(equal(r1, r2)); diff --git a/std/range/package.d b/std/range/package.d index ec8280f6af2..27e1e9c3c5f 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -227,9 +227,9 @@ to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range; -public import std.range.primitives; -public import std.range.interfaces; public import std.array; +public import std.range.interfaces; +public import std.range.primitives; public import std.typecons : Flag, Yes, No; import std.meta; // allSatisfy, staticMap @@ -1221,8 +1221,8 @@ pure @safe nothrow unittest */ pure @safe nothrow unittest { - import std.algorithm.sorting : sort; import std.algorithm.comparison : equal; + import std.algorithm.sorting : sort; int[] arr1 = [5, 2, 8]; int[] arr2 = [3, 7, 9]; @@ -2871,8 +2871,8 @@ pure @safe nothrow unittest // tail --lines=n import std.algorithm.comparison : equal; import std.algorithm.iteration : joiner; - import std.string : lineSplitter; import std.exception : assumeWontThrow; + import std.string : lineSplitter; assert("one\ntwo\nthree" .lineSplitter .tail(2) @@ -7815,8 +7815,8 @@ public: /// @safe pure nothrow unittest { - import std.array : array; import std.algorithm.comparison : equal; + import std.array : array; assert([0, 1, 2, 3].slide(2).equal!equal( [[0, 1], [1, 2], [2, 3]] @@ -7870,8 +7870,8 @@ public: // different window sizes @safe pure nothrow unittest { - import std.array : array; import std.algorithm.comparison : equal; + import std.array : array; assert([0, 1, 2, 3].slide(1).array == [[0], [1], [2], [3]]); assert([0, 1, 2, 3].slide(2).array == [[0, 1], [1, 2], [2, 3]]); @@ -7985,8 +7985,8 @@ public: // test slicing, length @safe pure nothrow unittest { - import std.array : array; import std.algorithm.comparison : equal; + import std.array : array; // test index assert(iota(3).slide(4)[0].equal([0, 1, 2])); @@ -11058,8 +11058,8 @@ if (isInputRange!R && isIntegral!(ElementType!R)) /// You can use bitwise to implement an uniform bool generator @safe unittest { - import std.random : rndGen; import std.algorithm.comparison : equal; + import std.random : rndGen; auto rb = rndGen.bitwise; static assert(isInfinite!(typeof(rb))); diff --git a/std/range/primitives.d b/std/range/primitives.d index 761dd5443f0..e4931fdf897 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1850,8 +1850,8 @@ if (isBidirectionalRange!Range) /// @safe unittest { - import std.algorithm.iteration : filterBidirectional; import std.algorithm.comparison : equal; + import std.algorithm.iteration : filterBidirectional; auto a = [1, 2, 3]; a.popFrontExactly(1); diff --git a/std/regex/internal/backtracking.d b/std/regex/internal/backtracking.d index c13e371e2ee..8e005d918fb 100644 --- a/std/regex/internal/backtracking.d +++ b/std/regex/internal/backtracking.d @@ -6,8 +6,8 @@ module std.regex.internal.backtracking; package(std.regex): -import std.regex.internal.ir; import std.range.primitives, std.typecons, std.traits, core.stdc.stdlib; +import std.regex.internal.ir; /+ BacktrackingMatcher implements backtracking scheme of matching diff --git a/std/regex/internal/generator.d b/std/regex/internal/generator.d index 8bfda4a1092..6831e59ed2e 100644 --- a/std/regex/internal/generator.d +++ b/std/regex/internal/generator.d @@ -12,11 +12,11 @@ module std.regex.internal.generator; */ @trusted private struct SampleGenerator(Char) { - import std.regex.internal.ir : Regex, IR, IRL; import std.array : appender, Appender; import std.format : formattedWrite; - import std.utf : isValidDchar, byChar; import std.random : Xorshift; + import std.regex.internal.ir : Regex, IR, IRL; + import std.utf : isValidDchar, byChar; Regex!Char re; Appender!(char[]) app; uint limit, seed; diff --git a/std/regex/internal/ir.d b/std/regex/internal/ir.d index 755eca176c7..49e510e0a50 100644 --- a/std/regex/internal/ir.d +++ b/std/regex/internal/ir.d @@ -707,10 +707,10 @@ template BackLooper(E) // @trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name) {//equal is @system? - import std.range : assumeSorted; - import std.conv : text; - import std.algorithm.iteration : map; import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.conv : text; + import std.range : assumeSorted; auto fnd = assumeSorted!"cmp(a,b) < 0"(map!"a.name"(dict)).lowerBound(name).length; enforce(fnd < dict.length && equal(dict[fnd].name, name), diff --git a/std/regex/internal/kickstart.d b/std/regex/internal/kickstart.d index 162ef7cc88d..f303b43b6ba 100644 --- a/std/regex/internal/kickstart.d +++ b/std/regex/internal/kickstart.d @@ -6,8 +6,8 @@ module std.regex.internal.kickstart; package(std.regex): -import std.regex.internal.ir; import std.range.primitives, std.utf; +import std.regex.internal.ir; //utility for shiftOr, returns a minimum number of bytes to test in a Char uint effectiveSize(Char)() @@ -395,8 +395,8 @@ public: // (that given the haystack in question is valid UTF string) @trusted size_t search(const(Char)[] haystack, size_t idx) {//@BUG: apparently assumes little endian machines - import std.conv : text; import core.stdc.string : memchr; + import std.conv : text; assert(!empty); auto p = cast(const(ubyte)*)(haystack.ptr+idx); uint state = uint.max; diff --git a/std/regex/internal/parser.d b/std/regex/internal/parser.d index 58e10225fbf..6498bddb74a 100644 --- a/std/regex/internal/parser.d +++ b/std/regex/internal/parser.d @@ -4,10 +4,10 @@ */ module std.regex.internal.parser; -import std.regex.internal.ir; +static import std.ascii; import std.range.primitives, std.uni, std.meta, std.traits, std.typecons, std.exception; -static import std.ascii; +import std.regex.internal.ir; // package relevant info from parser into a regex object auto makeRegex(S, CG)(Parser!(S, CG) p) diff --git a/std/regex/internal/tests.d b/std/regex/internal/tests.d index ccfb80c7dfa..80e278bf648 100644 --- a/std/regex/internal/tests.d +++ b/std/regex/internal/tests.d @@ -529,8 +529,8 @@ alias Sequence(int B, int E) = staticIota!(B, E); @safe unittest { - import std.algorithm.iteration : map; import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; enum cx = ctRegex!"(A|B|C)"; auto mx = match("B",cx); assert(mx); @@ -560,8 +560,8 @@ alias Sequence(int B, int E) = staticIota!(B, E); @safe unittest { - import std.algorithm.iteration : map; import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; //global matching void test_body(alias matchFn)() { @@ -603,8 +603,8 @@ alias Sequence(int B, int E) = staticIota!(B, E); //tests for accumulated std.regex issues and other regressions @safe unittest { - import std.algorithm.iteration : map; import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; void test_body(alias matchFn)() { //issue 5857 @@ -1014,9 +1014,9 @@ alias Sequence(int B, int E) = staticIota!(B, E); // bugzilla 14615 @safe unittest { - import std.stdio : writeln; - import std.regex : replaceFirst, replaceFirstInto, regex; import std.array : appender; + import std.regex : replaceFirst, replaceFirstInto, regex; + import std.stdio : writeln; auto example = "Hello, world!"; auto pattern = regex("^Hello, (bug)"); // won't find this one diff --git a/std/regex/internal/thompson.d b/std/regex/internal/thompson.d index 103726edb62..4d7deaa1f88 100644 --- a/std/regex/internal/thompson.d +++ b/std/regex/internal/thompson.d @@ -10,8 +10,8 @@ module std.regex.internal.thompson; package(std.regex): -import std.regex.internal.ir; import std.range.primitives; +import std.regex.internal.ir; //State of VM thread struct Thread(DataIndex) diff --git a/std/regex/package.d b/std/regex/package.d index c0c52a8f9b9..dec4bf3e1d7 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -1052,9 +1052,9 @@ if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R))) // another set of tests just to cover the new API @system unittest { - import std.conv : to; - import std.algorithm.iteration : map; import std.algorithm.comparison : equal; + import std.algorithm.iteration : map; + import std.conv : to; foreach (String; AliasSeq!(string, wstring, const(dchar)[])) { @@ -1138,8 +1138,8 @@ if (isOutputRange!(OutR, ElementEncodingType!R[]) && isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) { import std.algorithm.searching : find; - import std.conv : text, parse; import std.ascii : isDigit, isAlpha; + import std.conv : text, parse; import std.exception : enforce; enum State { Normal, Dollar } auto state = State.Normal; @@ -1706,16 +1706,16 @@ auto escaper(Range)(Range r) /// @system unittest { - import std.regex; import std.algorithm.comparison; + import std.regex; string s = `This is {unfriendly} to *regex*`; assert(s.escaper.equal(`This is \{unfriendly\} to \*regex\*`)); } @system unittest { - import std.conv; import std.algorithm.comparison; + import std.conv; foreach (S; AliasSeq!(string, wstring, dstring)) { auto s = "^".to!S; diff --git a/std/signals.d b/std/signals.d index 646908ac72b..4f4d81d409a 100644 --- a/std/signals.d +++ b/std/signals.d @@ -62,9 +62,9 @@ */ module std.signals; -import std.stdio; -import core.stdc.stdlib : calloc, realloc, free; import core.exception : onOutOfMemoryError; +import core.stdc.stdlib : calloc, realloc, free; +import std.stdio; // Special function for internal use only. // Use of this is where the slot had better be a delegate @@ -85,8 +85,8 @@ extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt ); mixin template Signal(T1...) { - static import core.stdc.stdlib; static import core.exception; + static import core.stdc.stdlib; /*** * A slot is implemented as a delegate. * The slot_t is the type of the delegate. diff --git a/std/socket.d b/std/socket.d index d5b4693700b..475dc411d99 100644 --- a/std/socket.d +++ b/std/socket.d @@ -60,8 +60,8 @@ version(Windows) pragma (lib, "ws2_32.lib"); pragma (lib, "wsock32.lib"); - public import core.sys.windows.winsock2; private import core.sys.windows.windows, std.windows.syserror; + public import core.sys.windows.winsock2; private alias _ctimeval = core.sys.windows.winsock2.timeval; private alias _clinger = core.sys.windows.winsock2.linger; @@ -85,16 +85,16 @@ else version(Posix) } } - import core.sys.posix.netdb; - import core.sys.posix.sys.un : sockaddr_un; - private import core.sys.posix.fcntl; - private import core.sys.posix.unistd; private import core.sys.posix.arpa.inet; - private import core.sys.posix.netinet.tcp; + private import core.sys.posix.fcntl; + import core.sys.posix.netdb; private import core.sys.posix.netinet.in_; - private import core.sys.posix.sys.time; + private import core.sys.posix.netinet.tcp; private import core.sys.posix.sys.select; private import core.sys.posix.sys.socket; + private import core.sys.posix.sys.time; + import core.sys.posix.sys.un : sockaddr_un; + private import core.sys.posix.unistd; private alias _ctimeval = core.sys.posix.sys.time.timeval; private alias _clinger = core.sys.posix.sys.socket.linger; diff --git a/std/stdio.d b/std/stdio.d index 06dc977cda3..1464445fd7f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -13,8 +13,8 @@ Authors: $(HTTP digitalmars.com, Walter Bright), */ module std.stdio; -public import core.stdc.stdio; import core.stdc.stddef; // wchar_t +public import core.stdc.stdio; import std.algorithm.mutation; // copy import std.meta; // allSatisfy import std.range.primitives; // ElementEncodingType, empty, front, @@ -500,9 +500,9 @@ Throws: $(D ErrnoException) in case of error. */ void reopen(string name, in char[] stdioOpenmode = "rb") @trusted { - import std.internal.cstring : tempCString; - import std.exception : enforce, errnoEnforce; import std.conv : text; + import std.exception : enforce, errnoEnforce; + import std.internal.cstring : tempCString; enforce(isOpen, "Attempting to reopen() an unopened file"); @@ -525,8 +525,8 @@ Throws: $(D ErrnoException) in case of error. @system unittest // Test changing filename { - static import std.file; import std.exception : assertThrown, assertNotThrown; + static import std.file; auto deleteme = testFilename(); std.file.write(deleteme, "foo"); @@ -547,8 +547,8 @@ Throws: $(D ErrnoException) in case of error. version (CRuntime_Microsoft) {} else // Not implemented @system unittest // Test changing mode { - static import std.file; import std.exception : assertThrown, assertNotThrown; + static import std.file; auto deleteme = testFilename(); std.file.write(deleteme, "foo"); @@ -649,9 +649,9 @@ Throws: $(D ErrnoException) in case of error. version(Windows) void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode) { + import core.stdc.stdint : intptr_t; import std.exception : errnoEnforce; import std.format : format; - import core.stdc.stdint : intptr_t; // Create file descriptors from the handles version (DIGITAL_MARS_STDIO) @@ -838,8 +838,8 @@ Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) f @safe unittest { // Issue 12349 - static import std.file; import std.exception : assertThrown; + static import std.file; auto deleteme = testFilename(); auto f = File(deleteme, "w"); @@ -1031,8 +1031,8 @@ Throws: $(D Exception) if the file is not opened. @system unittest { - static import std.file; import std.conv : text; + static import std.file; auto deleteme = testFilename(); auto f = File(deleteme, "w+"); @@ -1089,8 +1089,8 @@ Throws: $(D Exception) if the file is not opened. /// @system unittest { - static import std.file; import std.conv : text; + static import std.file; auto testFile = testFilename(); std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz"); @@ -1172,8 +1172,8 @@ Throws: $(D Exception) if the file is not opened. private static T wenforce(T)(T cond, string str) { - import std.windows.syserror : sysErrorString; import core.sys.windows.windows : GetLastError; + import std.windows.syserror : sysErrorString; if (cond) return cond; throw new Exception(str ~ ": " ~ sysErrorString(GetLastError())); @@ -1220,8 +1220,8 @@ $(UL enforce(isOpen, "Attempting to call lock() on an unopened file"); version (Posix) { - import std.exception : errnoEnforce; import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK; + import std.exception : errnoEnforce; immutable short type = lockType == LockType.readWrite ? F_WRLCK : F_RDLCK; errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1, @@ -1254,9 +1254,9 @@ specified file segment was already locked. enforce(isOpen, "Attempting to call tryLock() on an unopened file"); version (Posix) { - import std.exception : errnoEnforce; import core.stdc.errno : EACCES, EAGAIN, errno; import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK; + import std.exception : errnoEnforce; immutable short type = lockType == LockType.readWrite ? F_WRLCK : F_RDLCK; immutable res = lockImpl(F_SETLK, type, start, length); @@ -1294,8 +1294,8 @@ Removes the lock over the specified file segment. enforce(isOpen, "Attempting to call unlock() on an unopened file"); version (Posix) { - import std.exception : errnoEnforce; import core.sys.posix.fcntl : F_SETLK, F_UNLCK; + import std.exception : errnoEnforce; errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1, "Could not remove lock for file `"~_name~"'"); } @@ -1540,8 +1540,8 @@ void main() @system unittest { - static import std.file; import std.algorithm.comparison : equal; + static import std.file; import std.meta : AliasSeq; auto deleteme = testFilename(); @@ -2300,8 +2300,8 @@ $(REF readText, std,file) @system unittest { - static import std.file; import std.algorithm.comparison : equal; + static import std.file; scope(failure) printf("Failed test at line %d\n", __LINE__); auto deleteme = testFilename(); @@ -3021,9 +3021,9 @@ void main() @system unittest { - static import std.file; import std.algorithm.mutation : reverse; import std.exception : collectException; + static import std.file; import std.range : only, retro; import std.string : format; @@ -3231,8 +3231,8 @@ void main() @safe unittest { - static import std.file; import std.exception : collectException; + static import std.file; auto deleteme = testFilename(); scope(exit) collectException(std.file.remove(deleteme)); @@ -3395,8 +3395,8 @@ struct LockingTextReader @system unittest // bugzilla 13686 { - static import std.file; import std.algorithm.comparison : equal; + static import std.file; import std.utf : byDchar; auto deleteme = testFilename(); @@ -4564,9 +4564,9 @@ alias stdin = makeGlobal!(core.stdc.stdio.stdin); @safe unittest { // Read stdin, sort lines, write to stdout - import std.array : array; - import std.algorithm.sorting : sort; import std.algorithm.mutation : copy; + import std.algorithm.sorting : sort; + import std.array : array; import std.typecons : Yes; void main() { @@ -5113,12 +5113,12 @@ version(linux) { File openNetwork(string host, ushort port) { - static import sock = core.sys.posix.sys.socket; - static import core.sys.posix.unistd; import core.stdc.string : memcpy; import core.sys.posix.arpa.inet : htons; import core.sys.posix.netdb : gethostbyname; import core.sys.posix.netinet.in_ : sockaddr_in; + static import core.sys.posix.unistd; + static import sock = core.sys.posix.sys.socket; import std.conv : to; import std.exception : enforce; import std.internal.cstring : tempCString; diff --git a/std/string.d b/std/string.d index fa59969fed7..2635a78f521 100644 --- a/std/string.d +++ b/std/string.d @@ -185,9 +185,9 @@ private: } } -public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; public import std.format : format, sformat; import std.typecons : Flag, Yes, No; +public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; import std.meta; // AliasSeq, staticIndexOf import std.range.primitives; // back, ElementEncodingType, ElementType, front, @@ -2309,8 +2309,8 @@ S capitalize(S)(S input) @trusted pure if (isSomeString!S) { import std.array : array; - import std.utf : byUTF; import std.uni : asCapitalized; + import std.utf : byUTF; return input.asCapitalized.byUTF!(ElementEncodingType!(S)).array; } @@ -5853,8 +5853,8 @@ C1[] tr(C1, C2, C3, C4 = immutable char) @system pure unittest { - import std.exception : assertThrown; import core.exception : AssertError; + import std.exception : assertThrown; assertThrown!AssertError(tr("abcdef", "cd", "CD", "X")); } diff --git a/std/uni.d b/std/uni.d index 39b0474ad64..d71e482063b 100644 --- a/std/uni.d +++ b/std/uni.d @@ -6542,8 +6542,8 @@ if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) @safe unittest { import std.algorithm.comparison : equal; - import std.range : take, drop; import std.range.primitives : walkLength; + import std.range : take, drop; auto text = "noe\u0308l"; // noël using e + combining diaeresis assert(text.walkLength == 5); // 5 code points @@ -9024,8 +9024,8 @@ if (isSomeString!S) @system unittest //@@@BUG std.format is not @safe { - import std.format : format; static import std.ascii; + import std.format : format; foreach (ch; 0 .. 0x80) assert(std.ascii.toLower(ch) == toLower(ch)); assert(toLower('Я') == 'я'); @@ -9150,8 +9150,8 @@ dchar toUpper(dchar c) @safe unittest { - import std.format : format; static import std.ascii; + import std.format : format; foreach (ch; 0 .. 0x80) assert(std.ascii.toUpper(ch) == toUpper(ch)); assert(toUpper('я') == 'Я'); diff --git a/std/uri.d b/std/uri.d index 0f03d03bae2..75208e1982c 100644 --- a/std/uri.d +++ b/std/uri.d @@ -316,8 +316,8 @@ if (isSomeChar!Char) string decode(Char)(in Char[] encodedURI) if (isSomeChar!Char) { - import std.utf : encode; import std.algorithm.iteration : each; + import std.utf : encode; auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash); char[] r; s.each!(c => encode(r, c)); @@ -332,8 +332,8 @@ if (isSomeChar!Char) string decodeComponent(Char)(in Char[] encodedURIComponent) if (isSomeChar!Char) { - import std.utf : encode; import std.algorithm.iteration : each; + import std.utf : encode; auto s = URI_Decode(encodedURIComponent, 0); char[] r; s.each!(c => encode(r, c)); diff --git a/std/utf.d b/std/utf.d index e2860745859..339243cfcef 100644 --- a/std/utf.d +++ b/std/utf.d @@ -59,11 +59,11 @@ $(TR $(TD Miscellaneous) $(TD +/ module std.utf; +import std.exception; // basicExceptionCtors import std.meta; // AliasSeq import std.range.primitives; import std.traits; // isSomeChar, isSomeString import std.typecons; // Flag, Yes, No -import std.exception; // basicExceptionCtors //debug=utf; // uncomment to turn on debugging printf's @@ -346,10 +346,10 @@ body @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(string s, dchar c, size_t i = 0, size_t line = __LINE__) { enforce(stride(s, i) == codeLength!char(c), @@ -454,10 +454,10 @@ if (isInputRange!S && is(Unqual!(ElementType!S) == wchar)) @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(wstring s, dchar c, size_t i = 0, size_t line = __LINE__) { enforce(stride(s, i) == codeLength!wchar(c), @@ -533,10 +533,10 @@ if (is(S : const dchar[]) || @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(dstring s, dchar c, size_t i = 0, size_t line = __LINE__) { enforce(stride(s, i) == codeLength!dchar(c), @@ -679,10 +679,10 @@ if (isBidirectionalRange!S && is(Unqual!(ElementType!S) == char) && !isRandomAcc @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(string s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!char(c), @@ -776,10 +776,10 @@ if (is(S : const wchar[]) || @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(wstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!wchar(c), @@ -861,10 +861,10 @@ if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar)) @system unittest { + import core.exception : AssertError; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; static void test(dstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__) { enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!dchar(c), @@ -1747,8 +1747,8 @@ version(unittest) private void testDecode(R)(R range, size_t expectedIndex, size_t line = __LINE__) { - import std.string : format; import core.exception : AssertError; + import std.string : format; static if (hasLength!R) immutable lenBefore = range.length; @@ -1775,8 +1775,8 @@ version(unittest) private void testDecodeFront(R)(ref R range, size_t expectedNumCodeUnits, size_t line = __LINE__) { - import std.string : format; import core.exception : AssertError; + import std.string : format; static if (hasLength!R) immutable lenBefore = range.length; @@ -1805,8 +1805,8 @@ version(unittest) private void testDecodeBack(R)(ref R range, return; else { - import std.string : format; import core.exception : AssertError; + import std.string : format; static if (hasLength!R) immutable lenBefore = range.length; @@ -1842,8 +1842,8 @@ version(unittest) private void testAllDecode(R)(R range, version(unittest) private void testBadDecode(R)(R range, size_t index, size_t line = __LINE__) { - import std.string : format; import core.exception : AssertError; + import std.string : format; immutable initialIndex = index; @@ -1873,8 +1873,8 @@ version(unittest) private void testBadDecodeBack(R)(R range, size_t line = __LIN return; else { - import std.string : format; import core.exception : AssertError; + import std.string : format; static if (hasLength!R) immutable lenBefore = range.length; @@ -2615,9 +2615,9 @@ if (isInputRange!InputRange && !isInfinite!InputRange && is(ElementType!InputRan @safe unittest { + import std.algorithm.iteration : filter; import std.conv : to; import std.exception; - import std.algorithm.iteration : filter; assertCTFEable!( { @@ -2745,8 +2745,8 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) @system pure unittest { - import std.internal.test.dummyrange : ReferenceInputRange; import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : ReferenceInputRange; auto r1 = new ReferenceInputRange!dchar("Hellø"); auto r2 = new ReferenceInputRange!dchar("𐐷"); @@ -2795,8 +2795,8 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S)) @system pure unittest { - import std.internal.test.dummyrange : ReferenceInputRange; import std.algorithm.comparison : equal; + import std.internal.test.dummyrange : ReferenceInputRange; auto r1 = new ReferenceInputRange!dchar("𤭢"); auto r2 = new ReferenceInputRange!dchar("𐐷"); @@ -3004,11 +3004,11 @@ if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && @safe pure unittest { + import core.exception : AssertError; + import std.algorithm; import std.conv : to; import std.exception; import std.string : format; - import core.exception : AssertError; - import std.algorithm; assertCTFEable!( { diff --git a/std/uuid.d b/std/uuid.d index 879a99215e4..a2db742c4bb 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -402,9 +402,9 @@ public struct UUID @safe pure unittest { + import std.conv : to; import std.exception; import std.meta; - import std.conv : to; foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[], wchar[], const(wchar)[], immutable(wchar)[], @@ -1314,8 +1314,8 @@ UUID parseUUID(Range)(ref Range uuidRange) if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) { - import std.conv : ConvException, parse; import std.ascii : isHexDigit; + import std.conv : ConvException, parse; static if (isForwardRange!Range) auto errorCopy = uuidRange.save; @@ -1475,9 +1475,9 @@ if (isInputRange!Range @safe pure unittest { + import std.conv : to; import std.exception; import std.meta; - import std.conv : to; struct TestRange(bool forward) { diff --git a/std/variant.d b/std/variant.d index eb15fd6f26e..8ca53732568 100644 --- a/std/variant.d +++ b/std/variant.d @@ -2729,8 +2729,8 @@ if (isAlgebraic!VariantType && Handler.length > 0) @system unittest { // Bugzilla 15039 - import std.variant; import std.typecons; + import std.variant; alias IntTypedef = Typedef!int; alias Obj = Algebraic!(int, IntTypedef, This[]); diff --git a/std/windows/charset.d b/std/windows/charset.d index 5c40189f07e..c6abe7a381a 100644 --- a/std/windows/charset.d +++ b/std/windows/charset.d @@ -50,11 +50,11 @@ else: version (Windows): -private import std.conv; private import core.sys.windows.windows; -private import std.windows.syserror; -private import std.utf; +private import std.conv; private import std.string; +private import std.utf; +private import std.windows.syserror; import std.internal.cstring; diff --git a/std/windows/registry.d b/std/windows/registry.d index b1197b4b1e5..0b5e3d2d1c3 100644 --- a/std/windows/registry.d +++ b/std/windows/registry.d @@ -38,15 +38,15 @@ module std.windows.registry; version (Windows): -import std.array; -import std.system : Endian, endian; -import std.exception; import core.sys.windows.windows; -import std.windows.syserror; +import std.array; import std.conv; -import std.utf : toUTF8, toUTF16; -private import std.internal.windows.advapi32; +import std.exception; import std.internal.cstring; +private import std.internal.windows.advapi32; +import std.system : Endian, endian; +import std.utf : toUTF8, toUTF16; +import std.windows.syserror; //debug = winreg; debug(winreg) import std.stdio; diff --git a/std/windows/syserror.d b/std/windows/syserror.d index 83de681662a..863e0c1700c 100644 --- a/std/windows/syserror.d +++ b/std/windows/syserror.d @@ -65,11 +65,11 @@ else: version (Windows): -import std.windows.charset; +import core.sys.windows.windows; import std.array : appender; import std.conv : to; import std.format : formattedWrite; -import core.sys.windows.windows; +import std.windows.charset; string sysErrorString( DWORD errCode, @@ -164,8 +164,8 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file { static string trustedToString(const(wchar)* stringz) @trusted { - import std.conv : to; import core.stdc.wchar_ : wcslen; + import std.conv : to; auto len = wcslen(stringz); return to!string(stringz[0 .. len]); } @@ -180,9 +180,9 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file version(Windows) @system unittest { + import std.algorithm.searching : startsWith, endsWith; import std.exception; import std.string; - import std.algorithm.searching : startsWith, endsWith; auto e = collectException!WindowsException( DeleteFileA("unexisting.txt").wenforce("DeleteFile") diff --git a/std/zip.d b/std/zip.d index 961eaa4f974..45e8a84c1d9 100644 --- a/std/zip.d +++ b/std/zip.d @@ -286,8 +286,8 @@ final class ArchiveMember */ final class ZipArchive { - import std.bitmanip : littleEndianToNative, nativeToLittleEndian; import std.algorithm.comparison : max; + import std.bitmanip : littleEndianToNative, nativeToLittleEndian; import std.conv : to; import std.datetime : DosFileTime; @@ -887,8 +887,8 @@ debug(print) @system unittest { - import std.random : Mt19937, randomShuffle; import std.conv : to; + import std.random : Mt19937, randomShuffle; // Test if packing and unpacking preserves order. auto rand = Mt19937(15966); string[] names; diff --git a/std/zlib.d b/std/zlib.d index 68954719077..7965f3055b0 100644 --- a/std/zlib.d +++ b/std/zlib.d @@ -674,8 +674,8 @@ class UnCompress /* ========================== unittest ========================= */ -private import std.stdio; private import std.random; +private import std.stdio; @system unittest // by Dave { From 998ad51fd7e591f07b1202975ae8248b4eaf078a Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 08:12:09 +0200 Subject: [PATCH 247/262] Sort selective imports --- std/algorithm/comparison.d | 6 +-- std/algorithm/internal.d | 4 +- std/algorithm/iteration.d | 4 +- std/algorithm/mutation.d | 10 ++--- std/algorithm/setops.d | 12 +++--- std/algorithm/sorting.d | 16 ++++---- std/array.d | 4 +- std/bigint.d | 6 +-- std/bitmanip.d | 4 +- std/c/freebsd/socket.d | 2 +- std/c/process.d | 2 +- std/complex.d | 10 ++--- std/concurrency.d | 4 +- std/container/array.d | 2 +- std/container/dlist.d | 2 +- std/container/rbtree.d | 4 +- std/container/slist.d | 2 +- std/container/util.d | 2 +- std/conv.d | 10 ++--- std/datetime/date.d | 4 +- std/datetime/interval.d | 2 +- std/datetime/package.d | 2 +- std/datetime/systime.d | 26 ++++++------- std/datetime/timezone.d | 12 +++--- std/digest/digest.d | 4 +- std/digest/hmac.d | 4 +- std/exception.d | 4 +- .../building_blocks/affix_allocator.d | 2 +- .../allocator/building_blocks/free_list.d | 2 +- .../allocator/building_blocks/free_tree.d | 2 +- .../building_blocks/kernighan_ritchie.d | 2 +- .../allocator/building_blocks/region.d | 2 +- std/experimental/allocator/common.d | 4 +- std/experimental/allocator/mallocator.d | 2 +- std/experimental/allocator/package.d | 2 +- std/experimental/allocator/typed.d | 4 +- std/experimental/checkedint.d | 4 +- std/experimental/logger/core.d | 4 +- std/experimental/typecons.d | 2 +- std/file.d | 16 ++++---- std/format.d | 14 +++---- std/functional.d | 2 +- std/getopt.d | 2 +- std/internal/test/dummyrange.d | 2 +- std/json.d | 8 ++-- std/math.d | 2 +- std/meta.d | 2 +- std/net/curl.d | 10 ++--- std/net/isemail.d | 4 +- std/numeric.d | 6 +-- std/parallelism.d | 14 +++---- std/path.d | 20 +++++----- std/process.d | 12 +++--- std/random.d | 2 +- std/range/package.d | 12 +++--- std/range/primitives.d | 2 +- std/regex/internal/backtracking.d | 2 +- std/regex/internal/generator.d | 6 +-- std/regex/internal/tests.d | 2 +- std/regex/package.d | 12 +++--- std/signals.d | 2 +- std/stdio.d | 12 +++--- std/string.d | 38 +++++++++---------- std/uni.d | 20 +++++----- std/utf.d | 2 +- std/uuid.d | 8 ++-- std/variant.d | 4 +- std/windows/registry.d | 4 +- std/windows/syserror.d | 2 +- std/zip.d | 4 +- 70 files changed, 222 insertions(+), 222 deletions(-) diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index 566b686b16a..13901b554c1 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -64,7 +64,7 @@ import std.range.primitives; import std.traits; // FIXME import std.meta : allSatisfy; -import std.typecons; // : tuple, Tuple, Flag, Yes; +import std.typecons; // : Flag, Tuple, tuple, Yes; /** Find $(D value) _among $(D values), returning the 1-based index @@ -852,7 +852,7 @@ range of range (of range...) comparisons. @safe unittest { import std.algorithm.comparison : equal; - import std.range : iota, chunks; + import std.range : chunks, iota; assert(equal!(equal!equal)( [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], iota(0, 8).chunks(2).chunks(2) @@ -925,7 +925,7 @@ range of range (of range...) comparisons. @safe pure unittest { - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(equal("æøå".byChar, "æøå")); assert(equal("æøå", "æøå".byChar)); diff --git a/std/algorithm/internal.d b/std/algorithm/internal.d index 35ba503e3cc..5e0cfd68bcb 100644 --- a/std/algorithm/internal.d +++ b/std/algorithm/internal.d @@ -21,7 +21,7 @@ version(unittest) package string[] rndstuff(T : string)() { - import std.random : Random, unpredictableSeed, uniform; + import std.random : Random, uniform, unpredictableSeed; static Random rnd; static bool first = true; @@ -46,7 +46,7 @@ version(unittest) package int[] rndstuff(T : int)() { - import std.random : Random, unpredictableSeed, uniform; + import std.random : Random, uniform, unpredictableSeed; static Random rnd; static bool first = true; diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index ff832ab221a..f29b4cdddae 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -2748,7 +2748,7 @@ if (fun.length >= 1) alias binfuns = staticMap!(binaryFun, fun); static if (fun.length > 1) - import std.typecons : tuple, isTuple; + import std.typecons : isTuple, tuple; /++ No-seed version. The first element of $(D r) is used as the seed's value. @@ -3219,7 +3219,7 @@ if (fun.length >= 1) // Sum all elements with explicit seed assert(arr.fold!((a, b) => a + b)(6) == 21); - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; import std.typecons : tuple; // Compute minimum and maximum at the same time diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index 49f3c850c7f..23923242122 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -78,7 +78,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) module std.algorithm.mutation; import std.range.primitives; -import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar; +import std.traits : isArray, isBlitAssignable, isNarrowString, isSomeChar, Unqual; // FIXME import std.typecons; // : tuple, Tuple; @@ -279,7 +279,7 @@ Unicode integrity is not preserved: { import std.algorithm.comparison : equal; import std.conv : text; - import std.random : Random, unpredictableSeed, uniform; + import std.random : Random, uniform, unpredictableSeed; // a more elaborate test { @@ -845,7 +845,7 @@ See_Also: void initializeAll(Range)(Range range) if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) { - import core.stdc.string : memset, memcpy; + import core.stdc.string : memcpy, memset; import std.traits : hasElaborateAssign, isDynamicArray; alias T = ElementType!Range; @@ -901,7 +901,7 @@ if (is(Range == char[]) || is(Range == wchar[])) /// @system unittest { - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; struct S { @@ -2835,7 +2835,7 @@ if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = val /// nothrow @system unittest { - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5]; uninitializedFill(s, 42); diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index f948cb0635c..b9e384b3764 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -41,10 +41,10 @@ module std.algorithm.setops; import std.range.primitives; // FIXME -import std.functional; // : unaryFun, binaryFun; +import std.functional; // : binaryFun, unaryFun; import std.traits; // FIXME -import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy; +import std.meta; // : AliasSeq, allSatisfy, anySatisfy, staticMap; import std.algorithm.sorting; // : Merge; import std.typecons : No; @@ -82,13 +82,13 @@ auto cartesianProduct(R1, R2)(R1 range1, R2 range2) if (!allSatisfy!(isForwardRange, R1, R2) || anySatisfy!(isInfinite, R1, R2)) { - import std.algorithm.iteration : map, joiner; + import std.algorithm.iteration : joiner, map; static if (isInfinite!R1 && isInfinite!R2) { static if (isForwardRange!R1 && isForwardRange!R2) { - import std.range : zip, repeat, take, chain, sequence; + import std.range : chain, repeat, sequence, take, zip; // This algorithm traverses the cartesian product by alternately // covering the right and bottom edges of an increasing square area @@ -107,13 +107,13 @@ if (!allSatisfy!(isForwardRange, R1, R2) || } else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) { - import std.range : zip, repeat; + import std.range : repeat, zip; return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) (range1)); } else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) { - import std.range : zip, repeat; + import std.range : repeat, zip; return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) (range2)); } diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 9bc5bf311be..dbaec604a1a 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -116,7 +116,7 @@ void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2)) { import std.algorithm.mutation : bringToFront; - import std.range : chain, assumeSorted; + import std.range : assumeSorted, chain; // Probably this algorithm can be optimized by using in-place // merge auto lhsOriginal = lhs.release(); @@ -619,7 +619,7 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range) { assert(pivot < r.length || r.length == 0 && pivot == 0); if (r.length <= 1) return 0; - import std.algorithm.mutation : swapAt, move; + import std.algorithm.mutation : move, swapAt; alias lt = binaryFun!less; // Pivot at the front @@ -1939,7 +1939,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || { import std.algorithm.internal : rndstuff; import std.algorithm.mutation : swapRanges; - import std.random : Random, unpredictableSeed, uniform; + import std.random : Random, uniform, unpredictableSeed; import std.uni : toUpper; // sort using delegate @@ -2038,7 +2038,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || private void quickSortImpl(alias less, Range)(Range r, size_t depth) { - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; import std.algorithm.mutation : swap, swapAt; alias Elem = ElementType!(Range); @@ -2736,7 +2736,7 @@ private template TimSortImpl(alias pred, R) @safe unittest { - import std.random : Random, uniform, randomShuffle; + import std.random : Random, randomShuffle, uniform; // Element type with two fields static struct E @@ -2893,7 +2893,7 @@ schwartzSort(alias transform, alias less = "a < b", if (isRandomAccessRange!R && hasLength!R) { import std.conv : emplace; - import std.range : zip, SortedRange; + import std.range : SortedRange, zip; import std.string : representation; alias T = typeof(unaryFun!transform(r.front)); @@ -3437,7 +3437,7 @@ private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)( T minValue = 0, T maxValue = 255) { import std.algorithm.iteration : map; - import std.random : unpredictableSeed, Random, uniform; + import std.random : Random, uniform, unpredictableSeed; auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize); return iota(0, size).map!(_ => uniform(minValue, maxValue)).array; } @@ -3698,7 +3698,7 @@ if (isInputRange!(SRange) && isRandomAccessRange!(TRange) @system unittest { - import std.random : Random, unpredictableSeed, uniform, randomShuffle; + import std.random : Random, randomShuffle, uniform, unpredictableSeed; import std.typecons : Yes; auto r = Random(unpredictableSeed); diff --git a/std/array.d b/std/array.d index e3d94ad17ae..efbed4606fa 100644 --- a/std/array.d +++ b/std/array.d @@ -82,7 +82,7 @@ import std.meta; import std.traits; import std.range.primitives; -public import std.range.primitives : save, empty, popFront, popBack, front, back; +public import std.range.primitives : back, empty, front, popBack, popFront, save; /** * Allocates an array and initializes it with copies of the elements @@ -744,7 +744,7 @@ slice, returns that slice. Otherwise, returns the null slice. auto overlap(T, U)(T[] r1, U[] r2) @trusted pure nothrow if (is(typeof(r1.ptr < r2.ptr) == bool)) { - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; auto b = max(r1.ptr, r2.ptr); auto e = min(r1.ptr + r1.length, r2.ptr + r2.length); return b < e ? b[0 .. e - b] : null; diff --git a/std/bigint.d b/std/bigint.d index 861df64b1ce..b24ad5668f5 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -27,7 +27,7 @@ module std.bigint; import std.conv : ConvException; -private import std.format : FormatSpec, FormatException; +private import std.format : FormatException, FormatSpec; private import std.internal.math.biguintcore; private import std.range.primitives; private import std.traits; @@ -674,7 +674,7 @@ public: /// @system unittest { - import std.conv : to, ConvOverflowException; + import std.conv : ConvOverflowException, to; import std.exception : assertThrown; assert(BigInt("0").to!int == 0); @@ -687,7 +687,7 @@ public: @system unittest { - import std.conv : to, ConvOverflowException; + import std.conv : ConvOverflowException, to; import std.exception : assertThrown; assert(BigInt("-1").to!byte == -1); diff --git a/std/bitmanip.d b/std/bitmanip.d index c38a2a8cec4..4bd4f13d645 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -772,7 +772,7 @@ struct BitArray { private: - import core.bitop : bts, btr, bsf, bt; + import core.bitop : bsf, bt, btr, bts; import std.format : FormatSpec; size_t _len; @@ -2052,7 +2052,7 @@ public: */ @property auto bitsSet() const nothrow { - import std.algorithm.iteration : filter, map, joiner; + import std.algorithm.iteration : filter, joiner, map; import std.range : iota; return iota(dim). diff --git a/std/c/freebsd/socket.d b/std/c/freebsd/socket.d index 35fa4c7810f..dca4f520f63 100644 --- a/std/c/freebsd/socket.d +++ b/std/c/freebsd/socket.d @@ -14,4 +14,4 @@ public import core.sys.posix.netdb; public import core.sys.posix.netinet.in_ : IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP, IPPROTO_ND, IPPROTO_MAX, INADDR_LOOPBACK, INADDR_NONE; -public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, SOCK_RDM, MSG_NOSIGNAL; +public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, MSG_NOSIGNAL, SOCK_RDM; diff --git a/std/c/process.d b/std/c/process.d index 14fc729c6c7..8fcc1929340 100644 --- a/std/c/process.d +++ b/std/c/process.d @@ -13,7 +13,7 @@ deprecated("Import core.stdc.stdlib or the appropriate core.sys.posix.* modules module std.c.process; private import core.stdc.stddef; -public import core.stdc.stdlib : exit, abort, system; +public import core.stdc.stdlib : abort, exit, system; extern (C): diff --git a/std/complex.d b/std/complex.d index 77638229220..e9ad5eb98e7 100644 --- a/std/complex.d +++ b/std/complex.d @@ -298,7 +298,7 @@ if (isFloatingPoint!T) Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const if (op == "^^" && isNumeric!R) { - import std.math : cos, exp, log, sin, PI; + import std.math : cos, exp, log, PI, sin; Unqual!(CommonType!(T, R)) ab = void, ar = void; if (lhs >= 0) @@ -370,7 +370,7 @@ if (isFloatingPoint!T) ref Complex opOpAssign(string op, C)(C z) if (op == "^^" && is(C R == Complex!R)) { - import std.math : exp, log, cos, sin; + import std.math : cos, exp, log, sin; immutable r = abs(this); immutable t = arg(this); immutable ab = r^^z.re * exp(-t*z.im); @@ -799,7 +799,7 @@ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) @safe pure nothrow @nogc { - import std.math : sin, cos; + import std.math : cos, sin; return Complex!(CommonType!(T,U)) (modulus*cos(argument), modulus*sin(argument)); } @@ -822,7 +822,7 @@ Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) */ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc { - import std.math : expi, coshisinh; + import std.math : coshisinh, expi; auto cs = expi(z.re); auto csh = coshisinh(z.im); return typeof(return)(cs.im * csh.re, cs.re * csh.im); @@ -840,7 +840,7 @@ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc /// ditto Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc { - import std.math : expi, coshisinh; + import std.math : coshisinh, expi; auto cs = expi(z.re); auto csh = coshisinh(z.im); return typeof(return)(cs.re * csh.re, - cs.im * csh.im); diff --git a/std/concurrency.d b/std/concurrency.d index 2fbcd308bae..b54614208d0 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -2269,7 +2269,7 @@ private version (unittest) { import std.stdio; - import std.typecons : tuple, Tuple; + import std.typecons : Tuple, tuple; void testfn(Tid tid) { @@ -2410,7 +2410,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) // check that var is global, can't take address of a TLS variable static assert(is(typeof({ __gshared p = &var; })), "var must be 'static shared' or '__gshared'."); - import core.atomic : atomicLoad, MemoryOrder, atomicStore; + import core.atomic : atomicLoad, atomicStore, MemoryOrder; static shared bool flag; if (!atomicLoad!(MemoryOrder.acq)(flag)) diff --git a/std/container/array.d b/std/container/array.d index af01deff11e..0cc572389d0 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -251,7 +251,7 @@ private struct RangeT(A) struct Array(T) if (!is(Unqual!T == bool)) { - import core.stdc.stdlib : malloc, realloc, free; + import core.stdc.stdlib : free, malloc, realloc; import core.stdc.string : memcpy, memmove, memset; import core.memory : GC; diff --git a/std/container/dlist.d b/std/container/dlist.d index d7af3609c9c..a1865741c62 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -39,7 +39,7 @@ module std.container.dlist; // If you want to apply range operations, simply slice it. import std.algorithm.searching : countUntil; - import std.range : popFrontN, popBackN, walkLength; + import std.range : popBackN, popFrontN, walkLength; auto sl = DList!int([1, 2, 3, 4, 5]); assert(countUntil(sl[], 2) == 1); diff --git a/std/container/rbtree.d b/std/container/rbtree.d index 5342b1692e4..3be1871486e 100644 --- a/std/container/rbtree.d +++ b/std/container/rbtree.d @@ -742,7 +742,7 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) import std.meta : allSatisfy; import std.range : Take; import std.range.primitives : isInputRange, walkLength; - import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible; + import std.traits : isDynamicArray, isImplicitlyConvertible, isIntegral; alias _less = binaryFun!less; @@ -1820,7 +1820,7 @@ assert(equal(rbt[], [5])); test!byte(); } -import std.range.primitives : isInputRange, isSomeString, ElementType; +import std.range.primitives : ElementType, isInputRange, isSomeString; import std.traits : isArray; /++ diff --git a/std/container/slist.d b/std/container/slist.d index 820b0bbac45..b36584e3b55 100644 --- a/std/container/slist.d +++ b/std/container/slist.d @@ -57,7 +57,7 @@ struct SList(T) { import std.exception : enforce; import std.range : Take; - import std.range.primitives : isInputRange, isForwardRange, ElementType; + import std.range.primitives : ElementType, isForwardRange, isInputRange; import std.traits : isImplicitlyConvertible; private struct Node diff --git a/std/container/util.d b/std/container/util.d index 5be9e7d5470..1f7aeb3464a 100644 --- a/std/container/util.d +++ b/std/container/util.d @@ -105,7 +105,7 @@ if (is(T == struct) || is(T == class)) template make(alias Container, Args...) if (!is(Container)) { - import std.range : isInputRange, isInfinite; + import std.range : isInfinite, isInputRange; import std.traits : isDynamicArray; auto make(Range)(Range range) diff --git a/std/conv.d b/std/conv.d index 85a62cfdc08..1db8faf1744 100644 --- a/std/conv.d +++ b/std/conv.d @@ -1851,7 +1851,7 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) && // bugzilla 15800 @safe unittest { - import std.utf : byCodeUnit, byChar, byWchar, byDchar; + import std.utf : byChar, byCodeUnit, byDchar, byWchar; assert(to!int(byCodeUnit("10")) == 10); assert(to!int(byCodeUnit("10"), 10) == 10); @@ -2439,7 +2439,7 @@ in } body { - import core.checkedint : mulu, addu; + import core.checkedint : addu, mulu; import std.exception : enforce; if (radix == 10) @@ -2638,7 +2638,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum isFloatingPoint!Target && !is(Target == enum)) { import core.stdc.math : HUGE_VAL; - import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; + import std.ascii : isAlpha, isDigit, isHexDigit, toLower, toUpper; import std.exception : enforce; static if (isNarrowString!Source) @@ -3065,7 +3065,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum @safe unittest { import std.exception; - import std.math : isNaN, fabs; + import std.math : fabs, isNaN; // Compare reals with given precision bool feq(in real rx, in real ry, in real precision = 0.000001L) @@ -5814,7 +5814,7 @@ an even number of hexadecimal digits (regardless of the case). private bool isHexLiteral(String)(scope const String hexData) { import std.ascii : isHexDigit; - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; size_t i; foreach (const dchar c; hexData) { diff --git a/std/datetime/date.d b/std/datetime/date.d index a7d33830128..184bc4a058e 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -7281,7 +7281,7 @@ public: if (isSomeString!S) { import std.algorithm.searching : startsWith; - import std.conv : to, text, ConvException; + import std.conv : ConvException, text, to; import std.exception : enforce; import std.string : strip; @@ -8797,7 +8797,7 @@ public: static TimeOfDay fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) { - import std.conv : to, text, ConvException; + import std.conv : ConvException, text, to; import std.exception : enforce; import std.string : strip; diff --git a/std/datetime/interval.d b/std/datetime/interval.d index 302b4c28e99..cf097fd46ac 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -7,7 +7,7 @@ +/ module std.datetime.interval; -import core.time : Duration, dur; +import core.time : dur, Duration; import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek, DayOfWeek, isTimePoint, Month; import std.exception : enforce; diff --git a/std/datetime/package.d b/std/datetime/package.d index 976d06ddb79..11f8a61502e 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -118,7 +118,7 @@ public import std.datetime.timezone; import core.exception : AssertError; import std.functional : unaryFun; import std.traits; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; // Verify module example. diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 46eee5c8325..4a91f419def 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -1758,7 +1758,7 @@ public: /// @safe unittest { - import core.time : msecs, usecs, hnsecs, nsecs; + import core.time : hnsecs, msecs, nsecs, usecs; import std.datetime.date : DateTime; auto dt = DateTime(1982, 4, 1, 20, 59, 22); @@ -1846,7 +1846,7 @@ public: /// @safe unittest { - import core.time : Duration, msecs, hnsecs, nsecs; + import core.time : Duration, hnsecs, msecs, nsecs; import std.datetime.date : DateTime; auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); @@ -4564,7 +4564,7 @@ public: /// @safe unittest { - import core.time : msecs, hnsecs; + import core.time : hnsecs, msecs; import std.datetime.date : DateTime; auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); @@ -7383,7 +7383,7 @@ public: /// @safe unittest { - import core.time : msecs, usecs, hnsecs; + import core.time : hnsecs, msecs, usecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == @@ -7834,7 +7834,7 @@ public: /// @safe unittest { - import core.time : msecs, hnsecs; + import core.time : hnsecs, msecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == @@ -7966,7 +7966,7 @@ public: /// @safe unittest { - import core.time : msecs, hnsecs; + import core.time : hnsecs, msecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == @@ -8102,7 +8102,7 @@ public: /// @safe unittest { - import core.time : msecs, hnsecs; + import core.time : hnsecs, msecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == @@ -8247,7 +8247,7 @@ public: static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe if (isSomeString!S) { - import std.algorithm.searching : startsWith, find; + import std.algorithm.searching : find, startsWith; import std.conv : to; import std.string : strip; @@ -8310,7 +8310,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs, hnsecs; + import core.time : hnsecs, hours, msecs, usecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8559,7 +8559,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs, hnsecs; + import core.time : hnsecs, hours, msecs, usecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8785,7 +8785,7 @@ public: /// @safe unittest { - import core.time : hours, msecs, usecs, hnsecs; + import core.time : hnsecs, hours, msecs, usecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -9582,8 +9582,8 @@ SysTime parseRFC822DateTime(R)(R value) @safe if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) { - import std.algorithm.searching : find, all; - import std.ascii : isDigit, isAlpha, isPrintable; + import std.algorithm.searching : all, find; + import std.ascii : isAlpha, isDigit, isPrintable; import std.conv : to; import std.functional : not; import std.string : capitalize, format; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index 0b3bfa74e2d..a9c7a225a33 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -1199,7 +1199,7 @@ private: { long tm_gmtoff(long stdTime) @trusted const nothrow { - import core.stdc.time : localtime, gmtime, tm; + import core.stdc.time : gmtime, localtime, tm; time_t unixTime = stdTimeToUnixTime(stdTime); tm* buf = localtime(&unixTime); @@ -1595,7 +1595,7 @@ package: static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : startsWith, countUntil, all; + import std.algorithm.searching : all, countUntil, startsWith; import std.ascii : isDigit; import std.conv : to; import std.format : format; @@ -1739,7 +1739,7 @@ package: static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : startsWith, countUntil, all; + import std.algorithm.searching : all, countUntil, startsWith; import std.ascii : isDigit; import std.conv : to; import std.format : format; @@ -1914,11 +1914,11 @@ private: +/ final class PosixTimeZone : TimeZone { - import std.algorithm.searching : countUntil, canFind, startsWith; - import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; + import std.algorithm.searching : canFind, countUntil, startsWith; + import std.file : dirEntries, DirEntry, exists, isDir, isFile, SpanMode; import std.path : extension; import std.stdio : File; - import std.string : strip, representation; + import std.string : representation, strip; import std.traits : isArray, isSomeChar; public: diff --git a/std/digest/digest.d b/std/digest/digest.d index 9114cb3b2b1..99a83de42c0 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -413,7 +413,7 @@ if (isDigest!T) package template isDigestibleRange(Range) { import std.digest.md; - import std.range : isInputRange, ElementType; + import std.range : ElementType, isInputRange; enum bool isDigestibleRange = isInputRange!Range && is(typeof( { MD5 ha; //Could use any conformant hash @@ -1136,7 +1136,7 @@ if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && import std.internal.test.dummyrange : ReferenceInputRange; import std.range : takeExactly; import std.string : representation; - import std.utf : byWchar, byDchar; + import std.utf : byDchar, byWchar; { auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; diff --git a/std/digest/hmac.d b/std/digest/hmac.d index 63fffdd5004..77eb38942bb 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -16,7 +16,7 @@ Source: $(PHOBOSSRC std/digest/_hmac.d) module std.digest.hmac; -import std.digest.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType; +import std.digest.digest : DigestType, hasBlockSize, isDigest, isDigestibleRange; import std.meta : allSatisfy; /** @@ -286,7 +286,7 @@ if (isDigest!H) version(unittest) { - import std.digest.digest : toHexString, LetterCase; + import std.digest.digest : LetterCase, toHexString; alias hex = toHexString!(LetterCase.lower); } diff --git a/std/exception.d b/std/exception.d index 5767e8710a0..9c6d9089046 100644 --- a/std/exception.d +++ b/std/exception.d @@ -1511,7 +1511,7 @@ class ErrnoException : Exception @system unittest { - import core.stdc.errno : errno, EAGAIN; + import core.stdc.errno : EAGAIN, errno; auto old = errno; scope(exit) errno = old; @@ -2022,7 +2022,7 @@ pure @safe unittest { import std.algorithm.comparison : equal; import std.algorithm.iteration : map, splitter; - import std.conv : to, ConvException; + import std.conv : ConvException, to; auto s = "12,1337z32,54,2,7,9,1z,6,8"; diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index cfeae0cdb77..d4d6cc28b6a 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -370,8 +370,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) @system unittest { + import std.experimental.allocator : IAllocator, theAllocator; import std.experimental.allocator.gc_allocator : GCAllocator; - import std.experimental.allocator : theAllocator, IAllocator; // One word before and after each allocation. auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator); diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index ba47f6fc791..04e33d09a57 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -2,7 +2,7 @@ module std.experimental.allocator.building_blocks.free_list; import std.experimental.allocator.common; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; /** diff --git a/std/experimental/allocator/building_blocks/free_tree.d b/std/experimental/allocator/building_blocks/free_tree.d index 6b64659297e..ad1192b108c 100644 --- a/std/experimental/allocator/building_blocks/free_tree.d +++ b/std/experimental/allocator/building_blocks/free_tree.d @@ -52,7 +52,7 @@ struct FreeTree(ParentAllocator) static assert(ParentAllocator.alignment % size_t.alignof == 0, "FreeTree must be on top of a word-aligned allocator"); - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; import std.algorithm.mutation : swap; import std.traits : hasMember; diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 38c53848bae..2a810b5775f 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -95,7 +95,7 @@ information is available in client code at deallocation time.) */ struct KRRegion(ParentAllocator = NullAllocator) { - import std.experimental.allocator.common : stateSize, alignedAt; + import std.experimental.allocator.common : alignedAt, stateSize; import std.traits : hasMember; import std.typecons : Ternary; diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index 0cc60b9dd58..6b1f7726e90 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -3,7 +3,7 @@ module std.experimental.allocator.building_blocks.region; import std.experimental.allocator.building_blocks.null_allocator; import std.experimental.allocator.common; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; /** A $(D Region) allocator allocates memory straight from one contiguous chunk. diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index 0b0bde6a507..59a58151c9b 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -425,7 +425,7 @@ version(unittest) { import std.conv : text; import std.math : isPowerOf2; - import std.stdio : writeln, stderr; + import std.stdio : stderr, writeln; import std.typecons : Ternary; alias A = typeof(make()); scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); @@ -551,7 +551,7 @@ version(unittest) { import std.conv : text; import std.math : isPowerOf2; - import std.stdio : writeln, stderr; + import std.stdio : stderr, writeln; import std.typecons : Ternary; scope(failure) stderr.writeln("testAllocatorObject failed for ", AllocInterface.stringof); diff --git a/std/experimental/allocator/mallocator.d b/std/experimental/allocator/mallocator.d index 5dfdc999966..111ac759c17 100644 --- a/std/experimental/allocator/mallocator.d +++ b/std/experimental/allocator/mallocator.d @@ -230,7 +230,7 @@ struct AlignedMallocator @trusted @nogc nothrow void[] alignedAllocate(size_t bytes, uint a) shared { - import core.stdc.errno : ENOMEM, EINVAL; + import core.stdc.errno : EINVAL, ENOMEM; assert(a.isGoodDynamicAlignment); void* result; auto code = posix_memalign(&result, a, bytes); diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 2e971c8d7e1..19a32d712e2 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -229,7 +229,7 @@ public import std.experimental.allocator.common, // Example in the synopsis above @system unittest { - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; import std.experimental.allocator.building_blocks.bitmapped_block diff --git a/std/experimental/allocator/typed.d b/std/experimental/allocator/typed.d index 92828f3879e..3ba0f3eecce 100644 --- a/std/experimental/allocator/typed.d +++ b/std/experimental/allocator/typed.d @@ -15,8 +15,8 @@ import std.experimental.allocator; import std.experimental.allocator.common; import std.range : isInputRange, isForwardRange, walkLength, save, empty, front, popFront; -import std.traits : isPointer, hasElaborateDestructor; -import std.typecons : Flag, Yes, No; +import std.traits : hasElaborateDestructor, isPointer; +import std.typecons : Flag, No, Yes; /** Allocation-related flags dictated by type characteristics. `TypedAllocator` diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d index 96dc4f282cf..d0640cd52a0 100644 --- a/std/experimental/checkedint.d +++ b/std/experimental/checkedint.d @@ -1336,7 +1336,7 @@ struct Throw auto x1 = cast(T) x; assert(x1 == 42); x = T.max + 1; - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; assertThrown(cast(T) x); x = x.max; assertThrown(x += 42); @@ -2277,7 +2277,7 @@ if (isIntegral!L && isIntegral!R) else alias Result = typeof(mixin("L() " ~ x ~ " R()")); - import core.checkedint : addu, adds, subs, muls, subu, mulu; + import core.checkedint : adds, addu, muls, mulu, subs, subu; import std.algorithm.comparison : among; static if (x == "==") { diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 782dd5885e4..10bba3ea957 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -627,7 +627,7 @@ alias fatalf = defaultLogFunctionf!(LogLevel.fatal); private struct MsgRange { - import std.traits : isSomeString, isSomeChar; + import std.traits : isSomeChar, isSomeString; private Logger log; @@ -1939,7 +1939,7 @@ version(unittest) private void testFuncNames(Logger logger) @safe @safe unittest { import std.conv : to; - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; import std.format : format; auto l = new TestLogger(LogLevel.all); diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 13a21a9223c..899638f2c07 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -512,7 +512,7 @@ version(StdDdoc) /// @system unittest { - import std.traits : functionAttributes, FunctionAttribute; + import std.traits : FunctionAttribute, functionAttributes; interface A { int run(); } interface B { int stop(); @property int status(); } class X diff --git a/std/file.d b/std/file.d index f796d5c041c..efc75a54043 100644 --- a/std/file.d +++ b/std/file.d @@ -741,7 +741,7 @@ private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, if (!result) { import core.stdc.wchar_ : wcslen; - import std.conv : to, text; + import std.conv : text, to; if (!f) f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]); @@ -2321,7 +2321,7 @@ private bool ensureDirExists()(in char[] pathname) void mkdirRecurse(in char[] pathname) @safe { - import std.path : dirName, baseName; + import std.path : baseName, dirName; const left = dirName(pathname); if (left.length != pathname.length && !exists(left)) @@ -2338,7 +2338,7 @@ void mkdirRecurse(in char[] pathname) @safe { import std.exception : assertThrown; { - import std.path : buildPath, buildNormalizedPath; + import std.path : buildNormalizedPath, buildPath; immutable basepath = deleteme ~ "_dir"; scope(exit) () @trusted { rmdirRecurse(basepath); }(); @@ -2748,7 +2748,7 @@ else version (NetBSD) } else version (FreeBSD) { - import std.exception : errnoEnforce, assumeUnique; + import std.exception : assumeUnique, errnoEnforce; enum { CTL_KERN = 1, @@ -3436,7 +3436,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co else version(Posix) { static import core.stdc.stdio; - import std.conv : to, octal; + import std.conv : octal, to; immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY); cenforce(fdr != -1, f, fromz); @@ -3609,7 +3609,7 @@ version(Windows) @system unittest version(Posix) @system unittest { - import std.exception : enforce, collectException; + import std.exception : collectException, enforce; import std.process : executeShell; collectException(rmdirRecurse(deleteme)); auto d = deleteme~"/a/b/c/d/e/f/g"; @@ -3997,7 +3997,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true) import std.algorithm.searching : startsWith; import std.array : array; import std.conv : to; - import std.path : dirEntries, buildPath, absolutePath; + import std.path : absolutePath, buildPath, dirEntries; import std.process : thisProcessID; import std.range.primitives : walkLength; @@ -4091,7 +4091,7 @@ auto dirEntries(string path, string pattern, SpanMode mode, bool followSymlink = true) { import std.algorithm.iteration : filter; - import std.path : globMatch, baseName; + import std.path : baseName, globMatch; bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); } return filter!f(DirIterator(path, mode, followSymlink)); diff --git a/std/format.d b/std/format.d index e155372fc77..04c043db20a 100644 --- a/std/format.d +++ b/std/format.d @@ -1004,7 +1004,7 @@ struct FormatSpec(Char) if (is(Unqual!Char == Char)) { import std.algorithm.searching : startsWith; - import std.ascii : isDigit, isPunctuation, isAlpha; + import std.ascii : isAlpha, isDigit, isPunctuation; import std.conv : parse, text, to; /** @@ -2165,7 +2165,7 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) version (CRuntime_Microsoft) { - import std.math : isNaN, isInfinity; + import std.math : isInfinity, isNaN; immutable double tval = val; // convert early to get "inf" in case of overflow string s; if (isNaN(tval)) @@ -3653,7 +3653,7 @@ if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is // Issue 11175 version (Windows) { - import core.sys.windows.com : IUnknown, IID; + import core.sys.windows.com : IID, IUnknown; import core.sys.windows.windows : HRESULT; interface IUnknown2 : IUnknown { } @@ -4250,7 +4250,7 @@ void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, s { import core.stdc.string : strlen; import std.array : appender; - import std.conv : text, octal; + import std.conv : octal, text; import std.c.stdio : snprintf; debug(format) printf("std.format.format.unittest\n"); @@ -5072,7 +5072,7 @@ private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpe if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range)) { import std.algorithm.searching : find; - import std.conv : to, text; + import std.conv : text, to; if (spec.spec == 's' || spec.spec == 'c') { auto result = to!T(input.front); @@ -5798,7 +5798,7 @@ immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char) { import std.array : appender; - import std.format : formattedWrite, FormatException; + import std.format : FormatException, formattedWrite; auto w = appender!(immutable(Char)[]); auto n = formattedWrite(w, fmt, args); version (all) @@ -5874,7 +5874,7 @@ if (isSomeString!(typeof(fmt))) char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { import core.exception : RangeError; - import std.format : formattedWrite, FormatException; + import std.format : FormatException, formattedWrite; import std.utf : encode; size_t i; diff --git a/std/functional.d b/std/functional.d index d52e6cf8692..588ec520675 100644 --- a/std/functional.d +++ b/std/functional.d @@ -259,7 +259,7 @@ template binaryFun(alias fun, string parm1Name = "a", private uint _ctfeSkipOp(ref string op) { if (!__ctfe) assert(false); - import std.ascii : isASCII, isAlphaNum; + import std.ascii : isAlphaNum, isASCII; immutable oldLength = op.length; while (op.length) { diff --git a/std/getopt.d b/std/getopt.d index 890a3c3f233..66194645bb9 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1614,7 +1614,7 @@ Params: */ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) { - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; import std.format : formattedWrite; output.formattedWrite("%s\n", text); diff --git a/std/internal/test/dummyrange.d b/std/internal/test/dummyrange.d index a6bce0ada23..d7191bdf064 100644 --- a/std/internal/test/dummyrange.d +++ b/std/internal/test/dummyrange.d @@ -420,7 +420,7 @@ if (is(T == TestFoo)) @system unittest { import std.algorithm.comparison : equal; - import std.range : iota, retro, repeat; + import std.range : iota, repeat, retro; import std.traits : Unqual; static void testInputRange(T,Cmp)() diff --git a/std/json.d b/std/json.d index 008870aa341..14975e4ce72 100644 --- a/std/json.d +++ b/std/json.d @@ -100,7 +100,7 @@ JSON value node */ struct JSONValue { - import std.exception : enforceEx, enforce; + import std.exception : enforce, enforceEx; union Store { @@ -702,7 +702,7 @@ Params: JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) { - import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; + import std.ascii : isDigit, isHexDigit, isWhite, toLower, toUpper; import std.typecons : Yes; import std.utf : encode; @@ -1224,7 +1224,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o break; case JSON_TYPE.FLOAT: - import std.math : isNaN, isInfinity; + import std.math : isInfinity, isNaN; auto val = value.store.floating; @@ -1638,7 +1638,7 @@ EOF"; @safe unittest { import std.exception : assertThrown; - import std.math : isNaN, isInfinity; + import std.math : isInfinity, isNaN; // expected representations of NaN and Inf enum { diff --git a/std/math.d b/std/math.d index 43c18187f5d..ed8abf8ad15 100644 --- a/std/math.d +++ b/std/math.d @@ -686,7 +686,7 @@ float sin(float x) @safe pure nothrow @nogc { return sin(cast(real) x); } /// @safe unittest { - import std.math : sin, PI; + import std.math : PI, sin; import std.stdio : writefln; void someFunc() diff --git a/std/meta.d b/std/meta.d index 77ed211d6e0..6a4d87b9345 100644 --- a/std/meta.d +++ b/std/meta.d @@ -1164,7 +1164,7 @@ template aliasSeqOf(alias range) @safe unittest { - import std.conv : to, octal; + import std.conv : octal, to; import std.range : iota; //Testing compile time octal foreach (I2; aliasSeqOf!(iota(0, 8))) diff --git a/std/net/curl.d b/std/net/curl.d index e8212735363..a7fe0d3a9f7 100644 --- a/std/net/curl.d +++ b/std/net/curl.d @@ -238,7 +238,7 @@ version(unittest) private Request!T recvReq(T=char)(Socket s) { import std.algorithm.comparison : min; - import std.algorithm.searching : find, canFind; + import std.algorithm.searching : canFind, find; import std.conv : to; import std.regex : ctRegex, matchFirst; @@ -1287,7 +1287,7 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) void popFront() { - import std.algorithm.searching : findSplitAfter, findSplit; + import std.algorithm.searching : findSplit, findSplitAfter; enforce!CurlException(currentValid, "Cannot call popFront() on empty range"); if (lines.empty) @@ -2417,7 +2417,7 @@ struct HTTP { import std.algorithm.searching : startsWith; import std.conv : to; - import std.regex : regex, match; + import std.regex : match, regex; import std.uni : toLower; // Wrap incoming callback in order to separate http status line from @@ -4061,7 +4061,7 @@ class CurlTimeoutException : CurlException /// Equal to $(REF CURLcode, etc,c,curl) alias CurlCode = CURLcode; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; /// Flag to specify whether or not an exception is thrown on error. alias ThrowOnError = Flag!"throwOnError"; @@ -4099,7 +4099,7 @@ private struct CurlAPI { version (Posix) { - import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY; + import core.sys.posix.dlfcn : dlclose, dlopen, dlsym, RTLD_LAZY; alias loadSym = dlsym; } else version (Windows) diff --git a/std/net/isemail.d b/std/net/isemail.d index d37093db375..3998917a50b 100644 --- a/std/net/isemail.d +++ b/std/net/isemail.d @@ -28,7 +28,7 @@ module std.net.isemail; import std.range.primitives; // : ElementType; import std.regex; import std.traits; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; /** * Check that an email address conforms to RFCs 5321, 5322 and others. @@ -62,7 +62,7 @@ EmailStatus isEmail(Char)(const(Char)[] email, CheckDns checkDNS = No.checkDns, EmailStatusCode errorLevel = EmailStatusCode.none) if (isSomeChar!(Char)) { - import std.algorithm.iteration : uniq, filter, map; + import std.algorithm.iteration : filter, map, uniq; import std.algorithm.searching : canFind, maxElement; import std.array : array, split; import std.conv : to; diff --git a/std/numeric.d b/std/numeric.d index da287290f68..508f64f88a5 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -128,7 +128,7 @@ if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + /// @safe unittest { - import std.math : sin, cos; + import std.math : cos, sin; // Define a 16-bit floating point values CustomFloat!16 x; // Using the number of bits @@ -2188,7 +2188,7 @@ if (isRandomAccessRange!(R1) && hasLength!(R1) && isRandomAccessRange!(R2) && hasLength!(R2)) { import core.exception : onOutOfMemoryError; - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; import std.algorithm.mutation : swap; import std.functional : binaryFun; @@ -2316,7 +2316,7 @@ optimizations. struct GapWeightedSimilarityIncremental(Range, F = double) if (isRandomAccessRange!(Range) && hasLength!(Range)) { - import core.stdc.stdlib : malloc, realloc, alloca, free; + import core.stdc.stdlib : alloca, free, malloc, realloc; private: Range s, t; diff --git a/std/parallelism.d b/std/parallelism.d index 51d322172d0..42b87c74e10 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -105,7 +105,7 @@ version(Windows) // BUGS: Only works on Windows 2000 and above. shared static this() { - import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo; + import core.sys.windows.windows : GetSystemInfo, SYSTEM_INFO; import std.algorithm.comparison : max; SYSTEM_INFO si; @@ -118,7 +118,7 @@ else version(linux) { shared static this() { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + import core.sys.posix.unistd : sysconf, _SC_NPROCESSORS_ONLN; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } @@ -126,7 +126,7 @@ else version(Solaris) { shared static this() { - import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; + import core.sys.posix.unistd : sysconf, _SC_NPROCESSORS_ONLN; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } @@ -2625,7 +2625,7 @@ public: byte[maxStack] buf = void; immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof; - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; if (nBytesNeeded < maxStack) { tasks = (cast(RTask*) buf.ptr)[0 .. nWorkUnits]; @@ -3372,7 +3372,7 @@ private void submitAndExecute( immutable nThreads = pool.size + 1; alias PTask = typeof(scopedTask(doIt)); - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; import core.stdc.string : memcpy; // The logical thing to do would be to just use alloca() here, but that @@ -3931,12 +3931,12 @@ version(unittest) // These are the tests that should be run every time Phobos is compiled. @system unittest { - import std.algorithm.comparison : equal, min, max; + import std.algorithm.comparison : equal, max, min; import std.algorithm.iteration : filter, map, reduce; import std.array : split; import std.conv : text; import std.exception : assertThrown; - import std.math : approxEqual, sqrt, log; + import std.math : approxEqual, log, sqrt; import std.range : indexed, iota, join; import std.typecons : Tuple, tuple; diff --git a/std/path.d b/std/path.d index a9f0bd8015d..9b3049fe181 100644 --- a/std/path.d +++ b/std/path.d @@ -641,7 +641,7 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementT static assert(dirName("dir/file") == "dir"); import std.array; - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(dirName("".byChar).array == "."); assert(dirName("file"w.byWchar).array == "."w); @@ -1075,7 +1075,7 @@ if (isConvertibleToString!R) assert(stripExtension("file.ext1.ext2"d) == "file.ext1"); import std.array; - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(stripExtension("file".byChar).array == "file"); assert(stripExtension("file.ext"w.byWchar).array == "file"); @@ -1187,7 +1187,7 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element !isConvertibleToString!R && isSomeChar!C) { - import std.range : only, chain; + import std.range : chain, only; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R); @@ -1279,7 +1279,7 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element !isConvertibleToString!R && isSomeChar!C) { - import std.range : only, chain; + import std.range : chain, only; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R); @@ -1539,7 +1539,7 @@ if ((isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && isSomeChar!(Elem } else { - import std.range : only, chain; + import std.range : chain, only; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R1); @@ -2906,9 +2906,9 @@ if ((isNarrowString!R1 || import std.algorithm.comparison : mismatch; import std.algorithm.iteration : joiner; import std.array : array; + import std.range : chain, choose, repeat; import std.range.primitives : walkLength; - import std.range : repeat, chain, choose; - import std.utf : byCodeUnit, byChar; + import std.utf : byChar, byCodeUnit; // Remove matching prefix from basePS and pathPS auto tup = mismatch!((a, b) => filenameCmp!cs(a, b) == 0)(basePS, pathPS); @@ -3850,8 +3850,8 @@ string expandTilde(string inputPath) nothrow version(Posix) { import core.exception : onOutOfMemoryError; - import core.stdc.errno : errno, ERANGE; - import core.stdc.stdlib : malloc, free, realloc; + import core.stdc.errno : ERANGE, errno; + import core.stdc.stdlib : free, malloc, realloc; /* Joins a path from a C string to the remainder of path. @@ -3913,7 +3913,7 @@ string expandTilde(string inputPath) nothrow } else { - import core.sys.posix.pwd : passwd, getpwnam_r; + import core.sys.posix.pwd : getpwnam_r, passwd; import std.string : indexOf; assert(path.length > 2 || (path.length == 2 && !isDirSeparator(path[1]))); diff --git a/std/process.d b/std/process.d index 4063442c08f..42c315ea87e 100644 --- a/std/process.d +++ b/std/process.d @@ -386,7 +386,7 @@ private Pid spawnProcessImpl(in char[][] args, scope(exit) if (workDirFD >= 0) close(workDirFD); if (workDir.length) { - import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR; + import core.sys.posix.fcntl : fstat, open, O_RDONLY, stat_t, S_ISDIR; workDirFD = open(workDir.tempCString(), O_RDONLY); if (workDirFD < 0) throw ProcessException.newFromErrno("Failed to open working directory"); @@ -449,8 +449,8 @@ private Pid spawnProcessImpl(in char[][] args, if (!(config & Config.inheritFDs)) { import core.stdc.stdlib : malloc; - import core.sys.posix.poll : pollfd, poll, POLLNVAL; - import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; + import core.sys.posix.poll : poll, pollfd, POLLNVAL; + import core.sys.posix.sys.resource : getrlimit, rlimit, RLIMIT_NOFILE; // Get the maximum number of file descriptors that could be open. rlimit r; @@ -778,7 +778,7 @@ version (Posix) @safe unittest version (Posix) private void setCLOEXEC(int fd, bool on) nothrow @nogc { - import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; + import core.sys.posix.fcntl : fcntl, FD_CLOEXEC, F_GETFD, F_SETFD; auto flags = fcntl(fd, F_GETFD); if (flags >= 0) { @@ -1590,7 +1590,7 @@ void kill(Pid pid, int codeOrSignal) } else version (Posix) { - import core.sys.posix.signal : SIGTERM, SIGKILL; + import core.sys.posix.signal : SIGKILL, SIGTERM; TestScript prog = "while true; do sleep 1; done"; } auto pid = spawnProcess(prog.path); @@ -2491,7 +2491,7 @@ private struct TestScript ~this() { - import std.file : remove, exists; + import std.file : exists, remove; if (!path.empty && exists(path)) { try { remove(path); } diff --git a/std/random.d b/std/random.d index 20f88b1be5d..9cf7469c401 100644 --- a/std/random.d +++ b/std/random.d @@ -1287,7 +1287,7 @@ A single unsigned integer seed value, different on each successive call */ @property uint unpredictableSeed() @trusted { - import core.thread : Thread, getpid, MonoTime; + import core.thread : getpid, MonoTime, Thread; static bool seeded; static MinstdRand0 rand; if (!seeded) diff --git a/std/range/package.d b/std/range/package.d index 27e1e9c3c5f..6da8f9d90a6 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -230,7 +230,7 @@ module std.range; public import std.array; public import std.range.interfaces; public import std.range.primitives; -public import std.typecons : Flag, Yes, No; +public import std.typecons : Flag, No, Yes; import std.meta; // allSatisfy, staticMap import std.traits; // CommonType, isCallable, isFloatingPoint, isIntegral, @@ -4197,7 +4197,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges)) return ranges[0].length; //[min|max](ranges[0].length, ranges[1].length, ...) - import std.algorithm.comparison : min, max; + import std.algorithm.comparison : max, min; if (stoppingPolicy == StoppingPolicy.shortest) return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); else @@ -4360,7 +4360,7 @@ enum StoppingPolicy import std.algorithm.mutation : swap; import std.algorithm.sorting : sort; - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; import std.typecons : tuple; int[] a = [ 1, 2, 3 ]; @@ -5593,7 +5593,7 @@ debug @system unittest { import std.algorithm.comparison : equal; import std.algorithm.searching : count; - import std.math : approxEqual, nextUp, nextDown; + import std.math : approxEqual, nextDown, nextUp; import std.meta : AliasSeq; static assert(is(ElementType!(typeof(iota(0f))) == float)); @@ -8170,7 +8170,7 @@ public: @safe pure nothrow unittest { import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy, AllDummyRanges; + import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, RangeType, ReturnBy; import std.meta : AliasSeq; alias AllForwardDummyRanges = AliasSeq!( @@ -10729,7 +10729,7 @@ if (isInputRange!R && is(R == class)) @safe unittest // bug 9060 { - import std.algorithm.iteration : map, joiner, group; + import std.algorithm.iteration : group, joiner, map; import std.algorithm.searching : until; // fix for std.algorithm auto r = map!(x => 0)([1]); diff --git a/std/range/primitives.d b/std/range/primitives.d index e4931fdf897..2c4201bfc77 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1364,7 +1364,7 @@ template hasLvalueElements(R) /// @safe unittest { - import std.range : iota, chain; + import std.range : chain, iota; static assert( hasLvalueElements!(int[])); static assert( hasLvalueElements!(const(int)[])); diff --git a/std/regex/internal/backtracking.d b/std/regex/internal/backtracking.d index 8e005d918fb..06db40d8681 100644 --- a/std/regex/internal/backtracking.d +++ b/std/regex/internal/backtracking.d @@ -799,7 +799,7 @@ alias Sequence(int B, int E) = staticIota!(B, E); struct CtContext { - import std.conv : to, text; + import std.conv : text, to; //dirty flags bool counter; //to mark the portion of matches to save diff --git a/std/regex/internal/generator.d b/std/regex/internal/generator.d index 6831e59ed2e..8a51c58a20d 100644 --- a/std/regex/internal/generator.d +++ b/std/regex/internal/generator.d @@ -12,11 +12,11 @@ module std.regex.internal.generator; */ @trusted private struct SampleGenerator(Char) { - import std.array : appender, Appender; + import std.array : Appender, appender; import std.format : formattedWrite; import std.random : Xorshift; - import std.regex.internal.ir : Regex, IR, IRL; - import std.utf : isValidDchar, byChar; + import std.regex.internal.ir : IR, IRL, Regex; + import std.utf : byChar, isValidDchar; Regex!Char re; Appender!(char[]) app; uint limit, seed; diff --git a/std/regex/internal/tests.d b/std/regex/internal/tests.d index 80e278bf648..46f972d828e 100644 --- a/std/regex/internal/tests.d +++ b/std/regex/internal/tests.d @@ -1015,7 +1015,7 @@ alias Sequence(int B, int E) = staticIota!(B, E); @safe unittest { import std.array : appender; - import std.regex : replaceFirst, replaceFirstInto, regex; + import std.regex : regex, replaceFirst, replaceFirstInto; import std.stdio : writeln; auto example = "Hello, world!"; diff --git a/std/regex/package.d b/std/regex/package.d index dec4bf3e1d7..cac967e123c 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -299,7 +299,7 @@ module std.regex; import std.regex.internal.ir; import std.regex.internal.thompson; //TODO: get rid of this dependency import std.traits, std.range.primitives; -import std.typecons; // : Flag, Yes, No; +import std.typecons; // : Flag, No, Yes; /++ $(D Regex) object holds regular expression pattern in compiled form. @@ -409,7 +409,7 @@ if (isSomeString!(S)) public auto regexImpl(S)(S pattern, const(char)[] flags="") if (isSomeString!(S)) { - import std.regex.internal.parser : Parser, CodeGen; + import std.regex.internal.parser : CodeGen, Parser; auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags); auto r = parser.program; return r; @@ -692,7 +692,7 @@ public: if (isSomeString!R) { private: - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; EngineType _engine; @@ -810,7 +810,7 @@ public: private @trusted auto matchOnce(alias Engine, RegEx, R)(R input, RegEx re) { - import core.stdc.stdlib : malloc, free; + import core.stdc.stdlib : free, malloc; import std.exception : enforce; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; @@ -1138,8 +1138,8 @@ if (isOutputRange!(OutR, ElementEncodingType!R[]) && isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) { import std.algorithm.searching : find; - import std.ascii : isDigit, isAlpha; - import std.conv : text, parse; + import std.ascii : isAlpha, isDigit; + import std.conv : parse, text; import std.exception : enforce; enum State { Normal, Dollar } auto state = State.Normal; diff --git a/std/signals.d b/std/signals.d index 4f4d81d409a..ec9c1af1fcb 100644 --- a/std/signals.d +++ b/std/signals.d @@ -63,7 +63,7 @@ module std.signals; import core.exception : onOutOfMemoryError; -import core.stdc.stdlib : calloc, realloc, free; +import core.stdc.stdlib : calloc, free, realloc; import std.stdio; // Special function for internal use only. diff --git a/std/stdio.d b/std/stdio.d index 1464445fd7f..09e1126af6e 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -350,7 +350,7 @@ Hello, Jimmy! struct File { import std.range.primitives : ElementEncodingType; - import std.traits : isScalarType, isArray; + import std.traits : isArray, isScalarType; enum Orientation { unknown, narrow, wide } private struct Impl @@ -525,7 +525,7 @@ Throws: $(D ErrnoException) in case of error. @system unittest // Test changing filename { - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; static import std.file; auto deleteme = testFilename(); @@ -547,7 +547,7 @@ Throws: $(D ErrnoException) in case of error. version (CRuntime_Microsoft) {} else // Not implemented @system unittest // Test changing mode { - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; static import std.file; auto deleteme = testFilename(); @@ -1003,7 +1003,7 @@ Throws: $(D Exception) if the file is not opened. */ void seek(long offset, int origin = SEEK_SET) @trusted { - import std.conv : to, text; + import std.conv : text, to; import std.exception : enforce, errnoEnforce; enforce(isOpen, "Attempting to seek() in an unopened file"); @@ -1152,7 +1152,7 @@ Throws: $(D Exception) if the file is not opened. version(Windows) { - import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL; + import core.sys.windows.windows : BOOL, OVERLAPPED, ULARGE_INTEGER; private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, Flags flags) @@ -1395,7 +1395,7 @@ Throws: $(D Exception) if the file is not opened. */ void write(S...)(S args) { - import std.traits : isBoolean, isIntegral, isAggregateType; + import std.traits : isAggregateType, isBoolean, isIntegral; auto w = lockingTextWriter(); foreach (arg; args) { diff --git a/std/string.d b/std/string.d index 2635a78f521..1d2adaed1a0 100644 --- a/std/string.d +++ b/std/string.d @@ -186,7 +186,7 @@ private: } public import std.format : format, sformat; -import std.typecons : Flag, Yes, No; +import std.typecons : Flag, No, Yes; public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; import std.meta; // AliasSeq, staticIndexOf @@ -199,8 +199,8 @@ import std.traits; // isConvertibleToString, isNarrowString, isSomeChar, //public imports for backward compatibility public import std.algorithm.comparison : cmp; -public import std.algorithm.searching : startsWith, endsWith, count; -public import std.array : join, replace, replaceInPlace, split, empty; +public import std.algorithm.searching : count, endsWith, startsWith; +public import std.array : empty, join, replace, replaceInPlace, split; /* ************* Exceptions *************** */ @@ -257,7 +257,7 @@ inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow { immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow out (result) { - import core.stdc.string : strlen, memcmp; + import core.stdc.string : memcmp, strlen; if (result) { auto slen = s.length; @@ -378,7 +378,7 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && { static import std.ascii; static import std.uni; - import std.utf : byDchar, byCodeUnit, UTFException, codeLength; + import std.utf : byCodeUnit, byDchar, codeLength, UTFException; alias Char = Unqual!(ElementEncodingType!Range); if (cs == Yes.caseSensitive) @@ -579,7 +579,7 @@ if (isConvertibleToString!Range) import std.conv : to; import std.exception : assertCTFEable; import std.traits : EnumMembers; - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; debug(string) trustedPrintf("string.indexOf.unittest\n"); assertCTFEable!( @@ -635,7 +635,7 @@ if (isConvertibleToString!Range) { import std.conv : to; import std.traits : EnumMembers; - import std.utf : byCodeUnit, byChar, byWchar; + import std.utf : byChar, byCodeUnit, byWchar; debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n"); assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2); @@ -3214,7 +3214,7 @@ if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || isNarrowString!Range) && !isConvertibleToString!Range) { - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; if (str.empty) return str; @@ -3308,7 +3308,7 @@ if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || @safe pure unittest { - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; import std.utf : decode; assert(chomp(" hello world \n\r") == " hello world \n"); assert(chomp(" hello world \r\n") == " hello world "); @@ -3396,7 +3396,7 @@ if (isConvertibleToString!Range) // Ranges import std.array : array; - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(chomp("hello world\r\n" .byChar ).array == "hello world"); assert(chomp("hello world\r\n"w.byWchar).array == "hello world"w); assert(chomp("hello world\r\n"d.byDchar).array == "hello world"d); @@ -3496,7 +3496,7 @@ unittest // Ranges import std.array : array; - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(chompPrefix("hello world" .byChar , "hello"d).array == " world"); assert(chompPrefix("hello world"w.byWchar, "hello" ).array == " world"w); assert(chompPrefix("hello world"d.byDchar, "hello"w).array == " world"d); @@ -3608,7 +3608,7 @@ if (isConvertibleToString!Range) @safe pure unittest { import std.array : array; - import std.utf : byChar, byWchar, byDchar, byCodeUnit, invalidUTFstrings; + import std.utf : byChar, byCodeUnit, byDchar, byWchar, invalidUTFstrings; assert(chop("hello world".byChar).array == "hello worl"); assert(chop("hello world\n"w.byWchar).array == "hello world"w); @@ -3719,7 +3719,7 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byDchar, byChar; + import std.utf : byChar, byDchar; return leftJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -3862,7 +3862,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byDchar, byChar; + import std.utf : byChar, byDchar; return rightJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -4090,7 +4090,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byDchar, byChar; + import std.utf : byChar, byDchar; return centerJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -4240,7 +4240,7 @@ auto detabber(Range)(Range r, size_t tabSize = 8) if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && !isConvertibleToString!Range) { - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; import std.utf : codeUnitLimit, decodeFront; assert(tabSize > 0); @@ -4471,7 +4471,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) && auto entabber(Range)(Range r, size_t tabSize = 8) if (isForwardRange!Range && !isConvertibleToString!Range) { - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; import std.utf : codeUnitLimit, decodeFront; assert(tabSize > 0); @@ -6513,7 +6513,7 @@ if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) || else { // decoding not needed for wchars and dchars - import std.uni : lineSep, paraSep, nelSep; + import std.uni : lineSep, nelSep, paraSep; size_t column; @@ -6545,7 +6545,7 @@ if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) || /// @safe pure unittest { - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(column("1234 ") == 5); assert(column("1234 "w) == 5); diff --git a/std/uni.d b/std/uni.d index d71e482063b..93931d6504e 100644 --- a/std/uni.d +++ b/std/uni.d @@ -875,7 +875,7 @@ size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @ @safe pure nothrow @nogc unittest // for replicate { - import std.algorithm.iteration : sum, map; + import std.algorithm.iteration : map, sum; import std.range : iota; size_t m = 0b111; size_t m2 = 0b01; @@ -3401,7 +3401,7 @@ private: import std.algorithm.comparison : equal; import std.algorithm.mutation : copy; import std.conv : text; - import std.range : iota, chain; + import std.range : chain, iota; import std.range.primitives : isBidirectionalRange, isOutputRange; void funcRef(T)(ref T u24) { @@ -5762,7 +5762,7 @@ template idxTypes(Key, size_t fullBits, Prefix...) if (is(Char1 : dchar) && is(Char2 : dchar)) { import std.algorithm.comparison : cmp; - import std.algorithm.iteration : map, filter; + import std.algorithm.iteration : filter, map; import std.ascii : toLower; static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';} return cmp( @@ -6542,8 +6542,8 @@ if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) @safe unittest { import std.algorithm.comparison : equal; + import std.range : drop, take; import std.range.primitives : walkLength; - import std.range : take, drop; auto text = "noe\u0308l"; // noël using e + combining diaeresis assert(text.walkLength == 5); // 5 code points @@ -7492,7 +7492,7 @@ enum { public dchar compose(dchar first, dchar second) pure nothrow @safe { import std.algorithm.iteration : map; - import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask; + import std.internal.unicode_comp : composeCntShift, composeIdxMask, compositionTable; import std.range : assumeSorted; immutable packed = compositionJumpTrie[first]; if (packed == ushort.max) @@ -7539,7 +7539,7 @@ public dchar compose(dchar first, dchar second) pure nothrow @safe public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe { import std.algorithm.searching : until; - import std.internal.unicode_decomp : decompCompatTable, decompCanonTable; + import std.internal.unicode_decomp : decompCanonTable, decompCompatTable; static if (decompType == Canonical) { alias table = decompCanonTable; @@ -8115,7 +8115,7 @@ public bool isWhite(dchar c) @safe pure nothrow @nogc bool isLower(dchar c) { - import std.ascii : isLower, isASCII; + import std.ascii : isASCII, isLower; if (isASCII(c)) return isLower(c); return lowerCaseTrie[c]; @@ -8148,7 +8148,7 @@ bool isLower(dchar c) @safe pure nothrow @nogc bool isUpper(dchar c) { - import std.ascii : isUpper, isASCII; + import std.ascii : isASCII, isUpper; if (isASCII(c)) return isUpper(c); return upperCaseTrie[c]; @@ -8744,7 +8744,7 @@ private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure no private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure if (is(C == char) || is(C == wchar) || is(C == dchar)) { - import std.utf : decode, codeLength; + import std.utf : codeLength, decode; size_t curIdx = 0; size_t destIdx = 0; alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn); @@ -8812,7 +8812,7 @@ private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn) { size_t toCaseLength(C)(in C[] str) { - import std.utf : decode, codeLength; + import std.utf : codeLength, decode; size_t codeLen = 0; size_t lastNonTrivial = 0; size_t curIdx = 0; diff --git a/std/utf.d b/std/utf.d index 339243cfcef..f98e8182e13 100644 --- a/std/utf.d +++ b/std/utf.d @@ -75,7 +75,7 @@ debug (utf) import core.stdc.stdio : printf; +/ class UTFException : Exception { - import core.internal.string : unsignedToTempString, UnsignedStringBuf; + import core.internal.string : UnsignedStringBuf, unsignedToTempString; uint[4] sequence; size_t len; diff --git a/std/uuid.d b/std/uuid.d index a2db742c4bb..d4147c56f6a 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -333,7 +333,7 @@ public struct UUID */ this(T)(in T[] uuid) if (isSomeChar!(Unqual!T)) { - import std.conv : to, parse; + import std.conv : parse, to; if (uuid.length < 36) { throw new UUIDParsingException(to!string(uuid), 0, @@ -1233,7 +1233,7 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) /// @safe unittest { - import std.random : Xorshift192, unpredictableSeed; + import std.random : unpredictableSeed, Xorshift192; //simple call auto uuid = randomUUID(); @@ -1253,13 +1253,13 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) */ @safe unittest { - import std.random : rndGen, Mt19937; + import std.random : Mt19937, rndGen; static assert(is(typeof(rndGen) == Mt19937)); } @safe unittest { - import std.random : Xorshift192, unpredictableSeed; + import std.random : unpredictableSeed, Xorshift192; //simple call auto uuid = randomUUID(); diff --git a/std/variant.d b/std/variant.d index 8ca53732568..c94b4ff211a 100644 --- a/std/variant.d +++ b/std/variant.d @@ -796,7 +796,7 @@ public: @property T coerce(T)() { - import std.conv : to, text; + import std.conv : text, to; static if (isNumeric!T || isBoolean!T) { if (convertsTo!real) @@ -2690,7 +2690,7 @@ if (isAlgebraic!VariantType && Handler.length > 0) @system unittest { - import std.exception : assertThrown, assertNotThrown; + import std.exception : assertNotThrown, assertThrown; // Make sure Variant can handle types with opDispatch but no length field. struct SWithNoLength { diff --git a/std/windows/registry.d b/std/windows/registry.d index 0b5e3d2d1c3..dc68e013bb8 100644 --- a/std/windows/registry.d +++ b/std/windows/registry.d @@ -45,7 +45,7 @@ import std.exception; import std.internal.cstring; private import std.internal.windows.advapi32; import std.system : Endian, endian; -import std.utf : toUTF8, toUTF16; +import std.utf : toUTF16, toUTF8; import std.windows.syserror; //debug = winreg; @@ -82,7 +82,7 @@ class Win32Exception : WindowsException @property int error() { return super.code; } } -version(unittest) import std.string : startsWith, endsWith; +version(unittest) import std.string : endsWith, startsWith; @safe unittest { diff --git a/std/windows/syserror.d b/std/windows/syserror.d index 863e0c1700c..ff844dc41df 100644 --- a/std/windows/syserror.d +++ b/std/windows/syserror.d @@ -180,7 +180,7 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file version(Windows) @system unittest { - import std.algorithm.searching : startsWith, endsWith; + import std.algorithm.searching : endsWith, startsWith; import std.exception; import std.string; diff --git a/std/zip.d b/std/zip.d index 45e8a84c1d9..837e75b615b 100644 --- a/std/zip.d +++ b/std/zip.d @@ -97,7 +97,7 @@ enum CompressionMethod : ushort */ final class ArchiveMember { - import std.conv : to, octal; + import std.conv : octal, to; import std.datetime : DosFileTime, SysTime, SysTimeToDosFileTime; /** @@ -852,7 +852,7 @@ debug(print) assert(zip3.directory["foo"].compressedSize == am1.compressedSize); // Test if packing and unpacking produces the original data - import std.random : uniform, MinstdRand0; + import std.random : MinstdRand0, uniform; import std.stdio, std.conv; MinstdRand0 gen; const uint itemCount = 20, minSize = 10, maxSize = 500; From 2070d867ddef6bd939ca72b3d64153dca9ae9100 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Mon, 12 Jun 2017 08:18:25 +0200 Subject: [PATCH 248/262] Sort multiple packages within one line --- std/algorithm/iteration.d | 2 +- std/algorithm/sorting.d | 4 ++-- std/digest/digest.d | 12 ++++++------ std/digest/hmac.d | 14 +++++++------- std/experimental/logger/core.d | 2 +- std/experimental/typecons.d | 2 +- std/file.d | 2 +- std/functional.d | 4 ++-- std/process.d | 4 ++-- std/regex/internal/backtracking.d | 2 +- std/regex/internal/ir.d | 2 +- std/regex/package.d | 6 +++--- std/socket.d | 2 +- std/zip.d | 2 +- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index f29b4cdddae..2c4402ade51 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -165,7 +165,7 @@ if (isBidirectionalRange!Range) @safe unittest { import std.algorithm.comparison : equal; - import std.stdio, std.range; + import std.range, std.stdio; import std.typecons : tuple; ulong counter = 0; diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index dbaec604a1a..7cf9e5c41b4 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -1911,7 +1911,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || @safe unittest { // Simple regression benchmark - import std.random, std.algorithm.iteration, std.algorithm.mutation; + import std.algorithm.iteration, std.algorithm.mutation, std.random; Random rng; int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array; static uint comps; @@ -2833,7 +2833,7 @@ private template TimSortImpl(alias pred, R) @safe unittest { // Issue 14223 - import std.range, std.array; + import std.array, std.range; auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array; sort!("a < b", SwapStrategy.stable)(arr); } diff --git a/std/digest/digest.d b/std/digest/digest.d index 99a83de42c0..00acb9710d7 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -89,7 +89,7 @@ import std.traits; @system unittest { //Generating the hashes of a file, idiomatic D way - import std.digest.crc, std.digest.sha, std.digest.md; + import std.digest.crc, std.digest.md, std.digest.sha; import std.stdio; // Digests a file and prints the result. @@ -115,7 +115,7 @@ import std.traits; @system unittest { //Generating the hashes of a file using the template API - import std.digest.crc, std.digest.sha, std.digest.md; + import std.digest.crc, std.digest.md, std.digest.sha; import std.stdio; // Digests a file and prints the result. void digestFile(Hash)(ref Hash hash, string filename) @@ -154,7 +154,7 @@ import std.traits; /// @system unittest { - import std.digest.crc, std.digest.sha, std.digest.md; + import std.digest.crc, std.digest.md, std.digest.sha; import std.stdio; // Digests a file and prints the result. @@ -405,7 +405,7 @@ if (isDigest!T) /// @system unittest { - import std.digest.md, std.digest.hmac; + import std.digest.hmac, std.digest.md; static assert(hasBlockSize!MD5 && MD5.blockSize == 512); static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512); } @@ -468,7 +468,7 @@ if (allSatisfy!(isArray, typeof(data))) /// @system unittest { - import std.digest.md, std.digest.sha, std.digest.crc; + import std.digest.crc, std.digest.md, std.digest.sha; auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog"); auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog"); auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog"); @@ -641,7 +641,7 @@ interface Digest /// @system unittest { - import std.digest.md, std.digest.sha, std.digest.crc; + import std.digest.crc, std.digest.md, std.digest.sha; ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog"); ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog"); ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog"); diff --git a/std/digest/hmac.d b/std/digest/hmac.d index 77eb38942bb..7a9d0f3b12b 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -33,7 +33,7 @@ version(StdDdoc) /// Computes an HMAC over data read from stdin. @safe unittest { - import std.stdio, std.digest.hmac, std.digest.sha; + import std.digest.hmac, std.digest.sha, std.stdio; import std.string : representation; auto secret = "secret".representation; @@ -89,7 +89,7 @@ if (hashBlockSize % 8 == 0) /// @safe pure nothrow @nogc unittest { - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; auto hmac = HMAC!SHA1("My s3cR3T keY".representation); hmac.put("Hello, world".representation); @@ -126,7 +126,7 @@ if (hashBlockSize % 8 == 0) /// @safe pure nothrow @nogc unittest { - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; string data1 = "Hello, world", data2 = "Hola mundo"; auto hmac = HMAC!SHA1("My s3cR3T keY".representation); @@ -157,7 +157,7 @@ if (hashBlockSize % 8 == 0) /// @safe pure nothrow @nogc unittest { - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; string data1 = "Hello, world", data2 = "Hola mundo"; auto hmac = HMAC!SHA1("My s3cR3T keY".representation); @@ -193,7 +193,7 @@ if (hashBlockSize % 8 == 0) /// @safe pure nothrow @nogc unittest { - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; string data1 = "Hello, world", data2 = "Hola mundo"; auto hmac = HMAC!SHA1("My s3cR3T keY".representation); @@ -234,7 +234,7 @@ if (isDigest!H) /// @safe pure nothrow @nogc unittest { - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; string data1 = "Hello, world", data2 = "Hola mundo"; auto digest = hmac!SHA1("My s3cR3T keY".representation) @@ -269,7 +269,7 @@ if (isDigest!H) @system pure nothrow @nogc unittest { import std.algorithm.iteration : map; - import std.digest.sha, std.digest.hmac; + import std.digest.hmac, std.digest.sha; import std.string : representation; string data = "Hello, world"; auto digest = data.representation diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index 10bba3ea957..d150cf5934d 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -2996,7 +2996,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted // to shared logger @system unittest { - import std.concurrency, core.atomic, core.thread; + import core.atomic, core.thread, std.concurrency; static shared logged_count = 0; diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 899638f2c07..5a41567d636 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -661,7 +661,7 @@ template unwrap(Target) @system unittest { // Bugzilla 10377 - import std.range, std.algorithm; + import std.algorithm, std.range; interface MyInputRange(T) { diff --git a/std/file.d b/std/file.d index efc75a54043..d74de74a8e7 100644 --- a/std/file.d +++ b/std/file.d @@ -78,7 +78,7 @@ Source: $(PHOBOSSRC std/_file.d) */ module std.file; -import core.stdc.stdlib, core.stdc.string, core.stdc.errno; +import core.stdc.errno, core.stdc.stdlib, core.stdc.string; import std.datetime; import std.internal.cstring; diff --git a/std/functional.d b/std/functional.d index 588ec520675..44c9bb868d2 100644 --- a/std/functional.d +++ b/std/functional.d @@ -106,7 +106,7 @@ template unaryFun(alias fun, string parmName = "a") static if (!fun._ctfeMatchUnary(parmName)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; - import std.traits, std.typecons, std.meta; + import std.meta, std.traits, std.typecons; } auto unaryFun(ElementType)(auto ref ElementType __a) { @@ -191,7 +191,7 @@ template binaryFun(alias fun, string parm1Name = "a", static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; - import std.traits, std.typecons, std.meta; + import std.meta, std.traits, std.typecons; } auto binaryFun(ElementType1, ElementType2) (auto ref ElementType1 __a, auto ref ElementType2 __b) diff --git a/std/process.d b/std/process.d index 42c315ea87e..a2b39ab2ac4 100644 --- a/std/process.d +++ b/std/process.d @@ -3476,7 +3476,7 @@ import core.thread; version (Windows) { - import std.format, std.random, std.file; + import std.file, std.format, std.random; } version (Posix) { @@ -3484,7 +3484,7 @@ version (Posix) } version (unittest) { - import std.file, std.conv, std.random; + import std.conv, std.file, std.random; } diff --git a/std/regex/internal/backtracking.d b/std/regex/internal/backtracking.d index 06db40d8681..71ea9723823 100644 --- a/std/regex/internal/backtracking.d +++ b/std/regex/internal/backtracking.d @@ -6,7 +6,7 @@ module std.regex.internal.backtracking; package(std.regex): -import std.range.primitives, std.typecons, std.traits, core.stdc.stdlib; +import core.stdc.stdlib, std.range.primitives, std.traits, std.typecons; import std.regex.internal.ir; /+ diff --git a/std/regex/internal/ir.d b/std/regex/internal/ir.d index 49e510e0a50..28b199895d9 100644 --- a/std/regex/internal/ir.d +++ b/std/regex/internal/ir.d @@ -9,7 +9,7 @@ module std.regex.internal.ir; package(std.regex): -import std.exception, std.uni, std.meta, std.traits, std.range.primitives; +import std.exception, std.meta, std.range.primitives, std.traits, std.uni; debug(std_regex_parser) import std.stdio; // just a common trait, may be moved elsewhere diff --git a/std/regex/package.d b/std/regex/package.d index cac967e123c..f1d7f8763c5 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -296,9 +296,9 @@ Macros: +/ module std.regex; +import std.range.primitives, std.traits; import std.regex.internal.ir; import std.regex.internal.thompson; //TODO: get rid of this dependency -import std.traits, std.range.primitives; import std.typecons; // : Flag, No, Yes; /++ @@ -418,7 +418,7 @@ if (isSomeString!(S)) template ctRegexImpl(alias pattern, string flags=[]) { - import std.regex.internal.parser, std.regex.internal.backtracking; + import std.regex.internal.backtracking, std.regex.internal.parser; enum r = regex(pattern, flags); alias Char = BasicElementOf!(typeof(pattern)); enum source = ctGenRegExCode(r); @@ -1422,7 +1422,7 @@ if (isOutputRange!(Sink, dchar) && isSomeString!R && isRegexFor!(RegEx, R)) @system unittest { // insert comma as thousands delimiter in fifty randomly produced big numbers - import std.array, std.random, std.conv, std.range; + import std.array, std.conv, std.random, std.range; static re = regex(`(?<=\d)(?=(\d\d\d)+\b)`, "g"); auto sink = appender!(char [])(); enum ulong min = 10UL ^^ 10, max = 10UL ^^ 19; diff --git a/std/socket.d b/std/socket.d index 475dc411d99..effad6c6639 100644 --- a/std/socket.d +++ b/std/socket.d @@ -44,7 +44,7 @@ module std.socket; -import core.stdc.stdint, core.stdc.string, std.string, core.stdc.stdlib, std.conv; +import core.stdc.stdint, core.stdc.stdlib, core.stdc.string, std.conv, std.string; import core.stdc.config; import core.time : dur, Duration; diff --git a/std/zip.d b/std/zip.d index 837e75b615b..39dd57a8b07 100644 --- a/std/zip.d +++ b/std/zip.d @@ -852,8 +852,8 @@ debug(print) assert(zip3.directory["foo"].compressedSize == am1.compressedSize); // Test if packing and unpacking produces the original data + import std.conv, std.stdio; import std.random : MinstdRand0, uniform; - import std.stdio, std.conv; MinstdRand0 gen; const uint itemCount = 20, minSize = 10, maxSize = 500; foreach (variant; 0 .. 2) From 60e8209d98ce110fc114c7a119ce347f2f7186b6 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Mon, 12 Jun 2017 21:35:57 +0300 Subject: [PATCH 249/262] doc unittest, syntax refining --- std/container/dlist.d | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/std/container/dlist.d b/std/container/dlist.d index 155bba02108..a100e69a5c5 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -49,6 +49,17 @@ module std.container.dlist; popBackN(r, 2); assert(r.equal([3])); assert(walkLength(r) == 1); + + // DList.Range can be used to remove elements from the list + auto nl = DList!int([1, 2, 3, 4, 5]); + for (auto rn = nl[]; !rn.empty; rn.popFront()) + if (rn.front % 2 == 0) + nl.popFirstOf(rn); + assert(equal(nl[], [1, 3, 5])); + auto rs = nl[]; + rs.popFront(); + nl.remove(rs); + assert(equal(nl[], [1])); } import std.range.primitives; @@ -647,10 +658,7 @@ Compexity: $(BIGOH 1) assert(r._first, "Remove: Range is empty"); BaseNode.connect(r._first._prev, r._first._next); auto after = r._first._next; - if (after is _root) - return Range(null, null); - else - return Range(after, _last); + return after is _root ? Range(null, null) : Range(after, _last); } /** @@ -671,10 +679,7 @@ Compexity: $(BIGOH 1) assert(r._last, "Remove: Range is empty"); BaseNode.connect(r._last._prev, r._last._next); auto before = r._last._prev; - if (before is _root) - return Range(null, null); - else - return Range(_first, before); + return before is _root ? Range(null, null) : Range(_first, before); } /** @@ -867,11 +872,13 @@ private: auto dl = DList!int([1, 2, 3, 4, 5]); auto r = dl[]; r.popFront(); - dl.popFirstOf(r); + auto after = dl.popFirstOf(r); assert(equal(dl[], [1, 3, 4, 5])); + assert(equal(after, [3, 4, 5])); r.popBack(); - dl.popLastOf(r); + auto before = dl.popLastOf(r); assert(equal(dl[], [1, 3, 5])); + assert(equal(before, [1, 3])); DList!int empty_list; dl.popFirstOf(empty_list[]); assert(equal(dl[], [1, 3, 5])); From 641fc718b692fa1d5662e8fca2136ae1da372118 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Mon, 12 Jun 2017 21:53:29 +0300 Subject: [PATCH 250/262] work around range consistency checks --- std/container/dlist.d | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/std/container/dlist.d b/std/container/dlist.d index a100e69a5c5..68529df502a 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -52,9 +52,11 @@ module std.container.dlist; // DList.Range can be used to remove elements from the list auto nl = DList!int([1, 2, 3, 4, 5]); - for (auto rn = nl[]; !rn.empty; rn.popFront()) + for (auto rn = nl[]; !rn.empty;) if (rn.front % 2 == 0) - nl.popFirstOf(rn); + rn = nl.popFirstOf(rn); + else + rn.popFront(); assert(equal(nl[], [1, 3, 5])); auto rs = nl[]; rs.popFront(); @@ -637,15 +639,14 @@ Complexity: $(BIGOH 1) /// ditto Range linearRemove(Range r) { - return remove(r); + return remove(r); } /** Removes first element of $(D r), wich must be a range obrained originally from this container. -Returns: A range spanning the remaining elements in the container that -initially were right after the first element of $(D r). +Returns: A range spanning the remaining elements of $(D r). Compexity: $(BIGOH 1) */ @@ -658,15 +659,14 @@ Compexity: $(BIGOH 1) assert(r._first, "Remove: Range is empty"); BaseNode.connect(r._first._prev, r._first._next); auto after = r._first._next; - return after is _root ? Range(null, null) : Range(after, _last); + return after is _root ? Range(null, null) : Range(after, r._last); } /** Removes last element of $(D r), wich must be a range obrained originally from this container. -Returns: A range spanning the remaining elements in the container that -initially were right before the last element of $(D r). +Returns: A range spanning the remaining elements of $(D r). Compexity: $(BIGOH 1) */ @@ -679,7 +679,7 @@ Compexity: $(BIGOH 1) assert(r._last, "Remove: Range is empty"); BaseNode.connect(r._last._prev, r._last._next); auto before = r._last._prev; - return before is _root ? Range(null, null) : Range(_first, before); + return before is _root ? Range(null, null) : Range(r._first, before); } /** @@ -878,7 +878,7 @@ private: r.popBack(); auto before = dl.popLastOf(r); assert(equal(dl[], [1, 3, 5])); - assert(equal(before, [1, 3])); + assert(equal(before, [3])); DList!int empty_list; dl.popFirstOf(empty_list[]); assert(equal(dl[], [1, 3, 5])); From a5fbc4db78074565cdc469d9d4c67eb80ed2c6c1 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Mon, 12 Jun 2017 22:13:38 +0300 Subject: [PATCH 251/262] shring range too to keep it in consistent state --- std/container/dlist.d | 51 ++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/std/container/dlist.d b/std/container/dlist.d index 68529df502a..cb73d15bbfc 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -50,11 +50,11 @@ module std.container.dlist; assert(r.equal([3])); assert(walkLength(r) == 1); - // DList.Range can be used to remove elements from the list + // DList.Range can be used to remove elements from the list it spans auto nl = DList!int([1, 2, 3, 4, 5]); for (auto rn = nl[]; !rn.empty;) if (rn.front % 2 == 0) - rn = nl.popFirstOf(rn); + nl.popFirstOf(rn); else rn.popFront(); assert(equal(nl[], [1, 3, 5])); @@ -644,42 +644,34 @@ Complexity: $(BIGOH 1) /** Removes first element of $(D r), wich must be a range obrained originally -from this container. - -Returns: A range spanning the remaining elements of $(D r). +from this container, from both DList instance and range. Compexity: $(BIGOH 1) */ - Range popFirstOf(Range r) + void popFirstOf(ref Range r) { - if (r.empty) - return r; - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r._first, "Remove: Range is empty"); - BaseNode.connect(r._first._prev, r._first._next); - auto after = r._first._next; - return after is _root ? Range(null, null) : Range(after, r._last); + assert(r._first, "popFirstOf: Range is empty"); + auto prev = r._first._prev; + auto next = r._first._next; + r.popFront(); + BaseNode.connect(prev, next); } /** Removes last element of $(D r), wich must be a range obrained originally -from this container. - -Returns: A range spanning the remaining elements of $(D r). +from this container, from both DList instance and range. Compexity: $(BIGOH 1) */ - Range popLastOf(Range r) + void popLastOf(ref Range r) { - if (r.empty) - return r; - assert(_root !is null, "Cannot remove from an un-initialized List"); - assert(r._last, "Remove: Range is empty"); - BaseNode.connect(r._last._prev, r._last._next); - auto before = r._last._prev; - return before is _root ? Range(null, null) : Range(r._first, before); + assert(r._first, "popLastOf: Range is empty"); + auto prev = r._last._prev; + auto next = r._last._next; + r.popBack(); + BaseNode.connect(prev, next); } /** @@ -872,18 +864,13 @@ private: auto dl = DList!int([1, 2, 3, 4, 5]); auto r = dl[]; r.popFront(); - auto after = dl.popFirstOf(r); + dl.popFirstOf(r); assert(equal(dl[], [1, 3, 4, 5])); - assert(equal(after, [3, 4, 5])); + assert(equal(r, [3, 4, 5])); r.popBack(); auto before = dl.popLastOf(r); assert(equal(dl[], [1, 3, 5])); - assert(equal(before, [3])); - DList!int empty_list; - dl.popFirstOf(empty_list[]); - assert(equal(dl[], [1, 3, 5])); - dl.popLastOf(empty_list[]); - assert(equal(dl[], [1, 3, 5])); + assert(equal(r, [3])); dl = DList!int([0]); r = dl[]; dl.popFirstOf(r); From 8b09ce3c383b091be93ab900a5f94034a44d5151 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Mon, 12 Jun 2017 22:15:03 +0300 Subject: [PATCH 252/262] no return value --- std/container/dlist.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/container/dlist.d b/std/container/dlist.d index cb73d15bbfc..8aa22dd216c 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -868,7 +868,7 @@ private: assert(equal(dl[], [1, 3, 4, 5])); assert(equal(r, [3, 4, 5])); r.popBack(); - auto before = dl.popLastOf(r); + dl.popLastOf(r); assert(equal(dl[], [1, 3, 5])); assert(equal(r, [3])); dl = DList!int([0]); From 16cb9873ce4c8ba7f66ac6f1fb92af9fd46ef843 Mon Sep 17 00:00:00 2001 From: Boris-Barboris Date: Mon, 12 Jun 2017 22:23:16 +0300 Subject: [PATCH 253/262] errata --- std/container/dlist.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/container/dlist.d b/std/container/dlist.d index 8aa22dd216c..2af97ffd9b0 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -643,8 +643,8 @@ Complexity: $(BIGOH 1) } /** -Removes first element of $(D r), wich must be a range obrained originally -from this container, from both DList instance and range. +Removes first element of $(D r), wich must be a range obtained originally +from this container, from both DList instance and range $(D r). Compexity: $(BIGOH 1) */ @@ -659,8 +659,8 @@ Compexity: $(BIGOH 1) } /** -Removes last element of $(D r), wich must be a range obrained originally -from this container, from both DList instance and range. +Removes last element of $(D r), wich must be a range obtained originally +from this container, from both DList instance and range $(D r). Compexity: $(BIGOH 1) */ From 72f395084373b8c15518def33216485301e8de8a Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Fri, 30 Sep 2016 22:26:40 -0400 Subject: [PATCH 254/262] Fix Issue 16566 - hasLength should enforce that length has type size_t --- changelog/std-range-has-length.dd | 8 ++++ std/range/primitives.d | 66 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 changelog/std-range-has-length.dd diff --git a/changelog/std-range-has-length.dd b/changelog/std-range-has-length.dd new file mode 100644 index 00000000000..eaa97a6734d --- /dev/null +++ b/changelog/std-range-has-length.dd @@ -0,0 +1,8 @@ +`hasLength` now enforces that `length` has type `size_t` + +Historically `hasLength!R` yielded `true` for types whereby +`R.length` returns other types convertible to `ulong`, such as `int`, `ushort`, +`const(size_t)`, user-defined types using `alias this`, or notably `ulong` on +32-bit systems. This behavior has been deprecated. After December 2017, +$(REF hasLength, std, range, primitives) will yield `true` only if `R.length` +yields the exact type `size_t`. diff --git a/std/range/primitives.d b/std/range/primitives.d index aeedf605601..25a35dafdf8 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1390,28 +1390,56 @@ template hasLvalueElements(R) } /** -Returns $(D true) if $(D R) has a $(D length) member that returns an -integral type. $(D R) does not have to be a range. Note that $(D -length) is an optional primitive as no range must implement it. Some -ranges do not store their length explicitly, some cannot compute it -without actually exhausting the range (e.g. socket streams), and some -other ranges may be infinite. - -Although narrow string types ($(D char[]), $(D wchar[]), and their -qualified derivatives) do define a $(D length) property, $(D -hasLength) yields $(D false) for them. This is because a narrow -string's length does not reflect the number of characters, but instead -the number of encoding units, and as such is not useful with -range-oriented algorithms. - */ +Yields `true` if `R` has a `length` member that returns a value of `size_t` +type. `R` does not have to be a range. If `R` is a range, algorithms in the +standard library are only guaranteed to support `length` with type `size_t`. + +Note that `length` is an optional primitive as no range must implement it. Some +ranges do not store their length explicitly, some cannot compute it without +actually exhausting the range (e.g. socket streams), and some other ranges may +be infinite. + +Although narrow string types (`char[]`, `wchar[]`, and their qualified +derivatives) do define a `length` property, `hasLength` yields `false` for them. +This is because a narrow string's length does not reflect the number of +characters, but instead the number of encoding units, and as such is not useful +with range-oriented algorithms. To use strings as random-access ranges with +length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf). + +Deprecation: Historically `hasLength!R` yielded `true` for types whereby +`R.length` returns other types convertible to `ulong`, such as `int`, `ushort`, +`const(size_t)`, user-defined types using `alias this`, or notably `ulong` on +32-bit systems. This behavior has been deprecated. After December 2017, +`hasLength` will yield `true` only if `R.length` yields the exact type `size_t`. +*/ template hasLength(R) { - enum bool hasLength = !isNarrowString!R && is(typeof( - (inout int = 0) + static if (is(typeof(((R* r) => r.length)(null)) Length)) { - R r = R.init; - ulong l = r.length; - })); + static if (is(Length == size_t)) + { + enum bool hasLength = !isNarrowString!R; + } + else static if (is(Length : ulong)) + { + // @@@DEPRECATED_2017-12@@@ + // Uncomment the deprecated(...) message and take the pragma(msg) + // out once https://issues.dlang.org/show_bug.cgi?id=10181 is fixed. + pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~ + "): Note: length must have type size_t on all systems" ~ + ", please update your code by December 2017."); + //deprecated("length must have type size_t on all systems") + enum bool hasLength = true; + } + else + { + enum bool hasLength = false; + } + } + else + { + enum bool hasLength = false; + } } /// From d0b9555a06050c1f6778e7062f17dcb089700775 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 13 Jun 2017 17:51:52 +0000 Subject: [PATCH 255/262] Revert "Sort selective imports" This reverts commit 998ad51fd7e591f07b1202975ae8248b4eaf078a. --- std/algorithm/comparison.d | 6 +-- std/algorithm/internal.d | 4 +- std/algorithm/iteration.d | 4 +- std/algorithm/mutation.d | 10 ++--- std/algorithm/setops.d | 12 +++--- std/algorithm/sorting.d | 16 ++++---- std/array.d | 4 +- std/bigint.d | 6 +-- std/bitmanip.d | 4 +- std/c/freebsd/socket.d | 2 +- std/c/process.d | 2 +- std/complex.d | 10 ++--- std/concurrency.d | 4 +- std/container/array.d | 2 +- std/container/dlist.d | 2 +- std/container/rbtree.d | 4 +- std/container/slist.d | 2 +- std/container/util.d | 2 +- std/conv.d | 10 ++--- std/datetime/date.d | 4 +- std/datetime/interval.d | 2 +- std/datetime/package.d | 2 +- std/datetime/systime.d | 26 ++++++------- std/datetime/timezone.d | 12 +++--- std/digest/digest.d | 4 +- std/digest/hmac.d | 4 +- std/exception.d | 4 +- .../building_blocks/affix_allocator.d | 2 +- .../allocator/building_blocks/free_list.d | 2 +- .../allocator/building_blocks/free_tree.d | 2 +- .../building_blocks/kernighan_ritchie.d | 2 +- .../allocator/building_blocks/region.d | 2 +- std/experimental/allocator/common.d | 4 +- std/experimental/allocator/mallocator.d | 2 +- std/experimental/allocator/package.d | 2 +- std/experimental/allocator/typed.d | 4 +- std/experimental/checkedint.d | 4 +- std/experimental/logger/core.d | 4 +- std/experimental/typecons.d | 2 +- std/file.d | 16 ++++---- std/format.d | 14 +++---- std/functional.d | 2 +- std/getopt.d | 2 +- std/internal/test/dummyrange.d | 2 +- std/json.d | 8 ++-- std/math.d | 2 +- std/meta.d | 2 +- std/net/curl.d | 10 ++--- std/net/isemail.d | 4 +- std/numeric.d | 6 +-- std/parallelism.d | 14 +++---- std/path.d | 20 +++++----- std/process.d | 12 +++--- std/random.d | 2 +- std/range/package.d | 12 +++--- std/range/primitives.d | 2 +- std/regex/internal/backtracking.d | 2 +- std/regex/internal/generator.d | 6 +-- std/regex/internal/tests.d | 2 +- std/regex/package.d | 12 +++--- std/signals.d | 2 +- std/stdio.d | 12 +++--- std/string.d | 38 +++++++++---------- std/uni.d | 20 +++++----- std/utf.d | 2 +- std/uuid.d | 8 ++-- std/variant.d | 4 +- std/windows/registry.d | 4 +- std/windows/syserror.d | 2 +- std/zip.d | 4 +- 70 files changed, 222 insertions(+), 222 deletions(-) diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index 13901b554c1..566b686b16a 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -64,7 +64,7 @@ import std.range.primitives; import std.traits; // FIXME import std.meta : allSatisfy; -import std.typecons; // : Flag, Tuple, tuple, Yes; +import std.typecons; // : tuple, Tuple, Flag, Yes; /** Find $(D value) _among $(D values), returning the 1-based index @@ -852,7 +852,7 @@ range of range (of range...) comparisons. @safe unittest { import std.algorithm.comparison : equal; - import std.range : chunks, iota; + import std.range : iota, chunks; assert(equal!(equal!equal)( [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], iota(0, 8).chunks(2).chunks(2) @@ -925,7 +925,7 @@ range of range (of range...) comparisons. @safe pure unittest { - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(equal("æøå".byChar, "æøå")); assert(equal("æøå", "æøå".byChar)); diff --git a/std/algorithm/internal.d b/std/algorithm/internal.d index 5e0cfd68bcb..35ba503e3cc 100644 --- a/std/algorithm/internal.d +++ b/std/algorithm/internal.d @@ -21,7 +21,7 @@ version(unittest) package string[] rndstuff(T : string)() { - import std.random : Random, uniform, unpredictableSeed; + import std.random : Random, unpredictableSeed, uniform; static Random rnd; static bool first = true; @@ -46,7 +46,7 @@ version(unittest) package int[] rndstuff(T : int)() { - import std.random : Random, uniform, unpredictableSeed; + import std.random : Random, unpredictableSeed, uniform; static Random rnd; static bool first = true; diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 2c4402ade51..74eba6c3447 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -2748,7 +2748,7 @@ if (fun.length >= 1) alias binfuns = staticMap!(binaryFun, fun); static if (fun.length > 1) - import std.typecons : isTuple, tuple; + import std.typecons : tuple, isTuple; /++ No-seed version. The first element of $(D r) is used as the seed's value. @@ -3219,7 +3219,7 @@ if (fun.length >= 1) // Sum all elements with explicit seed assert(arr.fold!((a, b) => a + b)(6) == 21); - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; import std.typecons : tuple; // Compute minimum and maximum at the same time diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index 23923242122..49f3c850c7f 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -78,7 +78,7 @@ T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) module std.algorithm.mutation; import std.range.primitives; -import std.traits : isArray, isBlitAssignable, isNarrowString, isSomeChar, Unqual; +import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar; // FIXME import std.typecons; // : tuple, Tuple; @@ -279,7 +279,7 @@ Unicode integrity is not preserved: { import std.algorithm.comparison : equal; import std.conv : text; - import std.random : Random, uniform, unpredictableSeed; + import std.random : Random, unpredictableSeed, uniform; // a more elaborate test { @@ -845,7 +845,7 @@ See_Also: void initializeAll(Range)(Range range) if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) { - import core.stdc.string : memcpy, memset; + import core.stdc.string : memset, memcpy; import std.traits : hasElaborateAssign, isDynamicArray; alias T = ElementType!Range; @@ -901,7 +901,7 @@ if (is(Range == char[]) || is(Range == wchar[])) /// @system unittest { - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; struct S { @@ -2835,7 +2835,7 @@ if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = val /// nothrow @system unittest { - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5]; uninitializedFill(s, 42); diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index b9e384b3764..f948cb0635c 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -41,10 +41,10 @@ module std.algorithm.setops; import std.range.primitives; // FIXME -import std.functional; // : binaryFun, unaryFun; +import std.functional; // : unaryFun, binaryFun; import std.traits; // FIXME -import std.meta; // : AliasSeq, allSatisfy, anySatisfy, staticMap; +import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy; import std.algorithm.sorting; // : Merge; import std.typecons : No; @@ -82,13 +82,13 @@ auto cartesianProduct(R1, R2)(R1 range1, R2 range2) if (!allSatisfy!(isForwardRange, R1, R2) || anySatisfy!(isInfinite, R1, R2)) { - import std.algorithm.iteration : joiner, map; + import std.algorithm.iteration : map, joiner; static if (isInfinite!R1 && isInfinite!R2) { static if (isForwardRange!R1 && isForwardRange!R2) { - import std.range : chain, repeat, sequence, take, zip; + import std.range : zip, repeat, take, chain, sequence; // This algorithm traverses the cartesian product by alternately // covering the right and bottom edges of an increasing square area @@ -107,13 +107,13 @@ if (!allSatisfy!(isForwardRange, R1, R2) || } else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) { - import std.range : repeat, zip; + import std.range : zip, repeat; return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) (range1)); } else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) { - import std.range : repeat, zip; + import std.range : zip, repeat; return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) (range2)); } diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 7cf9e5c41b4..99ac551649e 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -116,7 +116,7 @@ void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2)) { import std.algorithm.mutation : bringToFront; - import std.range : assumeSorted, chain; + import std.range : chain, assumeSorted; // Probably this algorithm can be optimized by using in-place // merge auto lhsOriginal = lhs.release(); @@ -619,7 +619,7 @@ if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range) { assert(pivot < r.length || r.length == 0 && pivot == 0); if (r.length <= 1) return 0; - import std.algorithm.mutation : move, swapAt; + import std.algorithm.mutation : swapAt, move; alias lt = binaryFun!less; // Pivot at the front @@ -1939,7 +1939,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || { import std.algorithm.internal : rndstuff; import std.algorithm.mutation : swapRanges; - import std.random : Random, uniform, unpredictableSeed; + import std.random : Random, unpredictableSeed, uniform; import std.uni : toUpper; // sort using delegate @@ -2038,7 +2038,7 @@ if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || private void quickSortImpl(alias less, Range)(Range r, size_t depth) { - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; import std.algorithm.mutation : swap, swapAt; alias Elem = ElementType!(Range); @@ -2736,7 +2736,7 @@ private template TimSortImpl(alias pred, R) @safe unittest { - import std.random : Random, randomShuffle, uniform; + import std.random : Random, uniform, randomShuffle; // Element type with two fields static struct E @@ -2893,7 +2893,7 @@ schwartzSort(alias transform, alias less = "a < b", if (isRandomAccessRange!R && hasLength!R) { import std.conv : emplace; - import std.range : SortedRange, zip; + import std.range : zip, SortedRange; import std.string : representation; alias T = typeof(unaryFun!transform(r.front)); @@ -3437,7 +3437,7 @@ private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)( T minValue = 0, T maxValue = 255) { import std.algorithm.iteration : map; - import std.random : Random, uniform, unpredictableSeed; + import std.random : unpredictableSeed, Random, uniform; auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize); return iota(0, size).map!(_ => uniform(minValue, maxValue)).array; } @@ -3698,7 +3698,7 @@ if (isInputRange!(SRange) && isRandomAccessRange!(TRange) @system unittest { - import std.random : Random, randomShuffle, uniform, unpredictableSeed; + import std.random : Random, unpredictableSeed, uniform, randomShuffle; import std.typecons : Yes; auto r = Random(unpredictableSeed); diff --git a/std/array.d b/std/array.d index efbed4606fa..e3d94ad17ae 100644 --- a/std/array.d +++ b/std/array.d @@ -82,7 +82,7 @@ import std.meta; import std.traits; import std.range.primitives; -public import std.range.primitives : back, empty, front, popBack, popFront, save; +public import std.range.primitives : save, empty, popFront, popBack, front, back; /** * Allocates an array and initializes it with copies of the elements @@ -744,7 +744,7 @@ slice, returns that slice. Otherwise, returns the null slice. auto overlap(T, U)(T[] r1, U[] r2) @trusted pure nothrow if (is(typeof(r1.ptr < r2.ptr) == bool)) { - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; auto b = max(r1.ptr, r2.ptr); auto e = min(r1.ptr + r1.length, r2.ptr + r2.length); return b < e ? b[0 .. e - b] : null; diff --git a/std/bigint.d b/std/bigint.d index b24ad5668f5..861df64b1ce 100644 --- a/std/bigint.d +++ b/std/bigint.d @@ -27,7 +27,7 @@ module std.bigint; import std.conv : ConvException; -private import std.format : FormatException, FormatSpec; +private import std.format : FormatSpec, FormatException; private import std.internal.math.biguintcore; private import std.range.primitives; private import std.traits; @@ -674,7 +674,7 @@ public: /// @system unittest { - import std.conv : ConvOverflowException, to; + import std.conv : to, ConvOverflowException; import std.exception : assertThrown; assert(BigInt("0").to!int == 0); @@ -687,7 +687,7 @@ public: @system unittest { - import std.conv : ConvOverflowException, to; + import std.conv : to, ConvOverflowException; import std.exception : assertThrown; assert(BigInt("-1").to!byte == -1); diff --git a/std/bitmanip.d b/std/bitmanip.d index 4bd4f13d645..c38a2a8cec4 100644 --- a/std/bitmanip.d +++ b/std/bitmanip.d @@ -772,7 +772,7 @@ struct BitArray { private: - import core.bitop : bsf, bt, btr, bts; + import core.bitop : bts, btr, bsf, bt; import std.format : FormatSpec; size_t _len; @@ -2052,7 +2052,7 @@ public: */ @property auto bitsSet() const nothrow { - import std.algorithm.iteration : filter, joiner, map; + import std.algorithm.iteration : filter, map, joiner; import std.range : iota; return iota(dim). diff --git a/std/c/freebsd/socket.d b/std/c/freebsd/socket.d index dca4f520f63..35fa4c7810f 100644 --- a/std/c/freebsd/socket.d +++ b/std/c/freebsd/socket.d @@ -14,4 +14,4 @@ public import core.sys.posix.netdb; public import core.sys.posix.netinet.in_ : IPPROTO_IGMP, IPPROTO_GGP, IPPROTO_PUP, IPPROTO_IDP, IPPROTO_ND, IPPROTO_MAX, INADDR_LOOPBACK, INADDR_NONE; -public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, MSG_NOSIGNAL, SOCK_RDM; +public import core.sys.posix.sys.socket : AF_APPLETALK, AF_IPX, SOCK_RDM, MSG_NOSIGNAL; diff --git a/std/c/process.d b/std/c/process.d index 8fcc1929340..14fc729c6c7 100644 --- a/std/c/process.d +++ b/std/c/process.d @@ -13,7 +13,7 @@ deprecated("Import core.stdc.stdlib or the appropriate core.sys.posix.* modules module std.c.process; private import core.stdc.stddef; -public import core.stdc.stdlib : abort, exit, system; +public import core.stdc.stdlib : exit, abort, system; extern (C): diff --git a/std/complex.d b/std/complex.d index e9ad5eb98e7..77638229220 100644 --- a/std/complex.d +++ b/std/complex.d @@ -298,7 +298,7 @@ if (isFloatingPoint!T) Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const if (op == "^^" && isNumeric!R) { - import std.math : cos, exp, log, PI, sin; + import std.math : cos, exp, log, sin, PI; Unqual!(CommonType!(T, R)) ab = void, ar = void; if (lhs >= 0) @@ -370,7 +370,7 @@ if (isFloatingPoint!T) ref Complex opOpAssign(string op, C)(C z) if (op == "^^" && is(C R == Complex!R)) { - import std.math : cos, exp, log, sin; + import std.math : exp, log, cos, sin; immutable r = abs(this); immutable t = arg(this); immutable ab = r^^z.re * exp(-t*z.im); @@ -799,7 +799,7 @@ Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) @safe pure nothrow @nogc { - import std.math : cos, sin; + import std.math : sin, cos; return Complex!(CommonType!(T,U)) (modulus*cos(argument), modulus*sin(argument)); } @@ -822,7 +822,7 @@ Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) */ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc { - import std.math : coshisinh, expi; + import std.math : expi, coshisinh; auto cs = expi(z.re); auto csh = coshisinh(z.im); return typeof(return)(cs.im * csh.re, cs.re * csh.im); @@ -840,7 +840,7 @@ Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc /// ditto Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc { - import std.math : coshisinh, expi; + import std.math : expi, coshisinh; auto cs = expi(z.re); auto csh = coshisinh(z.im); return typeof(return)(cs.re * csh.re, - cs.im * csh.im); diff --git a/std/concurrency.d b/std/concurrency.d index b54614208d0..2fbcd308bae 100644 --- a/std/concurrency.d +++ b/std/concurrency.d @@ -2269,7 +2269,7 @@ private version (unittest) { import std.stdio; - import std.typecons : Tuple, tuple; + import std.typecons : tuple, Tuple; void testfn(Tid tid) { @@ -2410,7 +2410,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex) // check that var is global, can't take address of a TLS variable static assert(is(typeof({ __gshared p = &var; })), "var must be 'static shared' or '__gshared'."); - import core.atomic : atomicLoad, atomicStore, MemoryOrder; + import core.atomic : atomicLoad, MemoryOrder, atomicStore; static shared bool flag; if (!atomicLoad!(MemoryOrder.acq)(flag)) diff --git a/std/container/array.d b/std/container/array.d index 0cc572389d0..af01deff11e 100644 --- a/std/container/array.d +++ b/std/container/array.d @@ -251,7 +251,7 @@ private struct RangeT(A) struct Array(T) if (!is(Unqual!T == bool)) { - import core.stdc.stdlib : free, malloc, realloc; + import core.stdc.stdlib : malloc, realloc, free; import core.stdc.string : memcpy, memmove, memset; import core.memory : GC; diff --git a/std/container/dlist.d b/std/container/dlist.d index 5caf987a08a..633371fa67f 100644 --- a/std/container/dlist.d +++ b/std/container/dlist.d @@ -39,7 +39,7 @@ module std.container.dlist; // If you want to apply range operations, simply slice it. import std.algorithm.searching : countUntil; - import std.range : popBackN, popFrontN, walkLength; + import std.range : popFrontN, popBackN, walkLength; auto sl = DList!int([1, 2, 3, 4, 5]); assert(countUntil(sl[], 2) == 1); diff --git a/std/container/rbtree.d b/std/container/rbtree.d index 3be1871486e..5342b1692e4 100644 --- a/std/container/rbtree.d +++ b/std/container/rbtree.d @@ -742,7 +742,7 @@ if (is(typeof(binaryFun!less(T.init, T.init)))) import std.meta : allSatisfy; import std.range : Take; import std.range.primitives : isInputRange, walkLength; - import std.traits : isDynamicArray, isImplicitlyConvertible, isIntegral; + import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible; alias _less = binaryFun!less; @@ -1820,7 +1820,7 @@ assert(equal(rbt[], [5])); test!byte(); } -import std.range.primitives : ElementType, isInputRange, isSomeString; +import std.range.primitives : isInputRange, isSomeString, ElementType; import std.traits : isArray; /++ diff --git a/std/container/slist.d b/std/container/slist.d index b36584e3b55..820b0bbac45 100644 --- a/std/container/slist.d +++ b/std/container/slist.d @@ -57,7 +57,7 @@ struct SList(T) { import std.exception : enforce; import std.range : Take; - import std.range.primitives : ElementType, isForwardRange, isInputRange; + import std.range.primitives : isInputRange, isForwardRange, ElementType; import std.traits : isImplicitlyConvertible; private struct Node diff --git a/std/container/util.d b/std/container/util.d index 1f7aeb3464a..5be9e7d5470 100644 --- a/std/container/util.d +++ b/std/container/util.d @@ -105,7 +105,7 @@ if (is(T == struct) || is(T == class)) template make(alias Container, Args...) if (!is(Container)) { - import std.range : isInfinite, isInputRange; + import std.range : isInputRange, isInfinite; import std.traits : isDynamicArray; auto make(Range)(Range range) diff --git a/std/conv.d b/std/conv.d index 1db8faf1744..85a62cfdc08 100644 --- a/std/conv.d +++ b/std/conv.d @@ -1851,7 +1851,7 @@ if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) && // bugzilla 15800 @safe unittest { - import std.utf : byChar, byCodeUnit, byDchar, byWchar; + import std.utf : byCodeUnit, byChar, byWchar, byDchar; assert(to!int(byCodeUnit("10")) == 10); assert(to!int(byCodeUnit("10"), 10) == 10); @@ -2439,7 +2439,7 @@ in } body { - import core.checkedint : addu, mulu; + import core.checkedint : mulu, addu; import std.exception : enforce; if (radix == 10) @@ -2638,7 +2638,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum isFloatingPoint!Target && !is(Target == enum)) { import core.stdc.math : HUGE_VAL; - import std.ascii : isAlpha, isDigit, isHexDigit, toLower, toUpper; + import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit; import std.exception : enforce; static if (isNarrowString!Source) @@ -3065,7 +3065,7 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum @safe unittest { import std.exception; - import std.math : fabs, isNaN; + import std.math : isNaN, fabs; // Compare reals with given precision bool feq(in real rx, in real ry, in real precision = 0.000001L) @@ -5814,7 +5814,7 @@ an even number of hexadecimal digits (regardless of the case). private bool isHexLiteral(String)(scope const String hexData) { import std.ascii : isHexDigit; - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; size_t i; foreach (const dchar c; hexData) { diff --git a/std/datetime/date.d b/std/datetime/date.d index 184bc4a058e..a7d33830128 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -7281,7 +7281,7 @@ public: if (isSomeString!S) { import std.algorithm.searching : startsWith; - import std.conv : ConvException, text, to; + import std.conv : to, text, ConvException; import std.exception : enforce; import std.string : strip; @@ -8797,7 +8797,7 @@ public: static TimeOfDay fromISOString(S)(in S isoString) @safe pure if (isSomeString!S) { - import std.conv : ConvException, text, to; + import std.conv : to, text, ConvException; import std.exception : enforce; import std.string : strip; diff --git a/std/datetime/interval.d b/std/datetime/interval.d index cf097fd46ac..302b4c28e99 100644 --- a/std/datetime/interval.d +++ b/std/datetime/interval.d @@ -7,7 +7,7 @@ +/ module std.datetime.interval; -import core.time : dur, Duration; +import core.time : Duration, dur; import std.datetime.date : AllowDayOverflow, DateTimeException, daysToDayOfWeek, DayOfWeek, isTimePoint, Month; import std.exception : enforce; diff --git a/std/datetime/package.d b/std/datetime/package.d index 11f8a61502e..976d06ddb79 100644 --- a/std/datetime/package.d +++ b/std/datetime/package.d @@ -118,7 +118,7 @@ public import std.datetime.timezone; import core.exception : AssertError; import std.functional : unaryFun; import std.traits; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; // Verify module example. diff --git a/std/datetime/systime.d b/std/datetime/systime.d index 4a91f419def..46eee5c8325 100644 --- a/std/datetime/systime.d +++ b/std/datetime/systime.d @@ -1758,7 +1758,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs, nsecs, usecs; + import core.time : msecs, usecs, hnsecs, nsecs; import std.datetime.date : DateTime; auto dt = DateTime(1982, 4, 1, 20, 59, 22); @@ -1846,7 +1846,7 @@ public: /// @safe unittest { - import core.time : Duration, hnsecs, msecs, nsecs; + import core.time : Duration, msecs, hnsecs, nsecs; import std.datetime.date : DateTime; auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22)); @@ -4564,7 +4564,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs; + import core.time : msecs, hnsecs; import std.datetime.date : DateTime; auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); @@ -7383,7 +7383,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs, usecs; + import core.time : msecs, usecs, hnsecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == @@ -7834,7 +7834,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs; + import core.time : msecs, hnsecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == @@ -7966,7 +7966,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs; + import core.time : msecs, hnsecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == @@ -8102,7 +8102,7 @@ public: /// @safe unittest { - import core.time : hnsecs, msecs; + import core.time : msecs, hnsecs; import std.datetime.date : DateTime; assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == @@ -8247,7 +8247,7 @@ public: static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe if (isSomeString!S) { - import std.algorithm.searching : find, startsWith; + import std.algorithm.searching : startsWith, find; import std.conv : to; import std.string : strip; @@ -8310,7 +8310,7 @@ public: /// @safe unittest { - import core.time : hnsecs, hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8559,7 +8559,7 @@ public: /// @safe unittest { - import core.time : hnsecs, hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -8785,7 +8785,7 @@ public: /// @safe unittest { - import core.time : hnsecs, hours, msecs, usecs; + import core.time : hours, msecs, usecs, hnsecs; import std.datetime.date : DateTime; import std.datetime.timezone : SimpleTimeZone, UTC; @@ -9582,8 +9582,8 @@ SysTime parseRFC822DateTime(R)(R value) @safe if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) { - import std.algorithm.searching : all, find; - import std.ascii : isAlpha, isDigit, isPrintable; + import std.algorithm.searching : find, all; + import std.ascii : isDigit, isAlpha, isPrintable; import std.conv : to; import std.functional : not; import std.string : capitalize, format; diff --git a/std/datetime/timezone.d b/std/datetime/timezone.d index a9c7a225a33..0b3bfa74e2d 100644 --- a/std/datetime/timezone.d +++ b/std/datetime/timezone.d @@ -1199,7 +1199,7 @@ private: { long tm_gmtoff(long stdTime) @trusted const nothrow { - import core.stdc.time : gmtime, localtime, tm; + import core.stdc.time : localtime, gmtime, tm; time_t unixTime = stdTimeToUnixTime(stdTime); tm* buf = localtime(&unixTime); @@ -1595,7 +1595,7 @@ package: static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : all, countUntil, startsWith; + import std.algorithm.searching : startsWith, countUntil, all; import std.ascii : isDigit; import std.conv : to; import std.format : format; @@ -1739,7 +1739,7 @@ package: static immutable(SimpleTimeZone) fromISOExtString(S)(S isoExtString) @safe pure if (isSomeString!S) { - import std.algorithm.searching : all, countUntil, startsWith; + import std.algorithm.searching : startsWith, countUntil, all; import std.ascii : isDigit; import std.conv : to; import std.format : format; @@ -1914,11 +1914,11 @@ private: +/ final class PosixTimeZone : TimeZone { - import std.algorithm.searching : canFind, countUntil, startsWith; - import std.file : dirEntries, DirEntry, exists, isDir, isFile, SpanMode; + import std.algorithm.searching : countUntil, canFind, startsWith; + import std.file : isDir, isFile, exists, dirEntries, SpanMode, DirEntry; import std.path : extension; import std.stdio : File; - import std.string : representation, strip; + import std.string : strip, representation; import std.traits : isArray, isSomeChar; public: diff --git a/std/digest/digest.d b/std/digest/digest.d index 00acb9710d7..3e120922828 100644 --- a/std/digest/digest.d +++ b/std/digest/digest.d @@ -413,7 +413,7 @@ if (isDigest!T) package template isDigestibleRange(Range) { import std.digest.md; - import std.range : ElementType, isInputRange; + import std.range : isInputRange, ElementType; enum bool isDigestibleRange = isInputRange!Range && is(typeof( { MD5 ha; //Could use any conformant hash @@ -1136,7 +1136,7 @@ if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && import std.internal.test.dummyrange : ReferenceInputRange; import std.range : takeExactly; import std.string : representation; - import std.utf : byDchar, byWchar; + import std.utf : byWchar, byDchar; { auto hex1 = "02CA3484C375EDD3C0F08D3F50D119E61077".representation; diff --git a/std/digest/hmac.d b/std/digest/hmac.d index 7a9d0f3b12b..20d9821488c 100644 --- a/std/digest/hmac.d +++ b/std/digest/hmac.d @@ -16,7 +16,7 @@ Source: $(PHOBOSSRC std/digest/_hmac.d) module std.digest.hmac; -import std.digest.digest : DigestType, hasBlockSize, isDigest, isDigestibleRange; +import std.digest.digest : isDigest, hasBlockSize, isDigestibleRange, DigestType; import std.meta : allSatisfy; /** @@ -286,7 +286,7 @@ if (isDigest!H) version(unittest) { - import std.digest.digest : LetterCase, toHexString; + import std.digest.digest : toHexString, LetterCase; alias hex = toHexString!(LetterCase.lower); } diff --git a/std/exception.d b/std/exception.d index 9c6d9089046..5767e8710a0 100644 --- a/std/exception.d +++ b/std/exception.d @@ -1511,7 +1511,7 @@ class ErrnoException : Exception @system unittest { - import core.stdc.errno : EAGAIN, errno; + import core.stdc.errno : errno, EAGAIN; auto old = errno; scope(exit) errno = old; @@ -2022,7 +2022,7 @@ pure @safe unittest { import std.algorithm.comparison : equal; import std.algorithm.iteration : map, splitter; - import std.conv : ConvException, to; + import std.conv : to, ConvException; auto s = "12,1337z32,54,2,7,9,1z,6,8"; diff --git a/std/experimental/allocator/building_blocks/affix_allocator.d b/std/experimental/allocator/building_blocks/affix_allocator.d index d4d6cc28b6a..cfeae0cdb77 100644 --- a/std/experimental/allocator/building_blocks/affix_allocator.d +++ b/std/experimental/allocator/building_blocks/affix_allocator.d @@ -370,8 +370,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void) @system unittest { - import std.experimental.allocator : IAllocator, theAllocator; import std.experimental.allocator.gc_allocator : GCAllocator; + import std.experimental.allocator : theAllocator, IAllocator; // One word before and after each allocation. auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator); diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 04e33d09a57..ba47f6fc791 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -2,7 +2,7 @@ module std.experimental.allocator.building_blocks.free_list; import std.experimental.allocator.common; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; /** diff --git a/std/experimental/allocator/building_blocks/free_tree.d b/std/experimental/allocator/building_blocks/free_tree.d index ad1192b108c..6b64659297e 100644 --- a/std/experimental/allocator/building_blocks/free_tree.d +++ b/std/experimental/allocator/building_blocks/free_tree.d @@ -52,7 +52,7 @@ struct FreeTree(ParentAllocator) static assert(ParentAllocator.alignment % size_t.alignof == 0, "FreeTree must be on top of a word-aligned allocator"); - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; import std.algorithm.mutation : swap; import std.traits : hasMember; diff --git a/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/std/experimental/allocator/building_blocks/kernighan_ritchie.d index 2a810b5775f..38c53848bae 100644 --- a/std/experimental/allocator/building_blocks/kernighan_ritchie.d +++ b/std/experimental/allocator/building_blocks/kernighan_ritchie.d @@ -95,7 +95,7 @@ information is available in client code at deallocation time.) */ struct KRRegion(ParentAllocator = NullAllocator) { - import std.experimental.allocator.common : alignedAt, stateSize; + import std.experimental.allocator.common : stateSize, alignedAt; import std.traits : hasMember; import std.typecons : Ternary; diff --git a/std/experimental/allocator/building_blocks/region.d b/std/experimental/allocator/building_blocks/region.d index 6b1f7726e90..0cc60b9dd58 100644 --- a/std/experimental/allocator/building_blocks/region.d +++ b/std/experimental/allocator/building_blocks/region.d @@ -3,7 +3,7 @@ module std.experimental.allocator.building_blocks.region; import std.experimental.allocator.building_blocks.null_allocator; import std.experimental.allocator.common; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; /** A $(D Region) allocator allocates memory straight from one contiguous chunk. diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index 59a58151c9b..0b0bde6a507 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -425,7 +425,7 @@ version(unittest) { import std.conv : text; import std.math : isPowerOf2; - import std.stdio : stderr, writeln; + import std.stdio : writeln, stderr; import std.typecons : Ternary; alias A = typeof(make()); scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); @@ -551,7 +551,7 @@ version(unittest) { import std.conv : text; import std.math : isPowerOf2; - import std.stdio : stderr, writeln; + import std.stdio : writeln, stderr; import std.typecons : Ternary; scope(failure) stderr.writeln("testAllocatorObject failed for ", AllocInterface.stringof); diff --git a/std/experimental/allocator/mallocator.d b/std/experimental/allocator/mallocator.d index 111ac759c17..5dfdc999966 100644 --- a/std/experimental/allocator/mallocator.d +++ b/std/experimental/allocator/mallocator.d @@ -230,7 +230,7 @@ struct AlignedMallocator @trusted @nogc nothrow void[] alignedAllocate(size_t bytes, uint a) shared { - import core.stdc.errno : EINVAL, ENOMEM; + import core.stdc.errno : ENOMEM, EINVAL; assert(a.isGoodDynamicAlignment); void* result; auto code = posix_memalign(&result, a, bytes); diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 7b4de93474e..1b5367e565b 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -229,7 +229,7 @@ public import std.experimental.allocator.common, // Example in the synopsis above @system unittest { - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; import std.experimental.allocator.building_blocks.bitmapped_block diff --git a/std/experimental/allocator/typed.d b/std/experimental/allocator/typed.d index 3ba0f3eecce..92828f3879e 100644 --- a/std/experimental/allocator/typed.d +++ b/std/experimental/allocator/typed.d @@ -15,8 +15,8 @@ import std.experimental.allocator; import std.experimental.allocator.common; import std.range : isInputRange, isForwardRange, walkLength, save, empty, front, popFront; -import std.traits : hasElaborateDestructor, isPointer; -import std.typecons : Flag, No, Yes; +import std.traits : isPointer, hasElaborateDestructor; +import std.typecons : Flag, Yes, No; /** Allocation-related flags dictated by type characteristics. `TypedAllocator` diff --git a/std/experimental/checkedint.d b/std/experimental/checkedint.d index d0640cd52a0..96dc4f282cf 100644 --- a/std/experimental/checkedint.d +++ b/std/experimental/checkedint.d @@ -1336,7 +1336,7 @@ struct Throw auto x1 = cast(T) x; assert(x1 == 42); x = T.max + 1; - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; assertThrown(cast(T) x); x = x.max; assertThrown(x += 42); @@ -2277,7 +2277,7 @@ if (isIntegral!L && isIntegral!R) else alias Result = typeof(mixin("L() " ~ x ~ " R()")); - import core.checkedint : adds, addu, muls, mulu, subs, subu; + import core.checkedint : addu, adds, subs, muls, subu, mulu; import std.algorithm.comparison : among; static if (x == "==") { diff --git a/std/experimental/logger/core.d b/std/experimental/logger/core.d index d150cf5934d..c794d19461a 100644 --- a/std/experimental/logger/core.d +++ b/std/experimental/logger/core.d @@ -627,7 +627,7 @@ alias fatalf = defaultLogFunctionf!(LogLevel.fatal); private struct MsgRange { - import std.traits : isSomeChar, isSomeString; + import std.traits : isSomeString, isSomeChar; private Logger log; @@ -1939,7 +1939,7 @@ version(unittest) private void testFuncNames(Logger logger) @safe @safe unittest { import std.conv : to; - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; import std.format : format; auto l = new TestLogger(LogLevel.all); diff --git a/std/experimental/typecons.d b/std/experimental/typecons.d index 5a41567d636..1d92d614d2b 100644 --- a/std/experimental/typecons.d +++ b/std/experimental/typecons.d @@ -512,7 +512,7 @@ version(StdDdoc) /// @system unittest { - import std.traits : FunctionAttribute, functionAttributes; + import std.traits : functionAttributes, FunctionAttribute; interface A { int run(); } interface B { int stop(); @property int status(); } class X diff --git a/std/file.d b/std/file.d index d74de74a8e7..1fff42df695 100644 --- a/std/file.d +++ b/std/file.d @@ -741,7 +741,7 @@ private void renameImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, if (!result) { import core.stdc.wchar_ : wcslen; - import std.conv : text, to; + import std.conv : to, text; if (!f) f = to!(typeof(f))(fromz[0 .. wcslen(fromz)]); @@ -2321,7 +2321,7 @@ private bool ensureDirExists()(in char[] pathname) void mkdirRecurse(in char[] pathname) @safe { - import std.path : baseName, dirName; + import std.path : dirName, baseName; const left = dirName(pathname); if (left.length != pathname.length && !exists(left)) @@ -2338,7 +2338,7 @@ void mkdirRecurse(in char[] pathname) @safe { import std.exception : assertThrown; { - import std.path : buildNormalizedPath, buildPath; + import std.path : buildPath, buildNormalizedPath; immutable basepath = deleteme ~ "_dir"; scope(exit) () @trusted { rmdirRecurse(basepath); }(); @@ -2748,7 +2748,7 @@ else version (NetBSD) } else version (FreeBSD) { - import std.exception : assumeUnique, errnoEnforce; + import std.exception : errnoEnforce, assumeUnique; enum { CTL_KERN = 1, @@ -3436,7 +3436,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co else version(Posix) { static import core.stdc.stdio; - import std.conv : octal, to; + import std.conv : to, octal; immutable fdr = core.sys.posix.fcntl.open(fromz, O_RDONLY); cenforce(fdr != -1, f, fromz); @@ -3609,7 +3609,7 @@ version(Windows) @system unittest version(Posix) @system unittest { - import std.exception : collectException, enforce; + import std.exception : enforce, collectException; import std.process : executeShell; collectException(rmdirRecurse(deleteme)); auto d = deleteme~"/a/b/c/d/e/f/g"; @@ -3997,7 +3997,7 @@ auto dirEntries(string path, SpanMode mode, bool followSymlink = true) import std.algorithm.searching : startsWith; import std.array : array; import std.conv : to; - import std.path : absolutePath, buildPath, dirEntries; + import std.path : dirEntries, buildPath, absolutePath; import std.process : thisProcessID; import std.range.primitives : walkLength; @@ -4091,7 +4091,7 @@ auto dirEntries(string path, string pattern, SpanMode mode, bool followSymlink = true) { import std.algorithm.iteration : filter; - import std.path : baseName, globMatch; + import std.path : globMatch, baseName; bool f(DirEntry de) { return globMatch(baseName(de.name), pattern); } return filter!f(DirIterator(path, mode, followSymlink)); diff --git a/std/format.d b/std/format.d index 04c043db20a..e155372fc77 100644 --- a/std/format.d +++ b/std/format.d @@ -1004,7 +1004,7 @@ struct FormatSpec(Char) if (is(Unqual!Char == Char)) { import std.algorithm.searching : startsWith; - import std.ascii : isAlpha, isDigit, isPunctuation; + import std.ascii : isDigit, isPunctuation, isAlpha; import std.conv : parse, text, to; /** @@ -2165,7 +2165,7 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) version (CRuntime_Microsoft) { - import std.math : isInfinity, isNaN; + import std.math : isNaN, isInfinity; immutable double tval = val; // convert early to get "inf" in case of overflow string s; if (isNaN(tval)) @@ -3653,7 +3653,7 @@ if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is // Issue 11175 version (Windows) { - import core.sys.windows.com : IID, IUnknown; + import core.sys.windows.com : IUnknown, IID; import core.sys.windows.windows : HRESULT; interface IUnknown2 : IUnknown { } @@ -4250,7 +4250,7 @@ void formatTest(T)(string fmt, T val, string[] expected, size_t ln = __LINE__, s { import core.stdc.string : strlen; import std.array : appender; - import std.conv : octal, text; + import std.conv : text, octal; import std.c.stdio : snprintf; debug(format) printf("std.format.format.unittest\n"); @@ -5072,7 +5072,7 @@ private T unformatValueImpl(T, Range, Char)(ref Range input, const ref FormatSpe if (isInputRange!Range && isSomeChar!T && !is(T == enum) && isSomeChar!(ElementType!Range)) { import std.algorithm.searching : find; - import std.conv : text, to; + import std.conv : to, text; if (spec.spec == 's' || spec.spec == 'c') { auto result = to!T(input.front); @@ -5798,7 +5798,7 @@ immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char) { import std.array : appender; - import std.format : FormatException, formattedWrite; + import std.format : formattedWrite, FormatException; auto w = appender!(immutable(Char)[]); auto n = formattedWrite(w, fmt, args); version (all) @@ -5874,7 +5874,7 @@ if (isSomeString!(typeof(fmt))) char[] sformat(Char, Args...)(char[] buf, in Char[] fmt, Args args) { import core.exception : RangeError; - import std.format : FormatException, formattedWrite; + import std.format : formattedWrite, FormatException; import std.utf : encode; size_t i; diff --git a/std/functional.d b/std/functional.d index 44c9bb868d2..128c89c5807 100644 --- a/std/functional.d +++ b/std/functional.d @@ -259,7 +259,7 @@ template binaryFun(alias fun, string parm1Name = "a", private uint _ctfeSkipOp(ref string op) { if (!__ctfe) assert(false); - import std.ascii : isAlphaNum, isASCII; + import std.ascii : isASCII, isAlphaNum; immutable oldLength = op.length; while (op.length) { diff --git a/std/getopt.d b/std/getopt.d index 66194645bb9..890a3c3f233 100644 --- a/std/getopt.d +++ b/std/getopt.d @@ -1614,7 +1614,7 @@ Params: */ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt) { - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; import std.format : formattedWrite; output.formattedWrite("%s\n", text); diff --git a/std/internal/test/dummyrange.d b/std/internal/test/dummyrange.d index d7191bdf064..a6bce0ada23 100644 --- a/std/internal/test/dummyrange.d +++ b/std/internal/test/dummyrange.d @@ -420,7 +420,7 @@ if (is(T == TestFoo)) @system unittest { import std.algorithm.comparison : equal; - import std.range : iota, repeat, retro; + import std.range : iota, retro, repeat; import std.traits : Unqual; static void testInputRange(T,Cmp)() diff --git a/std/json.d b/std/json.d index 14975e4ce72..008870aa341 100644 --- a/std/json.d +++ b/std/json.d @@ -100,7 +100,7 @@ JSON value node */ struct JSONValue { - import std.exception : enforce, enforceEx; + import std.exception : enforceEx, enforce; union Store { @@ -702,7 +702,7 @@ Params: JSONValue parseJSON(T)(T json, int maxDepth = -1, JSONOptions options = JSONOptions.none) if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T)) { - import std.ascii : isDigit, isHexDigit, isWhite, toLower, toUpper; + import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; import std.typecons : Yes; import std.utf : encode; @@ -1224,7 +1224,7 @@ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions o break; case JSON_TYPE.FLOAT: - import std.math : isInfinity, isNaN; + import std.math : isNaN, isInfinity; auto val = value.store.floating; @@ -1638,7 +1638,7 @@ EOF"; @safe unittest { import std.exception : assertThrown; - import std.math : isInfinity, isNaN; + import std.math : isNaN, isInfinity; // expected representations of NaN and Inf enum { diff --git a/std/math.d b/std/math.d index ed8abf8ad15..43c18187f5d 100644 --- a/std/math.d +++ b/std/math.d @@ -686,7 +686,7 @@ float sin(float x) @safe pure nothrow @nogc { return sin(cast(real) x); } /// @safe unittest { - import std.math : PI, sin; + import std.math : sin, PI; import std.stdio : writefln; void someFunc() diff --git a/std/meta.d b/std/meta.d index 6a4d87b9345..77ed211d6e0 100644 --- a/std/meta.d +++ b/std/meta.d @@ -1164,7 +1164,7 @@ template aliasSeqOf(alias range) @safe unittest { - import std.conv : octal, to; + import std.conv : to, octal; import std.range : iota; //Testing compile time octal foreach (I2; aliasSeqOf!(iota(0, 8))) diff --git a/std/net/curl.d b/std/net/curl.d index a7fe0d3a9f7..e8212735363 100644 --- a/std/net/curl.d +++ b/std/net/curl.d @@ -238,7 +238,7 @@ version(unittest) private Request!T recvReq(T=char)(Socket s) { import std.algorithm.comparison : min; - import std.algorithm.searching : canFind, find; + import std.algorithm.searching : find, canFind; import std.conv : to; import std.regex : ctRegex, matchFirst; @@ -1287,7 +1287,7 @@ if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator) void popFront() { - import std.algorithm.searching : findSplit, findSplitAfter; + import std.algorithm.searching : findSplitAfter, findSplit; enforce!CurlException(currentValid, "Cannot call popFront() on empty range"); if (lines.empty) @@ -2417,7 +2417,7 @@ struct HTTP { import std.algorithm.searching : startsWith; import std.conv : to; - import std.regex : match, regex; + import std.regex : regex, match; import std.uni : toLower; // Wrap incoming callback in order to separate http status line from @@ -4061,7 +4061,7 @@ class CurlTimeoutException : CurlException /// Equal to $(REF CURLcode, etc,c,curl) alias CurlCode = CURLcode; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; /// Flag to specify whether or not an exception is thrown on error. alias ThrowOnError = Flag!"throwOnError"; @@ -4099,7 +4099,7 @@ private struct CurlAPI { version (Posix) { - import core.sys.posix.dlfcn : dlclose, dlopen, dlsym, RTLD_LAZY; + import core.sys.posix.dlfcn : dlsym, dlopen, dlclose, RTLD_LAZY; alias loadSym = dlsym; } else version (Windows) diff --git a/std/net/isemail.d b/std/net/isemail.d index 3998917a50b..d37093db375 100644 --- a/std/net/isemail.d +++ b/std/net/isemail.d @@ -28,7 +28,7 @@ module std.net.isemail; import std.range.primitives; // : ElementType; import std.regex; import std.traits; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; /** * Check that an email address conforms to RFCs 5321, 5322 and others. @@ -62,7 +62,7 @@ EmailStatus isEmail(Char)(const(Char)[] email, CheckDns checkDNS = No.checkDns, EmailStatusCode errorLevel = EmailStatusCode.none) if (isSomeChar!(Char)) { - import std.algorithm.iteration : filter, map, uniq; + import std.algorithm.iteration : uniq, filter, map; import std.algorithm.searching : canFind, maxElement; import std.array : array, split; import std.conv : to; diff --git a/std/numeric.d b/std/numeric.d index 508f64f88a5..da287290f68 100644 --- a/std/numeric.d +++ b/std/numeric.d @@ -128,7 +128,7 @@ if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + /// @safe unittest { - import std.math : cos, sin; + import std.math : sin, cos; // Define a 16-bit floating point values CustomFloat!16 x; // Using the number of bits @@ -2188,7 +2188,7 @@ if (isRandomAccessRange!(R1) && hasLength!(R1) && isRandomAccessRange!(R2) && hasLength!(R2)) { import core.exception : onOutOfMemoryError; - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; import std.algorithm.mutation : swap; import std.functional : binaryFun; @@ -2316,7 +2316,7 @@ optimizations. struct GapWeightedSimilarityIncremental(Range, F = double) if (isRandomAccessRange!(Range) && hasLength!(Range)) { - import core.stdc.stdlib : alloca, free, malloc, realloc; + import core.stdc.stdlib : malloc, realloc, alloca, free; private: Range s, t; diff --git a/std/parallelism.d b/std/parallelism.d index 42b87c74e10..51d322172d0 100644 --- a/std/parallelism.d +++ b/std/parallelism.d @@ -105,7 +105,7 @@ version(Windows) // BUGS: Only works on Windows 2000 and above. shared static this() { - import core.sys.windows.windows : GetSystemInfo, SYSTEM_INFO; + import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo; import std.algorithm.comparison : max; SYSTEM_INFO si; @@ -118,7 +118,7 @@ else version(linux) { shared static this() { - import core.sys.posix.unistd : sysconf, _SC_NPROCESSORS_ONLN; + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } @@ -126,7 +126,7 @@ else version(Solaris) { shared static this() { - import core.sys.posix.unistd : sysconf, _SC_NPROCESSORS_ONLN; + import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); } } @@ -2625,7 +2625,7 @@ public: byte[maxStack] buf = void; immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof; - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; if (nBytesNeeded < maxStack) { tasks = (cast(RTask*) buf.ptr)[0 .. nWorkUnits]; @@ -3372,7 +3372,7 @@ private void submitAndExecute( immutable nThreads = pool.size + 1; alias PTask = typeof(scopedTask(doIt)); - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; import core.stdc.string : memcpy; // The logical thing to do would be to just use alloca() here, but that @@ -3931,12 +3931,12 @@ version(unittest) // These are the tests that should be run every time Phobos is compiled. @system unittest { - import std.algorithm.comparison : equal, max, min; + import std.algorithm.comparison : equal, min, max; import std.algorithm.iteration : filter, map, reduce; import std.array : split; import std.conv : text; import std.exception : assertThrown; - import std.math : approxEqual, log, sqrt; + import std.math : approxEqual, sqrt, log; import std.range : indexed, iota, join; import std.typecons : Tuple, tuple; diff --git a/std/path.d b/std/path.d index 9b3049fe181..a9f0bd8015d 100644 --- a/std/path.d +++ b/std/path.d @@ -641,7 +641,7 @@ if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementT static assert(dirName("dir/file") == "dir"); import std.array; - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(dirName("".byChar).array == "."); assert(dirName("file"w.byWchar).array == "."w); @@ -1075,7 +1075,7 @@ if (isConvertibleToString!R) assert(stripExtension("file.ext1.ext2"d) == "file.ext1"); import std.array; - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(stripExtension("file".byChar).array == "file"); assert(stripExtension("file.ext"w.byWchar).array == "file"); @@ -1187,7 +1187,7 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element !isConvertibleToString!R && isSomeChar!C) { - import std.range : chain, only; + import std.range : only, chain; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R); @@ -1279,7 +1279,7 @@ if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(Element !isConvertibleToString!R && isSomeChar!C) { - import std.range : chain, only; + import std.range : only, chain; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R); @@ -1539,7 +1539,7 @@ if ((isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && isSomeChar!(Elem } else { - import std.range : chain, only; + import std.range : only, chain; import std.utf : byUTF; alias CR = Unqual!(ElementEncodingType!R1); @@ -2906,9 +2906,9 @@ if ((isNarrowString!R1 || import std.algorithm.comparison : mismatch; import std.algorithm.iteration : joiner; import std.array : array; - import std.range : chain, choose, repeat; import std.range.primitives : walkLength; - import std.utf : byChar, byCodeUnit; + import std.range : repeat, chain, choose; + import std.utf : byCodeUnit, byChar; // Remove matching prefix from basePS and pathPS auto tup = mismatch!((a, b) => filenameCmp!cs(a, b) == 0)(basePS, pathPS); @@ -3850,8 +3850,8 @@ string expandTilde(string inputPath) nothrow version(Posix) { import core.exception : onOutOfMemoryError; - import core.stdc.errno : ERANGE, errno; - import core.stdc.stdlib : free, malloc, realloc; + import core.stdc.errno : errno, ERANGE; + import core.stdc.stdlib : malloc, free, realloc; /* Joins a path from a C string to the remainder of path. @@ -3913,7 +3913,7 @@ string expandTilde(string inputPath) nothrow } else { - import core.sys.posix.pwd : getpwnam_r, passwd; + import core.sys.posix.pwd : passwd, getpwnam_r; import std.string : indexOf; assert(path.length > 2 || (path.length == 2 && !isDirSeparator(path[1]))); diff --git a/std/process.d b/std/process.d index a2b39ab2ac4..87feb4e1a23 100644 --- a/std/process.d +++ b/std/process.d @@ -386,7 +386,7 @@ private Pid spawnProcessImpl(in char[][] args, scope(exit) if (workDirFD >= 0) close(workDirFD); if (workDir.length) { - import core.sys.posix.fcntl : fstat, open, O_RDONLY, stat_t, S_ISDIR; + import core.sys.posix.fcntl : open, O_RDONLY, stat_t, fstat, S_ISDIR; workDirFD = open(workDir.tempCString(), O_RDONLY); if (workDirFD < 0) throw ProcessException.newFromErrno("Failed to open working directory"); @@ -449,8 +449,8 @@ private Pid spawnProcessImpl(in char[][] args, if (!(config & Config.inheritFDs)) { import core.stdc.stdlib : malloc; - import core.sys.posix.poll : poll, pollfd, POLLNVAL; - import core.sys.posix.sys.resource : getrlimit, rlimit, RLIMIT_NOFILE; + import core.sys.posix.poll : pollfd, poll, POLLNVAL; + import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE; // Get the maximum number of file descriptors that could be open. rlimit r; @@ -778,7 +778,7 @@ version (Posix) @safe unittest version (Posix) private void setCLOEXEC(int fd, bool on) nothrow @nogc { - import core.sys.posix.fcntl : fcntl, FD_CLOEXEC, F_GETFD, F_SETFD; + import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD; auto flags = fcntl(fd, F_GETFD); if (flags >= 0) { @@ -1590,7 +1590,7 @@ void kill(Pid pid, int codeOrSignal) } else version (Posix) { - import core.sys.posix.signal : SIGKILL, SIGTERM; + import core.sys.posix.signal : SIGTERM, SIGKILL; TestScript prog = "while true; do sleep 1; done"; } auto pid = spawnProcess(prog.path); @@ -2491,7 +2491,7 @@ private struct TestScript ~this() { - import std.file : exists, remove; + import std.file : remove, exists; if (!path.empty && exists(path)) { try { remove(path); } diff --git a/std/random.d b/std/random.d index 9cf7469c401..20f88b1be5d 100644 --- a/std/random.d +++ b/std/random.d @@ -1287,7 +1287,7 @@ A single unsigned integer seed value, different on each successive call */ @property uint unpredictableSeed() @trusted { - import core.thread : getpid, MonoTime, Thread; + import core.thread : Thread, getpid, MonoTime; static bool seeded; static MinstdRand0 rand; if (!seeded) diff --git a/std/range/package.d b/std/range/package.d index 6da8f9d90a6..27e1e9c3c5f 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -230,7 +230,7 @@ module std.range; public import std.array; public import std.range.interfaces; public import std.range.primitives; -public import std.typecons : Flag, No, Yes; +public import std.typecons : Flag, Yes, No; import std.meta; // allSatisfy, staticMap import std.traits; // CommonType, isCallable, isFloatingPoint, isIntegral, @@ -4197,7 +4197,7 @@ if (Ranges.length && allSatisfy!(isInputRange, Ranges)) return ranges[0].length; //[min|max](ranges[0].length, ranges[1].length, ...) - import std.algorithm.comparison : max, min; + import std.algorithm.comparison : min, max; if (stoppingPolicy == StoppingPolicy.shortest) return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); else @@ -4360,7 +4360,7 @@ enum StoppingPolicy import std.algorithm.mutation : swap; import std.algorithm.sorting : sort; - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; import std.typecons : tuple; int[] a = [ 1, 2, 3 ]; @@ -5593,7 +5593,7 @@ debug @system unittest { import std.algorithm.comparison : equal; import std.algorithm.searching : count; - import std.math : approxEqual, nextDown, nextUp; + import std.math : approxEqual, nextUp, nextDown; import std.meta : AliasSeq; static assert(is(ElementType!(typeof(iota(0f))) == float)); @@ -8170,7 +8170,7 @@ public: @safe pure nothrow unittest { import std.algorithm.comparison : equal; - import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, RangeType, ReturnBy; + import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy, AllDummyRanges; import std.meta : AliasSeq; alias AllForwardDummyRanges = AliasSeq!( @@ -10729,7 +10729,7 @@ if (isInputRange!R && is(R == class)) @safe unittest // bug 9060 { - import std.algorithm.iteration : group, joiner, map; + import std.algorithm.iteration : map, joiner, group; import std.algorithm.searching : until; // fix for std.algorithm auto r = map!(x => 0)([1]); diff --git a/std/range/primitives.d b/std/range/primitives.d index 2c4201bfc77..e4931fdf897 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1364,7 +1364,7 @@ template hasLvalueElements(R) /// @safe unittest { - import std.range : chain, iota; + import std.range : iota, chain; static assert( hasLvalueElements!(int[])); static assert( hasLvalueElements!(const(int)[])); diff --git a/std/regex/internal/backtracking.d b/std/regex/internal/backtracking.d index 71ea9723823..ffc9779923a 100644 --- a/std/regex/internal/backtracking.d +++ b/std/regex/internal/backtracking.d @@ -799,7 +799,7 @@ alias Sequence(int B, int E) = staticIota!(B, E); struct CtContext { - import std.conv : text, to; + import std.conv : to, text; //dirty flags bool counter; //to mark the portion of matches to save diff --git a/std/regex/internal/generator.d b/std/regex/internal/generator.d index 8a51c58a20d..6831e59ed2e 100644 --- a/std/regex/internal/generator.d +++ b/std/regex/internal/generator.d @@ -12,11 +12,11 @@ module std.regex.internal.generator; */ @trusted private struct SampleGenerator(Char) { - import std.array : Appender, appender; + import std.array : appender, Appender; import std.format : formattedWrite; import std.random : Xorshift; - import std.regex.internal.ir : IR, IRL, Regex; - import std.utf : byChar, isValidDchar; + import std.regex.internal.ir : Regex, IR, IRL; + import std.utf : isValidDchar, byChar; Regex!Char re; Appender!(char[]) app; uint limit, seed; diff --git a/std/regex/internal/tests.d b/std/regex/internal/tests.d index 46f972d828e..80e278bf648 100644 --- a/std/regex/internal/tests.d +++ b/std/regex/internal/tests.d @@ -1015,7 +1015,7 @@ alias Sequence(int B, int E) = staticIota!(B, E); @safe unittest { import std.array : appender; - import std.regex : regex, replaceFirst, replaceFirstInto; + import std.regex : replaceFirst, replaceFirstInto, regex; import std.stdio : writeln; auto example = "Hello, world!"; diff --git a/std/regex/package.d b/std/regex/package.d index f1d7f8763c5..3b7c4e7c2a8 100644 --- a/std/regex/package.d +++ b/std/regex/package.d @@ -299,7 +299,7 @@ module std.regex; import std.range.primitives, std.traits; import std.regex.internal.ir; import std.regex.internal.thompson; //TODO: get rid of this dependency -import std.typecons; // : Flag, No, Yes; +import std.typecons; // : Flag, Yes, No; /++ $(D Regex) object holds regular expression pattern in compiled form. @@ -409,7 +409,7 @@ if (isSomeString!(S)) public auto regexImpl(S)(S pattern, const(char)[] flags="") if (isSomeString!(S)) { - import std.regex.internal.parser : CodeGen, Parser; + import std.regex.internal.parser : Parser, CodeGen; auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags); auto r = parser.program; return r; @@ -692,7 +692,7 @@ public: if (isSomeString!R) { private: - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; EngineType _engine; @@ -810,7 +810,7 @@ public: private @trusted auto matchOnce(alias Engine, RegEx, R)(R input, RegEx re) { - import core.stdc.stdlib : free, malloc; + import core.stdc.stdlib : malloc, free; import std.exception : enforce; alias Char = BasicElementOf!R; alias EngineType = Engine!Char; @@ -1138,8 +1138,8 @@ if (isOutputRange!(OutR, ElementEncodingType!R[]) && isOutputRange!(OutR, ElementEncodingType!(Capt.String)[])) { import std.algorithm.searching : find; - import std.ascii : isAlpha, isDigit; - import std.conv : parse, text; + import std.ascii : isDigit, isAlpha; + import std.conv : text, parse; import std.exception : enforce; enum State { Normal, Dollar } auto state = State.Normal; diff --git a/std/signals.d b/std/signals.d index ec9c1af1fcb..4f4d81d409a 100644 --- a/std/signals.d +++ b/std/signals.d @@ -63,7 +63,7 @@ module std.signals; import core.exception : onOutOfMemoryError; -import core.stdc.stdlib : calloc, free, realloc; +import core.stdc.stdlib : calloc, realloc, free; import std.stdio; // Special function for internal use only. diff --git a/std/stdio.d b/std/stdio.d index 09e1126af6e..1464445fd7f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -350,7 +350,7 @@ Hello, Jimmy! struct File { import std.range.primitives : ElementEncodingType; - import std.traits : isArray, isScalarType; + import std.traits : isScalarType, isArray; enum Orientation { unknown, narrow, wide } private struct Impl @@ -525,7 +525,7 @@ Throws: $(D ErrnoException) in case of error. @system unittest // Test changing filename { - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; static import std.file; auto deleteme = testFilename(); @@ -547,7 +547,7 @@ Throws: $(D ErrnoException) in case of error. version (CRuntime_Microsoft) {} else // Not implemented @system unittest // Test changing mode { - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; static import std.file; auto deleteme = testFilename(); @@ -1003,7 +1003,7 @@ Throws: $(D Exception) if the file is not opened. */ void seek(long offset, int origin = SEEK_SET) @trusted { - import std.conv : text, to; + import std.conv : to, text; import std.exception : enforce, errnoEnforce; enforce(isOpen, "Attempting to seek() in an unopened file"); @@ -1152,7 +1152,7 @@ Throws: $(D Exception) if the file is not opened. version(Windows) { - import core.sys.windows.windows : BOOL, OVERLAPPED, ULARGE_INTEGER; + import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL; private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, Flags flags) @@ -1395,7 +1395,7 @@ Throws: $(D Exception) if the file is not opened. */ void write(S...)(S args) { - import std.traits : isAggregateType, isBoolean, isIntegral; + import std.traits : isBoolean, isIntegral, isAggregateType; auto w = lockingTextWriter(); foreach (arg; args) { diff --git a/std/string.d b/std/string.d index 1d2adaed1a0..2635a78f521 100644 --- a/std/string.d +++ b/std/string.d @@ -186,7 +186,7 @@ private: } public import std.format : format, sformat; -import std.typecons : Flag, No, Yes; +import std.typecons : Flag, Yes, No; public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace; import std.meta; // AliasSeq, staticIndexOf @@ -199,8 +199,8 @@ import std.traits; // isConvertibleToString, isNarrowString, isSomeChar, //public imports for backward compatibility public import std.algorithm.comparison : cmp; -public import std.algorithm.searching : count, endsWith, startsWith; -public import std.array : empty, join, replace, replaceInPlace, split; +public import std.algorithm.searching : startsWith, endsWith, count; +public import std.array : join, replace, replaceInPlace, split, empty; /* ************* Exceptions *************** */ @@ -257,7 +257,7 @@ inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow { immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow out (result) { - import core.stdc.string : memcmp, strlen; + import core.stdc.string : strlen, memcmp; if (result) { auto slen = s.length; @@ -378,7 +378,7 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && { static import std.ascii; static import std.uni; - import std.utf : byCodeUnit, byDchar, codeLength, UTFException; + import std.utf : byDchar, byCodeUnit, UTFException, codeLength; alias Char = Unqual!(ElementEncodingType!Range); if (cs == Yes.caseSensitive) @@ -579,7 +579,7 @@ if (isConvertibleToString!Range) import std.conv : to; import std.exception : assertCTFEable; import std.traits : EnumMembers; - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; debug(string) trustedPrintf("string.indexOf.unittest\n"); assertCTFEable!( @@ -635,7 +635,7 @@ if (isConvertibleToString!Range) { import std.conv : to; import std.traits : EnumMembers; - import std.utf : byChar, byCodeUnit, byWchar; + import std.utf : byCodeUnit, byChar, byWchar; debug(string) trustedPrintf("string.indexOf(startIdx).unittest\n"); assert("hello".byCodeUnit.indexOf(cast(dchar)'l', 1) == 2); @@ -3214,7 +3214,7 @@ if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) || isNarrowString!Range) && !isConvertibleToString!Range) { - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; if (str.empty) return str; @@ -3308,7 +3308,7 @@ if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) || @safe pure unittest { - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; import std.utf : decode; assert(chomp(" hello world \n\r") == " hello world \n"); assert(chomp(" hello world \r\n") == " hello world "); @@ -3396,7 +3396,7 @@ if (isConvertibleToString!Range) // Ranges import std.array : array; - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(chomp("hello world\r\n" .byChar ).array == "hello world"); assert(chomp("hello world\r\n"w.byWchar).array == "hello world"w); assert(chomp("hello world\r\n"d.byDchar).array == "hello world"d); @@ -3496,7 +3496,7 @@ unittest // Ranges import std.array : array; - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(chompPrefix("hello world" .byChar , "hello"d).array == " world"); assert(chompPrefix("hello world"w.byWchar, "hello" ).array == " world"w); assert(chompPrefix("hello world"d.byDchar, "hello"w).array == " world"d); @@ -3608,7 +3608,7 @@ if (isConvertibleToString!Range) @safe pure unittest { import std.array : array; - import std.utf : byChar, byCodeUnit, byDchar, byWchar, invalidUTFstrings; + import std.utf : byChar, byWchar, byDchar, byCodeUnit, invalidUTFstrings; assert(chop("hello world".byChar).array == "hello worl"); assert(chop("hello world\n"w.byWchar).array == "hello world"w); @@ -3719,7 +3719,7 @@ if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byChar, byDchar; + import std.utf : byDchar, byChar; return leftJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -3862,7 +3862,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byChar, byDchar; + import std.utf : byDchar, byChar; return rightJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -4090,7 +4090,7 @@ if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && static if (C.sizeof == 1) { - import std.utf : byChar, byDchar; + import std.utf : byDchar, byChar; return centerJustifier(r.byDchar, width, fillChar).byChar; } else static if (C.sizeof == 2) @@ -4240,7 +4240,7 @@ auto detabber(Range)(Range r, size_t tabSize = 8) if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) && !isConvertibleToString!Range) { - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; import std.utf : codeUnitLimit, decodeFront; assert(tabSize > 0); @@ -4471,7 +4471,7 @@ if (!(isForwardRange!Range && isSomeChar!(ElementEncodingType!Range)) && auto entabber(Range)(Range r, size_t tabSize = 8) if (isForwardRange!Range && !isConvertibleToString!Range) { - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; import std.utf : codeUnitLimit, decodeFront; assert(tabSize > 0); @@ -6513,7 +6513,7 @@ if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) || else { // decoding not needed for wchars and dchars - import std.uni : lineSep, nelSep, paraSep; + import std.uni : lineSep, paraSep, nelSep; size_t column; @@ -6545,7 +6545,7 @@ if ((isInputRange!Range && isSomeChar!(Unqual!(ElementEncodingType!Range)) || /// @safe pure unittest { - import std.utf : byChar, byDchar, byWchar; + import std.utf : byChar, byWchar, byDchar; assert(column("1234 ") == 5); assert(column("1234 "w) == 5); diff --git a/std/uni.d b/std/uni.d index 93931d6504e..d71e482063b 100644 --- a/std/uni.d +++ b/std/uni.d @@ -875,7 +875,7 @@ size_t replicateBits(size_t times, size_t bits)(size_t val) @safe pure nothrow @ @safe pure nothrow @nogc unittest // for replicate { - import std.algorithm.iteration : map, sum; + import std.algorithm.iteration : sum, map; import std.range : iota; size_t m = 0b111; size_t m2 = 0b01; @@ -3401,7 +3401,7 @@ private: import std.algorithm.comparison : equal; import std.algorithm.mutation : copy; import std.conv : text; - import std.range : chain, iota; + import std.range : iota, chain; import std.range.primitives : isBidirectionalRange, isOutputRange; void funcRef(T)(ref T u24) { @@ -5762,7 +5762,7 @@ template idxTypes(Key, size_t fullBits, Prefix...) if (is(Char1 : dchar) && is(Char2 : dchar)) { import std.algorithm.comparison : cmp; - import std.algorithm.iteration : filter, map; + import std.algorithm.iteration : map, filter; import std.ascii : toLower; static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';} return cmp( @@ -6542,8 +6542,8 @@ if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) @safe unittest { import std.algorithm.comparison : equal; - import std.range : drop, take; import std.range.primitives : walkLength; + import std.range : take, drop; auto text = "noe\u0308l"; // noël using e + combining diaeresis assert(text.walkLength == 5); // 5 code points @@ -7492,7 +7492,7 @@ enum { public dchar compose(dchar first, dchar second) pure nothrow @safe { import std.algorithm.iteration : map; - import std.internal.unicode_comp : composeCntShift, composeIdxMask, compositionTable; + import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask; import std.range : assumeSorted; immutable packed = compositionJumpTrie[first]; if (packed == ushort.max) @@ -7539,7 +7539,7 @@ public dchar compose(dchar first, dchar second) pure nothrow @safe public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe { import std.algorithm.searching : until; - import std.internal.unicode_decomp : decompCanonTable, decompCompatTable; + import std.internal.unicode_decomp : decompCompatTable, decompCanonTable; static if (decompType == Canonical) { alias table = decompCanonTable; @@ -8115,7 +8115,7 @@ public bool isWhite(dchar c) @safe pure nothrow @nogc bool isLower(dchar c) { - import std.ascii : isASCII, isLower; + import std.ascii : isLower, isASCII; if (isASCII(c)) return isLower(c); return lowerCaseTrie[c]; @@ -8148,7 +8148,7 @@ bool isLower(dchar c) @safe pure nothrow @nogc bool isUpper(dchar c) { - import std.ascii : isASCII, isUpper; + import std.ascii : isUpper, isASCII; if (isASCII(c)) return isUpper(c); return upperCaseTrie[c]; @@ -8744,7 +8744,7 @@ private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure no private void toCaseInPlace(alias indexFn, uint maxIdx, alias tableFn, C)(ref C[] s) @trusted pure if (is(C == char) || is(C == wchar) || is(C == dchar)) { - import std.utf : codeLength, decode; + import std.utf : decode, codeLength; size_t curIdx = 0; size_t destIdx = 0; alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn); @@ -8812,7 +8812,7 @@ private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn) { size_t toCaseLength(C)(in C[] str) { - import std.utf : codeLength, decode; + import std.utf : decode, codeLength; size_t codeLen = 0; size_t lastNonTrivial = 0; size_t curIdx = 0; diff --git a/std/utf.d b/std/utf.d index f98e8182e13..339243cfcef 100644 --- a/std/utf.d +++ b/std/utf.d @@ -75,7 +75,7 @@ debug (utf) import core.stdc.stdio : printf; +/ class UTFException : Exception { - import core.internal.string : UnsignedStringBuf, unsignedToTempString; + import core.internal.string : unsignedToTempString, UnsignedStringBuf; uint[4] sequence; size_t len; diff --git a/std/uuid.d b/std/uuid.d index d4147c56f6a..a2db742c4bb 100644 --- a/std/uuid.d +++ b/std/uuid.d @@ -333,7 +333,7 @@ public struct UUID */ this(T)(in T[] uuid) if (isSomeChar!(Unqual!T)) { - import std.conv : parse, to; + import std.conv : to, parse; if (uuid.length < 36) { throw new UUIDParsingException(to!string(uuid), 0, @@ -1233,7 +1233,7 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) /// @safe unittest { - import std.random : unpredictableSeed, Xorshift192; + import std.random : Xorshift192, unpredictableSeed; //simple call auto uuid = randomUUID(); @@ -1253,13 +1253,13 @@ if (isInputRange!RNG && isIntegral!(ElementType!RNG)) */ @safe unittest { - import std.random : Mt19937, rndGen; + import std.random : rndGen, Mt19937; static assert(is(typeof(rndGen) == Mt19937)); } @safe unittest { - import std.random : unpredictableSeed, Xorshift192; + import std.random : Xorshift192, unpredictableSeed; //simple call auto uuid = randomUUID(); diff --git a/std/variant.d b/std/variant.d index c94b4ff211a..8ca53732568 100644 --- a/std/variant.d +++ b/std/variant.d @@ -796,7 +796,7 @@ public: @property T coerce(T)() { - import std.conv : text, to; + import std.conv : to, text; static if (isNumeric!T || isBoolean!T) { if (convertsTo!real) @@ -2690,7 +2690,7 @@ if (isAlgebraic!VariantType && Handler.length > 0) @system unittest { - import std.exception : assertNotThrown, assertThrown; + import std.exception : assertThrown, assertNotThrown; // Make sure Variant can handle types with opDispatch but no length field. struct SWithNoLength { diff --git a/std/windows/registry.d b/std/windows/registry.d index dc68e013bb8..0b5e3d2d1c3 100644 --- a/std/windows/registry.d +++ b/std/windows/registry.d @@ -45,7 +45,7 @@ import std.exception; import std.internal.cstring; private import std.internal.windows.advapi32; import std.system : Endian, endian; -import std.utf : toUTF16, toUTF8; +import std.utf : toUTF8, toUTF16; import std.windows.syserror; //debug = winreg; @@ -82,7 +82,7 @@ class Win32Exception : WindowsException @property int error() { return super.code; } } -version(unittest) import std.string : endsWith, startsWith; +version(unittest) import std.string : startsWith, endsWith; @safe unittest { diff --git a/std/windows/syserror.d b/std/windows/syserror.d index ff844dc41df..863e0c1700c 100644 --- a/std/windows/syserror.d +++ b/std/windows/syserror.d @@ -180,7 +180,7 @@ T wenforce(T)(T condition, const(char)[] name, const(wchar)* namez, string file version(Windows) @system unittest { - import std.algorithm.searching : endsWith, startsWith; + import std.algorithm.searching : startsWith, endsWith; import std.exception; import std.string; diff --git a/std/zip.d b/std/zip.d index 39dd57a8b07..47a3aa27a78 100644 --- a/std/zip.d +++ b/std/zip.d @@ -97,7 +97,7 @@ enum CompressionMethod : ushort */ final class ArchiveMember { - import std.conv : octal, to; + import std.conv : to, octal; import std.datetime : DosFileTime, SysTime, SysTimeToDosFileTime; /** @@ -853,7 +853,7 @@ debug(print) // Test if packing and unpacking produces the original data import std.conv, std.stdio; - import std.random : MinstdRand0, uniform; + import std.random : uniform, MinstdRand0; MinstdRand0 gen; const uint itemCount = 20, minSize = 10, maxSize = 500; foreach (variant; 0 .. 2) From f5fed3d44cc2400525f0ffbf2eb98c1aa2c31ffa Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Tue, 13 Jun 2017 21:49:04 +0000 Subject: [PATCH 256/262] std.process: Fix brace style Fixes CircleCI style checks. --- std/process.d | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/std/process.d b/std/process.d index f9f2763d030..0569e9dca51 100644 --- a/std/process.d +++ b/std/process.d @@ -574,11 +574,10 @@ private Pid spawnProcessImpl(in char[][] args, assert(false); } - if (readExecResult == error.sizeof) { + if (readExecResult == error.sizeof) throw ProcessException.newFromErrno(error, errorMsg); - } else { + else throw new ProcessException(errorMsg); - } } // Parent process: Close streams and return. From e7111d4fd0d28f357896870c24a15a210b040ba1 Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Thu, 15 Jun 2017 10:58:40 +0200 Subject: [PATCH 257/262] Fix invalid undefined Ddoc macros --- std/algorithm/package.d | 2 +- std/datetime/date.d | 4 ++-- .../allocator/building_blocks/free_list.d | 4 ++-- std/json.d | 2 +- std/math.d | 23 +++++++++++++------ std/random.d | 3 ++- std/socket.d | 2 +- std/traits.d | 2 +- 8 files changed, 26 insertions(+), 16 deletions(-) diff --git a/std/algorithm/package.d b/std/algorithm/package.d index d357a4f0ef2..656dbcfd85e 100644 --- a/std/algorithm/package.d +++ b/std/algorithm/package.d @@ -3,7 +3,7 @@ /** This package implements generic algorithms oriented towards the processing of sequences. Sequences processed by these functions define range-based -interfaces. See also $(MREF_ALTTEF Reference on ranges, std, range) and +interfaces. See also $(MREF_ALTTEXT Reference on ranges, std, range) and $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges). $(SCRIPT inhibitQuickIndex = 1;) diff --git a/std/datetime/date.d b/std/datetime/date.d index 184bc4a058e..951de2c8dde 100644 --- a/std/datetime/date.d +++ b/std/datetime/date.d @@ -9496,10 +9496,10 @@ bool yearIsLeapYear(int year) @safe pure nothrow function as a time point. 1. $(D T) must define a static property named $(D min) which is the smallest - value of $(D T) as $(Unqual!T). + value of $(D T) as $(D Unqual!T). 2. $(D T) must define a static property named $(D max) which is the largest - value of $(D T) as $(Unqual!T). + value of $(D T) as $(D Unqual!T). 3. $(D T) must define an $(D opBinary) for addition and subtraction that accepts $(REF Duration, core,time) and returns $(D Unqual!T). diff --git a/std/experimental/allocator/building_blocks/free_list.d b/std/experimental/allocator/building_blocks/free_list.d index 04e33d09a57..08c26d43c5a 100644 --- a/std/experimental/allocator/building_blocks/free_list.d +++ b/std/experimental/allocator/building_blocks/free_list.d @@ -749,8 +749,8 @@ struct ContiguousFreeList(ParentAllocator, FreeList shared across threads. Allocation and deallocation are lock-free. The parameters have the same semantics as for $(D FreeList). -$(D expand) is defined to forward to $(ParentAllocator.expand) (it must be also -$(D shared)). +$(D expand) is defined to forward to $(D ParentAllocator.expand) +(it must be also $(D shared)). */ struct SharedFreeList(ParentAllocator, size_t minSize, size_t maxSize = minSize, size_t approxMaxNodes = unbounded) diff --git a/std/json.d b/std/json.d index 14975e4ce72..3f84f78e206 100644 --- a/std/json.d +++ b/std/json.d @@ -1090,7 +1090,7 @@ Any Object types will be serialized in a key-sorted order. If $(D pretty) is false no whitespaces are generated. If $(D pretty) is true serialized string is formatted to be human-readable. -Set the $(specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings. +Set the $(LREF JSONOptions.specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings. */ string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe { diff --git a/std/math.d b/std/math.d index ed8abf8ad15..b161ec219be 100644 --- a/std/math.d +++ b/std/math.d @@ -86,7 +86,13 @@ $(TR $(TDNW Hardware Control) $(TD * SV = $(TR $(TD $1) $(TD $2)) * TH3 = $(TR $(TH $1) $(TH $2) $(TH $3)) * TD3 = $(TR $(TD $1) $(TD $2) $(TD $3)) - * + * TABLE_DOMRG = + * $(SVH Domain X, Range Y) + $(SV $1, $2) + *
+ * DOMAIN=$1 + * RANGE=$1 + * NAN = $(RED NAN) * SUP = $0 * GAMMA = Γ @@ -1419,9 +1425,11 @@ public: * Mathematically, acosh(x) = log(x + sqrt( x*x - 1)) * * $(TABLE_DOMRG - * $(DOMAIN 1..$(INFIN)) - * $(RANGE 1 .. log(real.max), $(INFIN)) ) - * $(TABLE_SV + * $(DOMAIN 1..$(INFIN)), + * $(RANGE 0..$(INFIN)) + * ) + * + * $(TABLE_SV * $(SVH x, acosh(x) ) * $(SV $(NAN), $(NAN) ) * $(SV $(LT)1, $(NAN) ) @@ -1501,10 +1509,11 @@ float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real) x); } * * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2 * - * * $(TABLE_DOMRG - * $(DOMAIN -$(INFIN)..$(INFIN)) - * $(RANGE -1 .. 1) ) + * $(DOMAIN -$(INFIN)..$(INFIN)), + * $(RANGE -1 .. 1) + * ) + * $(BR) * $(TABLE_SV * $(SVH x, acosh(x) ) * $(SV $(NAN), $(NAN) ) diff --git a/std/random.d b/std/random.d index 9cf7469c401..ce7ac4210f4 100644 --- a/std/random.d +++ b/std/random.d @@ -989,8 +989,9 @@ alias Mt19937_64 = MersenneTwisterEngine!(ulong, 64, 312, 156, 31, * Xorshift generator using 32bit algorithm. * * Implemented according to $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs). + * Supporting bits are below, $(D bits) means second parameter of XorshiftEngine. * - * $(BOOKTABLE $(TEXTWITHCOMMAS Supporting bits are below, $(D bits) means second parameter of XorshiftEngine.), + * $(BOOKTABLE , * $(TR $(TH bits) $(TH period)) * $(TR $(TD 32) $(TD 2^32 - 1)) * $(TR $(TD 64) $(TD 2^64 - 1)) diff --git a/std/socket.d b/std/socket.d index effad6c6639..a375e2ee058 100644 --- a/std/socket.d +++ b/std/socket.d @@ -3291,7 +3291,7 @@ public: } /** - * Wait for a socket to change status. A wait timeout of $(Duration) or + * Wait for a socket to change status. A wait timeout of $(REF Duration, core, time) or * $(D TimeVal), may be specified; if a timeout is not specified or the * $(D TimeVal) is $(D null), the maximum timeout is used. The $(D TimeVal) * timeout has an unspecified value when $(D select) returns. diff --git a/std/traits.d b/std/traits.d index 506dd3ffeaf..e53fc8f7616 100644 --- a/std/traits.d +++ b/std/traits.d @@ -6852,7 +6852,7 @@ template CopyTypeQualifiers(FromType, ToType) } /** -Returns the type of `Target` with the "constness" of `Source`. A type's $(BOLD constness) +Returns the type of `Target` with the "constness" of `Source`. A type's $(B constness) refers to whether it is `const`, `immutable`, or `inout`. If `source` has no constness, the returned type will be the same as `Target`. */ From 2f16ae7cb7ccebe631a7c51b563f6a5a377ea7da Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 13:38:57 -0400 Subject: [PATCH 258/262] Eliminate shared this from std/process.d --- posix.mak | 2 +- std/internal/processinit.d | 22 ---------------------- std/process.d | 22 ++++++++++++++-------- win32.mak | 1 - win64.mak | 1 - 5 files changed, 15 insertions(+), 33 deletions(-) delete mode 100644 std/internal/processinit.d diff --git a/posix.mak b/posix.mak index c0107ed2c0f..93398d77258 100644 --- a/posix.mak +++ b/posix.mak @@ -224,7 +224,7 @@ EXTRA_MODULES_INTERNAL := $(addprefix std/, \ cstring digest/sha_SSSE3 \ $(addprefix math/, biguintcore biguintnoasm biguintx86 \ errorfunction gammafunction ) \ - processinit scopebuffer test/dummyrange \ + scopebuffer test/dummyrange \ $(addprefix unicode_, comp decomp grapheme norm tables) \ ) \ ) diff --git a/std/internal/processinit.d b/std/internal/processinit.d deleted file mode 100644 index df241089aa0..00000000000 --- a/std/internal/processinit.d +++ /dev/null @@ -1,22 +0,0 @@ -// Written in the D programming language. - -/++ - The only purpose of this module is to do the static construction for - std.process in order to eliminate cyclic construction errors. - - Copyright: Copyright 2011 - - License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - Authors: Jonathan M Davis and Kato Shoichi - Source: $(PHOBOSSRC std/internal/_processinit.d) - +/ -module std.internal.processinit; - -version(OSX) -{ - extern(C) void std_process_shared_static_this(); - - shared static this() - { - std_process_shared_static_this(); - } -} diff --git a/std/process.d b/std/process.d index 0569e9dca51..d431add4a49 100644 --- a/std/process.d +++ b/std/process.d @@ -99,7 +99,6 @@ version (Windows) } import std.internal.cstring; -import std.internal.processinit; import std.range.primitives; import std.stdio; @@ -133,20 +132,25 @@ version (Posix) { version (OSX) { - extern(C) char*** _NSGetEnviron() nothrow; - private __gshared const(char**)* environPtr; - extern(C) void std_process_shared_static_this() { environPtr = _NSGetEnviron(); } - const(char**) environ() @property @trusted nothrow { return *environPtr; } + private extern(C) char*** _NSGetEnviron() nothrow; + private const(char**) getEnvironPtr() @property @trusted + { + return *_NSGetEnviron; + } } else { // Made available by the C runtime: - extern(C) extern __gshared const char** environ; + private extern(C) extern __gshared const char** environ; + private const(char**) getEnvironPtr() @property @trusted + { + return environ; + } } @system unittest { - new Thread({assert(environ !is null);}).start(); + new Thread({assert(getEnvironPtr !is null);}).start(); } } @@ -703,6 +707,7 @@ private const(char*)* createEnv(const string[string] childEnv, { // Determine the number of strings in the parent's environment. int parentEnvLength = 0; + auto environ = getEnvironPtr; if (mergeWithParentEnv) { if (childEnv.length == 0) return environ; @@ -737,6 +742,7 @@ version (Posix) @system unittest auto e2 = createEnv(null, true); assert(e2 != null); int i = 0; + auto environ = getEnvironPtr; for (; environ[i] != null; ++i) { assert(e2[i] != null); @@ -3333,6 +3339,7 @@ static: string[string] aa; version (Posix) { + auto environ = getEnvironPtr; for (int i=0; environ[i] != null; ++i) { import std.string : indexOf; @@ -3902,4 +3909,3 @@ else version (Posix) } else static assert(0, "os not supported"); - diff --git a/win32.mak b/win32.mak index 854ed9a6a6d..db2108b3f3b 100644 --- a/win32.mak +++ b/win32.mak @@ -270,7 +270,6 @@ SRC_STD_C_FREEBSD= \ SRC_STD_INTERNAL= \ std\internal\cstring.d \ - std\internal\processinit.d \ std\internal\unicode_tables.d \ std\internal\unicode_comp.d \ std\internal\unicode_decomp.d \ diff --git a/win64.mak b/win64.mak index 9e59883561d..dc5ec92a9c2 100644 --- a/win64.mak +++ b/win64.mak @@ -295,7 +295,6 @@ SRC_STD_C_FREEBSD= \ SRC_STD_INTERNAL= \ std\internal\cstring.d \ - std\internal\processinit.d \ std\internal\unicode_tables.d \ std\internal\unicode_comp.d \ std\internal\unicode_decomp.d \ From 442a94ab44c0588e94c46774bafc32937d155fd8 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 11 Jun 2017 20:51:28 +0000 Subject: [PATCH 259/262] std.process: Correct indentation of private block --- std/process.d | 58 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/std/process.d b/std/process.d index d431add4a49..e280718f31f 100644 --- a/std/process.d +++ b/std/process.d @@ -112,47 +112,47 @@ version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME; private { -// Microsoft Visual C Runtime (MSVCRT) declarations. -version (Windows) -{ - version (DMC_RUNTIME) { } else + // Microsoft Visual C Runtime (MSVCRT) declarations. + version (Windows) { - import core.stdc.stdint; - enum + version (DMC_RUNTIME) { } else { - STDIN_FILENO = 0, - STDOUT_FILENO = 1, - STDERR_FILENO = 2, + import core.stdc.stdint; + enum + { + STDIN_FILENO = 0, + STDOUT_FILENO = 1, + STDERR_FILENO = 2, + } } } -} -// POSIX API declarations. -version (Posix) -{ - version (OSX) + // POSIX API declarations. + version (Posix) { - private extern(C) char*** _NSGetEnviron() nothrow; - private const(char**) getEnvironPtr() @property @trusted + version (OSX) { - return *_NSGetEnviron; + private extern(C) char*** _NSGetEnviron() nothrow; + private const(char**) getEnvironPtr() @property @trusted + { + return *_NSGetEnviron; + } } - } - else - { - // Made available by the C runtime: - private extern(C) extern __gshared const char** environ; - private const(char**) getEnvironPtr() @property @trusted + else { - return environ; + // Made available by the C runtime: + private extern(C) extern __gshared const char** environ; + private const(char**) getEnvironPtr() @property @trusted + { + return environ; + } } - } - @system unittest - { - new Thread({assert(getEnvironPtr !is null);}).start(); + @system unittest + { + new Thread({assert(getEnvironPtr !is null);}).start(); + } } -} } // private From 3b3cd774958c4e721cd1517274b94b4cb8b4a87a Mon Sep 17 00:00:00 2001 From: Andrei Alexandrescu Date: Sun, 11 Jun 2017 13:52:39 -0400 Subject: [PATCH 260/262] Adjust private --- std/process.d | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/std/process.d b/std/process.d index e280718f31f..93627da1e13 100644 --- a/std/process.d +++ b/std/process.d @@ -111,7 +111,6 @@ version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME; // Some of the following should be moved to druntime. private { - // Microsoft Visual C Runtime (MSVCRT) declarations. version (Windows) { @@ -132,8 +131,8 @@ private { version (OSX) { - private extern(C) char*** _NSGetEnviron() nothrow; - private const(char**) getEnvironPtr() @property @trusted + extern(C) char*** _NSGetEnviron() nothrow; + const(char**) getEnvironPtr() @trusted { return *_NSGetEnviron; } @@ -141,8 +140,8 @@ private else { // Made available by the C runtime: - private extern(C) extern __gshared const char** environ; - private const(char**) getEnvironPtr() @property @trusted + extern(C) extern __gshared const char** environ; + const(char**) getEnvironPtr() @trusted { return environ; } @@ -153,8 +152,6 @@ private new Thread({assert(getEnvironPtr !is null);}).start(); } } - - } // private From 9713d711bf15a65db55e77c5739f00ca8eebc399 Mon Sep 17 00:00:00 2001 From: anonymous Date: Thu, 15 Jun 2017 20:42:13 +0200 Subject: [PATCH 261/262] Revert "Removed level of indirection from internal std.xml.ElementParser code" This reverts commit 25fc16fa219d6e0e1bc48f3c422b3f10b3cd3a67. --- std/xml.d | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/std/xml.d b/std/xml.d index 1143dfa6d20..d55c6661857 100644 --- a/std/xml.d +++ b/std/xml.d @@ -564,7 +564,7 @@ class Document : Element this(xml.tag); prolog = s[0 .. tagString.ptr - s.ptr]; parse(xml); - epilog = xml.s; + epilog = *xml.s; } /** @@ -1721,7 +1721,7 @@ class DocumentParser : ElementParser body { xmlText = xmlText_; - s = xmlText; + s = &xmlText; super(); // Initialize everything parse(); // Parse through the root tag (but not beyond) } @@ -1748,7 +1748,7 @@ class ElementParser { Tag tag_; string elementStart; - string s; + string* s; Handler commentHandler = null; Handler cdataHandler = null; @@ -1766,7 +1766,7 @@ class ElementParser } // Private constructor for empty tags - this(Tag tag, string t) @safe @nogc pure nothrow + this(Tag tag, string* t) @safe @nogc pure nothrow { s = t; this(); @@ -1849,7 +1849,7 @@ class ElementParser protected this() @safe @nogc pure nothrow { - elementStart = s; + elementStart = *s; } /** @@ -2005,37 +2005,37 @@ class ElementParser while (s.length != 0) { - if (startsWith(s,"")); + chop(*s,4); + t = chop(*s,indexOf(*s,"-->")); if (commentHandler.funcptr !is null) commentHandler(t); - chop(s,3); + chop(*s,3); } - else if (startsWith(s,"")); + chop(*s,9); + t = chop(*s,indexOf(*s,"]]>")); if (cdataHandler.funcptr !is null) cdataHandler(t); - chop(s,3); + chop(*s,3); } - else if (startsWith(s,"")); + chop(*s,2); + t = chop(*s,indexOf(*s,">")); if (xiHandler.funcptr !is null) xiHandler(t); - chop(s,1); + chop(*s,1); } - else if (startsWith(s,"")); + chop(*s,2); + t = chop(*s,indexOf(*s,"?>")); if (piHandler.funcptr !is null) piHandler(t); - chop(s,2); + chop(*s,2); } - else if (startsWith(s,"<")) + else if (startsWith(*s,"<")) { - tag_ = new Tag(s,true); + tag_ = new Tag(*s,true); if (root is null) return; // Return to constructor of derived class @@ -2091,7 +2091,7 @@ class ElementParser // Handle the pretend start tag string s2; - auto parser = new ElementParser(startTag,s2); + auto parser = new ElementParser(startTag,&s2); auto handler1 = startTag.name in onStartTag; if (handler1 !is null) (*handler1)(parser); else @@ -2113,7 +2113,7 @@ class ElementParser } else { - t = chop(s,indexOf(s,"<")); + t = chop(*s,indexOf(*s,"<")); if (rawTextHandler.funcptr !is null) rawTextHandler(t); else if (textHandler.funcptr !is null) From c7b6b87333fe731962f38a8a6ac0a3b5f675be06 Mon Sep 17 00:00:00 2001 From: anonymous Date: Thu, 15 Jun 2017 21:08:03 +0200 Subject: [PATCH 262/262] test against regression fixes issue 17511 --- std/xml.d | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/std/xml.d b/std/xml.d index d55c6661857..770c56fdbfb 100644 --- a/std/xml.d +++ b/std/xml.d @@ -1727,6 +1727,14 @@ class DocumentParser : ElementParser } } +@system unittest +{ + auto doc = new Document(""); + assert(doc.elements.length == 1); + assert(doc.elements[0].tag.name == "child"); + assert(doc.items == doc.elements); +} + /** * Class for parsing an XML element. *