3

I need to work with epoch times in c# and I have created the following two extension methods to do so:

public static DateTime ToDateTime(this double epochTime)
{
    return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(epochTime);
}

public static double ToEpochTime(this DateTime dt)
{
    var t = dt - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    return t.TotalSeconds;
}

I get a failure when I run the following test:

[Fact]
public void Test_EpochTime()
{
    var dateToTest = DateTime.Now;

    var epoch = dateToTest.ToEpochTime();
    var result = epoch.ToDateTime();

    Assert.Equal(result, dateToTest);
}

The result is:

Xunit.Sdk.EqualException: 'Assert.Equal() Failure

Expected: 2020-03-02T17:43:19.1830000Z

Actual: 2020-03-02T17:43:19.1831870+00:00'

Has anyone experienced this before, where there's an issue converting between the double/DateTime?

Thanks for any pointers in advance!

Rob
  • 6,819
  • 17
  • 71
  • 131
  • 1
    Curious, are you using fractional seconds? If they are integral, why not just use a long instead of a double? – Flydog57 Mar 02 '20 at 18:10
  • Don't use `.Now` in unit tests. It leads to tests that fail only at certain times (leap days, during daylight savings, etc). – Joel Coehoorn Mar 02 '20 at 18:12
  • Unless you're targeting .NET Framework before 4.6, there are already built in methods, as [this answer](https://stackoverflow.com/a/26225951/5762332) points out. – Magnetron Mar 02 '20 at 18:22
  • 2
    Your Expected and Actual are the wrong way round in the `Assert`. A double only has enough precision to hold down to milliseconds. If you want more precision, you will have to use `Ticks` which is a long. If you want milliseconds, then as @Magnetron points out, there are built-in methods for that. Above all, and this is general advice, if you need an exact value, don't use double or float types. – iakobski Mar 02 '20 at 18:39
  • @iakobski - that was exactly it! (please put this as the answer, so I can mark it) – Rob Mar 02 '20 at 18:46
  • Changing to use ticks (AddTicks and returning ticks) was the solution – Rob Mar 02 '20 at 18:46
  • Note @JoelCoehoorn - the use of DateTime.Now at the start of the test, should have any effect on the UTC date time conversion functionality (or am I missing something here?) – Rob Mar 02 '20 at 18:49
  • No but it's good general advice: don't use Now() (or other changing data) in a test, don't compare floating point values for equality (or things derived from floating point types), etc. – iakobski Mar 02 '20 at 18:53

2 Answers2

3

A double only has enough precision to hold down to milliseconds. If you want more precision, you will have to use DateTime.Ticks which is a long. A long is an exact value, it won't change.

If you want milliseconds, then as @Magnetron points out, there are built-in methods for that. Above all, and this is general advice, if you need an exact value, don't use double or float types.

iakobski
  • 1,000
  • 7
  • 8
0

For me the solution was to change and use Ticks for greater precision (thanks @iakobski). Here's the updated extension methods:

public static DateTime ToDateTime(this long epochTime)
{
    return new DateTime(1970, 1, 1, 0, 0, 0).AddTicks(epochTime);
}

public static long ToEpochTime(this DateTime dt)
{
    var t = dt.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0);
    return t.Ticks;
}
Rob
  • 6,819
  • 17
  • 71
  • 131