-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDateOnlyRange.cs
139 lines (119 loc) · 5.54 KB
/
DateOnlyRange.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// "Copyright (c) Cyrille NDOUMBE.
// Licenced under GNU General Public Licence, version 3.0"
#if NET6_0_OR_GREATER
using System;
using System.Numerics;
namespace Candoumbe.Types.Calendar;
/// <summary>
/// Represents a <see cref="DateOnly"/> range
/// </summary>
public record DateOnlyRange : Range<DateOnly>, IRange<DateOnlyRange, DateOnly>, ICanRepresentInfinite<DateOnlyRange, DateOnly>
#if NET7_0_OR_GREATER
, IAdditionOperators<DateOnlyRange, DateOnlyRange, DateOnlyRange>
#endif
{
/// <summary>
/// A <see cref="DateOnlyRange"/> that <see cref="Range{T}.Overlaps(Range{T})">overlaps</see> any other <see cref="DateOnlyRange"/> except <see cref="Empty"/>.
/// </summary>
public static DateOnlyRange Infinite => new(DateOnly.MinValue, DateOnly.MaxValue);
/// <summary>
/// A <see cref="DateOnlyRange"/> that <see cref="Range{T}.Overlaps(Range{T})">overlaps</see> no other <see cref="DateOnlyRange"/>.
/// </summary>
public static DateOnlyRange Empty => new(DateOnly.MinValue, DateOnly.MinValue);
/// <summary>
/// Builds a new <see cref="DateTimeRange"/>
/// </summary>
/// <param name="start">lower bound</param>
/// <param name="end">Upper bound</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> is after <paramref name="end"/>.</exception>
public DateOnlyRange(DateOnly start, DateOnly end) : base(start, end)
{
if (start > end)
{
throw new ArgumentOutOfRangeException(nameof(start), $"{nameof(start)} cannot be after {nameof(end)}");
}
}
/// <inheritdoc/>
public bool Overlaps(DateOnly date) => Start <= date && date <= End;
/// <summary>
/// Builds a new <see cref="DateOnlyRange"/> that spans from <see cref="DateOnly.MinValue"/> up to <paramref name="date"/>
/// </summary>
/// <param name="date">The desired upper limit</param>
/// <returns>a <see cref="DateOnlyRange"/> that spans up to <paramref name="date"/>.</returns>
public static DateOnlyRange UpTo(DateOnly date) => new(DateOnly.MinValue, date);
/// <summary>
/// Builds a new <see cref="DateOnlyRange"/> that spans from <paramref name="date"/> up to <see cref="DateOnly.MaxValue"/>.
/// </summary>
/// <param name="date">The desired lower limit</param>
/// <returns>a <see cref="DateOnlyRange"/> that starts from <paramref name="date"/>.</returns>
public static DateOnlyRange DownTo(DateOnly date) => new(date, DateOnly.MaxValue);
///<inheritdoc/>
public sealed override string ToString() => $"{Start} - {End}";
/// <summary>
/// Expands the current so that the resulting <see cref="DateOnlyRange"/> spans over both current and <paramref name="other"/>.
/// </summary>
/// <param name="other">The other <see cref="DateOnlyRange"/> to span over</param>
/// <returns>A new <see cref="DateOnlyRange"/> than spans over both current and <paramref name="other"/> range</returns>
/// <exception cref="InvalidOperationException">if current instance does not overlap or is not contiguous with <paramref name="other"/>.</exception>
public DateOnlyRange Merge(DateOnlyRange other)
{
DateOnlyRange result;
if (other.IsEmpty())
{
result = this;
}
else if (Overlaps(other) || IsContiguousWith(other))
{
result = (this with { Start = GetMinimum(Start, other.Start), End = GetMaximum(End, other.End) });
}
else
{
throw new InvalidOperationException($"Cannot build a {nameof(DateOnlyRange)} as union of '{this}' and {other}");
}
return result;
}
///<inheritdoc/>
public static DateOnlyRange operator +(DateOnlyRange left, DateOnlyRange right) => left?.Merge(right);
/// <summary>
/// Returns the oldest <see cref="DateOnly"/> between <paramref name="left"/> and <paramref name="right"/>.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
private static DateOnly GetMinimum(DateOnly left, DateOnly right) => left.CompareTo(right) switch
{
<= 0 => left,
_ => right
};
/// <summary>
/// Returns the furthest <see cref="DateOnly"/> between <paramref name="left"/> and <paramref name="right"/>.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
private static DateOnly GetMaximum(DateOnly left, DateOnly right) => left.CompareTo(right) switch
{
> 0 => left,
_ => right
};
/// <summary>
/// Computes <see cref="DateOnlyRange"/> value that is common between the current instance and <paramref name="other"/>.
/// </summary>
/// <remarks>
/// This method relies on <see cref="Range{T}.Overlaps(Range{T})"/> to see if there can be an intersection with <paramref name="other"/>.
/// </remarks>
/// <param name="other">The other instance to test</param>
/// <returns>a <see cref="DateOnlyRange"/> that represent the overlap between the current instance and <paramref name="other"/> or <see cref="Empty"/> when no intersection found.</returns>
public DateOnlyRange Intersect(DateOnlyRange other)
{
DateOnlyRange result = Empty;
if (Overlaps(other))
{
result = this with { Start = GetMaximum(Start, other.Start), End = GetMinimum(End, other.End) };
}
return result;
}
///<inheritdoc/>
public bool IsInfinite() => (Start, End).Equals((DateOnly.MinValue, DateOnly.MaxValue));
/// <inheritdoc />
public int CompareTo(DateOnlyRange other) => base.CompareTo(other);
}
#endif