How do I use Julian Day Numbers with the Java Calendar API?
Asked Answered
P

4

10

Julian Day Numbers are a means of representing timestamps as a continuous count of days (and fractional days) since noon UTC, January 1, 4713 B.C. The Java 7 SE API does not contain support for this format. Developers who have used the SQLite database may have used the native Julian Day support provided by the strftime() functions.

The advantages of representing timestamps as Julian Day Numbers include:

  • A date and time can be represented to millisecond precision in a primitive data type (double)
  • Days in a year are somewhat more concrete than seconds in a day
  • Circumvents the problem of "leap seconds" if this degree of precision is unimportant
  • Days between dates arithmetic is trivial; sorting precedence is easily determined
  • Very lightweight

Disadvantages

  • The Java Date/Time API does not have built-in support for JDN's
  • Unsuitable for very precise time measurements
  • Only defined for UTC and must be mapped from UTC to local time
  • Unsuitable for display to end-users; must be converted/formatted before display

Julian Day Numbers are commonly used in astronomical calculations and their definition is highly standardized and accepted. Similarly, Modified Julian Day Numbers (which count from midnight UTC, 17 November 1858) are standardly defined and used in aerospace applications (see http://tycho.usno.navy.mil/mjd.html).

For applications that make extensive use of date/time arithmetic or chronological sorting (or if persisting lightweight primitives is more appealing than persisting timestamps), internally representing dates and times as JDN's or MJD's may make sense for you.

The following code defines functions that facilitate using either Julian Day Numbers or Modified Julian Day Numbers with the Java Date/Time/Calendar API. The code is based on algorithms published in Jean Meeus's "Astronomical Algorithms", 1st ed., 1991.

public class JulianDay {

    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;

    :
    :

    // Converts a timestamp presented as an array of integers in the following
    // order (from index 0 to 6): year,month,day,hours,minutes,seconds,millis
    // month (1-12), day (1-28 or 29), hours (0-23), min/sec (0-59) to a
    // Modified Julian Day Number.
    // For clarity and simplicity, the input values are assumed to be well-formed;
    // error checking is not implemented in the snippet.

    public static double toMJD(int[] ymd_hms) {

        int y = ymd_hms[YEAR];
        int m = ymd_hms[MONTH];
        double d = (double) ymd_hms[DAY];

        d = d + ((ymd_hms[HOURS] / 24.0) +
                 (ymd_hms[MINUTES] / 1440.0) +
                 (ymd_hms[SECONDS] / 86400.0) +
                 (ymd_hms[MILLIS] / 86400000.0));

        if (m == 1 || m == 2) {
            y--;
            m = m + 12;
        }

        double a = Math.floor(y / 100);
        double b = 2 - a + Math.floor(a / 4);

        return (Math.floor(365.25 * (y + 4716.0)) +
               Math.floor(30.6001 * (m + 1)) +
               d + b - 1524.5) - 2400000.5;  // for Julian Day omit the 2400000.5 term
    }

    // Converts an Modified Julian Day Number (double) to an integer array representing
    // a timestamp (year,month,day,hours,mins,secs,millis). Works for all positive JDN

    public static int[] toTimestamp(double mjd) {

        int ymd_hms[] = { -1, -1, -1, -1, -1, -1, -1 };
        int a, b, c, d, e, z;

        double jd = mjd + 2400000.5 + 0.5;  // if a JDN is passed as argument,
                                            // omit the 2400000.5 term
        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;
    }
}

This answer has been provided here as well: How can I convert between a Java Date and Julian day number?. In the current post, references for the algorithm are provided along with some more discussion. The implementation of algorithms above also contains no Java API dependencies (aside from Math functions).

Pythagoras answered 20/2, 2013 at 19:37 Comment(2)
What is the question here?Nietzsche
No, that linked Question mistakenly took a number of whole seconds from the epoch of 1970 as a Julian date. Two totally different beasts.Mongeau
M
5

java.time

The java.time framework built into Java 8 and later supplants the old date-time classes bundled with the earliest versions of Java. See Oracle Tutorial. Much of the functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.

The java.time classes include the java.time.temporal.JulianFields. This class provides three implementations of TemporalField to give limited support for Julian date-only values (no time-of-day). So you can get whole number of days, not the double requested in the Question. Read that class doc closely to be sure it behaves to your expectations. Note that unlike most other java.time classes, these Julian classes ignore any offset-from-UTC or time zone information (always treated as a local date).

  • JULIAN_DAY → Count of whole days since day 0, which is January 1, 4713 BCE in the Julian calendar ( -4713-11-24 Gregorian ).
  • MODIFIED_JULIAN_DAY → Like JULIAN_DAY but subtracting 2_400_000.5 (basically dropping the first two digits of Julian date number). Note that results here are one fewer (-1) than Julian date number of item above.
  • RATA_DIE → Similar to the two items above in that it is a count of days from an epoch. But here the epoch is the ISO 8601 date of 0001-01-01.

In this example we start with the ISO 8601 date of 1970-01-01.

LocalDate localDate = LocalDate.of ( 1970 , 1 , 1 );
long julianDate = JulianFields.JULIAN_DAY.getFrom ( localDate );
long modifiedJulianDate = JulianFields.MODIFIED_JULIAN_DAY.getFrom ( localDate );
long rataDie = JulianFields.RATA_DIE.getFrom ( localDate );

localDate: 1970-01-01 | julianDate: 2440588 | modifiedJulianDate: 40587 | rataDie: 719163

ThreeTen-Extra

The ThreeTen-Extra project is the experimental proving grounds for possible future additions to java.time. The name comes from the JSR 310 that defines java.time.

This library includes additional support for Julian dates in its Julian calendar system (Chronology). Like the support in Java 8, this library is limited to date-only values (no partial days or time-of-day).

With this library you can instantiate JulianDate objects.

Many methods and features for you to examine there.

Mongeau answered 9/7, 2016 at 4:20 Comment(1)
The Julian calendar (in Threeten-Extra) is totally unrelated to the original question, see also comment of OP about the other answer given by Erik.Phidippides
H
2

I know that this is not a Java Calendar API, but maybe you should try Jodd tool.

JulianDateStamp julianStamp = new JulianDateStamp(julianDays);
JDateTime jdate = new JDateTime(julianStamp);
Date date = new Date(jdate.getTimeInMillis());

This works perfect for:

  • 2113488,2746855323 -> 1074.06.01 18:35
  • 2453479,5866961805 -> 2005.04.19 02:04

Read more.

Hebetude answered 19/5, 2017 at 6:18 Comment(0)
T
1

I wrote a small utility for this. It is Open Source, feel free to use as you wish.

Java Date is not ideal, since the time-zone really matters for Julian-Days, thus, I prefer ZonedDateTime. The code offers conversion ZonedDateTime <-> JulianDay, Modified Julian Day and a few other useful things. Also, the code is tested.

The code is at github and published at maven central.

Here is some example code you can write with this utility:

import xyz.wirklich.astro.time.JulianDay;
import java.time.ZonedDateTime;
import java.time.ZonedId;

public void jdFromDate(Date d) {
  ZonedDateTime zdt = ZonedDateTime.ofInstant(d.toInstant(),
                                              ZoneId.systemDefault());
  JulianDay jd = new JulianDay(yzt);
  double jd_value = jd.getJd();
  double mjd_value = jd.getMjd();
  ZonedDateTime date_value = jd.getDate();
  // ...
}

ZonedDateTime takes care of all annoying things like time zones, daylight saving times, etc. This is great for practical applications and can be extremely annoying to handle otherwise.

Tejada answered 29/9, 2023 at 7:12 Comment(0)
P
0

If you are willing to move outside the core JDK classes, then Joda can be a solution. Joda supports the Julian calendar system. From their doc page:

Chronology julianChrono = JulianChronology.getInstance();
DateTime dt = new DateTime(1066, 10, 14, 0, 0, 0, julianChrono);

That would be the Battle of Hastings 1066 in the Julian Calendar system.

Primogeniture answered 20/2, 2013 at 19:56 Comment(1)
One must make a distinction between a Julian Date and a Julian Day Number. The former is a point in time reckoned according to the Julian Calendar System, organized by Julius Caesar and used in Europe until the Gregorian reform of 1582, which marks the year in which the Gregorian Calendar replaced it. Thus, we have both Julian and Gregorian Calendars (since the Battle of Hastings occurred before the Gregorian reform, it is appropriate to represent it with the historically accurate Julian Calendar). A Julian Day Number is merely a simple count of consecutive days that elapsed since 4713 BC.Pythagoras

© 2022 - 2024 — McMap. All rights reserved.