如何在 Java 日历 API 中使用儒略日数?

2022-01-11 00:00:00 timestamp date calendar java julian-date

儒略日数是一种将时间戳表示为自 UTC 时间(公元前 4713 年 1 月 1 日中午)以来的连续天数(和小数天数)的方法.Java 7 SE API 不包含对这种格式的支持.使用过 SQLite 数据库的开发者可能使用过 strftime() 函数提供的原生 Julian Day 支持.

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:

  • 可以在原始数据类型(双精度)中以毫秒精度表示日期和时间
  • 一年中的几天比一天中的几秒钟更具体
  • 如果这种精度不重要,则可以绕过闰秒"问题
  • 日期之间的天数算术是微不足道的;排序优先级很容易确定
  • 非常轻量级

缺点

  • Java 日期/时间 API 没有对 JDN 的内置支持
  • 不适合非常精确的时间测量
  • 仅为 UTC 定义,必须从 UTC 映射到本地时间
  • 不适合向最终用户展示;必须在显示前转换/格式化

儒略日数字常用于天文计算,其定义高度标准化并被接受.同样,修改后的儒略日数(从 UTC 时间 1858 年 11 月 17 日午夜开始计算)是标准定义并用于航空航天应用(参见 http://tycho.usno.navy.mil/mjd.html).

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).

对于广泛使用日期/时间算术或时间排序的应用程序(或者如果持久化轻量级原语比持久化时间戳更有吸引力),在内部将日期和时间表示为 JDN 或 MJD 可能对您有意义.

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.

以下代码定义了有助于使用 Java 日期/时间/日历 API 的儒略日数字或修改的儒略日数字的函数.该代码基于 Jean Meeus 的Astronomical Algorithms",1991 年第 1 版中发表的算法.

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;
    }
}

这里也提供了这个答案:如何在 Java 日期和儒略日数之间进行转换?.在当前帖子中,提供了该算法的参考资料以及更多讨论.上述算法的实现也不包含 Java API 依赖项(除了数学函数).

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).

推荐答案

我知道这不是 Java 日历 API,但也许你应该试试 Jodd 工具.

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());

这非常适合:

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

阅读更多.

相关文章