Java 如何在不使用外部库的情况下从毫秒获取月份

Java How to get Month from milliseconds WITHOUT using external Libraries

我正在使用 System.currentTimeMillis() 来获取自 1970 年以来的毫秒数,我能够获取当前的小时、分钟、秒和年份。例如,我使用以下公式获取年份,它 returns 2015:

          ((((currTimeInMilliSec / 1000) / 3600) / 24) / 365) + 1970

但是,考虑到闰年和不同月份(如 28、29、30、31)的不同天数,我如何从毫秒计算月份。

注意:由于某些原因,我只需要使用 currentTimeMillis 函数来计算,我不想使用其他函数或外部库。我也看过相关的帖子,但没有找到确切的答案。

使用公历。

GregorianCalendar c = new GregorianCalendar();

c.setTimeInMillis(1l);

int month = c.get(Calendar.MONTH);

这个returns0,就是一月。想象一个包含一年中的 12 个月的数组。

是的,这是可能的。

有一些天文算法可以实现儒略日数字和日期时间戳之间的数字转换。我熟悉的算法由 J. Meeus 在他的 天文算法,第 2 版中发表。 该算法将儒略日数转换为表示相应的整数向量:

  • MONTH_IN_YEAR (1-12)
  • DAY_IN_MONTH(1-28,29,30,31视情况而定)
  • HOUR_IN_DAY (0-23)
  • MINUTE_IN_HOUR (0-59)
  • SECOND_IN_MINUTE (0-59)
  • MILLISECOND_IN_SECOND (0-999)

因为 POSIX 时间和儒略日数都是日期序列(连续时间单位的计数),所以相互转换很容易。因此,使用此算法的第一步是将 POSIX 时间(自 1970 年 1 月 1 日午夜以来的毫秒数)转换为儒略日数(自公元前 4714 年 11 月 24 日以来的天数,在公历中) .这是微不足道的,因为您只需将毫秒转换为天数并调整纪元。

常量如下:

/** Accessor index for year field from a date/time vector of ints. */
public static final int YEAR = 0;

/** Accessor index for month-in-year field from a date/time vector of ints */
public static final int MONTH = 1;

/** Accessor index for day-in-month field from a date/time vector of ints */
public static final int DAY = 2;

/** Accessor index for hour-in-day field from a date/time vector of ints */
public static final int HOURS = 3;

/** Accessor index for minute-in-hour field from a date/time vector of ints */
public static final int MINUTES = 4;

/** Accessor index for second-in-minute field from a date/time vector of ints */
public static final int SECONDS = 5;

/** Accessor index for millis-in-second field from a date/time vector of ints */
public static final int MILLIS = 6;

/** The POSIX Epoch represented as a modified Julian Day number */
public static final double POSIX_EPOCH_AS_MJD = 40587.0d;

这里是将儒略日数(以 double 形式提供)转换为整数向量的算法的方法。在下面的代码中,您可以将 trunc() 函数替换为 Math.floor() 并保留正确的行为:

public static int[] toVectorFromDayNumber(double julianDay) {

    int[] ymd_hmsm = {YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLIS};
    int a, b, c, d, e, z;
    double f, x;

    double jd = julianDay + 0.5;

    z = (int) trunc(jd);
    f = (jd - z) + (0.5 / (86400.0 * 1000.0));

    if (z >= 2299161) {
        int alpha = (int) trunc((z - 1867216.25) / 36524.25);
        a = z + 1 + alpha - (alpha / 4);
    } else {
        a = z;
    }

    b = a + 1524;
    c = (int) trunc((b - 122.1) / 365.25);
    d = (int) trunc(365.25 * c);
    e = (int) trunc((b - d) / 30.6001);

    ymd_hmsm[DAY] = b - d - (int) trunc(30.6001 * e);
    ymd_hmsm[MONTH] = (e < 14) 
            ? (e - 1) 
            : (e - 13);
    ymd_hmsm[YEAR] = (ymd_hmsm[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 = trunc(f);
        ymd_hmsm[i] = (int) x;
        f = f - x;
    }
    return ymd_hmsm;
}

例如,如果使用儒略日编号 2457272.5 调用该函数,它将 return 以下整数向量表示 UTC 2015 年 9 月 7 日(劳动节)午夜:

[ 2015, 9, 7, 0, 0, 0, 0 ]

编辑:Meeus 算法的一个显着之处在于它正确地考虑了闰年和世纪日(包括世纪日例外)。它仅使用整数和浮点运算,并且很可能比需要来自 Java 日历或日期时间 API 的对象实例化的解决方案更高效。

我的变体:

public class Main  {

    public static class MyDate {
        int month;
        int day;

        public MyDate(int month, int day) {
            this.month = month;
            this.day = day;
        }
    }

    public static final int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    public static void main(String[] args) {
        long millis = System.currentTimeMillis();
        long days = millis / 86400000;
        long millisToday = millis % 86400000;
        int yearsPassedApprox = (int) days / 365;
        int daysPassedThisYear = (int) (days - (yearsPassedApprox * 365 + leapYearsCount(yearsPassedApprox)));
        int year = yearsPassedApprox + 1970;
        MyDate myDate = getMonthAndDay(year, daysPassedThisYear);
        int hours = (int) (millisToday / 3600000);
        int minutes = (int) ((millisToday % 3600000) / 60000);
        int seconds = (int) ((millisToday % 60000) / 1000);


        System.out.println("Year: " + year);
        System.out.println("Month: " + myDate.month);
        System.out.println("Day: " + myDate.day);
        System.out.println("Hour: " + hours);
        System.out.println("Minutes: " + minutes);
        System.out.println("Seconds: " + seconds);
    }

    public static MyDate getMonthAndDay(int year, int daysPassedThisYear) {
        int i;
        int daysLeft = daysPassedThisYear;
        boolean leapYear = isLeapYear(year);
        for (i = 0; i < daysInMonth.length; i++) {
            int days = daysInMonth[i];
            if (leapYear && i == 1) {
                days++;
            }
            if (days <= daysLeft) {
                daysLeft -= days;
            } else {
                break;
            }
        }
        return new MyDate(i + 1, daysLeft + 1);
    }

    public static int leapYearsCount(long yearsPassed) {
        int count = 0;
        for (int i = 1970; i < 1970 + yearsPassed ; i++) {
            if (isLeapYear(i)) {
                count++;
            }
        }
        return count;
    }

    public static boolean isLeapYear(int year) {
        return (year % 4 == 0 && !(year % 100 == 0)) || year % 400 == 0;
    }

}