2

I have a DateTime represented as long (8 bytes), that came from DateTime.ToBinary(), let's call it dateTimeBin. Is there an optimal way of dropping the Time information (I only care for the date) so I can compare it to a start of day? Lets say we have this sample value as a start of day.

DateTime startOfDay = new DateTime(2020,3,4,0,0,0);
long startOfDayBin = startOfDay.ToBinary();

I obviously know I can always convert to a DateTime object then get the date component. However, this operation is going to happen billions of times and every little performance tweak helps.

  • Is there an efficient way of extracting the Date info of dateTimeBin without converting it to DateTime? Or any arithmetic operation on the long that will return the date only?
  • Is there a way to match startOfDay (or startOfDayBin) and dateTimeBin if they have the same date components?
  • Is there a way to see if (dateTimeBin >= startOfDayBin), I don't think the long comparison is valid.

N.B. all the dates are UTC

Adam
  • 3,872
  • 6
  • 36
  • 66
  • 1
    I've never seen a `GetBinary` method on a `DateTime` in c# - you sure thats not a custom extension? Or did you mean [`ToBinary`](https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tobinary?view=netcore-3.1) – Jamiec Nov 05 '20 at 14:10
  • @Jamiec good spot, I meant `ToBinary`, I fixed it. – Adam Nov 05 '20 at 14:13
  • 1
    Ok good. The problem you have is that the binary is not just a number (Ticks) it also encodes the `Kind` property too. I suspect what you're trying to do is possible with the `Ticks` alone, but the binary representation is specifically to be able to derialize/deserialize correctly – Jamiec Nov 05 '20 at 14:14
  • 1
    YOu could always look at the source for [DateTime](https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,df6b1eba7461813b) and operations like `ToBinary` and `Date` which a) shows you the maths and b) may make you wonder whether the JIT will just inline everything anyway and you should just use those methods. – Damien_The_Unbeliever Nov 05 '20 at 14:15
  • There is no "date part" or "time part" since it's just a number of ticks since the beginning of the epoch. Bit bashing doesn't help there. You should first establish this is an actual bottleneck; if it is, consider using a different on-wire format that splits date and time. There is little reason to assume you're going to write code that's much faster than what `DateTime` does on its own. – Jeroen Mostert Nov 05 '20 at 14:15
  • If all the dates are UTC then why not use `Ticks` instead? – juharr Nov 05 '20 at 14:15
  • @JeroenMostert I did a benchmark and converting every long back to DateTime for the sake of comparison added 2 minutes of execution time. 'Bit bashing' helps if you are building a custom database not doing business programming and processing terabytes of records. However, splitting the Date and Time on saving is a good idea. Thank you. – Adam Nov 05 '20 at 14:20
  • @juharr good suggestion, thanks. – Adam Nov 05 '20 at 14:22
  • You can extract date and time, if we assume `ToBinary` just returns `Ticks` for UTC dates (you can check source code to see if such assumption is correct, I don't know). Then you have ticks and to extract time - just do `yourBinaryThing % (24 * 60 * 60 * 10_000_000)` (10 million ticks in second, that much ticks in a day). To remove time and get only date, just do `yourBinaryThing - (yourBinaryThing % (24 * 60 * 60 * 10_000_000))` and so on. – Evk Nov 05 '20 at 14:22
  • (and from the above it should be obvious that regular comparision of `binary` dates is valid if they represent ticks). – Evk Nov 05 '20 at 14:31
  • @Evk saving and loading from Ticks solves all the problems given that this is using UTC only. Would you like to contribute a full answer so I can mark it as correct? – Adam Nov 05 '20 at 14:42

1 Answers1

2

Since you are working only with UTC dates - makes sense to use DateTime.Ticks instead of DateTime.ToBinary, because former has relatively clear meaning - number of ticks since epoch, just like the unix time, the only difference is unix time interval is second and not tick (where tick is 1/10.000.000 of a second), and epoch is midnight January 1st of 0001 year and not year 1970. While ToBinary only promises that you can restore original DateTime value back and that's it.

With ticks it's easy to extract time and date. To extract time, you need to remainder of division of ticks by number of ticks in a full day, so

long binTicks = myDateTime.Ticks;
long ticksInDay = 24L * 60 * 60 * 10_000_000;
long time = binTicks % ticksInDay;

You can then use convert that to TimeSpan:

var ts = TimeSpan.FromTicks(time);

for convenience, or use as is. The same with extracting only date: just substract time

long date = binTicks - (binTicks % ticksInDay);

Regular comparision (dateTimeBin >= startOfDayBin) in also valid for tick values.

Evk
  • 98,527
  • 8
  • 141
  • 191