Skip to content

Commit

Permalink
Implicit cast between Duration and TimeSpan (#1365)
Browse files Browse the repository at this point in the history
### Changes
- Change `Duration` from explicit to implicit cast to/from `TimeSpan`
- Remove operator overloads for `TimeSpan` now covered by implicit cast for all but left operands

### Background
See #1354 (comment)

One issue is that the operator overloads only work when `TimeSpan` is the right operand.

I changed the code generation to take this into account, but another option would be to make a breaking change where we just don't support `TimeSpan` as the left operand at all.

Then users would have to cast explicitly, or for multiplication just reverse the operands.

This would affect 13 operators:

```
TimeSpan * Acceleration
TimeSpan * ElectricCurrent
TimeSpan * ElectricCurrentGradient
TimeSpan * ForceChangeRate
TimeSpan * KinematicViscosity
TimeSpan * MassFlow
TimeSpan * MolarFlow
TimeSpan * Power
TimeSpan * PressureChangeRate
TimeSpan * RotationalSpeed
TimeSpan * Speed
TimeSpan * TemperatureChangeRate
TimeSpan * VolumeFlow
```

Of which only 6 are used in tests so I assume are supported in v5:
```
TimeSpan * KinematicViscosity
TimeSpan * MassFlow
TimeSpan * Power
TimeSpan * RotationalSpeed
TimeSpan * TemperatureChangeRate
TimeSpan * Speed
```

---------

Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
  • Loading branch information
Muximize and angularsen authored Feb 26, 2024
1 parent b4316d7 commit 240b19d
Show file tree
Hide file tree
Showing 29 changed files with 16 additions and 312 deletions.
15 changes: 2 additions & 13 deletions CodeGen/Generators/QuantityRelationsParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,6 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
})
.ToList());

// We can infer TimeSpan relations from Duration relations.
var timeSpanQuantity = pseudoQuantity with { Name = "TimeSpan" };
relations.AddRange(relations
.Where(r => r.LeftQuantity.Name is "Duration")
.Select(r => r with { LeftQuantity = timeSpanQuantity })
.ToList());
relations.AddRange(relations
.Where(r => r.RightQuantity.Name is "Duration")
.Select(r => r with { RightQuantity = timeSpanQuantity })
.ToList());

// Sort all relations to keep generated operators in a consistent order.
relations.Sort();

Expand All @@ -96,9 +85,9 @@ public static void ParseAndApplyRelations(string rootDir, Quantity[] quantities)
// The left operand of a relation is responsible for generating the operator.
quantityRelations.Add(relation);
}
else if (relation.RightQuantity == quantity && relation.LeftQuantity.Name is "double" or "TimeSpan")
else if (relation.RightQuantity == quantity && relation.LeftQuantity.Name is "double")
{
// Because we cannot add generated operators to double or TimeSpan, we make the right operand responsible in this case.
// Because we cannot add operators to double we make the right operand responsible in this case.
quantityRelations.Add(relation);
}
}
Expand Down
10 changes: 0 additions & 10 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -746,16 +746,6 @@ private void GenerateRelationalOperators()
var rightParameter = relation.RightQuantity.Name.ToCamelCase();
var rightConversionProperty = relation.RightUnit.PluralName;

if (relation.LeftQuantity.Name is nameof(TimeSpan))
{
leftConversionProperty = "Total" + leftConversionProperty;
}

if (relation.RightQuantity.Name is nameof(TimeSpan))
{
rightConversionProperty = "Total" + rightConversionProperty;
}

if (leftParameter == rightParameter)
{
leftParameter = "left";
Expand Down
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/KinematicViscosityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,6 @@ public static void KinematicViscosityTimesTimeSpanEqualsArea()
Assert.Equal(area, Area.FromSquareMeters(8));
}

[Fact]
public static void TimeSpanTimesKinematicViscosityEqualsArea()
{
Area area = TimeSpan.FromSeconds(2)*KinematicViscosity.FromSquareMetersPerSecond(4);
Assert.Equal(area, Area.FromSquareMeters(8));
}

[Fact]
public static void KinematicViscosityTimesDensityEqualsDynamicViscosity()
{
Expand Down
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/MassFlowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,6 @@ public void MassFlowTimesTimeSpanEqualsMass()
Assert.Equal(mass, Mass.FromKilograms(80.0));
}

[Fact]
public void TimeSpanTimesMassFlowEqualsMass()
{
Mass mass = TimeSpan.FromSeconds(4.0) * MassFlow.FromKilogramsPerSecond(20.0);
Assert.Equal(mass, Mass.FromKilograms(80.0));
}

[Fact]
public void MassFlowDividedByBrakeSpecificFuelConsumptionEqualsPower()
{
Expand Down
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/PowerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,6 @@ public void PowerTimesTimeSpanEqualsEnergy()
Assert.Equal(energy, Energy.FromJoules(40.0));
}

[Fact]
public void TimeSpanTimesPowerEqualsEnergy()
{
Energy energy = TimeSpan.FromSeconds(8.0) * Power.FromWatts(5.0);
Assert.Equal(energy, Energy.FromJoules(40.0));
}

