简体   繁体   中英

Java How to get Month from milliseconds WITHOUT using external Libraries

I am using System.currentTimeMillis() to get number of milliseconds since 1970, I am able to get current Hour, Minute, seconds and year. For example I am getting Year using following formula and it returns 2015:

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

But how can I calculate Month from milliseconds keeping in considering Leap Year and different number of days in different months like 28,29,30,31.

Note: For some reason, I need to use only currentTimeMillis function to calculate and I don't want to use other functions or external libraries. Also I have gone through related posts but didn't find exact answer.

Use GregorianCalendar.

GregorianCalendar c = new GregorianCalendar();

c.setTimeInMillis(1l);

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

This returns 0, that is January. Imagine an array with the 12 months of the year.

Yes, this is possible.

There are astronomical algorithms that enable the numerical translation between a Julian Day Number and a date-time stamp. The algorithm with which I am familiar was published by J. Meeus in his Astronomical Algorithms, 2nd Ed. This algorithm will convert a Julian Day Number to a vector of integers representing the corresponding:

  • YEAR
  • MONTH_IN_YEAR (1-12)
  • DAY_IN_MONTH (1-28,29,30,31 as appropriate)
  • HOUR_IN_DAY (0-23)
  • MINUTE_IN_HOUR (0-59)
  • SECOND_IN_MINUTE (0-59)
  • MILLISECOND_IN_SECOND (0-999)

Because both POSIX time and Julian Day Numbers are date serials (counts of consecutive time units) they are trivial to interconvert. Thus, the 1st step for using this algorithm would be to convert POSIX time (millis since midnight Jan 1, 1970) to a Julian Day Number (count of days since November 24, 4714 BC, in the proleptic Gregorian calendar). This is trivial to do since you simply convert from millis to days and adjust the epoch.

Here are the constants:

/** 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;

And here is the method for the algorithm that converts a Julian Day Number (supplied as a double ) to a vector of integers. In the code below, you can substitute the trunc() function with Math.floor() and retain the correct behavior:

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

For example, if the function is called with the Julian Day Number 2457272.5, it would return the following vector of integers representing midnight, Sept 7, 2015 (Labor Day) in UTC:

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

Edit: A remarkable thing about the Meeus algorithm is that it correctly accounts for both Leap Years and century days (including the century day exception). It uses only integer and floating point arithmetic and is very likely to be more performant than solutions which require object instantiations from the Java Calendar or Date-Time APIs.

My variant:

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM