【问题标题】:How do I use Julian Day Numbers with the Java Calendar API?如何在 Java 日历 API 中使用儒略日数?
【发布时间】:2013-02-05 23:05:49
【问题描述】:

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

将时间戳表示为儒略日数的优点包括:

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

缺点

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

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

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

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

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

这里也提供了这个答案:How can I convert between a Java Date and Julian day number?。在当前帖子中,提供了该算法的参考资料以及更多讨论。上述算法的实现也不包含 Java API 依赖项(除了数学函数)。

【问题讨论】:

  • 这里有什么问题?
  • 不,linked Question 错误地将 1970 年的整秒数作为儒略日期。两种完全不同的野兽。

标签: java date calendar timestamp julian-date


【解决方案1】:

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

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

Read more.

【讨论】:

    【解决方案2】:

    java.time

    Java 8 及更高版本中内置的 java.time 框架取代了与早期 Java 版本捆绑的旧日期时间类。见Oracle Tutorial。大部分功能已在 ThreeTen-Backport 中向后移植到 Java 6 和 7,并在 ThreeTenABP 中进一步适应 Android。

    java.time 类包括java.time.temporal.JulianFields。此类提供了TemporalField 的三个实现,以有限地支持 Julian 仅日期值(无时间)。因此,您可以获得整数天数,而不是问题中请求的 double。仔细阅读该类文档,以确保它符合您的期望。请注意,与大多数其他 java.time 类不同,这些 Julian 类忽略任何与 UTC 的偏移量或时区信息(始终被视为本地日期)。

    • JULIAN_DAY → 从第 0 天(即儒略历中的公元前 4713 年 1 月 1 日)算起的天数 (-4713-11-24 Gregorian)。
    • MODIFIED_JULIAN_DAY → 与 JULIAN_DAY 类似,但减去 2_400_000.5(基本上去掉 Julian 日期数字的前两位)。请注意,此处的结果比上述项目的儒略日期数少一个 (-1)
    • RATA_DIE → 与上述两项类似,它是一个纪元的天数。但这里的纪元是0001-01-01ISO 8601 日期。

    在本例中,我们从 ISO 8601 日期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 );
    

    本地日期:1970-01-01 |朱利安日期:2440588 |修改朱利安日期:40587 |模数:719163

    三十加分

    ThreeTen-Extra 项目是未来可能添加到 java.time 的试验性试验场。该名称来自定义 java.time 的JSR 310

    此库在其Julian calendar system (Chronology) 中包含对朱利安日期的额外支持。与 Java 8 中的支持一样,此库仅限于仅日期值(没有部分日期或时间)。

    使用这个库,您可以实例化JulianDate 对象。

    许多方法和功能供您检查。

    【讨论】:

    • 儒略历(在 Threeten-Extra 中)与原始问题完全无关,另请参阅 OP 对 Erik 给出的其他答案的评论。
    【解决方案3】:

    如果您愿意移出核心 JDK 类,那么Joda 可以是一个解决方案。 Joda 支持儒略历系统。从他们的文档页面:

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

    那将是儒略历系统中的黑斯廷斯战役 1066。

    【讨论】:

    • 必须区分儒略日期和儒略日数。前者是根据儒略历系统计算的时间点,由凯撒大帝组织并在欧洲使用,直到 1582 年公历改革,这标志着公历取代它的年份。因此,我们有儒略历和公历(因为黑斯廷斯战役发生在公历改革之前,所以用历史上准确的儒略历来表示它是合适的)。儒略日数只是对自公元前 4713 年以来经过的连续天数的简单计数。
    猜你喜欢
    • 2011-03-01
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多