Search code examples
javascriptjavaandroidgregorian-calendarjulian-date

Java - Calculation of the julian day number to gregorian date and time


Recently I have seen a nice javascript library which calculates the sun time of a specific date, latitude and longitude. Since I want to use this in some of my android development, I am rewriting that library to java. Since the calculation of the time is much easier with the julian day number instead of the gregorian date, I have to convert the date to julian date. But to give a response at which time some event happens, I have to convert the julian day number to gregorian date and time.

In javascript it is pretty easy to transfer from and to julian day number. The calculation of javascript is like the following:

var dayMS = 1000 * 60 * 60 * 24;
var J1970 = 2440588;
var J2000 = 2451545;

function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
function fromJulian(j)  { return new Date((j + 0.5 - J1970) * dayMs); }

But to convert from the julian day number gives me a lot of trouble. My version of the above javascript in java is:

double dayMs = 1000 * 60 * 60 * 24;
double J1970 = 2440588;
double J2000 = 245154;

public double toJulian(LocalDateTime date) {
        ZonedDateTime zdt = date.atZone(ZoneId.of("Europe/Vienna"));
        double result = zdt.toInstant().toEpochMilli(); //milliseconds since epoch
        result = result / dayMs - 0.5 + J1970;

        return result;
    }

public String fromJulian(double jdn) {

        System.out.println("fromJulian.jdn: " + jdn);

        int jalpha,ja,jb,jc,jd,je,year,month,day;
        double  julian = jdn,
                decimal= jdn % 1;
        ja = (int) julian;
        if (ja>= JGREG) {
            jalpha = (int) (((ja - 1867216) - 0.25) / 36524.25);
            ja = ja + 1 + jalpha - jalpha / 4;
        }

        jb = ja + 1524;
        jc = (int) (6680.0 + ((jb - 2439870) - 122.1) / 365.25);
        jd = 365 * jc + jc / 4;
        je = (int) ((jb - jd) / 30.6001);
        day = jb - jd - (int) (30.6001 * je);
        month = je - 1;
        if (month > 12) month = month - 12;
        year = jc - 4715;
        if (month > 2) year--;
        if (year <= 0) year--;

        double dhour = decimal * 24;
        int hour = (int) Math.round(dhour);

        decimal = dhour % 1;

        double dminute = decimal * 60;
        int minute = (int) Math.round(dminute);

        decimal = dminute % 1;

        double dsecond = decimal * 60;
        int second = (int) Math.round(dsecond);

        LocalDateTime ldt = LocalDateTime.of(
            year,
            month,
            day,
            hour,
            minute,
            second
        );
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
        ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.of("UTC"));

        return dtf.format(zdt);
    }

The method toJulian in javascript and java gives me the exact same value, but in the method fromJulian I am somehow getting a date, but a completely wrong one.


Example

Date = "26 December, 2020 00:00:00"

Javascript: Inserting the date to the function toJulian and the reinserting the value (2459209.4583333335) into the function fromJulian returns 26 December 00:00:00.

Java: Inserting the date to the function toJulian and the reinserting the value (2459209.4583333335) into the function fromJulian returns 25 December 11:00:00.


Since two days I have tried different ways of calculating the gregorian date from the julian day number, but have never achieved the desired output. I have uploaded the whole java library to github (https://github.com/stnieder/SunCalc_Java). I hope somebody is able to help me with my issue.


Solution

  • Edit: Java has a rough support for Julian day number. It counts the days correctly. It doesn’t begin a new day at noon as the Julian day defines, and it doesn’t support fraction of day. We can still use the support and make the proper adjustment ourselves.

    As far as I can see the following methods make a consistent conversion forth and back and agree with the Julian day number that you got from toJulian() in JavaScript.

    private static final double dayMs = Duration.ofDays(1).toMillis();
    
    public static double toJulian(LocalDateTime date) {
        LocalTime timeOfDay = date.toLocalTime();
        double result = date.getLong(JulianFields.JULIAN_DAY);
        result += timeOfDay.get(ChronoField.MILLI_OF_DAY) / dayMs - 0.5;
    
        return result;
    }
    
    public static LocalDateTime fromJulian(double jdn) {
        LocalDate date = LocalDate.EPOCH.with(JulianFields.JULIAN_DAY, (long) jdn);
        LocalDateTime result = date.atStartOfDay()
                .plusHours(12)
                .plus((long) (jdn % 1 * dayMs), ChronoUnit.MILLIS);
        
        return result;
    }
    

    Trying them out:

        LocalDateTime dateTime = LocalDateTime.of(2020, Month.DECEMBER, 26, 0, 0);
        double jdn = toJulian(dateTime);
        System.out.println(jdn);
        
        LocalDateTime convertedBack = fromJulian(jdn);
        System.out.println(convertedBack);
        System.out.println("Does it differ? " + Duration.between(dateTime, convertedBack));
    

    Output:

    2459209.5
    2020-12-26T00:00
    Does it differ? PT0S
    

    The last line contains PT0S, a period of time of 0 seconds, so the result does not differ from the starting point.

    Documentation link: JulianFields.JULIAN_DAY.