Convert Julian dates in an instant

I am faced with a situation where I would like to convert from Julian date to java.time.Instant (if that makes sense), or some Java time, which can be more easily understood. My understanding is that the Julian date comes from reading the Wikipedia page. There are several different options , and the date I'm trying to read uses a different era than any of these.

For example, let's say that the era is the beginning of the “Calendar (New Style)” 1750 , and the Julian date is 95906.27600694445 which in this case I think CE 2015 April 15 06: 37: 26.9 UT , how can I get a moment from this? I will need to adjust the time zone later.

I noticed that there is a class called JulianFields , but I don't know where / how to use it. In addition, most of the methods that I see in the package use int or long , and not something for double .

So, is there an easy way to convert from Julian date using a different era in Java 8 Instant (or some other time if my thinking is wrong).

+6
source share
4 answers

Here is a solution using the new Java 8 classes:

 public class JulianDay { private static final double NANOS_PER_DAY = 24.0 * 60.0 * 60.0 * 1000000000.0; // Calculate Instants for some epochs as defined in Wikipedia. public static final Instant REDUCED_JD = ZonedDateTime.of(1858, 11, 16, 12, 0, 0, 0, ZoneOffset.UTC).toInstant(); public static final Instant MODIFIED_JD = ZonedDateTime.of(1858, 11, 17, 0, 0, 0, 0, ZoneOffset.UTC).toInstant(); public static final Instant JULIAN_DATE = REDUCED_JD.minus(2400000, ChronoUnit.DAYS); private final Instant epoch; public JulianDay(Instant epoch) { super(); this.epoch = epoch; } public Instant toInstant(double day) { long l = (long) day; return epoch .plus(l, ChronoUnit.DAYS) .plusNanos(Math.round((day - l) * NANOS_PER_DAY)); } public static void main(String[] args) { // Use the example values from Wikipedia for 2015-09-07 13:21 UTC. System.out.println(new JulianDay(REDUCED_JD).toInstant(57273.05625)); // Output: 2015-09-07T13:21:00.000000126Z System.out.println(new JulianDay(MODIFIED_JD).toInstant(57272.55625)); // Output: 2015-09-07T13:21:00.000000126Z System.out.println(new JulianDay(JULIAN_DATE).toInstant(2457273.05625)); // Output: 2015-09-07T13:20:59.999991953Z } } 

Regarding the JulianFields you were asking about, you can define a custom formatter like this:

 DateTimeFormatter formatter = new DateTimeFormatterBuilder() .appendValue(JulianFields.MODIFIED_JULIAN_DAY) .toFormatter().withZone(ZoneOffset.UTC); 

Unfortunately, it does not support fractions of days:

 System.out.println(formatter.format(Instant.now())); // Output: 57249 System.out.println(LocalDate.from(formatter.parse("57249"))); // Output: 2015-08-15 
+5
source

I'm going to assume that you have a numerical timestamp, which is kind of a modified Julian's day number, i.e. a continuous number of days from a particular era.

For example, the definition of “Modified Julian Day” is a continuous number of days from midnight November 17, 1858. I believe you are asking:

How to convert a continuous number of days in England, since the Gregorian calendar was officially adopted for a moment?

I’m not sure that the Gregorian era officially began after the New Style calendar is in effect. I will consider that this is January 1, 1752, i.e. The number 95906.276 is the continuous number of days since.

METHOD1: Here is the algorithm for processing the number of days in the representation of an integer array in a year, month (1-12), per day (1-31), in hours (0-23), min (0-59), sec (0-59) , millis:

  private static final int YEAR = 0; private static final int MONTH = 1; private static final int DAY = 2; private static final int HOURS = 3; private static final int MINUTES = 4; private static final int SECONDS = 5; private static final int MILLIS = 6; public static int[] toTimeStampArray(double yourEpochDayNumber) { int ymd_hms[] = { -1, -1, -1, -1, -1, -1, -1 }; int a, b, c, d, e, z; // convert from your epoch (1/1/1752) to Julian Day Number double jd = yourEpochDayNumber + 2360965.5 + 0.5; double f, x; z = (int) Math.floor(jd); f = jd - z; if (z >= 2299161) { int alpha = (int) Math.floor((z - 1867216.25) / 36524.25); a = z + 1 + alpha - (int) Math.floor(alpha / 4); } else { a = z; } b = a + 1524; c = (int) Math.floor((b - 122.1) / 365.25); d = (int) Math.floor(365.25 * c); e = (int) Math.floor((b - d) / 30.6001); ymd_hms[DAY] = b - d - (int) Math.floor(30.6001 * e); ymd_hms[MONTH] = (e < 14) ? (e - 1) : (e - 13); ymd_hms[YEAR] = (ymd_hms[MONTH] > 2) ? (c - 4716) : (c - 4715); for (int i = HOURS; i <= MILLIS; i++) { switch(i) { case HOURS: f = f * 24.0; break; case MINUTES: case SECONDS: f = f * 60.0; break; case MILLIS: f = f * 1000.0; break; } x = Math.floor(f); ymd_hms[i] = (int) x; f = f - x; } return ymd_hms; } 

Algorithm adapted from Meeus J., Astronomical Algorithms, 2nd Ed.

From this data, you can create an instance of LocalDateTime . You can combine this with an instance of ZoneId to create a ZonedDateTime and get Instant .

METHOD 2. If your day number is already taken into account in GMT / UTC and does not require any offsets for the time zone or daylight saving time, you can directly convert from the day number (in your era) to Instant as follows:

 public Instant dayNumberToInstant(double dayNumber) { long millisFromPosixEpoch; final double POSIX_EPOCH_AS_DAYNUM = 79622.0 millisFromPosixEpoch = (long) ((dayNumber - POSIX_EPOCH_AS_DAYNUM) * (86400.0 * 1000.0)); return Instant.ofEpochMillis(millisFromPosixEpoch); } 
+3
source

Age of September 14, 1752

My reading of the Wikipedia page on the Calendar Act (New Style) 1750 indicates the date of the link to the era of 1752-09-14.

If we add the integer part of your input number 95906.27600694445 , 95_906L , we really get your target date on April 15, 2015 (in the modern calendar system).

 long input = 95_906L; LocalDate epochCalendarNewStyleActOf1750 = LocalDate.of ( 1752 , Month.SEPTEMBER , 14 ); LocalDate localDate = epochCalendarNewStyleActOf1750.plusDays ( input ); System.out.println ( input + " days from epoch of: " + epochCalendarNewStyleActOf1750 + " is " + localDate ); 

95906 days from the era: 1752-09-14 - 2015-04-15

As for the fractional number, which I assume is the fraction of the number of seconds in the total 24-hour day.

While LocalDate refers only to a date - without a time of day, we need the time of day, which is represented by our fractional number. Therefore, instead of LocalDate we switch to OffsetDateTime .

 OffsetDateTime epochCalendarNewStyleActOf1750 = LocalDate.of ( 1752 , Month.SEPTEMBER , 14 ).atStartOfDay ().atOffset ( ZoneOffset.UTC ); 

We use BigDecimal as a double and double floating point technology that eliminates precision for execution speed.

 String input = "95906.27600694445"; BigDecimal bd = new BigDecimal ( input ); 

Get the number of whole days out of it.

 long days = bd.toBigInteger ().longValue (); 

Work on a fraction of the day. Extract the fractional number by subtracting the integer part.

 BigDecimal fractionOfADay = bd.subtract ( new BigDecimal ( days ) ); // Extract the fractional number, separate from the integer number. 

We assume that this decimal is part of the number of seconds per day. Thus, we can multiply by the number of seconds - this is the day.

 BigDecimal secondsFractional = new BigDecimal ( TimeUnit.DAYS.toSeconds ( 1 ) ).multiply ( fractionOfADay ); 

Extract the number of whole seconds. From the remainder, produce an integer number of nanoseconds, resolving the java.time classes, including OffsetDateTime and Duration .

 long secondsWhole = secondsFractional.longValue (); long nanos = secondsFractional.subtract ( new BigDecimal ( secondsWhole ) ).multiply ( new BigDecimal ( 1_000_000_000L ) ).longValue (); 

Create a Duration to represent the amount of time we want to add to our era.

 Duration duration = Duration.ofDays ( days ).plusSeconds ( secondsWhole ).plusNanos ( nanos ); 

Add duration to the era to get the final result.

 OffsetDateTime odt = epochCalendarNewStyleActOf1750.plus ( duration ); 

You can retrieve the Instant object from OffsetDateTime .

 Instant instant = odt.toInstant(); 

Dump for the console.

 System.out.println ( "bd: " + bd ); System.out.println ( "days: " + days ); System.out.println ( "fractionOfADay.toString(): " + fractionOfADay ); System.out.println ( "secondsFractional: " + secondsFractional ); System.out.println ( "secondsWhole: " + secondsWhole ); System.out.println ( "nanos: " + nanos ); System.out.println ( "duration.toString(): " + duration ); System.out.println ( "duration.toDays(): " + duration.toDays () ); System.out.println ( "odt.toString(): " + odt ); 

This code is working correctly. The result here corresponds to the expectation specified in the Question, the second, although we do not agree with a split second.

There are no guarantees; this code is fresh from my head and is quite untested and unproven.

See this code run on IdeOne.com .

bd: 95906.27600694445

days: 95906

fractionOfADay.toString (): 0.27600694445

secondsFractional: 23847.00000048000

secondsWhole: 23847

nanos: 480

duration.toString (): PT2301750H37M27.00000048S

duration.toDays (): 95906

odt.toString (): 2015-04-15T06: 37: 27.000000480Z

Of course, this math could be simpler. But I thought it would be interesting to show the pieces. One simple way is to multiply 95906.27600694445 BigDecimal by the number of seconds in a total 24-hour day. Then, separate the resulting integer from its decimal and feed each to Duration.ofSeconds and Duration::plusNanos , as it corresponds to the Duration internal data model, the total number of seconds and the total number of nano in a split second. We would skip the part where we called Duration.ofDays .


About java.time

The java.time framework is built into Java 8 and later. These classes supersede the nasty old legacy time classes such as java.util.Date , Calendar and SimpleDateFormat .

The Joda-Time project, now in maintenance mode , we recommend switching to the java.time classes.

To learn more, see the Oracle Tutorial . And search for qaru for many examples and explanations. JSR 310 specification .

Where to get java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is proof of possible future additions to java.time. Here you can find useful classes such as Interval , YearWeek , YearQuarter and more .

+2
source

The most complete and shortest approach was obtained in my Time4J library, see this snippet using the JulianDay class:

 double customJD = 95906.27600694445; // my comment: I have never seen Julian days with that epoch until now HistoricCalendar hcal = // date when the new style calendar act took effect HistoricCalendar.of(ChronoHistory.of(Locale.UK), HistoricEra.AD, 1752, 9, 14); // last term 0.5 necessary because julian days start at noon double value = customJD + hcal.get(EpochDays.JULIAN_DAY_NUMBER) - 0.5; JulianDay jd = JulianDay.ofSimplifiedTime(value); Instant instant = jd.toMoment().toTemporalAccessor(); System.out.println(instant); // 2015-04-15T06:37:27Z 

However, it should be noted that astronomy is the most dominant application for Julian days; see also the IAU official recommendation . And in this area, delta-T correction is much more common, i.e. Define Julian days using the TT (Earth Time) timeline. For this purpose, Time4J offers the JulianDay.ofEphemerisTime(...) methods. If you are serious about handling time scales such as TT, you better work with the Moment class instead of Instant , because the latter cannot understand TT, UTC, UT, including spasmodic second processing, etc.

0
source

All Articles