Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTime ToLocalTime failing

Tags:

c#

mongodb

I'm working with a MongoDB database. I know when you insert a DateTime into Mongo, it converts it to UTC. But I'm making a unit test, and my Assert is failing.

[TestMethod]
public void MongoDateConversion() {
    DateTime beforeInsert = DateTime.Now;
    DateTime afterInsert;

    Car entity = new Car {
        Name = "Putt putt",
        LastTimestamp = beforeInsert
    };

    // insert 'entity'
    // update 'entity' from the database

    afterInsert = entity.LastTimestamp.ToLocalTime();

    Assert.AreEqual(beforeInsert, afterInsert); // fails here
}

I must be missing something obvious. When I look in the debugger, I can see that the datetime's match, but the assert still says they do not (but they do):

Result Message: Assert.AreEqual failed. Expected:<5/21/2015 8:27:04 PM>. Actual:<5/21/2015 8:27:04 PM>.

Any ideas what I'm doing wrong here?

EDIT:

I've come up with two possible solutions, both of which require me to remember to do something (which isn't always the best thing to rely on...):

One is to use an extension method to truncate any DateTime coming out of the database:

public static DateTime Truncate(this DateTime dateTime) {
    var timeSpan = TimeSpan.FromMilliseconds(1);
    var ticks = -(dateTime.Ticks % timeSpan.Ticks);
    return dateTime.AddTicks(ticks);
}

The other, after reading http://alexmg.com/datetime-precision-with-mongodb-and-the-c-driver/, is to tag any DateTime in the POCO class:

public class Car : IEntity {
    public Guid Id { get; set; }

    [BsonDateTimeOptions(Representation = BsonType.Document)]
    public DateTime LastTimestamp { get; set; }
}
like image 490
Dan Champagne Avatar asked May 22 '15 00:05

Dan Champagne


Video Answer


1 Answers

MongoDB stores DateTimes as a 64-bit count of milliseconds since the UNIX epoch. See this page: http://alexmg.com/datetime-precision-with-mongodb-and-the-c-driver/

Since the resolution of .NET's DateTime is 100 nanoseconds, MongoDB is almost certain to truncate any DateTime time you round-trip like this.

You have a few options.

Option 1: Make sure you truncate LastTimestamp when you set it, or before you insert the record:

long excessTicks = timestamp.Ticks % 10000;

timestamp= new DateTime(
    ticks: timestamp.Ticks - excessTicks, 
    kind: timestamp.Kind
);

This approach is going to be error-prone. Anyone who sets LastTimestamp will have to remember to truncate it, or you could truncate the value right before inserting the record, but you might need to change the CLR object unexpectedly.

Alternately, you could use a getter/setter and just truncate LastTimestamp to milliseconds every time it's set. But that might lead to some other unit test further up the line failing for the exact same reason this test is failing.

Option 2: If sub-millisecond accuracy isn't important, just put some tolerance into your assertion:

TimeSpan delta = beforeInsert - afterInsert;

Assert.IsTrue(Math.Abs(delta.TotalMilliseconds) <= 2);
like image 109
Daryl Avatar answered Oct 01 '22 02:10

Daryl