2

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.

user1809095
  • 381
  • 1
  • 5
  • 15
  • Please note I have been through following, they are related but they don't provide exact answer for "Months". http://stackoverflow.com/questions/8211744/convert-time-interval-given-in-seconds-into-more-human-readable-form http://stackoverflow.com/questions/19097484/formula-to-calculate-number-of-years-months-days-from-long-milliseconds – user1809095 Dec 18 '15 at 19:07
  • 4
    You'll need to store the information about how many days are in each month, and the rules for which years are leap years, and use that to calculate the month. If you're not going to get that functionality from some place where that's already been done, you'll need to do it yourself. – azurefrog Dec 18 '15 at 19:11
  • Probably I will need to do myself, right. I can stores information (for days in Months) also but how can I construct some formula because I will not know that I have to divide or take mod with 30 or 31? Because I don't know the month. – user1809095 Dec 18 '15 at 19:17
  • Why don't you want to use java internals (say Calendar)? Note that your "year" calculation is wrong because of leap years –  Dec 18 '15 at 19:29
  • Try this again in 14 days and I think you will even get the wrong year. – Tim Bender Dec 18 '15 at 19:42
  • 4
    It's a really complicate task. Except from leap year there is daylight saving time, there are regional changes in timezone and there are leap seconds. Go and trust in java standard classes. – notes-jj Dec 18 '15 at 20:18

3 Answers3

4

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.

  • 1
    Um ... he's specifically asking if he can do it WITHOUT external libraries. – scottb Dec 18 '15 at 20:34
  • 2
    GregorianCalendar is not external but part of JDK. And normally we get the calendar as instance like this: Calendar c = Calendar.getInstance(); – Laszlo Hirdi Dec 18 '15 at 20:50
  • 1
    Please refer to the original post: *"For some reason, I need to use only currentTimeMillis function to calculate and I don't want to use other functions or external libraries. "* – scottb Dec 18 '15 at 21:01
  • As scottb said I specifically mentioned to use only currentTimeMillis function. – user1809095 Dec 19 '15 at 08:09
2

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.

scottb
  • 9,908
  • 3
  • 40
  • 56
1

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

}
Rustam
  • 1,397
  • 1
  • 13
  • 17