Skip to content
Snippets Groups Projects
Commit 50b83653 authored by Jan Tattermusch's avatar Jan Tattermusch
Browse files

Timespec.FromDateTime implementation and tests

parent 4113ba54
No related branches found
No related tags found
No related merge requests found
...@@ -92,18 +92,16 @@ namespace Grpc.Core.Internal.Tests ...@@ -92,18 +92,16 @@ namespace Grpc.Core.Internal.Tests
// before epoch // before epoch
Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10), Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10),
new Timespec(new IntPtr(-5), 1000).ToDateTime()); new Timespec(new IntPtr(-5), 1000).ToDateTime());
}
[Test] // infinity
public void ToDateTime_RoundUp() Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime());
{ Assert.AreEqual(DateTime.MinValue, Timespec.InfPast.ToDateTime());
// nanos are rounded to ticks are rounded up
Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1), Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1),
new Timespec(IntPtr.Zero, 99).ToDateTime()); new Timespec(IntPtr.Zero, 99).ToDateTime());
}
[Test] // Illegal inputs
public void ToDateTime_WrongInputs()
{
Assert.Throws(typeof(InvalidOperationException), Assert.Throws(typeof(InvalidOperationException),
() => new Timespec(new IntPtr(0), -2).ToDateTime()); () => new Timespec(new IntPtr(0), -2).ToDateTime());
Assert.Throws(typeof(InvalidOperationException), Assert.Throws(typeof(InvalidOperationException),
...@@ -120,14 +118,7 @@ namespace Grpc.Core.Internal.Tests ...@@ -120,14 +118,7 @@ namespace Grpc.Core.Internal.Tests
} }
[Test] [Test]
public void ToDateTime_Infinity() public void ToDateTime_Overflow()
{
Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime());
Assert.AreEqual(DateTime.MinValue, Timespec.InfPast.ToDateTime());
}
[Test]
public void ToDateTime_OverflowGivesMaxOrMinVal()
{ {
// we can only get overflow in ticks arithmetic on 64-bit // we can only get overflow in ticks arithmetic on 64-bit
if (IntPtr.Size == 8) if (IntPtr.Size == 8)
...@@ -145,7 +136,7 @@ namespace Grpc.Core.Internal.Tests ...@@ -145,7 +136,7 @@ namespace Grpc.Core.Internal.Tests
} }
[Test] [Test]
public void ToDateTime_OutOfRangeGivesMaxOrMinVal() public void ToDateTime_OutOfDateTimeRange()
{ {
// we can only get out of range on 64-bit, on 32 bit the max // we can only get out of range on 64-bit, on 32 bit the max
// timestamp is ~ Jan 19 2038, which is far within range of DateTime // timestamp is ~ Jan 19 2038, which is far within range of DateTime
...@@ -167,5 +158,45 @@ namespace Grpc.Core.Internal.Tests ...@@ -167,5 +158,45 @@ namespace Grpc.Core.Internal.Tests
Console.WriteLine("Test cannot be run on this platform, skipping the test"); Console.WriteLine("Test cannot be run on this platform, skipping the test");
} }
} }
[Test]
public void FromDateTime()
{
Assert.AreEqual(new Timespec(IntPtr.Zero, 0),
Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)));
Assert.AreEqual(new Timespec(new IntPtr(10), 5000),
Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50)));
Assert.AreEqual(new Timespec(new IntPtr(1437452508), 0),
Timespec.FromDateTime(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc)));
// before epoch
Assert.AreEqual(new Timespec(new IntPtr(-5), 1000),
Timespec.FromDateTime(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10)));
// infinity
Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(DateTime.MaxValue));
Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(DateTime.MinValue));
// illegal inputs
Assert.Throws(typeof(ArgumentException),
() => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)));
}
[Test]
public void FromDateTime_OutOfTimespecRange()
{
// we can only get overflow in Timespec on 32-bit
if (IntPtr.Size == 4)
{
Assert.AreEqual(Timespec.InfFuture, new DateTime(2040, 1, 1, 0, 0, 0, DateTimeKind.Utc));
Assert.AreEqual(Timespec.InfPast, new DateTime(1800, 1, 1, 0, 0, 0, DateTimeKind.Utc));
}
else
{
Console.WriteLine("Test cannot be run on this platform, skipping the test.");
}
}
} }
} }
...@@ -180,6 +180,14 @@ namespace Grpc.Core.Internal ...@@ -180,6 +180,14 @@ namespace Grpc.Core.Internal
} }
} }
/// <summary>
/// Creates DateTime to Timespec.
/// DateTime has to be in UTC (DateTimeKind.Utc) unless it's DateTime.MaxValue or DateTime.MinValue.
/// For DateTime.MaxValue of date time after the largest representable Timespec, Timespec.InfFuture is returned.
/// For DateTime.MinValue of date time before the lowest representable Timespec, Timespec.InfPast is returned.
/// </summary>
/// <returns>The date time.</returns>
/// <param name="dateTime">Date time.</param>
public static Timespec FromDateTime(DateTime dateTime) public static Timespec FromDateTime(DateTime dateTime)
{ {
if (dateTime == DateTime.MaxValue) if (dateTime == DateTime.MaxValue)
...@@ -199,11 +207,16 @@ namespace Grpc.Core.Internal ...@@ -199,11 +207,16 @@ namespace Grpc.Core.Internal
TimeSpan timeSpan = dateTime - UnixEpoch; TimeSpan timeSpan = dateTime - UnixEpoch;
long ticks = timeSpan.Ticks; long ticks = timeSpan.Ticks;
IntPtr seconds = new IntPtr(ticks / TicksPerSecond); // possible OverflowException long seconds = ticks / TicksPerSecond;
// (x % m + m) % m is workaround for modulo semantics with negative numbers. int nanos = (int)((ticks % TicksPerSecond) * NanosPerTick);
int nanos = (int)(((ticks % TicksPerSecond + TicksPerSecond) % TicksPerSecond) * NanosPerTick); if (nanos < 0)
{
return new Timespec(seconds, nanos); // correct the result based on C# modulo semantics for negative dividend
seconds--;
nanos += (int)NanosPerSecond;
}
// new IntPtr possibly throws OverflowException
return new Timespec(new IntPtr(seconds), nanos);
} }
catch (OverflowException) catch (OverflowException)
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment