From 314a5af9ec6f32e9e371070b7859bc0c3136e5ec Mon Sep 17 00:00:00 2001 From: Andreas Saurwein <saurwein@gmail.com> Date: Fri, 14 Apr 2023 18:01:45 +0100 Subject: [PATCH 1/3] refactored the Ranges added unit tests for the ranges --- src/MoreDateTime/DateOnlyRange.cs | 88 ++++++- src/MoreDateTime/DateTimeRange.cs | 66 ++++- ...ns.Every.cs => DateTimeExtensions.Sets.cs} | 13 +- src/MoreDateTime/Interfaces/IDateOnlyRange.cs | 25 -- src/MoreDateTime/Interfaces/IDateTimeRange.cs | 27 --- src/MoreDateTime/Interfaces/IRange.cs | 74 ++++++ src/MoreDateTime/Interfaces/ITimeOnlyRange.cs | 25 -- src/MoreDateTime/TimeOnlyRange.cs | 65 ++++- src/MoreDateTime/docs | 207 +++++++++++----- tests/MoreDateTime.Test/DateOnlyRangeTests.cs | 226 ++++++++++++++++++ tests/MoreDateTime.Test/DateTimeRangeTests.cs | 193 ++++++++++++++- tests/MoreDateTime.Test/TimeOnlyRangeTests.cs | 225 +++++++++++++++++ 12 files changed, 1080 insertions(+), 154 deletions(-) rename src/MoreDateTime/Extensions/{DateTimeExtensions.Every.cs => DateTimeExtensions.Sets.cs} (59%) delete mode 100644 src/MoreDateTime/Interfaces/IDateOnlyRange.cs delete mode 100644 src/MoreDateTime/Interfaces/IDateTimeRange.cs create mode 100644 src/MoreDateTime/Interfaces/IRange.cs delete mode 100644 src/MoreDateTime/Interfaces/ITimeOnlyRange.cs create mode 100644 tests/MoreDateTime.Test/DateOnlyRangeTests.cs create mode 100644 tests/MoreDateTime.Test/TimeOnlyRangeTests.cs diff --git a/src/MoreDateTime/DateOnlyRange.cs b/src/MoreDateTime/DateOnlyRange.cs index e47850f..80e6b53 100644 --- a/src/MoreDateTime/DateOnlyRange.cs +++ b/src/MoreDateTime/DateOnlyRange.cs @@ -4,9 +4,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="IDateOnlyRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class DateOnlyRange : IDateOnlyRange + public class DateOnlyRange : IRange<DateOnly, DateOnlyRange> { /// <summary> /// Initializes a new instance of the <see cref="DateOnlyRange"/> class. @@ -19,12 +19,94 @@ public DateOnlyRange(DateOnly startTime, DateOnly endTime) End = endTime; } - /// <inheritdoc/> + /// <summary> + /// Initializes a new instance of the <see cref="DateOnlyRange"/> class. + /// </summary> + /// <param name="startTime">The start time.</param> + /// <param name="endTime">The end time.</param> + public DateOnlyRange(DateTime startTime, DateTime endTime) + { + Start = startTime.ToDateOnly(); + End = endTime.ToDateOnly(); + } + + /// <summary> + /// Initializes a copied new instance of the <see cref="DateOnlyRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public DateOnlyRange(DateOnlyRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public DateOnlyRange Offset(TimeSpan timeSpan) + { + return new DateOnlyRange(this.Start.Add(timeSpan), this.End.Add(timeSpan)); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateOnlyRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + // DateOnly operates only on timeSpans greater than 1 day + if(timeSpan.Days < 1) + { + return new(this); + } + + return direction switch + { + RangeDirection.Both => new DateOnlyRange(this.Start.Sub(timeSpan / 2), this.End.Add(timeSpan / 2)), + RangeDirection.Start => new DateOnlyRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new DateOnlyRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateOnlyRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + // DateOnly operates only on timeSpans greater than 1 day + if (timeSpan.Days < 1) + { + return new(this); + } + + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new DateOnlyRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new DateOnlyRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new DateOnlyRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public DateOnly Start { get; set; } diff --git a/src/MoreDateTime/DateTimeRange.cs b/src/MoreDateTime/DateTimeRange.cs index 0344104..79e3928 100644 --- a/src/MoreDateTime/DateTimeRange.cs +++ b/src/MoreDateTime/DateTimeRange.cs @@ -6,9 +6,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="IDateTimeRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class DateTimeRange : IDateTimeRange + public class DateTimeRange : IRange<DateTime, DateTimeRange> { /// <summary> /// Initializes a new instance of the <see cref="DateTimeRange"/> class. @@ -32,12 +32,72 @@ public DateTimeRange(DateOnly startTime, DateOnly endTime) End = endTime.ToDateTime(); // Should we include this day until midnight - 1 millisecond? } - /// <inheritdoc/> + /// <summary> + /// Initializes a copied new instance of the <see cref="DateTimeRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public DateTimeRange(DateTimeRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public DateTimeRange Offset(TimeSpan timeSpan) + { + return new DateTimeRange(this.Start + timeSpan, this.End + timeSpan); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateTimeRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + return direction switch + { + + RangeDirection.Both => new DateTimeRange(this.Start.Sub(timeSpan / 2), this.End.Add( timeSpan / 2)), + RangeDirection.Start => new DateTimeRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new DateTimeRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateTimeRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new DateTimeRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new DateTimeRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new DateTimeRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public DateTime Start { get; set; } diff --git a/src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs b/src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs similarity index 59% rename from src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs rename to src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs index e5f405c..c2966bd 100644 --- a/src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs +++ b/src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs @@ -16,11 +16,16 @@ namespace MoreDateTime.Extensions public static partial class DateTimeExtensions { // Todo: EveryX - - /* - public static IEnumerable<DateTime> EveryNDays(this DateTime from, DateTime to) + public static DateTimeRange Union(this DateTimeRange a, DateTimeRange b) { + DateTimeRange ab = new(a); + + // calculate the union of the two ranges + ab.Start = a.Start < b.Start ? a.Start : b.Start; + ab.End = a.End > b.End ? a.End : b.End; + + + return ab; } - */ } } diff --git a/src/MoreDateTime/Interfaces/IDateOnlyRange.cs b/src/MoreDateTime/Interfaces/IDateOnlyRange.cs deleted file mode 100644 index 3d8522f..0000000 --- a/src/MoreDateTime/Interfaces/IDateOnlyRange.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface IDateOnlyRange - { - /// <summary> - /// Gets or sets the start date - /// </summary> - DateOnly Start { get; set; } - - /// <summary> - /// Gets or sets the end date - /// </summary> - DateOnly End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/Interfaces/IDateTimeRange.cs b/src/MoreDateTime/Interfaces/IDateTimeRange.cs deleted file mode 100644 index 81f800e..0000000 --- a/src/MoreDateTime/Interfaces/IDateTimeRange.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface IDateTimeRange - { - /// <summary> - /// Gets or sets the start date - /// </summary> - DateTime Start { get; set; } - - /// <summary> - /// Gets or sets the end date - /// </summary> - DateTime End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/Interfaces/IRange.cs b/src/MoreDateTime/Interfaces/IRange.cs new file mode 100644 index 0000000..bc1c492 --- /dev/null +++ b/src/MoreDateTime/Interfaces/IRange.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MoreDateTime.Interfaces +{ + /// <summary> + /// An interface for ranges of dates and times + /// </summary> + /// <typeparam name="T">The base type to handle</typeparam> + /// <typeparam name="Treturn">A self reference as a return type</typeparam> + internal interface IRange<T, Treturn> where T : IComparable<T> + { + /// <summary> + /// Gets or sets the start <typeparamref name="T"/> + /// </summary> + T Start { get; set; } + + /// <summary> + /// Gets or sets the end <typeparamref name="T"/> + /// </summary> + T End { get; set; } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> + public TimeSpan Distance(); + + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public Treturn Offset(TimeSpan timeSpan); + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public Treturn Extend(TimeSpan timeSpan, RangeDirection direction); + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public Treturn Reduce(TimeSpan timeSpan, RangeDirection direction); + + public string ToString(); + } + + /// <summary> + /// The range direction enum, indicates in which direction to move the ranges values + /// </summary> + public enum RangeDirection + { + /// <summary> + /// Move start and end + /// </summary> + Both, + + /// <summary> + /// Move start only + /// </summary> + Start, + + /// <summary> + /// Move end only + /// </summary> + End + } +} diff --git a/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs b/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs deleted file mode 100644 index e594d64..0000000 --- a/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface ITimeOnlyRange - { - /// <summary> - /// Gets or sets the start time - /// </summary> - TimeOnly Start { get; set; } - - /// <summary> - /// Gets or sets the end time - /// </summary> - TimeOnly End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/TimeOnlyRange.cs b/src/MoreDateTime/TimeOnlyRange.cs index 1cb1ea5..05ca310 100644 --- a/src/MoreDateTime/TimeOnlyRange.cs +++ b/src/MoreDateTime/TimeOnlyRange.cs @@ -4,9 +4,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="ITimeOnlyRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class TimeOnlyRange : ITimeOnlyRange + public class TimeOnlyRange : IRange<TimeOnly, TimeOnlyRange> { /// <summary> /// Initializes a new instance of the <see cref="TimeOnlyRange"/> class. @@ -19,12 +19,71 @@ public TimeOnlyRange(TimeOnly startTime, TimeOnly endTime) End = endTime; } - /// <inheritdoc/> + /// <summary> + /// Initializes a copied new instance of the <see cref="TimeOnlyRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public TimeOnlyRange(TimeOnlyRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public TimeOnlyRange Offset(TimeSpan timeSpan) + { + return new TimeOnlyRange(this.Start.Add(timeSpan), this.End.Add(timeSpan)); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public TimeOnlyRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + return direction switch + { + RangeDirection.Both => new TimeOnlyRange(this.Start.Sub(timeSpan / 2), this.End.Add(timeSpan / 2)), + RangeDirection.Start => new TimeOnlyRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new TimeOnlyRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public TimeOnlyRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new TimeOnlyRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new TimeOnlyRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new TimeOnlyRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public TimeOnly Start { get; set; } diff --git a/src/MoreDateTime/docs b/src/MoreDateTime/docs index 681d1ba..ea8dd73 100644 --- a/src/MoreDateTime/docs +++ b/src/MoreDateTime/docs @@ -6,7 +6,7 @@ <members> <member name="T:MoreDateTime.DateOnlyRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.IDateOnlyRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> members </summary> </member> <member name="M:MoreDateTime.DateOnlyRange.#ctor(System.DateOnly,System.DateOnly)"> @@ -16,8 +16,43 @@ <param name="startTime">The start DateOnly</param> <param name="endTime">The end DateOnly</param> </member> + <member name="M:MoreDateTime.DateOnlyRange.#ctor(System.DateTime,System.DateTime)"> + <summary> + Initializes a new instance of the <see cref="T:MoreDateTime.DateOnlyRange"/> class. + </summary> + <param name="startTime">The start time.</param> + <param name="endTime">The end time.</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.#ctor(MoreDateTime.DateOnlyRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.DateOnlyRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.DateOnlyRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and the <see cref="P:MoreDateTime.DateOnlyRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.DateOnlyRange.Start"> <inheritdoc/> @@ -69,7 +104,7 @@ </member> <member name="T:MoreDateTime.DateTimeRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.IDateTimeRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> members </summary> </member> <member name="M:MoreDateTime.DateTimeRange.#ctor(System.DateTime,System.DateTime)"> @@ -86,8 +121,36 @@ <param name="startTime">The start datetime</param> <param name="endTime">The end datetime</param> </member> + <member name="M:MoreDateTime.DateTimeRange.#ctor(MoreDateTime.DateTimeRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.DateTimeRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.DateTimeRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and the <see cref="P:MoreDateTime.DateTimeRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.DateTimeRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.DateTimeRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and/or <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.DateTimeRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and/or <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.DateTimeRange.Start"> <inheritdoc/> @@ -928,15 +991,15 @@ DateTime related extension methods </summary> <inheritdoc/> - <summary> - DateTime related extension methods - </summary> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> + <summary> + DateTime related extension methods + </summary> <inheritdoc/>> </member> <member name="T:MoreDateTime.Extensions.DateTimeExtensions.DateTruncate"> @@ -2443,26 +2506,6 @@ <param name="dt">The TimeSpan object</param> <returns>The Truncated TimeSpan object</returns> </member> - <member name="T:MoreDateTime.Interfaces.IDateOnlyRange"> - <summary> - A Date/Time range provider, with an start and an end date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateOnlyRange.Start"> - <summary> - Gets or sets the start date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateOnlyRange.End"> - <summary> - Gets or sets the end date - </summary> - </member> - <member name="M:MoreDateTime.Interfaces.IDateOnlyRange.Distance"> - <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IDateOnlyRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IDateOnlyRange.End"/> - </summary> - </member> <member name="T:MoreDateTime.Interfaces.IDateTimeProvider"> <summary> The interface for providing DateTime information @@ -2488,26 +2531,6 @@ Gets the date part of the current UTC DateTime with the time set to 00:00:00 (or the mock value if set) </summary> </member> - <member name="T:MoreDateTime.Interfaces.IDateTimeRange"> - <summary> - A Date/Time range provider, with an start and an end date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateTimeRange.Start"> - <summary> - Gets or sets the start date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateTimeRange.End"> - <summary> - Gets or sets the end date - </summary> - </member> - <member name="M:MoreDateTime.Interfaces.IDateTimeRange.Distance"> - <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IDateTimeRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IDateTimeRange.End"/> - </summary> - </member> <member name="T:MoreDateTime.Interfaces.IHolidayProvider"> <summary> An interface to provide access to holiday information @@ -2536,24 +2559,66 @@ <param name="year">The year for which the number of holidays is requested</param> <param name="cultureInfo">The </param> </member> - <member name="T:MoreDateTime.Interfaces.ITimeOnlyRange"> + <member name="T:MoreDateTime.Interfaces.IRange`2"> <summary> - A Date/Time range provider, with an start and an end date + An interface for ranges of dates and times </summary> + <typeparam name="T">The base type to handle</typeparam> + <typeparam name="Treturn">A self reference as a return type</typeparam> </member> - <member name="P:MoreDateTime.Interfaces.ITimeOnlyRange.Start"> + <member name="P:MoreDateTime.Interfaces.IRange`2.Start"> <summary> - Gets or sets the start time + Gets or sets the start <typeparamref name="T"/> </summary> </member> - <member name="P:MoreDateTime.Interfaces.ITimeOnlyRange.End"> + <member name="P:MoreDateTime.Interfaces.IRange`2.End"> <summary> - Gets or sets the end time + Gets or sets the end <typeparamref name="T"/> </summary> </member> - <member name="M:MoreDateTime.Interfaces.ITimeOnlyRange.Distance"> + <member name="M:MoreDateTime.Interfaces.IRange`2.Distance"> <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.ITimeOnlyRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.ITimeOnlyRange.End"/> + Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> + </summary> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and/or <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and/or <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="T:MoreDateTime.Interfaces.RangeDirection"> + <summary> + The range direction enum, indicates in which direction to move the ranges values + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.Both"> + <summary> + Move start and end + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.Start"> + <summary> + Move start only + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.End"> + <summary> + Move end only </summary> </member> <member name="T:MoreDateTime.NagerHolidayProvider"> @@ -2584,7 +2649,7 @@ </member> <member name="T:MoreDateTime.TimeOnlyRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.ITimeOnlyRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> members </summary> </member> <member name="M:MoreDateTime.TimeOnlyRange.#ctor(System.TimeOnly,System.TimeOnly)"> @@ -2594,8 +2659,36 @@ <param name="startTime">The start TimeOnly</param> <param name="endTime">The end TimeOnly</param> </member> + <member name="M:MoreDateTime.TimeOnlyRange.#ctor(MoreDateTime.TimeOnlyRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.TimeOnlyRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.TimeOnlyRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and the <see cref="P:MoreDateTime.TimeOnlyRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.TimeOnlyRange.Start"> <inheritdoc/> diff --git a/tests/MoreDateTime.Test/DateOnlyRangeTests.cs b/tests/MoreDateTime.Test/DateOnlyRangeTests.cs new file mode 100644 index 0000000..8d025f9 --- /dev/null +++ b/tests/MoreDateTime.Test/DateOnlyRangeTests.cs @@ -0,0 +1,226 @@ +namespace MoreDateTime.Tests +{ + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="DateOnlyRange"/>. + /// </summary> + [TestClass] + public class DateOnlyRangeTests + { + private DateOnlyRange _testClass; + private readonly DateOnly _startDate = new DateOnly(2020, 05, 15); // Friday + + private readonly DateOnly _midDate = new DateOnly(2021, 02, 20); // Saturday + private readonly DateOnly _endDate = new DateOnly(2021, 05, 14); // Friday + + /// <summary> + /// Creates the date time range. + /// </summary> + /// <returns>A DateOnlyRange.</returns> + private DateOnlyRange CreateDateTimeRange() + { + return new DateOnlyRange(_startDate, _endDate); + } + + /// <summary> + /// Sets the up. + /// </summary> + [TestInitialize] + public void SetUp() + { + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct() + { + // Act + var instance = new DateOnlyRange(this._startDate, this._endDate); + + // Assert + instance.ShouldNotBeNull(); + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End.ToDateTime() - _testClass.Start.ToDateTime()); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Sub(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow.ToDateOnly(); + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); + } + + /// <summary> + /// Checks that the End property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_End() + { + // Arrange + var testValue = DateTime.UtcNow.ToDateOnly(); + + // Act + this._testClass.End = testValue; + + // Assert + this._testClass.End.ShouldBe(testValue); + } + } +} \ No newline at end of file diff --git a/tests/MoreDateTime.Test/DateTimeRangeTests.cs b/tests/MoreDateTime.Test/DateTimeRangeTests.cs index 3294936..8a31a57 100644 --- a/tests/MoreDateTime.Test/DateTimeRangeTests.cs +++ b/tests/MoreDateTime.Test/DateTimeRangeTests.cs @@ -1,5 +1,15 @@ -namespace MoreDateTime.Tests +namespace MoreDateTime.Tests { + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + /// <summary> /// The date time range tests. /// </summary> @@ -9,6 +19,8 @@ public class DateTimeRangeTests private readonly DateTime _startDate = new DateTime(2000, 05, 15); private readonly DateTime _endDate = new DateTime(2001, 02, 20); + private DateTimeRange _testClass; + /// <summary> /// Creates the date time range. /// </summary> @@ -24,23 +36,190 @@ private DateTimeRange CreateDateTimeRange() [TestInitialize] public void SetUp() { - // Method intentionally left empty. + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct() + { + // Act + var instance = new DateTimeRange(this._startDate, this._endDate); + + // Assert + instance.ShouldNotBeNull(); + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End - _testClass.Start); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Sub(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow; + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); } /// <summary> - /// Tests the method1. + /// Checks that the End property can be read from and written to. /// </summary> [TestMethod] - public void TestMethod1() + public void CanSetAndGet_End() { // Arrange - var dateTimeRange = this.CreateDateTimeRange(); + var testValue = DateTime.UtcNow; // Act + this._testClass.End = testValue; // Assert - Assert.AreEqual(dateTimeRange.Start, _startDate); - Assert.AreEqual(dateTimeRange.End, _endDate); + this._testClass.End.ShouldBe(testValue); } } } diff --git a/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs b/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs new file mode 100644 index 0000000..dcbda70 --- /dev/null +++ b/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs @@ -0,0 +1,225 @@ +namespace MoreDateTime.Tests +{ + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="TimeOnlyRange"/>. + /// </summary> + [TestClass] + public class TimeOnlyRangeTests + { + private TimeOnlyRange _testClass; + private static readonly TimeOnly _startTime = new TimeOnly(1, 2, 3, 4); + private static readonly TimeOnly _midTime = new TimeOnly(5, 6, 7, 8); + private static readonly TimeOnly _endTime = new TimeOnly(10, 11, 12, 987); + + /// <summary> + /// Creates the date time range. + /// </summary> + /// <returns>A TimeOnlyRange.</returns> + private TimeOnlyRange CreateDateTimeRange() + { + return new TimeOnlyRange(_startTime, _startTime); + } + + /// <summary> + /// Sets the up. + /// </summary> + [TestInitialize] + public void SetUp() + { + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct() + { + // Act + var instance = new TimeOnlyRange(_startTime, _startTime); + + // Assert + instance.ShouldNotBeNull(); + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End - _testClass.Start); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow.ToTimeOnly(); + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); + } + + /// <summary> + /// Checks that the End property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_End() + { + // Arrange + var testValue = DateTime.UtcNow.ToTimeOnly(); + + // Act + this._testClass.End = testValue; + + // Assert + this._testClass.End.ShouldBe(testValue); + } + } +} \ No newline at end of file From ac2aac984e1b81b0bc78665df99274e960875479 Mon Sep 17 00:00:00 2001 From: Andreas Saurwein <saurwein@gmail.com> Date: Fri, 14 Apr 2023 18:01:45 +0100 Subject: [PATCH 2/3] documentation fixes added EndOfWeek and StartOfWeek for DateTime and DateOnly fixed truncation errors with CultureInfo added more unit tests refactored the Ranges added unit tests for the ranges --- README.md | 2 + src/MoreDateTime/DateOnlyRange.cs | 98 ++++- src/MoreDateTime/DateTimeRange.cs | 66 +++- .../Extensions/DateOnlyExtensions.NextPrev.cs | 1 + .../Extensions/DateOnlyExtensions.Truncate.cs | 11 +- .../Extensions/DateOnlyExtensions.cs | 26 +- .../DateTimeExtensions.Enumerate.cs | 2 +- .../Extensions/DateTimeExtensions.NextPrev.cs | 236 ++++++------ ...ns.Every.cs => DateTimeExtensions.Sets.cs} | 13 +- .../Extensions/TimeOnlyExtensions.Is.cs | 22 +- .../Extensions/TimeOnlyExtensions.IsEqual.cs | 6 +- .../Extensions/TimeOnlyExtensions.Truncate.cs | 2 +- src/MoreDateTime/Interfaces/IDateOnlyRange.cs | 25 -- src/MoreDateTime/Interfaces/IDateTimeRange.cs | 27 -- src/MoreDateTime/Interfaces/IRange.cs | 72 ++++ src/MoreDateTime/Interfaces/ITimeOnlyRange.cs | 25 -- src/MoreDateTime/TimeOnlyRange.cs | 65 +++- src/MoreDateTime/docs | 362 ++++++++++++------ tests/MoreDateTime.Test/DateOnlyRangeTests.cs | 340 ++++++++++++++++ tests/MoreDateTime.Test/DateTimeRangeTests.cs | 262 ++++++++++++- .../DefaultHolidayProviderTests.cs | 98 +++++ .../DateOnlyExtensions.NextPrevTests.cs | 66 ++++ .../DateOnlyExtensions.TruncateTests.cs | 16 + .../DateTimeExtensions.NextPrevTests.cs | 68 +++- .../Extensions/DateTimeExtensionsTests.cs | 11 +- .../TimeOnlyExtensions.IsEqualTests.cs | 68 ++++ .../Extensions/TimeOnlyExtensions.IsTests.cs | 35 ++ .../TimeOnlyExtensions.TruncateTests.cs | 16 + .../Extensions/TimeOnlyExtensionsTests.cs | 2 + .../NagerHolidayProviderTests.cs | 98 +++++ .../NullHolidayProviderTests.cs | 99 +++++ tests/MoreDateTime.Test/TimeOnlyRangeTests.cs | 273 +++++++++++++ 32 files changed, 2156 insertions(+), 357 deletions(-) rename src/MoreDateTime/Extensions/{DateTimeExtensions.Every.cs => DateTimeExtensions.Sets.cs} (58%) delete mode 100644 src/MoreDateTime/Interfaces/IDateOnlyRange.cs delete mode 100644 src/MoreDateTime/Interfaces/IDateTimeRange.cs create mode 100644 src/MoreDateTime/Interfaces/IRange.cs delete mode 100644 src/MoreDateTime/Interfaces/ITimeOnlyRange.cs create mode 100644 tests/MoreDateTime.Test/DateOnlyRangeTests.cs create mode 100644 tests/MoreDateTime.Test/DefaultHolidayProviderTests.cs create mode 100644 tests/MoreDateTime.Test/NagerHolidayProviderTests.cs create mode 100644 tests/MoreDateTime.Test/NullHolidayProviderTests.cs create mode 100644 tests/MoreDateTime.Test/TimeOnlyRangeTests.cs diff --git a/README.md b/README.md index 80ca792..f46a1e0 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ This library is built from the ground up to be a simple, easy to use, and intuitive date and time library for .NET, that simplifies common tasks and provides a consistent API for working with dates and times, throughout the DateTime, DateOnly and TimeOnly .NET objects. It adds many operations that one expects to find in a date and time library, but are missing from the .NET standard library. +It is however not intended to be a replacement for the standard .NET library, but rather an extension to it. It is built upon the existing .NET standard library and does not replace any of the existing functionality. It is also not intended to be a replacement for the [NodaTime](https://nodatime.org/) library, but it can be used in conjunction. + For example: `NextWeek()` to advance a DateTime or DateOnly to the same weekday in the next week, `NextYear()` to add a year to a DateTime or DateOlnly object (yes, of course you can use the existing `.AddYears(1)` method, but it does not look as clear), `NextWorkday()` to advance the DateTime or DateOnly object to the next working day on the given `Calendar`. Or things like `IsWeekend()` which of course you can also do with `myDate.DayOfWeek == DayOfWeek.Saturday || myDate.DayOfWeek == DayOfWeek.Sunday`, but it just aint as nice to read. Then we also have `IsWorkday()` which is the opposite of `IsWeekend()` and also `IsHoliday()` which checks if the given date is a holiday on the given calendar. diff --git a/src/MoreDateTime/DateOnlyRange.cs b/src/MoreDateTime/DateOnlyRange.cs index e47850f..223b61b 100644 --- a/src/MoreDateTime/DateOnlyRange.cs +++ b/src/MoreDateTime/DateOnlyRange.cs @@ -4,9 +4,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="IDateOnlyRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class DateOnlyRange : IDateOnlyRange + public class DateOnlyRange : IRange<DateOnly, DateOnlyRange> { /// <summary> /// Initializes a new instance of the <see cref="DateOnlyRange"/> class. @@ -19,12 +19,104 @@ public DateOnlyRange(DateOnly startTime, DateOnly endTime) End = endTime; } - /// <inheritdoc/> + /// <summary> + /// Initializes a new instance of the <see cref="DateOnlyRange"/> class. + /// </summary> + /// <param name="startTime">The start time.</param> + /// <param name="endTime">The end time.</param> + public DateOnlyRange(DateTime startTime, DateTime endTime) + { + Start = startTime.ToDateOnly(); + End = endTime.ToDateOnly(); + } + + /// <summary> + /// Initializes a copied new instance of the <see cref="DateOnlyRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public DateOnlyRange(DateOnlyRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Initializes a copied new instance of the <see cref="DateTimeRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public DateOnlyRange(DateTimeRange range) + { + Start = range.Start.ToDateOnly(); + End = range.End.ToDateOnly(); + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public DateOnlyRange Offset(TimeSpan timeSpan) + { + return new DateOnlyRange(this.Start.Add(timeSpan), this.End.Add(timeSpan)); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateOnlyRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + // DateOnly operates only on timeSpans greater than 1 day + if(timeSpan.Days < 1) + { + return new(this); + } + + return direction switch + { + RangeDirection.Both => new DateOnlyRange(this.Start.Sub(timeSpan / 2), this.End.Add(timeSpan / 2)), + RangeDirection.Start => new DateOnlyRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new DateOnlyRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateOnlyRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + // DateOnly operates only on timeSpans greater than 1 day + if (timeSpan.Days < 1) + { + return new(this); + } + + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new DateOnlyRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new DateOnlyRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new DateOnlyRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public DateOnly Start { get; set; } diff --git a/src/MoreDateTime/DateTimeRange.cs b/src/MoreDateTime/DateTimeRange.cs index 0344104..79e3928 100644 --- a/src/MoreDateTime/DateTimeRange.cs +++ b/src/MoreDateTime/DateTimeRange.cs @@ -6,9 +6,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="IDateTimeRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class DateTimeRange : IDateTimeRange + public class DateTimeRange : IRange<DateTime, DateTimeRange> { /// <summary> /// Initializes a new instance of the <see cref="DateTimeRange"/> class. @@ -32,12 +32,72 @@ public DateTimeRange(DateOnly startTime, DateOnly endTime) End = endTime.ToDateTime(); // Should we include this day until midnight - 1 millisecond? } - /// <inheritdoc/> + /// <summary> + /// Initializes a copied new instance of the <see cref="DateTimeRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public DateTimeRange(DateTimeRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public DateTimeRange Offset(TimeSpan timeSpan) + { + return new DateTimeRange(this.Start + timeSpan, this.End + timeSpan); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateTimeRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + return direction switch + { + + RangeDirection.Both => new DateTimeRange(this.Start.Sub(timeSpan / 2), this.End.Add( timeSpan / 2)), + RangeDirection.Start => new DateTimeRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new DateTimeRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public DateTimeRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new DateTimeRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new DateTimeRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new DateTimeRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public DateTime Start { get; set; } diff --git a/src/MoreDateTime/Extensions/DateOnlyExtensions.NextPrev.cs b/src/MoreDateTime/Extensions/DateOnlyExtensions.NextPrev.cs index 24c9fd2..1aa78ce 100644 --- a/src/MoreDateTime/Extensions/DateOnlyExtensions.NextPrev.cs +++ b/src/MoreDateTime/Extensions/DateOnlyExtensions.NextPrev.cs @@ -243,5 +243,6 @@ public static DateOnly PreviousHoliday(this DateOnly dt, CultureInfo? cultureInf return nextWorkingDay; } + } } \ No newline at end of file diff --git a/src/MoreDateTime/Extensions/DateOnlyExtensions.Truncate.cs b/src/MoreDateTime/Extensions/DateOnlyExtensions.Truncate.cs index ca2cc89..2068878 100644 --- a/src/MoreDateTime/Extensions/DateOnlyExtensions.Truncate.cs +++ b/src/MoreDateTime/Extensions/DateOnlyExtensions.Truncate.cs @@ -18,9 +18,12 @@ public static partial class DateOnlyExtensions /// </summary> /// <param name="dt">The DateTime object</param> /// <param name="TruncateTo">The precision to truncate to</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null to use current culture</param> /// <returns>The Truncated dateTime object</returns> - public static DateOnly TruncateTo(this DateOnly dt, DateTruncate TruncateTo) + public static DateOnly TruncateTo(this DateOnly dt, DateTruncate TruncateTo, CultureInfo? cultureInfo = null) { + cultureInfo ??= CultureInfo.CurrentCulture; + return TruncateTo switch { DateTruncate.Year => new DateOnly(dt.Year, 1, 1), @@ -29,9 +32,9 @@ public static DateOnly TruncateTo(this DateOnly dt, DateTruncate TruncateTo) _ => new DateOnly(dt.Year, dt.Month, dt.Day) }; - static DateOnly CalcDayOfWeek(DateOnly dt) + DateOnly CalcDayOfWeek(DateOnly dt) { - while (dt.DayOfWeek != CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek) + while (dt.DayOfWeek != cultureInfo.DateTimeFormat.FirstDayOfWeek) { dt = dt.AddDays(-1); } @@ -58,7 +61,7 @@ public static DateOnly TruncateToMonth(this DateOnly dt) /// <returns>The Truncated DateOnly object</returns> public static DateOnly TruncateToWeek(this DateOnly dt, CultureInfo? cultureInfo = null) { - return dt.TruncateTo(DateTruncate.Week); + return dt.TruncateTo(DateTruncate.Week, cultureInfo); } /// <summary> diff --git a/src/MoreDateTime/Extensions/DateOnlyExtensions.cs b/src/MoreDateTime/Extensions/DateOnlyExtensions.cs index 2732393..5343d46 100644 --- a/src/MoreDateTime/Extensions/DateOnlyExtensions.cs +++ b/src/MoreDateTime/Extensions/DateOnlyExtensions.cs @@ -70,9 +70,9 @@ public static DateOnly StartOfMonth(this DateOnly me) } /// <summary> - /// Returns a DateTime object representing the first day of the current month + /// Returns a DateTime object representing the last day of the current month /// </summary> - /// <param name="me">The DateTime value of which the first day is requested</param> + /// <param name="me">The DateTime value of which the last day is requested</param> /// <returns>A DateTime object with the last day of the month, time members set to 0 (00:00:00)</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateOnly EndOfMonth(this DateOnly me) @@ -80,6 +80,28 @@ public static DateOnly EndOfMonth(this DateOnly me) return me.NextMonth().TruncateToMonth().PreviousDay(); } + /// <summary> + /// Returns a DateTime object representing the first weekday of the given Week + /// </summary> + /// <param name="me">The DateTime value of which the first day is requested</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + /// <returns>A DateTime object with first day of the week</returns> + public static DateOnly StartOfWeek(this DateOnly me, CultureInfo? cultureInfo = null) + { + return me.TruncateToWeek(cultureInfo); + } + + /// <summary> + /// Returns a DateTime object representing the last day of the current Week + /// </summary> + /// <param name="me">The DateTime value of which the first day is requested</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + /// <returns>A DateTime object with the last day of the Week, time members set to 0 (00:00:00)</returns> + public static DateOnly EndOfWeek(this DateOnly me, CultureInfo? cultureInfo = null) + { + return me.NextWeek().TruncateToWeek(cultureInfo).PreviousDay(); + } + /// <summary> /// Returns the <see cref="DateTime"/> as <see cref="DateOnly"/> /// </summary> diff --git a/src/MoreDateTime/Extensions/DateTimeExtensions.Enumerate.cs b/src/MoreDateTime/Extensions/DateTimeExtensions.Enumerate.cs index 56ff97b..ff7e65d 100644 --- a/src/MoreDateTime/Extensions/DateTimeExtensions.Enumerate.cs +++ b/src/MoreDateTime/Extensions/DateTimeExtensions.Enumerate.cs @@ -176,7 +176,7 @@ public static IEnumerable<DateTime> EnumerateHolidaysUntil(this DateTime from, D { for (var day = from.Date.PreviousHoliday(cultureInfo); day.Date >= to.Date; day = day.PreviousHoliday(cultureInfo)) { - if (day == DateTime.MaxValue) + if (day == DateTime.MinValue) break; yield return day; diff --git a/src/MoreDateTime/Extensions/DateTimeExtensions.NextPrev.cs b/src/MoreDateTime/Extensions/DateTimeExtensions.NextPrev.cs index 53c96c7..1ddb770 100644 --- a/src/MoreDateTime/Extensions/DateTimeExtensions.NextPrev.cs +++ b/src/MoreDateTime/Extensions/DateTimeExtensions.NextPrev.cs @@ -6,43 +6,44 @@ namespace MoreDateTime.Extensions public static partial class DateTimeExtensions { /// <summary> - /// Gets the DateTime value of the next millisecond + /// Returns a DateTime object representing the last day of the current Week /// </summary> - /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime NextMillisecond(this DateTime dt) + /// <param name="me">The DateTime value of which the first day is requested</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + /// <returns>A DateTime object with the last day of the Week, time members set to 0 (00:00:00)</returns> + public static DateTime EndOfWeek(this DateTime me, CultureInfo? cultureInfo = null) { - return dt.AddMilliseconds(1); + return me.NextWeek().TruncateToWeek(cultureInfo).PreviousDay(); } /// <summary> - /// Gets the DateTime value of the next second + /// Gets the DateTime value of the next day /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextSecond(this DateTime dt) + public static DateTime NextDay(this DateTime dt) { - return dt.AddSeconds(1); + return dt.AddDays(1); } /// <summary> - /// Gets the DateTime value of the next full second (10:15:20.350 to 10:15:21.000) + /// Gets the DateTime value of the next full day (01/01/2010 10:15 to 02/01/2010 00:00, 01/01/2010 10:45 to 02/01/2010 00:00, etc) /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextFullSecond(this DateTime dt) + public static DateTime NextFullDay(this DateTime dt) { - return dt.AddSeconds(1).TruncateToSecond(); + return dt.AddDays(1).TruncateToDay(); } /// <summary> - /// Gets the DateTime value of the next minute + /// Gets the DateTime value of the next full hour (10:15 to 11:00, 10:45 to 11:00, etc) /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextMinute(this DateTime dt) + public static DateTime NextFullHour(this DateTime dt) { - return dt.AddMinutes(1); + return dt.AddHours(1).TruncateToHour(); } /// <summary> @@ -55,6 +56,48 @@ public static DateTime NextFullMinute(this DateTime dt) return dt.AddMinutes(1).TruncateToMinute(); } + /// <summary> + /// Gets the DateTime value of the next full second (10:15:20.350 to 10:15:21.000) + /// </summary> + /// <param name="dt">The DateTime object</param> + /// <returns>A DateTime object</returns> + public static DateTime NextFullSecond(this DateTime dt) + { + return dt.AddSeconds(1).TruncateToSecond(); + } + + /// <summary> + /// Gets the DateTime value of the next public holiday according to the given CultureInfo.Calendar<br/> + /// If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, + /// or if you set a custom holiday provider, it will use that. Otherwise the default implementation will return DateTime.MaxValue + /// </summary> + /// <param name="dt">The DateTime object</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + /// <returns>A DateTime object</returns> + public static DateTime NextHoliday(this DateTime dt, CultureInfo? cultureInfo = null) + { + cultureInfo ??= CultureInfo.CurrentCulture; + + DateTime nextWorkingDay = dt; + + // Loop until we find a weekday that is not a holiday or weekend + nextWorkingDay = nextWorkingDay.NextDay(); + + // Note: lets assume that after 2050 this library will no longer be in use + // and that no dates beyond 2050 will be calculated using holidays + while (!CurrentHolidayProvider.IsPublicHoliday(nextWorkingDay, cultureInfo)) + { + if (nextWorkingDay.Year >= 2050) + { + return DateTime.MaxValue; + } + + nextWorkingDay = nextWorkingDay.NextDay(); + } + + return nextWorkingDay; + } + /// <summary> /// Gets the DateTime value of the next hour /// </summary> @@ -66,35 +109,44 @@ public static DateTime NextHour(this DateTime dt) } /// <summary> - /// Gets the DateTime value of the next full hour (10:15 to 11:00, 10:45 to 11:00, etc) + /// Gets the DateTime value of the next millisecond /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextFullHour(this DateTime dt) + public static DateTime NextMillisecond(this DateTime dt) { - return dt.AddHours(1).TruncateToHour(); + return dt.AddMilliseconds(1); } /// <summary> - /// Gets the DateTime value of the next day + /// Gets the DateTime value of the next minute /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextDay(this DateTime dt) + public static DateTime NextMinute(this DateTime dt) { - return dt.AddDays(1); + return dt.AddMinutes(1); } /// <summary> - /// Gets the DateTime value of the next full day (01/01/2010 10:15 to 02/01/2010 00:00, 01/01/2010 10:45 to 02/01/2010 00:00, etc) + /// Gets the DateTime value of the next month /// </summary> /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime NextFullDay(this DateTime dt) + /// <returns>A DateTime object whose value is the same day the next month of the given. If the month has less days then the day may change (e.g. 31 Jan to 28 Feb)</returns> + public static DateTime NextMonth(this DateTime dt) { - return dt.AddDays(1).TruncateToDay(); + return dt.AddMonths(1); } + /// <summary> + /// Gets the DateTime value of the next second + /// </summary> + /// <param name="dt">The DateTime object</param> + /// <returns>A DateTime object</returns> + public static DateTime NextSecond(this DateTime dt) + { + return dt.AddSeconds(1); + } /// <summary> /// Gets the DateTime value of the same (week)day next week /// </summary> @@ -150,57 +202,66 @@ public static DateTime NextWorkday(this DateTime dt, CultureInfo? cultureInfo = return nextWorkingDay; } + /// <summary> + /// Gets the DateTime value of the next year + /// </summary> + /// <param name="dt">The DateTime object</param> + /// <returns>A DateTime object</returns> + public static DateTime NextYear(this DateTime dt) + { + return dt.AddYears(1); + } /// <summary> - /// Gets the DateTime value of the next public holiday according to the given CultureInfo.Calendar<br/> - /// If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, - /// or if you set a custom holiday provider, it will use that. Otherwise the default implementation will return DateTime.MaxValue + /// Gets the DateTime value of the year before + /// </summary> + /// <param name="dt">The DateTime object</param> + /// <returns>A DateTime object</returns> + public static DateTime PreviousDay(this DateTime dt) + { + return dt.AddDays(-1); + } + + /// <summary> + /// Gets the DateTime value of the previous holiday<br/> + /// If you have a license for the Nager nuget package, this method will calculate the holidays based on the calendar in cultureInfo, + /// otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. /// </summary> /// <param name="dt">The DateTime object</param> /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> /// <returns>A DateTime object</returns> - public static DateTime NextHoliday(this DateTime dt, CultureInfo? cultureInfo = null) + public static DateTime PreviousHoliday(this DateTime dt, CultureInfo? cultureInfo = null) { cultureInfo ??= CultureInfo.CurrentCulture; DateTime nextWorkingDay = dt; // Loop until we find a weekday that is not a holiday or weekend - nextWorkingDay = nextWorkingDay.NextDay(); + nextWorkingDay = nextWorkingDay.PreviousDay(); // Note: lets assume that after 2050 this library will no longer be in use // and that no dates beyond 2050 will be calculated using holidays while (!CurrentHolidayProvider.IsPublicHoliday(nextWorkingDay, cultureInfo)) { - if (nextWorkingDay.Year >= 2050) + if (nextWorkingDay.Year <= DateTime.MinValue.Year) { return DateTime.MaxValue; } - nextWorkingDay = nextWorkingDay.NextDay(); + nextWorkingDay = nextWorkingDay.PreviousDay(); } return nextWorkingDay; } /// <summary> - /// Gets the DateTime value of the next month - /// </summary> - /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object whose value is the same day the next month of the given. If the month has less days then the day may change (e.g. 31 Jan to 28 Feb)</returns> - public static DateTime NextMonth(this DateTime dt) - { - return dt.AddMonths(1); - } - - /// <summary> - /// Gets the DateTime value of the next year + /// Gets the DateTime value of the previous hour /// </summary> /// <param name="dt">The DateTime object</param> /// <returns>A DateTime object</returns> - public static DateTime NextYear(this DateTime dt) + public static DateTime PreviousHour(this DateTime dt) { - return dt.AddYears(1); + return dt.AddHours(-1); } /// <summary> @@ -213,16 +274,6 @@ public static DateTime PreviousMillisecond(this DateTime dt) return dt.AddMilliseconds(-1); } - /// <summary> - /// Gets the DateTime value of the previous second - /// </summary> - /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime PreviousSecond(this DateTime dt) - { - return dt.AddSeconds(-1); - } - /// <summary> /// Gets the DateTime value of the previous minute /// </summary> @@ -234,50 +285,24 @@ public static DateTime PreviousMinute(this DateTime dt) } /// <summary> - /// Gets the DateTime value of the previous hour - /// </summary> - /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime PreviousHour(this DateTime dt) - { - return dt.AddHours(-1); - } - - /// <summary> - /// Gets the DateTime value of the year before + /// Gets the DateTime value of the month before /// </summary> /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime PreviousDay(this DateTime dt) + /// <returns>A DateTime object whose value is the same day the month before the given. If the month has less days then the day may change (e.g. 31 March to 28 Feb)</returns> + public static DateTime PreviousMonth(this DateTime dt) { - return dt.AddDays(-1); + return dt.AddMonths(-1); } /// <summary> - /// Gets the DateTime value of the previous working day according to the given CultureInfo.Calendar<br/> - /// If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, - /// otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. + /// Gets the DateTime value of the previous second /// </summary> /// <param name="dt">The DateTime object</param> - /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> /// <returns>A DateTime object</returns> - public static DateTime PreviousWorkday(this DateTime dt, CultureInfo? cultureInfo = null) + public static DateTime PreviousSecond(this DateTime dt) { - cultureInfo ??= CultureInfo.CurrentCulture; - - DateTime nextWorkingDay = dt; - - nextWorkingDay = nextWorkingDay.PreviousDay(); - - // Loop until we find a weekday that is not a holiday or weekend - while (CurrentHolidayProvider.IsPublicHoliday(nextWorkingDay, cultureInfo) || nextWorkingDay.IsWeekend()) - { - nextWorkingDay = nextWorkingDay.PreviousDay(); - } - - return nextWorkingDay; + return dt.AddSeconds(-1); } - /// <summary> /// Gets the DateOnly value of the same day the week before /// </summary> @@ -309,55 +334,48 @@ public static DateTime PreviousWeekend(this DateTime dt) } /// <summary> - /// Gets the DateTime value of the previous holiday<br/> - /// If you have a license for the Nager nuget package, this method will calculate the holidays based on the calendar in cultureInfo, + /// Gets the DateTime value of the previous working day according to the given CultureInfo.Calendar<br/> + /// If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, /// otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. /// </summary> /// <param name="dt">The DateTime object</param> /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> /// <returns>A DateTime object</returns> - public static DateTime PreviousHoliday(this DateTime dt, CultureInfo? cultureInfo = null) + public static DateTime PreviousWorkday(this DateTime dt, CultureInfo? cultureInfo = null) { cultureInfo ??= CultureInfo.CurrentCulture; DateTime nextWorkingDay = dt; - // Loop until we find a weekday that is not a holiday or weekend nextWorkingDay = nextWorkingDay.PreviousDay(); - // Note: lets assume that after 2050 this library will no longer be in use - // and that no dates beyond 2050 will be calculated using holidays - while (!CurrentHolidayProvider.IsPublicHoliday(nextWorkingDay, cultureInfo)) + // Loop until we find a weekday that is not a holiday or weekend + while (CurrentHolidayProvider.IsPublicHoliday(nextWorkingDay, cultureInfo) || nextWorkingDay.IsWeekend()) { - if (nextWorkingDay.Year <= DateTime.MinValue.Year) - { - return DateTime.MaxValue; - } - nextWorkingDay = nextWorkingDay.PreviousDay(); } return nextWorkingDay; } - /// <summary> - /// Gets the DateTime value of the month before + /// Gets the DateTime value of the previous year /// </summary> /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object whose value is the same day the month before the given. If the month has less days then the day may change (e.g. 31 March to 28 Feb)</returns> - public static DateTime PreviousMonth(this DateTime dt) + /// <returns>A DateTime object</returns> + public static DateTime PreviousYear(this DateTime dt) { - return dt.AddMonths(-1); + return dt.AddYears(-1); } /// <summary> - /// Gets the DateTime value of the previous year + /// Returns a DateTime object representing the first weekday of the given Week /// </summary> - /// <param name="dt">The DateTime object</param> - /// <returns>A DateTime object</returns> - public static DateTime PreviousYear(this DateTime dt) + /// <param name="me">The DateTime value of which the first day is requested</param> + /// <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + /// <returns>A DateTime object with first day of the week</returns> + public static DateTime StartOfWeek(this DateTime me, CultureInfo? cultureInfo = null) { - return dt.AddYears(-1); + return me.TruncateToWeek(cultureInfo); } } } \ No newline at end of file diff --git a/src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs b/src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs similarity index 58% rename from src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs rename to src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs index e5f405c..a902a96 100644 --- a/src/MoreDateTime/Extensions/DateTimeExtensions.Every.cs +++ b/src/MoreDateTime/Extensions/DateTimeExtensions.Sets.cs @@ -15,12 +15,15 @@ namespace MoreDateTime.Extensions /// </summary> public static partial class DateTimeExtensions { - // Todo: EveryX - - /* - public static IEnumerable<DateTime> EveryNDays(this DateTime from, DateTime to) + public static DateTimeRange Union(this DateTimeRange a, DateTimeRange b) { + DateTimeRange ab = new(a); + + // calculate the union of the two ranges + ab.Start = a.Start < b.Start ? a.Start : b.Start; + ab.End = a.End > b.End ? a.End : b.End; + + return ab; } - */ } } diff --git a/src/MoreDateTime/Extensions/TimeOnlyExtensions.Is.cs b/src/MoreDateTime/Extensions/TimeOnlyExtensions.Is.cs index c5b2793..1cdc656 100644 --- a/src/MoreDateTime/Extensions/TimeOnlyExtensions.Is.cs +++ b/src/MoreDateTime/Extensions/TimeOnlyExtensions.Is.cs @@ -106,12 +106,7 @@ public static bool IsBetween(this TimeOnly me, TimeOnly startTime, TimeOnly endT /// <returns>True if the value is greater or equal startTime and less than or equal endTime</returns> public static bool IsBetween(this DateTime me, TimeOnly startTime, TimeOnly endTime) { - if (endTime < startTime) - { - throw new ArgumentException("endTime must be greater than startTime"); - } - - return (me.ToTimeOnly() > startTime) && (me.ToTimeOnly() < endTime); + return me.ToTimeOnly().IsBetween(startTime, endTime); } /// <summary> @@ -124,11 +119,9 @@ public static bool IsBetween(this DateTime me, TimeOnly startTime, TimeOnly endT public static bool IsWithin(this TimeOnly me, TimeOnly startTime, TimeOnly endTime) { if (endTime < startTime) - { - throw new ArgumentException("endTime must be greater than startTime"); - } - - return (me >= startTime) && (me <= endTime); + return (me <= startTime) || (me >= endTime); + else + return (me >= startTime) && (me <= endTime); } /// <summary> @@ -140,12 +133,7 @@ public static bool IsWithin(this TimeOnly me, TimeOnly startTime, TimeOnly endTi /// <returns>True if the value is greater or equal startTime and less than or equal endTime</returns> public static bool IsWithin(this DateTime me, TimeOnly startTime, TimeOnly endTime) { - if (endTime < startTime) - { - throw new ArgumentException("endTime must be greater than startTime"); - } - - return (me.ToTimeOnly() >= startTime) && (me.ToTimeOnly() <= endTime); + return IsWithin(me.ToTimeOnly(), startTime, endTime); } } } \ No newline at end of file diff --git a/src/MoreDateTime/Extensions/TimeOnlyExtensions.IsEqual.cs b/src/MoreDateTime/Extensions/TimeOnlyExtensions.IsEqual.cs index 22f791a..3eeb88e 100644 --- a/src/MoreDateTime/Extensions/TimeOnlyExtensions.IsEqual.cs +++ b/src/MoreDateTime/Extensions/TimeOnlyExtensions.IsEqual.cs @@ -23,14 +23,10 @@ public static bool IsEqual(this TimeOnly dt, TimeOnly other, DateTruncate trunca return truncateTo switch { - DateTruncate.Year => true, - DateTruncate.Month => true, - DateTruncate.Week => true, - DateTruncate.Day => true, DateTruncate.Hour => dt.Hour == other.Hour, DateTruncate.Minute => dt.Hour == other.Hour && dt.Minute == other.Minute, DateTruncate.Second => dt.Hour == other.Hour && dt.Minute == other.Minute && dt.Second == other.Second, - _ => false, + _ => true, }; } diff --git a/src/MoreDateTime/Extensions/TimeOnlyExtensions.Truncate.cs b/src/MoreDateTime/Extensions/TimeOnlyExtensions.Truncate.cs index 7408faf..df7e520 100644 --- a/src/MoreDateTime/Extensions/TimeOnlyExtensions.Truncate.cs +++ b/src/MoreDateTime/Extensions/TimeOnlyExtensions.Truncate.cs @@ -22,7 +22,7 @@ public static TimeOnly TruncateTo(this TimeOnly dt, DateTruncate TruncateTo) DateTruncate.Hour => new TimeOnly(dt.Hour, 0, 0), DateTruncate.Minute => new TimeOnly(dt.Hour, dt.Minute, 0), DateTruncate.Second => new TimeOnly(dt.Hour, dt.Minute, dt.Second), - _ => new TimeOnly(dt.Hour, dt.Minute, dt.Second) + _ => new TimeOnly(dt.Hour, dt.Minute, dt.Second, dt.Millisecond) }; } diff --git a/src/MoreDateTime/Interfaces/IDateOnlyRange.cs b/src/MoreDateTime/Interfaces/IDateOnlyRange.cs deleted file mode 100644 index 3d8522f..0000000 --- a/src/MoreDateTime/Interfaces/IDateOnlyRange.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface IDateOnlyRange - { - /// <summary> - /// Gets or sets the start date - /// </summary> - DateOnly Start { get; set; } - - /// <summary> - /// Gets or sets the end date - /// </summary> - DateOnly End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/Interfaces/IDateTimeRange.cs b/src/MoreDateTime/Interfaces/IDateTimeRange.cs deleted file mode 100644 index 81f800e..0000000 --- a/src/MoreDateTime/Interfaces/IDateTimeRange.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface IDateTimeRange - { - /// <summary> - /// Gets or sets the start date - /// </summary> - DateTime Start { get; set; } - - /// <summary> - /// Gets or sets the end date - /// </summary> - DateTime End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/Interfaces/IRange.cs b/src/MoreDateTime/Interfaces/IRange.cs new file mode 100644 index 0000000..bd46742 --- /dev/null +++ b/src/MoreDateTime/Interfaces/IRange.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MoreDateTime.Interfaces +{ + /// <summary> + /// An interface for ranges of dates and times + /// </summary> + /// <typeparam name="T">The base type to handle</typeparam> + /// <typeparam name="Treturn">A self reference as a return type</typeparam> + internal interface IRange<T, Treturn> where T : IComparable<T> + { + /// <summary> + /// Gets or sets the start <typeparamref name="T"/> + /// </summary> + T Start { get; set; } + + /// <summary> + /// Gets or sets the end <typeparamref name="T"/> + /// </summary> + T End { get; set; } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> + public TimeSpan Distance(); + + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public Treturn Offset(TimeSpan timeSpan); + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public Treturn Extend(TimeSpan timeSpan, RangeDirection direction); + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public Treturn Reduce(TimeSpan timeSpan, RangeDirection direction); + } + + /// <summary> + /// The range direction enum, indicates in which direction to move the ranges values + /// </summary> + public enum RangeDirection + { + /// <summary> + /// Move start and end + /// </summary> + Both, + + /// <summary> + /// Move start only + /// </summary> + Start, + + /// <summary> + /// Move end only + /// </summary> + End + } +} diff --git a/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs b/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs deleted file mode 100644 index e594d64..0000000 --- a/src/MoreDateTime/Interfaces/ITimeOnlyRange.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MoreDateTime.Interfaces -{ - /// <summary> - /// A Date/Time range provider, with an start and an end date - /// </summary> - public interface ITimeOnlyRange - { - /// <summary> - /// Gets or sets the start time - /// </summary> - TimeOnly Start { get; set; } - - /// <summary> - /// Gets or sets the end time - /// </summary> - TimeOnly End { get; set; } - - /// <summary> - /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> - /// </summary> - public TimeSpan Distance(); - - } - -} diff --git a/src/MoreDateTime/TimeOnlyRange.cs b/src/MoreDateTime/TimeOnlyRange.cs index 1cb1ea5..05ca310 100644 --- a/src/MoreDateTime/TimeOnlyRange.cs +++ b/src/MoreDateTime/TimeOnlyRange.cs @@ -4,9 +4,9 @@ namespace MoreDateTime { /// <summary> - /// Implements the <see cref="ITimeOnlyRange"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members + /// Implements the <see cref="IRange{T, Treturn}"/> interface and provides a time range through its <see cref="Start"/> and <see cref="End"/> members /// </summary> - public class TimeOnlyRange : ITimeOnlyRange + public class TimeOnlyRange : IRange<TimeOnly, TimeOnlyRange> { /// <summary> /// Initializes a new instance of the <see cref="TimeOnlyRange"/> class. @@ -19,12 +19,71 @@ public TimeOnlyRange(TimeOnly startTime, TimeOnly endTime) End = endTime; } - /// <inheritdoc/> + /// <summary> + /// Initializes a copied new instance of the <see cref="TimeOnlyRange"/> class. + /// </summary> + /// <param name="range">The range to copy</param> + public TimeOnlyRange(TimeOnlyRange range) + { + Start = range.Start; + End = range.End; + } + + /// <summary> + /// Gets the distance between the <see cref="Start"/> and the <see cref="End"/> + /// </summary> public TimeSpan Distance() { return this.Start.Distance(this.End); } + /// <summary> + /// Offsets the <see cref="Start"/> and <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + public TimeOnlyRange Offset(TimeSpan timeSpan) + { + return new TimeOnlyRange(this.Start.Add(timeSpan), this.End.Add(timeSpan)); + } + + /// <summary> + /// Extends the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public TimeOnlyRange Extend(TimeSpan timeSpan, RangeDirection direction) + { + return direction switch + { + RangeDirection.Both => new TimeOnlyRange(this.Start.Sub(timeSpan / 2), this.End.Add(timeSpan / 2)), + RangeDirection.Start => new TimeOnlyRange(this.Start.Sub(timeSpan), this.End), + RangeDirection.End => new TimeOnlyRange(this.Start, this.End.Add(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + + /// <summary> + /// Reduces the <see cref="Start"/> and/or <see cref="End"/> by the specified <paramref name="timeSpan"/> + /// </summary> + /// <param name="timeSpan">The time span.</param> + /// <param name="direction">The direction in which to extend</param> + public TimeOnlyRange Reduce(TimeSpan timeSpan, RangeDirection direction) + { + var distance = this.Start.Distance(this.End); + if (distance <= timeSpan) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan, "The timeSpan is too large to reduce the range"); + } + + return direction switch + { + RangeDirection.Both => new TimeOnlyRange(this.Start.Add(timeSpan / 2), this.End.Sub(timeSpan / 2)), + RangeDirection.Start => new TimeOnlyRange(this.Start.Add(timeSpan), this.End), + RangeDirection.End => new TimeOnlyRange(this.Start, this.End.Sub(timeSpan)), + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } + /// <inheritdoc/> public TimeOnly Start { get; set; } diff --git a/src/MoreDateTime/docs b/src/MoreDateTime/docs index 681d1ba..5c229c3 100644 --- a/src/MoreDateTime/docs +++ b/src/MoreDateTime/docs @@ -6,7 +6,7 @@ <members> <member name="T:MoreDateTime.DateOnlyRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.IDateOnlyRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> members </summary> </member> <member name="M:MoreDateTime.DateOnlyRange.#ctor(System.DateOnly,System.DateOnly)"> @@ -16,8 +16,49 @@ <param name="startTime">The start DateOnly</param> <param name="endTime">The end DateOnly</param> </member> + <member name="M:MoreDateTime.DateOnlyRange.#ctor(System.DateTime,System.DateTime)"> + <summary> + Initializes a new instance of the <see cref="T:MoreDateTime.DateOnlyRange"/> class. + </summary> + <param name="startTime">The start time.</param> + <param name="endTime">The end time.</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.#ctor(MoreDateTime.DateOnlyRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.DateOnlyRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.#ctor(MoreDateTime.DateTimeRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.DateTimeRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.DateOnlyRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and the <see cref="P:MoreDateTime.DateOnlyRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.DateOnlyRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.DateOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.DateOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.DateOnlyRange.Start"> <inheritdoc/> @@ -69,7 +110,7 @@ </member> <member name="T:MoreDateTime.DateTimeRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.IDateTimeRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> members </summary> </member> <member name="M:MoreDateTime.DateTimeRange.#ctor(System.DateTime,System.DateTime)"> @@ -86,8 +127,36 @@ <param name="startTime">The start datetime</param> <param name="endTime">The end datetime</param> </member> + <member name="M:MoreDateTime.DateTimeRange.#ctor(MoreDateTime.DateTimeRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.DateTimeRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.DateTimeRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and the <see cref="P:MoreDateTime.DateTimeRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.DateTimeRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.DateTimeRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and/or <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.DateTimeRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.DateTimeRange.Start"/> and/or <see cref="P:MoreDateTime.DateTimeRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.DateTimeRange.Start"> <inheritdoc/> @@ -177,11 +246,27 @@ </member> <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.EndOfMonth(System.DateOnly)"> <summary> - Returns a DateTime object representing the first day of the current month + Returns a DateTime object representing the last day of the current month </summary> - <param name="me">The DateTime value of which the first day is requested</param> + <param name="me">The DateTime value of which the last day is requested</param> <returns>A DateTime object with the last day of the month, time members set to 0 (00:00:00)</returns> </member> + <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.StartOfWeek(System.DateOnly,System.Globalization.CultureInfo)"> + <summary> + Returns a DateTime object representing the first weekday of the given Week + </summary> + <param name="me">The DateTime value of which the first day is requested</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + <returns>A DateTime object with first day of the week</returns> + </member> + <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.EndOfWeek(System.DateOnly,System.Globalization.CultureInfo)"> + <summary> + Returns a DateTime object representing the last day of the current Week + </summary> + <param name="me">The DateTime value of which the first day is requested</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + <returns>A DateTime object with the last day of the Week, time members set to 0 (00:00:00)</returns> + </member> <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.ToDateOnly(System.DateTime)"> <summary> Returns the <see cref="T:System.DateTime"/> as <see cref="T:System.DateOnly"/> @@ -893,12 +978,13 @@ <param name="cultureInfo">The culture info for workdays and holidays, can be null to use current</param> <returns>An int.</returns> </member> - <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.TruncateTo(System.DateOnly,MoreDateTime.Extensions.DateTimeExtensions.DateTruncate)"> + <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.TruncateTo(System.DateOnly,MoreDateTime.Extensions.DateTimeExtensions.DateTruncate,System.Globalization.CultureInfo)"> <summary> Truncates the precision of a DateTime object to the given precision </summary> <param name="dt">The DateTime object</param> <param name="TruncateTo">The precision to truncate to</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null to use current culture</param> <returns>The Truncated dateTime object</returns> </member> <member name="M:MoreDateTime.Extensions.DateOnlyExtensions.TruncateToMonth(System.DateOnly)"> @@ -928,15 +1014,15 @@ DateTime related extension methods </summary> <inheritdoc/> - <summary> - DateTime related extension methods - </summary> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> <inheritdoc/> + <summary> + DateTime related extension methods + </summary> <inheritdoc/>> </member> <member name="T:MoreDateTime.Extensions.DateTimeExtensions.DateTruncate"> @@ -1536,30 +1622,31 @@ <param name="other">The DateTime argument to compare with</param> <returns>True if the dates are on the same year</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMillisecond(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.EndOfWeek(System.DateTime,System.Globalization.CultureInfo)"> <summary> - Gets the DateTime value of the next millisecond + Returns a DateTime object representing the last day of the current Week </summary> - <param name="dt">The DateTime object</param> - <returns>A DateTime object</returns> + <param name="me">The DateTime value of which the first day is requested</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + <returns>A DateTime object with the last day of the Week, time members set to 0 (00:00:00)</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextSecond(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextDay(System.DateTime)"> <summary> - Gets the DateTime value of the next second + Gets the DateTime value of the next day </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullSecond(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullDay(System.DateTime)"> <summary> - Gets the DateTime value of the next full second (10:15:20.350 to 10:15:21.000) + Gets the DateTime value of the next full day (01/01/2010 10:15 to 02/01/2010 00:00, 01/01/2010 10:45 to 02/01/2010 00:00, etc) </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMinute(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullHour(System.DateTime)"> <summary> - Gets the DateTime value of the next minute + Gets the DateTime value of the next full hour (10:15 to 11:00, 10:45 to 11:00, etc) </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> @@ -1571,6 +1658,23 @@ <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullSecond(System.DateTime)"> + <summary> + Gets the DateTime value of the next full second (10:15:20.350 to 10:15:21.000) + </summary> + <param name="dt">The DateTime object</param> + <returns>A DateTime object</returns> + </member> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextHoliday(System.DateTime,System.Globalization.CultureInfo)"> + <summary> + Gets the DateTime value of the next public holiday according to the given CultureInfo.Calendar<br/> + If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, + or if you set a custom holiday provider, it will use that. Otherwise the default implementation will return DateTime.MaxValue + </summary> + <param name="dt">The DateTime object</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + <returns>A DateTime object</returns> + </member> <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextHour(System.DateTime)"> <summary> Gets the DateTime value of the next hour @@ -1578,23 +1682,30 @@ <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullHour(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMillisecond(System.DateTime)"> <summary> - Gets the DateTime value of the next full hour (10:15 to 11:00, 10:45 to 11:00, etc) + Gets the DateTime value of the next millisecond </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextDay(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMinute(System.DateTime)"> <summary> - Gets the DateTime value of the next day + Gets the DateTime value of the next minute </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextFullDay(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMonth(System.DateTime)"> <summary> - Gets the DateTime value of the next full day (01/01/2010 10:15 to 02/01/2010 00:00, 01/01/2010 10:45 to 02/01/2010 00:00, etc) + Gets the DateTime value of the next month + </summary> + <param name="dt">The DateTime object</param> + <returns>A DateTime object whose value is the same day the next month of the given. If the month has less days then the day may change (e.g. 31 Jan to 28 Feb)</returns> + </member> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextSecond(System.DateTime)"> + <summary> + Gets the DateTime value of the next second </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> @@ -1623,40 +1734,40 @@ <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextHoliday(System.DateTime,System.Globalization.CultureInfo)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextYear(System.DateTime)"> <summary> - Gets the DateTime value of the next public holiday according to the given CultureInfo.Calendar<br/> - If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, - or if you set a custom holiday provider, it will use that. Otherwise the default implementation will return DateTime.MaxValue + Gets the DateTime value of the next year </summary> <param name="dt">The DateTime object</param> - <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextMonth(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousDay(System.DateTime)"> <summary> - Gets the DateTime value of the next month + Gets the DateTime value of the year before </summary> <param name="dt">The DateTime object</param> - <returns>A DateTime object whose value is the same day the next month of the given. If the month has less days then the day may change (e.g. 31 Jan to 28 Feb)</returns> + <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NextYear(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousHoliday(System.DateTime,System.Globalization.CultureInfo)"> <summary> - Gets the DateTime value of the next year + Gets the DateTime value of the previous holiday<br/> + If you have a license for the Nager nuget package, this method will calculate the holidays based on the calendar in cultureInfo, + otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. </summary> <param name="dt">The DateTime object</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousMillisecond(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousHour(System.DateTime)"> <summary> - Gets the DateTime value of the previous millisecond + Gets the DateTime value of the previous hour </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousSecond(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousMillisecond(System.DateTime)"> <summary> - Gets the DateTime value of the previous second + Gets the DateTime value of the previous millisecond </summary> <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> @@ -1668,28 +1779,18 @@ <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousHour(System.DateTime)"> - <summary> - Gets the DateTime value of the previous hour - </summary> - <param name="dt">The DateTime object</param> - <returns>A DateTime object</returns> - </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousDay(System.DateTime)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousMonth(System.DateTime)"> <summary> - Gets the DateTime value of the year before + Gets the DateTime value of the month before </summary> <param name="dt">The DateTime object</param> - <returns>A DateTime object</returns> + <returns>A DateTime object whose value is the same day the month before the given. If the month has less days then the day may change (e.g. 31 March to 28 Feb)</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousWorkday(System.DateTime,System.Globalization.CultureInfo)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousSecond(System.DateTime)"> <summary> - Gets the DateTime value of the previous working day according to the given CultureInfo.Calendar<br/> - If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, - otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. + Gets the DateTime value of the previous second </summary> <param name="dt">The DateTime object</param> - <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> <returns>A DateTime object</returns> </member> <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousWeek(System.DateTime)"> @@ -1706,23 +1807,16 @@ <param name="dt">The DateTime object</param> <returns>A DateTime object representing the previous weekend (always Sunday)</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousHoliday(System.DateTime,System.Globalization.CultureInfo)"> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousWorkday(System.DateTime,System.Globalization.CultureInfo)"> <summary> - Gets the DateTime value of the previous holiday<br/> - If you have a license for the Nager nuget package, this method will calculate the holidays based on the calendar in cultureInfo, + Gets the DateTime value of the previous working day according to the given CultureInfo.Calendar<br/> + If you have a license for the Nager nuget package, this method will calculate the working days based on the calendar in cultureInfo, otherwise it will simply skip weekends, or if you set a custom holiday provider, it will use that. </summary> <param name="dt">The DateTime object</param> <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> <returns>A DateTime object</returns> </member> - <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousMonth(System.DateTime)"> - <summary> - Gets the DateTime value of the month before - </summary> - <param name="dt">The DateTime object</param> - <returns>A DateTime object whose value is the same day the month before the given. If the month has less days then the day may change (e.g. 31 March to 28 Feb)</returns> - </member> <member name="M:MoreDateTime.Extensions.DateTimeExtensions.PreviousYear(System.DateTime)"> <summary> Gets the DateTime value of the previous year @@ -1730,6 +1824,14 @@ <param name="dt">The DateTime object</param> <returns>A DateTime object</returns> </member> + <member name="M:MoreDateTime.Extensions.DateTimeExtensions.StartOfWeek(System.DateTime,System.Globalization.CultureInfo)"> + <summary> + Returns a DateTime object representing the first weekday of the given Week + </summary> + <param name="me">The DateTime value of which the first day is requested</param> + <param name="cultureInfo">The CultureInfo to use for week calculation, can be null for current culture</param> + <returns>A DateTime object with first day of the week</returns> + </member> <member name="M:MoreDateTime.Extensions.DateTimeExtensions.NumberOfDaysUntil(System.DateTime,System.DateTime,System.Globalization.CultureInfo)"> <summary> Count the number of Days between startDate and endDate @@ -2443,26 +2545,6 @@ <param name="dt">The TimeSpan object</param> <returns>The Truncated TimeSpan object</returns> </member> - <member name="T:MoreDateTime.Interfaces.IDateOnlyRange"> - <summary> - A Date/Time range provider, with an start and an end date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateOnlyRange.Start"> - <summary> - Gets or sets the start date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateOnlyRange.End"> - <summary> - Gets or sets the end date - </summary> - </member> - <member name="M:MoreDateTime.Interfaces.IDateOnlyRange.Distance"> - <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IDateOnlyRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IDateOnlyRange.End"/> - </summary> - </member> <member name="T:MoreDateTime.Interfaces.IDateTimeProvider"> <summary> The interface for providing DateTime information @@ -2488,26 +2570,6 @@ Gets the date part of the current UTC DateTime with the time set to 00:00:00 (or the mock value if set) </summary> </member> - <member name="T:MoreDateTime.Interfaces.IDateTimeRange"> - <summary> - A Date/Time range provider, with an start and an end date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateTimeRange.Start"> - <summary> - Gets or sets the start date - </summary> - </member> - <member name="P:MoreDateTime.Interfaces.IDateTimeRange.End"> - <summary> - Gets or sets the end date - </summary> - </member> - <member name="M:MoreDateTime.Interfaces.IDateTimeRange.Distance"> - <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IDateTimeRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IDateTimeRange.End"/> - </summary> - </member> <member name="T:MoreDateTime.Interfaces.IHolidayProvider"> <summary> An interface to provide access to holiday information @@ -2536,24 +2598,66 @@ <param name="year">The year for which the number of holidays is requested</param> <param name="cultureInfo">The </param> </member> - <member name="T:MoreDateTime.Interfaces.ITimeOnlyRange"> + <member name="T:MoreDateTime.Interfaces.IRange`2"> <summary> - A Date/Time range provider, with an start and an end date + An interface for ranges of dates and times </summary> + <typeparam name="T">The base type to handle</typeparam> + <typeparam name="Treturn">A self reference as a return type</typeparam> </member> - <member name="P:MoreDateTime.Interfaces.ITimeOnlyRange.Start"> + <member name="P:MoreDateTime.Interfaces.IRange`2.Start"> <summary> - Gets or sets the start time + Gets or sets the start <typeparamref name="T"/> </summary> </member> - <member name="P:MoreDateTime.Interfaces.ITimeOnlyRange.End"> + <member name="P:MoreDateTime.Interfaces.IRange`2.End"> <summary> - Gets or sets the end time + Gets or sets the end <typeparamref name="T"/> </summary> </member> - <member name="M:MoreDateTime.Interfaces.ITimeOnlyRange.Distance"> + <member name="M:MoreDateTime.Interfaces.IRange`2.Distance"> <summary> - Gets the distance between the <see cref="P:MoreDateTime.Interfaces.ITimeOnlyRange.Start"/> and the <see cref="P:MoreDateTime.Interfaces.ITimeOnlyRange.End"/> + Gets the distance between the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and the <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> + </summary> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and/or <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.Interfaces.IRange`2.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.Interfaces.IRange`2.Start"/> and/or <see cref="P:MoreDateTime.Interfaces.IRange`2.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="T:MoreDateTime.Interfaces.RangeDirection"> + <summary> + The range direction enum, indicates in which direction to move the ranges values + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.Both"> + <summary> + Move start and end + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.Start"> + <summary> + Move start only + </summary> + </member> + <member name="F:MoreDateTime.Interfaces.RangeDirection.End"> + <summary> + Move end only </summary> </member> <member name="T:MoreDateTime.NagerHolidayProvider"> @@ -2584,7 +2688,7 @@ </member> <member name="T:MoreDateTime.TimeOnlyRange"> <summary> - Implements the <see cref="T:MoreDateTime.Interfaces.ITimeOnlyRange"/> interface and provides a time range through its <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> members + Implements the <see cref="T:MoreDateTime.Interfaces.IRange`2"/> interface and provides a time range through its <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> members </summary> </member> <member name="M:MoreDateTime.TimeOnlyRange.#ctor(System.TimeOnly,System.TimeOnly)"> @@ -2594,8 +2698,36 @@ <param name="startTime">The start TimeOnly</param> <param name="endTime">The end TimeOnly</param> </member> + <member name="M:MoreDateTime.TimeOnlyRange.#ctor(MoreDateTime.TimeOnlyRange)"> + <summary> + Initializes a copied new instance of the <see cref="T:MoreDateTime.TimeOnlyRange"/> class. + </summary> + <param name="range">The range to copy</param> + </member> <member name="M:MoreDateTime.TimeOnlyRange.Distance"> - <inheritdoc/> + <summary> + Gets the distance between the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and the <see cref="P:MoreDateTime.TimeOnlyRange.End"/> + </summary> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Offset(System.TimeSpan)"> + <summary> + Offsets the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Extend(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Extends the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> + </member> + <member name="M:MoreDateTime.TimeOnlyRange.Reduce(System.TimeSpan,MoreDateTime.Interfaces.RangeDirection)"> + <summary> + Reduces the <see cref="P:MoreDateTime.TimeOnlyRange.Start"/> and/or <see cref="P:MoreDateTime.TimeOnlyRange.End"/> by the specified <paramref name="timeSpan"/> + </summary> + <param name="timeSpan">The time span.</param> + <param name="direction">The direction in which to extend</param> </member> <member name="P:MoreDateTime.TimeOnlyRange.Start"> <inheritdoc/> diff --git a/tests/MoreDateTime.Test/DateOnlyRangeTests.cs b/tests/MoreDateTime.Test/DateOnlyRangeTests.cs new file mode 100644 index 0000000..9645c84 --- /dev/null +++ b/tests/MoreDateTime.Test/DateOnlyRangeTests.cs @@ -0,0 +1,340 @@ +namespace MoreDateTime.Tests +{ + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="DateOnlyRange"/>. + /// </summary> + [TestClass] + public partial class DateOnlyRangeTests + { + private DateOnlyRange _testClass = null!; + private readonly DateOnly _startDate = new DateOnly(2020, 05, 15); // Friday + + private readonly DateOnly _midDate = new DateOnly(2021, 02, 20); // Saturday + private readonly DateOnly _endDate = new DateOnly(2021, 05, 14); // Friday + + private readonly DateTime _startDateTime = new DateTime(2020, 05, 15, 2, 3, 4); // Friday + + private readonly DateTime _endDateTime = new DateTime(2021, 05, 14, 6, 7, 8); // Friday + + /// <summary> + /// Creates the date time range. + /// </summary> + /// <returns>A DateOnlyRange.</returns> + private DateOnlyRange CreateDateTimeRange() + { + return new DateOnlyRange(_startDate, _endDate); + } + + /// <summary> + /// Sets the up. + /// </summary> + [TestInitialize] + public void SetUp() + { + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTwoDateOnly() + { + // Act + var instance = new DateOnlyRange(this._startDate, this._endDate); + + // Assert + instance.ShouldNotBeNull(); + instance.Start.ShouldBe(_startDate); + instance.End.ShouldBe(_endDate); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTwoDateTime() + { + // Act + var instance = new DateOnlyRange(this._startDateTime, this._endDateTime); + + // Assert + instance.ShouldNotBeNull(); + instance.Start.ShouldBe(_startDateTime.ToDateOnly()); + instance.End.ShouldBe(_endDateTime.ToDateOnly()); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromDateOnlyRange() + { + // Act + var instance = new DateOnlyRange(this._startDate, this._endDate); + var instance2 = new DateOnlyRange(instance); + + // Assert + instance2.ShouldNotBeNull(); + instance2.Start.ShouldBe(_startDate); + instance2.End.ShouldBe(_endDate); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromDateTimeRange() + { + // Act + var instance = new DateTimeRange(this._startDateTime, this._endDateTime); + var instance2 = new DateOnlyRange(instance); + + // Assert + instance2.ShouldNotBeNull(); + instance2.Start.ShouldBe(_startDateTime.ToDateOnly()); + instance2.End.ShouldBe(_endDateTime.ToDateOnly()); + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End.ToDateTime() - _testClass.Start.ToDateTime()); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_WithTimeSpanLessThanDay() + { + // Arrange + var timeSpan = TimeSpan.FromMinutes(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.ShouldBeEquivalentTo(_testClass); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_WithTimeSpanLessThanDay() + { + // Arrange + var timeSpan = TimeSpan.FromMinutes(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.ShouldBeEquivalentTo(_testClass); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromDays(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Sub(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow.ToDateOnly(); + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); + } + + /// <summary> + /// Checks that the End property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_End() + { + // Arrange + var testValue = DateTime.UtcNow.ToDateOnly(); + + // Act + this._testClass.End = testValue; + + // Assert + this._testClass.End.ShouldBe(testValue); + } + + /// <summary> + /// Checks that calling Extend with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ExtendWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Extend(TimeSpan.FromDays(60), (RangeDirection)99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromDays(60), (RangeDirection)99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithTimeSpanLessThanDistance() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromDays(400), RangeDirection.Both)); + } + } +} \ No newline at end of file diff --git a/tests/MoreDateTime.Test/DateTimeRangeTests.cs b/tests/MoreDateTime.Test/DateTimeRangeTests.cs index 3294936..351cc2e 100644 --- a/tests/MoreDateTime.Test/DateTimeRangeTests.cs +++ b/tests/MoreDateTime.Test/DateTimeRangeTests.cs @@ -1,13 +1,25 @@ -namespace MoreDateTime.Tests +namespace MoreDateTime.Tests { + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + /// <summary> /// The date time range tests. /// </summary> [TestClass] public class DateTimeRangeTests { - private readonly DateTime _startDate = new DateTime(2000, 05, 15); - private readonly DateTime _endDate = new DateTime(2001, 02, 20); + private readonly DateTime _startDate = new DateTime(2000, 05, 15, 6, 12, 30); + private readonly DateTime _endDate = new DateTime(2001, 02, 20, 9, 30, 59); + + private DateTimeRange _testClass = null!; /// <summary> /// Creates the date time range. @@ -24,23 +36,255 @@ private DateTimeRange CreateDateTimeRange() [TestInitialize] public void SetUp() { - // Method intentionally left empty. + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTwoDateTime() + { + // Act + var instance = new DateTimeRange(this._startDate, this._endDate); + + // Assert + instance.ShouldNotBeNull(); + instance.Start.ShouldBe(_startDate); + instance.End.ShouldBe(_endDate); + + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTwoDateOnly() + { + // Act + var instance = new DateTimeRange(this._startDate.ToDateOnly(), this._endDate.ToDateOnly()); + + // Assert + instance.ShouldNotBeNull(); + instance.Start.ShouldBe(_startDate.Date); + instance.End.ShouldBe(_endDate.Date); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromDateTimeRange() + { + // Arrange + var instance = new DateTimeRange(this._startDate, this._endDate); + + // Act + var instance2 = new DateTimeRange(instance); + + // Assert + instance2.ShouldNotBeNull(); + instance2.ShouldBeEquivalentTo(instance); + instance2.Start.ShouldBe(_startDate); + instance2.End.ShouldBe(_endDate); + + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End - _testClass.Start); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); } /// <summary> - /// Tests the method1. + /// Checks that the Extend method functions correctly. /// </summary> [TestMethod] - public void TestMethod1() + public void CanCall_Extend_FromEnd() { // Arrange - var dateTimeRange = this.CreateDateTimeRange(); + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; // Act + var result = _testClass.Extend(timeSpan, direction); // Assert - Assert.AreEqual(dateTimeRange.Start, _startDate); - Assert.AreEqual(dateTimeRange.End, _endDate); + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Sub(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow; + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); + } + + /// <summary> + /// Checks that the End property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_End() + { + // Arrange + var testValue = DateTime.UtcNow; + + // Act + this._testClass.End = testValue; + + // Assert + this._testClass.End.ShouldBe(testValue); + } + + /// <summary> + /// Checks that calling Extend with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ExtendWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Extend(TimeSpan.FromSeconds(60), (RangeDirection) 99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromSeconds(60), (RangeDirection) 99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithTimeSpanLessThanDistance() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromDays(400), RangeDirection.Both)); } } } diff --git a/tests/MoreDateTime.Test/DefaultHolidayProviderTests.cs b/tests/MoreDateTime.Test/DefaultHolidayProviderTests.cs new file mode 100644 index 0000000..be52971 --- /dev/null +++ b/tests/MoreDateTime.Test/DefaultHolidayProviderTests.cs @@ -0,0 +1,98 @@ +namespace MoreDateTime.Tests +{ + using System; + using System.Globalization; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="DefaultHolidayProvider"/>. + /// </summary> + [TestClass] + public class DefaultHolidayProviderTests + { + private DefaultHolidayProvider _testClass; + + /// <summary> + /// Sets up the dependencies required for the tests for <see cref="DefaultHolidayProvider"/>. + /// </summary> + [TestInitialize] + public void SetUp() + { + this._testClass = new DefaultHolidayProvider(); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateTimeAndCultureInfo() + { + // Arrange + var date = DateTime.UtcNow; + var cultureInfo = CultureInfo.InvariantCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateTimeAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(DateTime.UtcNow, default(CultureInfo))); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateOnlyAndCultureInfo() + { + // Arrange + var date = new DateOnly(); + var cultureInfo = CultureInfo.InvariantCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateOnlyAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(new DateOnly(), default(CultureInfo))); + } + + /// <summary> + /// Checks that the NumberOfKnownHolidays method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_NumberOfKnownHolidays() + { + // Arrange + var year = 205745105; + var cultureInfo = CultureInfo.CurrentCulture; + + // Act + var result = this._testClass.NumberOfKnownHolidays(year, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + } +} \ No newline at end of file diff --git a/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.NextPrevTests.cs b/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.NextPrevTests.cs index b48a218..140b04a 100644 --- a/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.NextPrevTests.cs +++ b/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.NextPrevTests.cs @@ -1,6 +1,7 @@ namespace MoreDateTime.Tests.Extensions { using System; + using System.Globalization; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -215,5 +216,70 @@ public void CanCall_PreviousYear() // Assert result.Year.ShouldBe(2019); } + + /// <summary> + /// Checks that the EndOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_EndOfWeek_Europe() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("de-DE"); + + // Act + var result = _startDate.EndOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(2)); // Sunday + } + + /// <summary> + /// Checks that the StartOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_StartOfWeek_Europe() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("de-DE"); + + // Act + var result = _startDate.StartOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(-4)); // Monday + } + + /// <summary> + /// Checks that the EndOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_EndOfWeek_US() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("en-US"); + + // Act + var result = _startDate.EndOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(1)); // saturday + } + + /// <summary> + /// Checks that the StartOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_StartOfWeek_US() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("en-US"); + + // Act + var result = _startDate.StartOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(-5)); // sunday + } + } } \ No newline at end of file diff --git a/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.TruncateTests.cs b/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.TruncateTests.cs index 7c0197a..035e331 100644 --- a/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.TruncateTests.cs +++ b/tests/MoreDateTime.Test/Extensions/DateOnlyExtensions.TruncateTests.cs @@ -21,6 +21,22 @@ public void CanCall_TruncateTo() // implicitly tested through all the other TruncateToX methods } + /// <summary> + /// Checks that the TruncateTo method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_TruncateTo_WithUnsupportedKind() + { + // Arrange + var dt = _startDate; + + // Act + var result = dt.TruncateTo(DateTimeExtensions.DateTruncate.Hour); + + // Assert + result.ShouldBe(_startDate); + } + /// <summary> /// Checks that the TruncateToMonth method functions correctly. /// </summary> diff --git a/tests/MoreDateTime.Test/Extensions/DateTimeExtensions.NextPrevTests.cs b/tests/MoreDateTime.Test/Extensions/DateTimeExtensions.NextPrevTests.cs index 01b6d97..2b2df75 100644 --- a/tests/MoreDateTime.Test/Extensions/DateTimeExtensions.NextPrevTests.cs +++ b/tests/MoreDateTime.Test/Extensions/DateTimeExtensions.NextPrevTests.cs @@ -424,7 +424,7 @@ public void CanCall_PreviousWeek() var result = _startDate.PreviousWeek(); // Assert - result.ShouldBe(new DateTime(2020, 5, 15-7)); + result.ShouldBe(new DateTime(2020, 5, 15 - 7)); } /// <summary> @@ -437,7 +437,71 @@ public void CanCall_PreviousWeekend() var result = _startDate.PreviousWeekend(); // Assert - result.ShouldBe(new DateTime(2020, 5, 10)); // sunday + result.ShouldBe(new DateTime(2020, 5, 10)); // sunday + } + + /// <summary> + /// Checks that the EndOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_EndOfWeek_Europe() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("de-DE"); + + // Act + var result = _startDate.EndOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(2)); // Sunday + } + + /// <summary> + /// Checks that the StartOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_StartOfWeek_Europe() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("de-DE"); + + // Act + var result = _startDate.StartOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(-4)); // Monday + } + + /// <summary> + /// Checks that the EndOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_EndOfWeek_US() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("en-US"); + + // Act + var result = _startDate.EndOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(1)); // saturday + } + + /// <summary> + /// Checks that the StartOfWeek method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_StartOfWeek_US() + { + // Arrange + var cultureInfo = CultureInfo.GetCultureInfo("en-US"); + + // Act + var result = _startDate.StartOfWeek(cultureInfo); + + // Assert + result.ShouldBe(_startDate.AddDays(-5)); // sunday } } } \ No newline at end of file diff --git a/tests/MoreDateTime.Test/Extensions/DateTimeExtensionsTests.cs b/tests/MoreDateTime.Test/Extensions/DateTimeExtensionsTests.cs index fe3decd..2d417aa 100644 --- a/tests/MoreDateTime.Test/Extensions/DateTimeExtensionsTests.cs +++ b/tests/MoreDateTime.Test/Extensions/DateTimeExtensionsTests.cs @@ -255,7 +255,7 @@ public void CanCall_SplitWithStartDateAndEndDateAndParts() result.Count().ShouldBe(parts); // verify that all parts are the same size - var partSize = _startDate.Distance(_endDate) / parts; + var partSize = _startDate.Distance(_endDate) / parts; foreach (var part in result) { part.Distance().ShouldBe(partSize); @@ -343,5 +343,14 @@ public void CannotCall_SplitWithStartDateAndDistanceAndParts_WithZeroParts() { Should.Throw<ArgumentOutOfRangeException>(() => _startDate.Split(TimeSpan.FromMinutes(10), 0)); } + + /// <summary> + /// Checks that the Split method throws when the dates parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_SplitWithDistanceLessThanParts() + { + Should.Throw<ArgumentOutOfRangeException>(() => _startDate.Split(TimeSpan.FromTicks(10), 12)); + } } } diff --git a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsEqualTests.cs b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsEqualTests.cs index 9e3f605..794ea96 100644 --- a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsEqualTests.cs +++ b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsEqualTests.cs @@ -12,5 +12,73 @@ namespace MoreDateTime.Tests.Extensions /// <inheritdoc/> public partial class TimeOnlyExtensionsTests { + /// <summary> + /// Checks that the IsEqual method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsEqual() + { + // Arrange + var dt = _startTime; + var other = _startTime.AddHours(1); + var truncateTo = DateTimeExtensions.DateTruncate.Week; + + // Act + var result = dt.IsEqual(other, truncateTo); + + // Assert + result.ShouldBeTrue(); + } + + /// <summary> + /// Checks that the IsEqualDownToHour method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsEqualDownToHour() + { + // Arrange + var dt = _startTime; + var other = _startTime.AddMinutes(5); + + // Act + var result = dt.IsEqualDownToHour(other); + + // Assert + result.ShouldBeTrue(); + } + + /// <summary> + /// Checks that the IsEqualDownToMinute method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsEqualDownToMinute() + { + // Arrange + var dt = _startTime; + var other = _startTime.AddSeconds(10); + + // Act + var result = dt.IsEqualDownToMinute(other); + + // Assert + result.ShouldBeTrue(); + } + + /// <summary> + /// Checks that the IsEqualDownToSecond method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsEqualDownToSecond() + { + // Arrange + var dt = _startTime; + var other = _startTime.AddMilliseconds(100); + + // Act + var result = dt.IsEqualDownToSecond(other); + + // Assert + result.ShouldBeTrue(); + } } } \ No newline at end of file diff --git a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsTests.cs b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsTests.cs index 9ccc36e..11c2c94 100644 --- a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsTests.cs +++ b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.IsTests.cs @@ -170,6 +170,7 @@ public void CanCall_IsFullMinute() result3.ShouldBeTrue(); result4.ShouldBeFalse(); } + /// <summary> /// Checks that the IsWithin method functions correctly. /// </summary> @@ -186,5 +187,39 @@ public void CanCall_IsWithin_WithTimeOnly() result1.ShouldBeTrue(); result2.ShouldBeFalse(); } + + /// <summary> + /// Checks that the IsWithin method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsWithin_WithMidnight() + { + // Arrange + + // Act + var result1 = _zeroTime.IsWithin(_startTime, _endTime); + var result2 = _zeroTime.IsWithin(_endTime, _startTime); + + // Assert + result1.ShouldBeFalse(); + result2.ShouldBeTrue(); + } + + /// <summary> + /// Checks that the IsWithin method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsWithin_DateTimeWithTimeOnly() + { + // Arrange + + // Act + var result1 = _startDateTime.IsWithin(_startTime, _endTime); + var result2 = _startDateTime.IsWithin(_zeroTime, _startTime); + + // Assert + result1.ShouldBeTrue(); + result2.ShouldBeFalse(); + } } } \ No newline at end of file diff --git a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.TruncateTests.cs b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.TruncateTests.cs index 431b6e0..66aed63 100644 --- a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.TruncateTests.cs +++ b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensions.TruncateTests.cs @@ -21,6 +21,22 @@ public void CanCall_TruncateTo() // implicitly tested through all the other TruncateToX methods } + /// <summary> + /// Checks that the TruncateTo method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_TruncateTo_WithUnsupportedKind() + { + // Arrange + var dt = _startTime.AddHours(2).AddMinutes(5); + + // Act + var result = dt.TruncateTo(DateTimeExtensions.DateTruncate.Day); + + // Assert + result.ShouldBe(dt); + } + /// <summary> /// Checks that the TruncateToHour method functions correctly. /// </summary> diff --git a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensionsTests.cs b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensionsTests.cs index d53e6a5..a0ee3c0 100644 --- a/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensionsTests.cs +++ b/tests/MoreDateTime.Test/Extensions/TimeOnlyExtensionsTests.cs @@ -19,6 +19,8 @@ public partial class TimeOnlyExtensionsTests private static readonly TimeOnly _midTime = new TimeOnly(5, 6, 7, 8); private static readonly TimeOnly _endTime = new TimeOnly(10, 11, 12, 987); + private readonly DateTime _startDateTime = new DateTime(2020, 05, 15, 2, 3, 4); // Friday + private readonly int _hoursInStartTimeToEndTime = 10; private readonly int _minutesInStartTimeToEndTime = 10 * 60; // not right private readonly int _secondsInStartTimeToEndTime = 10 * 60 * 60; // not right diff --git a/tests/MoreDateTime.Test/NagerHolidayProviderTests.cs b/tests/MoreDateTime.Test/NagerHolidayProviderTests.cs new file mode 100644 index 0000000..bf0828b --- /dev/null +++ b/tests/MoreDateTime.Test/NagerHolidayProviderTests.cs @@ -0,0 +1,98 @@ +namespace MoreDateTime.Tests +{ + using System; + using System.Globalization; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="NagerHolidayProvider"/>. + /// </summary> + [TestClass] + public class NagerHolidayProviderTests + { + private NagerHolidayProvider _testClass; + + /// <summary> + /// Sets up the dependencies required for the tests for <see cref="NagerHolidayProvider"/>. + /// </summary> + [TestInitialize] + public void SetUp() + { + this._testClass = new NagerHolidayProvider(); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateTimeAndCultureInfo() + { + // Arrange + var date = DateTime.UtcNow; + var cultureInfo = CultureInfo.CurrentCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateTimeAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(DateTime.UtcNow, default(CultureInfo))); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateOnlyAndCultureInfo() + { + // Arrange + var date = new DateOnly(); + var cultureInfo = CultureInfo.InvariantCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateOnlyAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(new DateOnly(), default(CultureInfo))); + } + + /// <summary> + /// Checks that the NumberOfKnownHolidays method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_NumberOfKnownHolidays() + { + // Arrange + var year = 1005842840; + var cultureInfo = CultureInfo.InvariantCulture; + + // Act + var result = this._testClass.NumberOfKnownHolidays(year, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + } +} \ No newline at end of file diff --git a/tests/MoreDateTime.Test/NullHolidayProviderTests.cs b/tests/MoreDateTime.Test/NullHolidayProviderTests.cs new file mode 100644 index 0000000..7b3dac8 --- /dev/null +++ b/tests/MoreDateTime.Test/NullHolidayProviderTests.cs @@ -0,0 +1,99 @@ +namespace MoreDateTime.Tests +{ + using System; + using System.Globalization; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Interfaces; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="NullHolidayProvider"/>. + /// </summary> + [TestClass] + public class NullHolidayProviderTests + { + private NullHolidayProvider _testClass; + + /// <summary> + /// Sets up the dependencies required for the tests for <see cref="NullHolidayProvider"/>. + /// </summary> + [TestInitialize] + public void SetUp() + { + this._testClass = new NullHolidayProvider(); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateTimeAndCultureInfo() + { + // Arrange + var date = DateTime.UtcNow; + var cultureInfo = CultureInfo.CurrentCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateTimeAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(DateTime.UtcNow, default(CultureInfo))); + } + + /// <summary> + /// Checks that the IsPublicHoliday method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_IsPublicHolidayWithDateOnlyAndCultureInfo() + { + // Arrange + var date = new DateOnly(); + var cultureInfo = CultureInfo.InvariantCulture; + + // Act + var result = this._testClass.IsPublicHoliday(date, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + + /// <summary> + /// Checks that the IsPublicHoliday method throws when the cultureInfo parameter is null. + /// </summary> + [TestMethod] + public void CannotCall_IsPublicHolidayWithDateOnlyAndCultureInfo_WithNullCultureInfo() + { + Should.Throw<ArgumentNullException>(() => this._testClass.IsPublicHoliday(new DateOnly(), default(CultureInfo))); + } + + /// <summary> + /// Checks that the NumberOfKnownHolidays method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_NumberOfKnownHolidays() + { + // Arrange + var year = 130150902; + var cultureInfo = CultureInfo.CurrentCulture; + + // Act + var result = ((IHolidayProvider)this._testClass).NumberOfKnownHolidays(year, cultureInfo); + + // Assert + Assert.Fail("Create or modify test"); + } + } +} \ No newline at end of file diff --git a/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs b/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs new file mode 100644 index 0000000..86238c4 --- /dev/null +++ b/tests/MoreDateTime.Test/TimeOnlyRangeTests.cs @@ -0,0 +1,273 @@ +namespace MoreDateTime.Tests +{ + using System; + + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using MoreDateTime; + using MoreDateTime.Extensions; + using MoreDateTime.Interfaces; + + using Shouldly; + + /// <summary> + /// Unit tests for the type <see cref="TimeOnlyRange"/>. + /// </summary> + [TestClass] + public class TimeOnlyRangeTests + { + private TimeOnlyRange _testClass = null!; + private static readonly TimeOnly _startTime = new TimeOnly(1, 2, 3, 4); + private static readonly TimeOnly _midTime = new TimeOnly(5, 6, 7, 8); + private static readonly TimeOnly _endTime = new TimeOnly(10, 11, 12, 987); + + /// <summary> + /// Creates the date time range. + /// </summary> + /// <returns>A TimeOnlyRange.</returns> + private TimeOnlyRange CreateDateTimeRange() + { + return new TimeOnlyRange(_startTime, _endTime); + } + + /// <summary> + /// Sets the up. + /// </summary> + [TestInitialize] + public void SetUp() + { + _testClass = CreateDateTimeRange(); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTwoTimeOnly() + { + // Act + var instance = new TimeOnlyRange(_startTime, _endTime); + + // Assert + instance.ShouldNotBeNull(); + instance.Start.ShouldBe(_startTime); + instance.End.ShouldBe(_endTime); + } + + /// <summary> + /// Checks that instance construction works. + /// </summary> + [TestMethod] + public void CanConstruct_FromTimeOnlyRange() + { + // Arrange + var instance = new TimeOnlyRange(_startTime, _endTime); + + // Act + var instance2 = new TimeOnlyRange(instance); + + // Assert + instance2.ShouldNotBeNull(); + instance2.ShouldBeEquivalentTo(instance); + instance2.Start.ShouldBe(_startTime); + instance2.End.ShouldBe(_endTime); + } + + /// <summary> + /// Checks that the Distance method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Distance() + { + // Act + var result = this._testClass.Distance(); + + // Assert + result.ShouldBe(_testClass.End - _testClass.Start); + } + + /// <summary> + /// Checks that the Offset method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Offset() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + + // Act + var result = this._testClass.Offset(timeSpan); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Add(timeSpan)); + } + + /// <summary> + /// Checks that the Extend method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Extend_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Extend(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Sub(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Add(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromStart() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Start; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan)); + result.End.ShouldBe(_testClass.End); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromEnd() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.End; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start); + result.End.ShouldBe(_testClass.End.Sub(timeSpan)); + } + + /// <summary> + /// Checks that the Reduce method functions correctly. + /// </summary> + [TestMethod] + public void CanCall_Reduce_FromBoth() + { + // Arrange + var timeSpan = TimeSpan.FromSeconds(60); + var direction = RangeDirection.Both; + + // Act + var result = _testClass.Reduce(timeSpan, direction); + + // Assert + result.Start.ShouldBe(_testClass.Start.Add(timeSpan / 2)); + result.End.ShouldBe(_testClass.End.Sub(timeSpan / 2)); + } + + /// <summary> + /// Checks that the Start property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_Start() + { + // Arrange + var testValue = DateTime.UtcNow.ToTimeOnly(); + + // Act + this._testClass.Start = testValue; + + // Assert + this._testClass.Start.ShouldBe(testValue); + } + + /// <summary> + /// Checks that the End property can be read from and written to. + /// </summary> + [TestMethod] + public void CanSetAndGet_End() + { + // Arrange + var testValue = DateTime.UtcNow.ToTimeOnly(); + + // Act + this._testClass.End = testValue; + + // Assert + this._testClass.End.ShouldBe(testValue); + } + + /// <summary> + /// Checks that calling Extend with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ExtendWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Extend(TimeSpan.FromSeconds(60), (RangeDirection)99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithInvalidDirection() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromSeconds(60), (RangeDirection)99)); + } + + /// <summary> + /// Checks that calling Reduce with invalid direction throws an exception. + /// </summary> + [TestMethod] + public void CannotCall_ReduceWithTimeSpanLessThanDistance() + { + Should.Throw<ArgumentOutOfRangeException>(() => this._testClass.Reduce(TimeSpan.FromHours(20), RangeDirection.Both)); + } + } +} \ No newline at end of file From 172047718b0be1f49cfbd34133f4390bc098ed8d Mon Sep 17 00:00:00 2001 From: Andreas Saurwein <saurwein@gmail.com> Date: Wed, 19 Apr 2023 16:05:31 +0100 Subject: [PATCH 3/3] version up --- src/MoreDateTime/MoreDateTime.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MoreDateTime/MoreDateTime.csproj b/src/MoreDateTime/MoreDateTime.csproj index 0619f46..d5b5e65 100644 --- a/src/MoreDateTime/MoreDateTime.csproj +++ b/src/MoreDateTime/MoreDateTime.csproj @@ -17,7 +17,7 @@ <NeutralLanguage>en</NeutralLanguage> <PackageTags>datetime dateonly timeonly dateoperations timeoperations workdays holidays</PackageTags> <PackageLicenseExpression>MIT</PackageLicenseExpression> - <Version>1.1.0</Version> + <Version>1.2.0</Version> <GenerateDocumentationFile>True</GenerateDocumentationFile> <DocumentationFile>.\docs</DocumentationFile> </PropertyGroup>