Skip to content

Commit

Permalink
Added generic math support.
Browse files Browse the repository at this point in the history
  • Loading branch information
Maarten Kok authored and jskeet committed Sep 28, 2024
1 parent 34a6d44 commit 6d4ff46
Show file tree
Hide file tree
Showing 18 changed files with 160 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/NodaTime.Test/PeriodTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,14 @@ public void Subtraction_With_IdenticalPeriodTypes()
Assert.AreEqual(difference, Period.Subtract(p1, p2));
}

[Test]
public void UnaryNegation()
{
Period period = new Period(2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
Period negation = -period;
Assert.AreEqual(new Period(-2, -3, -4, -5, -6, -7, -8, -9, -10, -11), negation);
}

[Test]
public void Equality_WhenEqual()
{
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/AnnualDate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -35,6 +36,9 @@ namespace NodaTime
[TypeConverter(typeof(AnnualDateTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct AnnualDate : IEquatable<AnnualDate>, IComparable<AnnualDate>, IComparable, IFormattable, IXmlSerializable
#if NET8_0_OR_GREATER
, IComparisonOperators<AnnualDate, AnnualDate, bool>
#endif
{
// The underlying value. We only care about the month and day, but for the sake of
// compatibility with the default value, this ends up being in year 1. This would
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/DateInterval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using static System.FormattableString;

namespace NodaTime
Expand All @@ -31,6 +32,9 @@ namespace NodaTime
/// <threadsafety>This type is immutable reference type. See the thread safety section of the user guide for more information.</threadsafety>
[Immutable]
public sealed class DateInterval : IEquatable<DateInterval?>, IEnumerable<LocalDate>
#if NET8_0_OR_GREATER
, IEqualityOperators<DateInterval, DateInterval, bool>
#endif
{
/// <summary>
/// Gets the start date of the interval.
Expand Down
21 changes: 21 additions & 0 deletions src/NodaTime/Duration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ namespace NodaTime
[TypeConverter(typeof(DurationTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct Duration : IEquatable<Duration>, IComparable<Duration>, IComparable, IXmlSerializable, IFormattable
#if NET8_0_OR_GREATER
, IAdditionOperators<Duration, Duration, Duration>
, ISubtractionOperators<Duration, Duration, Duration>
, IUnaryNegationOperators<Duration, Duration>
, IUnaryPlusOperators<Duration, Duration>
, IComparisonOperators<Duration, Duration, bool>
, IMinMaxValue<Duration>
, IAdditiveIdentity<Duration, Duration>
#endif
{
// This is one more bit than we really need, but it allows Instant.BeforeMinValue and Instant.AfterMaxValue
// to be easily constructed with valid durations, even though the result is a deliberately-invalid instant.
Expand Down Expand Up @@ -93,6 +102,11 @@ namespace NodaTime
/// <value>The zero <see cref="Duration"/> value.</value>
public static Duration Zero => default;

/// <summary>
/// Gets the additive identity.
/// </summary>
public static Duration AdditiveIdentity => Zero;

/// <summary>
/// Gets a <see cref="Duration"/> value equal to 1 nanosecond; the smallest amount by which an instant can vary.
/// </summary>
Expand Down Expand Up @@ -500,6 +514,13 @@ public string ToString(string? patternText, IFormatProvider? formatProvider) =>
}
}

/// <summary>
/// Implements the operator + (unary).
/// </summary>
/// <param name="duration">The duration.</param>
/// <returns>The same duration <see cref="Duration"/> as provided.</returns>
public static Duration operator +(Duration duration) => duration;

/// <summary>
/// Adds one duration to another. Friendly alternative to <c>operator+()</c>.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/NodaTime/Instant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -36,6 +37,13 @@ namespace NodaTime
[TypeConverter(typeof(InstantTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct Instant : IEquatable<Instant>, IComparable<Instant>, IFormattable, IComparable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<Instant, Duration, Instant>
, ISubtractionOperators<Instant, Duration, Instant>
, ISubtractionOperators<Instant, Instant, Duration>
, IComparisonOperators<Instant, Instant, bool>
, IMinMaxValue<Instant>
#endif
{
// These correspond to -9998-01-01 and 9999-12-31 respectively.
internal const int MinDays = -4371222;
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/Interval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using NodaTime.Text;
using NodaTime.Utility;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -36,6 +37,9 @@ namespace NodaTime
/// <threadsafety>This type is an immutable value type. See the thread safety section of the user guide for more information.</threadsafety>
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct Interval : IEquatable<Interval>, IXmlSerializable
#if NET8_0_OR_GREATER
, IEqualityOperators<Interval, Interval, bool>
#endif
{
/// <summary>The start of the interval.</summary>
private readonly Instant start;
Expand Down
8 changes: 8 additions & 0 deletions src/NodaTime/LocalDate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand All @@ -37,6 +38,13 @@ namespace NodaTime
[TypeConverter(typeof(LocalDateTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct LocalDate : IEquatable<LocalDate>, IComparable<LocalDate>, IComparable, IFormattable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<LocalDate, Period, LocalDate>
, IAdditionOperators<LocalDate, LocalTime, LocalDateTime>
, ISubtractionOperators<LocalDate, Period, LocalDate>
, ISubtractionOperators<LocalDate, LocalDate, Period>
, IComparisonOperators<LocalDate, LocalDate, bool>
#endif
{
private readonly YearMonthDayCalendar yearMonthDayCalendar;

Expand Down
7 changes: 7 additions & 0 deletions src/NodaTime/LocalDateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -43,6 +44,12 @@ namespace NodaTime
[TypeConverter(typeof(LocalDateTimeTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct LocalDateTime : IEquatable<LocalDateTime>, IComparable<LocalDateTime>, IComparable, IFormattable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<LocalDateTime, Period, LocalDateTime>
, ISubtractionOperators<LocalDateTime, LocalDateTime, Period>
, ISubtractionOperators<LocalDateTime, Period, LocalDateTime>
, IComparisonOperators<LocalDateTime, LocalDateTime, bool>
#endif
{
/// <summary>
/// The maximum (latest) date and time representable in the ISO calendar system.
Expand Down
8 changes: 8 additions & 0 deletions src/NodaTime/LocalTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand All @@ -35,6 +36,13 @@ namespace NodaTime
[TypeConverter(typeof(LocalTimeTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct LocalTime : IEquatable<LocalTime>, IComparable<LocalTime>, IFormattable, IComparable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<LocalTime, Period, LocalTime>
, ISubtractionOperators<LocalTime, LocalTime, Period>
, ISubtractionOperators<LocalTime, Period, LocalTime>
, IComparisonOperators<LocalTime, LocalTime, bool>
, IMinMaxValue<LocalTime>
#endif
{
/// <summary>
/// Local time at midnight, i.e. 0 hours, 0 minutes, 0 seconds.
Expand Down
27 changes: 27 additions & 0 deletions src/NodaTime/Offset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -41,6 +42,15 @@ namespace NodaTime
[TypeConverter(typeof(OffsetTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct Offset : IEquatable<Offset>, IComparable<Offset>, IFormattable, IComparable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<Offset, Offset, Offset>
, ISubtractionOperators<Offset, Offset, Offset>
, IUnaryNegationOperators<Offset, Offset>
, IUnaryPlusOperators<Offset, Offset>
, IComparisonOperators<Offset, Offset, bool>
, IMinMaxValue<Offset>
, IAdditiveIdentity<Offset, Offset>
#endif
{
// Note: these public fields are unfortunate; they really should be properties, like all other public constants.
// Unfortunately that would now be a breaking change.
Expand All @@ -50,6 +60,11 @@ namespace NodaTime
/// </summary>
public static readonly Offset Zero = FromSeconds(0);

/// <summary>
/// Gets the additive identity.
/// </summary>
public static Offset AdditiveIdentity => Zero;

/// <summary>
/// The minimum permitted offset; 18 hours before UTC.
/// </summary>
Expand All @@ -59,6 +74,18 @@ namespace NodaTime
/// </summary>
public static readonly Offset MaxValue = FromHours(18);

#if NET8_0_OR_GREATER
/// <summary>
/// The minimum permitted offset; 18 hours before UTC.
/// </summary>
static Offset IMinMaxValue<Offset>.MinValue => MinValue;

/// <summary>
/// The maximum permitted offset; 18 hours after UTC.
/// </summary>
static Offset IMinMaxValue<Offset>.MaxValue => MaxValue;
#endif

private const int MinHours = -18;
private const int MaxHours = 18;
internal const int MinSeconds = -18 * SecondsPerHour;
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/OffsetDate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand All @@ -32,6 +33,9 @@ namespace NodaTime
[TypeConverter(typeof(OffsetDateTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct OffsetDate : IEquatable<OffsetDate>, IXmlSerializable, IFormattable
#if NET8_0_OR_GREATER
, IEqualityOperators<OffsetDate, OffsetDate, bool>
#endif
{
private readonly LocalDate date;
private readonly Offset offset;
Expand Down
6 changes: 6 additions & 0 deletions src/NodaTime/OffsetDateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand Down Expand Up @@ -46,6 +47,11 @@ namespace NodaTime
[TypeConverter(typeof(OffsetDateTimeTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct OffsetDateTime : IEquatable<OffsetDateTime>, IFormattable, IXmlSerializable
#if NET8_0_OR_GREATER
, IAdditionOperators<OffsetDateTime, Duration, OffsetDateTime>
, ISubtractionOperators<OffsetDateTime, Duration, OffsetDateTime>
, IEqualityOperators<OffsetDateTime, OffsetDateTime, bool>
#endif
{
private const int MinBclOffsetMinutes = -14 * MinutesPerHour;
private const int MaxBclOffsetMinutes = 14 * MinutesPerHour;
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/OffsetTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Xml;
using System.Xml.Schema;
Expand All @@ -30,6 +31,9 @@ namespace NodaTime
[TypeConverter(typeof(OffsetTimeTypeConverter))]
[XmlSchemaProvider(nameof(AddSchema))]
public readonly struct OffsetTime : IEquatable<OffsetTime>, IXmlSerializable, IFormattable
#if NET8_0_OR_GREATER
, IEqualityOperators<OffsetTime, OffsetTime, bool>
#endif
{
private const int NanosecondsBits = 47;
private const long NanosecondsMask = (1L << NanosecondsBits) - 1;
Expand Down
28 changes: 28 additions & 0 deletions src/NodaTime/Period.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Numerics;
using static NodaTime.NodaConstants;

namespace NodaTime
Expand Down Expand Up @@ -57,6 +58,14 @@ namespace NodaTime
[Immutable]
[TypeConverter(typeof(PeriodTypeConverter))]
public sealed class Period : IEquatable<Period?>
#if NET8_0_OR_GREATER
, IAdditionOperators<Period, Period, Period>
, ISubtractionOperators<Period, Period, Period>
, IUnaryNegationOperators<Period, Period>
, IUnaryPlusOperators<Period, Period>
, IAdditiveIdentity<Period, Period>
, IMinMaxValue<Period>
#endif
{
// General implementation note: operations such as normalization work out the total number of nanoseconds as an Int64
// value. This can handle +/- 106,751 days, or 292 years. We could move to using BigInteger if we feel that's required,
Expand All @@ -81,6 +90,11 @@ public sealed class Period : IEquatable<Period?>
/// <value>A period containing the minimum value for all properties.</value>
public static Period MinValue { get; } = new Period(int.MinValue, int.MinValue, int.MinValue, int.MinValue, long.MinValue, long.MinValue, long.MinValue, long.MinValue, long.MinValue, long.MinValue);

/// <summary>
/// Gets the additive identity.
/// </summary>
public static Period AdditiveIdentity => Zero;

/// <summary>
/// Returns an equality comparer which compares periods by first normalizing them - so 24 hours is deemed equal to 1 day, and so on.
/// Note that as per the <see cref="Normalize"/> method, years and months are unchanged by normalization - so 12 months does not
Expand Down Expand Up @@ -375,6 +389,13 @@ internal Period(int years, int months, int weeks, int days, long hours, long min
minuend.Nanoseconds - subtrahend.Nanoseconds);
}

/// <summary>
/// Implements the unary negation operator.
/// </summary>
/// <param name="period">Period to negate</param>
/// <returns>The negative value of this period</returns>
public static Period operator -(Period period) => Zero - period;

/// <summary>
/// Subtracts one period from another, by simply subtracting each property value.
/// </summary>
Expand Down Expand Up @@ -890,6 +911,13 @@ public Period Normalize()
/// <returns>A formatted representation of this period.</returns>
public override string ToString() => PeriodPattern.Roundtrip.Format(this);

/// <summary>
/// Implements the operator + (unary).
/// </summary>
/// <param name="period">The period.</param>
/// <returns>The same period <see cref="Period"/> as provided.</returns>
public static Period operator +(Period period) => period;

/// <summary>
/// Compares the given object for equality with this one, as per <see cref="Equals(Period?)"/>.
/// See the type documentation for a description of equality semantics.
Expand Down
4 changes: 4 additions & 0 deletions src/NodaTime/TimeZones/ZoneInterval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using NodaTime.Utility;
using System;
using System.Diagnostics;
using System.Numerics;
using static System.FormattableString;

namespace NodaTime.TimeZones
Expand All @@ -22,6 +23,9 @@ namespace NodaTime.TimeZones
/// <threadsafety>This type is an immutable reference type. See the thread safety section of the user guide for more information.</threadsafety>
[Immutable]
public sealed class ZoneInterval : IEquatable<ZoneInterval?>
#if NET8_0_OR_GREATER
, IEqualityOperators<ZoneInterval, ZoneInterval, bool>
#endif
{

/// <summary>
Expand Down
Loading

0 comments on commit 6d4ff46

Please sign in to comment.