Simple DateTime to Julian Day and UnixTime Values
Four one-line methods to convert between DateTime objects and Julian Days or Unix Time Stamps
Introduction
There are a lot of systems around that keep timestamps as either UnixTime or Julian Days, whereas your C# code tends to use the System.DateTime
structure for working with dates and times. Fortunately, the DateTime
structure is able to help you out with these conversions, albeit these conversions are not immediately obvious.
Background
I'm currently writing a project using an SQLite database, SQLite's date functions take its formats in ISO-8601 (or derivatives thereof), or in the Julian Days or Unix Timestamp formats. The DateTime
structure intrinsically handles all ISO-8601 formats fine, but I wanted to handle the other ones also.
So I started looking around for conversion methods. The best information on what these values entail, and how they are calculated, came from Wikipedia pages on Unix Time and Julian Day. Most of the solutions I found are centered around the calculations described on those pages.
Then I started to think about what those numbers are. Basically, they are simply a period of time from some arbitrary point in time way back in history.
For unix time, this is 1970-01-01 00:00:00, and the intervals used is seconds. So, to go from a DateTime
to Unix time, or visa-versa, it is simply a matter calculating those seconds. DateTime
has all the mathematics for adding and subtracting time periods built in, so rather than re-inventing the wheel and writing a function with all of that math, I let DateTime
do it for me.
For the Julian Day, it is a little more complicated. This number represents the number of days (with the fractional part being the part of the day i.e. the time) since Midday on November 24, 4714 B.C. (if we stick to using the same calendar). Not only is calculating the actual number of days between any two days tricky, considering the differing numbers of days in each month, as well as taking into account leap years and their variants, but 4214 B.C. is well out of range of the DateTime
object. But then something clicked from my old days of COM programming - the OLE date time was stored as a count of the number of days, with the fractional part representing the time, just using a relatively modern epoch. It turns out that that instant in time was Midnight on December 31, 1899. So all that is needed to calculate the Julian Day is to get that OLE number, and add it to the number of days between the Julian Day epoch and the OLE day epoch - that number turns out to be 2,415,018.5 days (which is just the Julian Day of the OLE epoch - 1). Once again, DateTime
has built in methods for converting to and from the OLE number - and so there it is.
Using the Code
The functions to convert to a Unix Time and a Julian Day, I have written as an extension to the DateTime
object - so they can be called just like any other method on this object. To get the current time as a Unix Time or Julian day, simply:
DateTime dt = DateTime.Now;
double UnixTime = dt.ToUnixTime;
double JulianDay = dt.ToJulianDay;
Going the other way is not quite as simple, as it is not possible to create static
object extensions - and a static
method to create a DateTime
object is what is needed. Therefore, I have simply written plain static functions contained within the extensions class.
DateTime myBirthday = DateTimeExtensions.FromJulianDay(2437249D);
DateTime myBirthdayThisYear = DateTimeExtensions.FromUnixTime(1447156800D);
Points of Interest
This was a lot of talk on the Wikipedia page about taking into account leap-seconds. I don't believe the DateTime
object takes these into account, which means that the calculations here do not either. This means that the UnixTime values may be up to 26 seconds out (that's how many leap-seconds we've had since the Unix Epoch). I guess it would be feasible to set up a table to add in these leap-seconds when they occurred, but (a) that would make the UnixTime and the DateTime
object slightly out-of sync, and if Microsoft does eventually take them into account, then the code would break in the other direction, and (b) a maximum of 26 seconds of error will not kill my real-world applications, providing I know about it.
I made the time element in the second example above equal to midday, simply because I like to sleep in on my birthday.