[Fact]
public void PowerTimesBrakeSpecificFuelConsumptionEqualsMassFlow()
{
Expand Down
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/RotationalSpeedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,5 @@ public void RotationalSpeedTimesTimeSpanEqualsAngle()
Angle angle = RotationalSpeed.FromRadiansPerSecond(10.0)*TimeSpan.FromSeconds(9.0);
Assert.Equal(angle, Angle.FromRadians(90.0));
}

[Fact]
public void TimeSpanTimesRotationalSpeedEqualsAngle()
{
Angle angle = TimeSpan.FromSeconds(9.0)*RotationalSpeed.FromRadiansPerSecond(10.0);
Assert.Equal(angle, Angle.FromRadians(90.0));
}
}
}
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/SpeedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,6 @@ public void SpeedTimesTimeSpanEqualsLength()
Assert.Equal(length, Length.FromMeters(40));
}

[Fact]
public void TimeSpanTimesSpeedEqualsLength()
{
Length length = TimeSpan.FromSeconds(2)*Speed.FromMetersPerSecond(20);
Assert.Equal(length, Length.FromMeters(40));
}

[Fact]
public void SpeedTimesLengthEqualsKinematicViscosity()
{
Expand Down
7 changes: 0 additions & 7 deletions UnitsNet.Tests/CustomCode/TemperatureChangeRateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ public void TemperatureChangeRateMultipliedWithTimeSpanEqualsTemperatureDelta()
Assert.Equal(TemperatureDelta.FromDegreesCelsius(20), d);
}

[Fact]
public void TimeSpanMultipliedWithTemperatureChangeRateEqualsTemperatureDelta()
{
TemperatureDelta d = new TimeSpan(0, 0, -10) * TemperatureChangeRate.FromDegreesCelsiusPerSecond(2);
Assert.Equal(TemperatureDelta.FromDegreesCelsius(-20), d);
}

[Fact]
public void TemperatureChangeRateMultipliedWithDurationEqualsTemperatureDelta()
{
Expand Down
22 changes: 14 additions & 8 deletions UnitsNet/CustomCode/Quantities/Duration.extra.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ public partial struct Duration
/// <summary>
/// Convert a Duration to a TimeSpan.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Throws if the TimeSpan can't represent the Duration exactly </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Throws if the duration exceeds the <see cref="TimeSpan"/>.<see cref="TimeSpan.MaxValue"/> or
/// <see cref="TimeSpan.MinValue"/>, which would cause it to roll over from positive to negative and vice versa.
/// </exception>
/// <returns>The TimeSpan with the same time as the duration</returns>
public TimeSpan ToTimeSpan()
{
if ( Seconds > TimeSpan.MaxValue.TotalSeconds ||
Seconds < TimeSpan.MinValue.TotalSeconds )
throw new ArgumentOutOfRangeException( nameof( Duration ), "The duration is too large or small to fit in a TimeSpan" );
if (Seconds > TimeSpan.MaxValue.TotalSeconds) throw new ArgumentOutOfRangeException(nameof(Seconds),
"The duration is too large for a TimeSpan, which would roll over from positive to negative.");

if (Seconds < TimeSpan.MinValue.TotalSeconds) throw new ArgumentOutOfRangeException(nameof(Seconds),
"The duration is too small for a TimeSpan, which would roll over from negative to positive.");

return TimeSpan.FromTicks((long)(Seconds * TimeSpan.TicksPerSecond));
}

Expand All @@ -33,14 +39,14 @@ public TimeSpan ToTimeSpan()
return time.AddSeconds(-duration.Seconds);
}

/// <summary>Explicitly cast <see cref="Duration"/> to <see cref="TimeSpan"/>.</summary>
public static explicit operator TimeSpan(Duration duration)
/// <summary>Implicitly cast <see cref="Duration"/> to <see cref="TimeSpan"/>.</summary>
public static implicit operator TimeSpan(Duration duration)
{
return duration.ToTimeSpan();
}

/// <summary>Explicitly cast <see cref="TimeSpan"/> to <see cref="Duration"/>.</summary>
public static explicit operator Duration(TimeSpan duration)
/// <summary>Implicitly cast <see cref="TimeSpan"/> to <see cref="Duration"/>.</summary>
public static implicit operator Duration(TimeSpan duration)
{
return FromSeconds(duration.TotalSeconds);
}
Expand Down
20 changes: 0 additions & 20 deletions UnitsNet/GeneratedCode/Quantities/Acceleration.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions UnitsNet/GeneratedCode/Quantities/Angle.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions UnitsNet/GeneratedCode/Quantities/ElectricCharge.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 0 additions & 20 deletions UnitsNet/GeneratedCode/Quantities/ElectricCurrent.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions UnitsNet/GeneratedCode/Quantities/ElectricCurrentGradient.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions UnitsNet/GeneratedCode/Quantities/Energy.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 0 additions & 13 deletions UnitsNet/GeneratedCode/Quantities/ForceChangeRate.g.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 240b19d

Please sign in to comment.