Skip to content

Commit

Permalink
Fixed precision of fromUnixMillis (fixes #4)
Browse files Browse the repository at this point in the history
  • Loading branch information
igr committed Jun 4, 2024
1 parent eabfae6 commit f8e02a8
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 4 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ java {
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.+'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.hamcrest:hamcrest:2.2'
}

tasks.named('test') {
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/jodd/julianday/DayValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class DayValue {
*/
private final int day;

/**
/**
* Returns integer part of the day.
*/
public int day() {
Expand Down Expand Up @@ -91,4 +91,10 @@ public String toString() {
", time=" + this.time +
'}';
}

public static DayValue of(final double value) {
final int day = (int) value;
final double time = value - day;
return new DayValue(day, time);
}
}
26 changes: 26 additions & 0 deletions src/main/java/jodd/julianday/JulianDay.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jodd.julianday;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;

Expand Down Expand Up @@ -158,6 +159,12 @@ public LocalDateTime toGregorianDate() {
return LocalDateTime.of(d[0], d[1], d[2], t[0], t[1], t[2], t[3] * 1_000_000);
}

/**
* Converts Julian Day to an instant.
*/
public Instant toInstant() {
return Instant.ofEpochMilli(this.toUnixMilliseconds());
}
/**
* Converts Julian Day to Unix time in milliseconds.
*/
Expand Down Expand Up @@ -240,6 +247,13 @@ public static JulianDay ofUnixMilliseconds(final long milliseconds) {
return JulianDay.of(JulianDayFunctions.fromUnixMillis(milliseconds));
}

/**
* Creates Julian Day from an instant.
*/
public static JulianDay ofInstant(final Instant instant) {
return JulianDay.ofUnixMilliseconds(instant.toEpochMilli());
}

/**
* Creates Julian Day from the current Gregorian calendar date and time.
*/
Expand Down Expand Up @@ -293,20 +307,32 @@ public static JulianDay ofReducedJulianDay(final DayValue value) {
return JulianDay.REDUCED_JULIAN_DAY_0.add(value);
}

public static JulianDay ofReducedJulianDay(final double value) {
return ofReducedJulianDay(DayValue.of(value));
}

/**
* Creates Julian Day from the precise value of Modified Julian Day (MJD).
*/
public static JulianDay ofModifiedJulianDay(final DayValue value) {
return JulianDay.MODIFIED_JULIAN_DAY_0.add(value);
}

public static JulianDay ofModifiedJulianDay(final double value) {
return ofModifiedJulianDay(DayValue.of(value));
}

/**
* Creates Julian Day from the precise value of truncated Julian Day (TJD).
*/
public static JulianDay ofTruncatedJulianDay(final DayValue value) {
return JulianDay.TRUNCATED_JULIAN_DAY_0.add(value);
}

public static JulianDay ofTruncatedJulianDay(final double value) {
return ofTruncatedJulianDay(DayValue.of(value));
}

// </editor-fold>

// <editor-fold desc="equals & hashCode & clone" defaultstate="collapsed">
Expand Down
16 changes: 13 additions & 3 deletions src/main/java/jodd/julianday/JulianDayFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ static double mod(final double a, final double b) {
/**
* High-precision calculation of Julian Day from given day and time value.
* We can't simply add day and time, because of floating point precision:
* day is a big number and time is a small number. Hence, we need to add them separately.
* day is a big number, and time is a small number. Hence, we need to add them separately.
*/
static JulianDay julianDay(final double dayAtMidnight, final double timeOfDay) {
final double jd = dayAtMidnight + timeOfDay;
Expand Down Expand Up @@ -176,8 +176,18 @@ static long toUnixMillis(final JulianDay jd) {

/**
* Converts Unix time in milliseconds to Julian Day.
* This is a high-precision conversion.
*/
static double fromUnixMillis(final long unixMillis) {
return (unixMillis / 8_6400_000.0) + J1970;
static DayValue fromUnixMillis(final long unixMillis) {
final int intPart = (int) (unixMillis / 8_6400_000L);
final int delta = (int) (unixMillis - intPart * 8_6400_000L);
final double time = delta / 8_6400_000.0;

if (time > 0.5) {
return new DayValue(intPart + 2440588, time - 0.5);
} else {
return new DayValue(intPart + 2440587, time + 0.5);
}
// return (unixMillis / 8_6400_000.0) + J1970;
}
}
28 changes: 28 additions & 0 deletions src/test/java/jodd/julianday/Issue4Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package jodd.julianday;

import org.junit.jupiter.api.Test;

import java.time.Instant;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

class Issue4Test {

public static Instant fromMJD(final double mjd) {
return JulianDay.ofModifiedJulianDay(mjd).toInstant();
}

public static double toMJD(final Instant instant) {
return JulianDay.ofInstant(instant).valueAsModifiedJulianDay().toDouble();
}

@Test
public void test_time_convertsToMjd() {
assertThat(toMJD(Instant.parse("2016-12-31T21:36:00Z")), is(57753.9));
assertThat(toMJD(Instant.parse("2017-01-01T00:00:00Z")), is(57754.0));
assertThat(toMJD(Instant.parse("2017-01-01T02:24:00Z")), is(57754.1));
assertThat(toMJD(Instant.parse("2017-01-01T04:48:00Z")), is(57754.2));
assertThat(toMJD(Instant.parse("2017-01-01T07:12:00Z")), is(57754.3));
}
}

0 comments on commit f8e02a8

Please sign in to comment.