I'm running into a situation where I would like to convert from a Julian date to an java.time.Instant
(if that makes sense), or some Java time that can be more easily understood. My understanding of what a Julian date is comes from reading the Wikipedia page. There are bunch of different variants, and the date I am trying to read uses a different epoch than any of these.
For example, let's say the epoch is the beginning of the Calendar (New Style) Act 1750, and the Julian date is 95906.27600694445
which in this case I believe is CE 2015 April 15 06:37:26.9 UT, how do I get an instant from this? I will need to adjust for the timezone later.
I noticed there is a class called JulianFields
, but I don't know where/how to use it. Also, most of the methods I see in the package make use of int
or long
, not really anything for double
.
So, is there a simple way to convert from a Julian date using a different epoch to a Java 8 Instant
(or some other time if my thinking is wrong).
Epoch of 14 September 1752
My reading of the Wikipedia page on Calendar (New Style) Act 1750 indicates an epoch reference date of 1752-09-14.
If we add the integer portion of your input number of
95906.27600694445
,95_906L
, we do indeed get your target date of April 15, 2015 (in modern calendar system).Regarding the fractional number, which I presume is the fraction of the number of seconds in a generic 24-hour day.
While
LocalDate
is for date-only without time-of-day, we now need time-of-day which is represented by our fractional number. So in place ofLocalDate
, we switch toOffsetDateTime
.We use
BigDecimal
asdouble
andDouble
are floating-point technology that trades away accuracy for speed of execution.Pull from that the number of whole days.
Work on the fraction of a day. Extract the fractional number by subtracting the integer portion.
We assume this decimal fraction is a fraction of the number of seconds in a day. So we can multiply by the number of seconds is a day.
From that, extract the number of whole seconds. From the remainder, produce a whole number nanoseconds, the resolution of the java.time classes including
OffsetDateTime
andDuration
.Create a
Duration
to represent the amount of time we want to add to our epoch.Add the duration to the epoch to get our final result.
You can extract a
Instant
object from theOffsetDateTime
.Dump to console.
This code seems to be working properly. The result here matches the expectation stated in the Question to the second, though we disagree on the fraction of a second.
No guarantees here; this code is fresh off the top of my head, and is quite untested and unproven.
See this code run live at IdeOne.com.
Of course this math could be simpler. But I thought it might be interesting to show the pieces. One simpler ways is to multiply the
95906.27600694445
BigDecimal by the number of seconds in a generic 24 hour day. Then separate the resulting integer from its decimal fraction, and feed each toDuration.ofSeconds
andDuration::plusNanos
as that fits the internal data model ofDuration
, a total number of seconds and a total number of nanos in a fraction of a second. We would be skipping the part where we calledDuration.ofDays
.About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.Here is a solution using the new Java 8 classes:
Regarding the
JulianFields
you asked about, you can define a custom formatter like this:Unfortunately it doesn't support fractions of days:
The most complete and also shortest approach is obtained by my library Time4J, see this snippet using the class JulianDay:
However, it should be noted that the most dominating field of application of Julian days is the astronomy, see also the official recommendation of the IAU. And on that field, it is much more common to include a delta-T-correction i.e. to define the Julian days on the time scale TT (Terrestrial Time). Time4J offers the methods
JulianDay.ofEphemerisTime(...)
for this purpose. If you seriously consider to handle time scales such as TT then you should rather work with the classMoment
instead ofInstant
because last one cannot understand TT, UTC, UT including leap second handling etc.I am going to assume that you have a numeric timestamp that is a kind of modified Julian Day Number, i.e. a continuous count of days since a defined epoch.
For example, the definition of a "Modified Julian Day Number" is a continuous count of days since midnight on Nov 17, 1858. I believe what you are asking is:
I'm not certain where the Gregorian Epoch officially began after the New Style Calendar act. I will assume that it is January 1, 1752, i.e. the number
95906.276
is a continuous count of days since then.METHOD1: Here is an algorithm for processing a day number to an integer array representation in year, month(1-12), day(1-31), hours(0-23), min(0-59), sec(0-59), millis:
Algorithm is adapted from Meeus J., Astronomical Algorithms, 2nd Ed.
From these data, you can create a
LocalDateTime
instance. You can combine that with aZoneId
instance to create aZonedDateTime
and get anInstant
.METHOD 2. If your day number is already reckoned in GMT/UTC and does not require any offsets for time zone or daylight savings, then you can convert directly from a day number (in your epoch) to an
Instant
as follows: