Java Как получить месяц из миллисекунд БЕЗ использования внешних библиотек

Я использую System.currentTimeMillis(), чтобы получить количество миллисекунд с 1970 года, я могу получить текущие часы, минуты, секунды и год. Например, я получаю год, используя следующую формулу, и он возвращает 2015 год:

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

Но как я могу рассчитать месяц из миллисекунд, учитывая високосный год и разное количество дней в разных месяцах, например 28,29,30,31.

Примечание. По какой-то причине мне нужно использовать только функцию currentTimeMillis для расчета, и я не хочу использовать другие функции или внешние библиотеки. Также я просмотрел соответствующие сообщения, но не нашел точного ответа.


person user1809095    schedule 18.12.2015    source источник
comment
Обратите внимание, что я следил за ними, они связаны, но не дают точного ответа на месяцы. stackoverflow.com/questions/8211744/ stackoverflow.com/questions/19097484/   -  person user1809095    schedule 18.12.2015
comment
Вам нужно будет хранить информацию о том, сколько дней в каждом месяце и правилах, для которых годы являются високосными, и использовать это для расчета месяца. Если вы не собираетесь получать эту функциональность из какого-то места, где это уже было сделано, вам нужно будет сделать это самостоятельно.   -  person azurefrog    schedule 18.12.2015
comment
Наверное, мне нужно будет сделать самому, правильно. Я также могу хранить информацию (дни в месяцах), но как мне построить какую-то формулу, потому что я не буду знать, что мне нужно разделить или взять мод с 30 или 31? Потому что я не знаю месяца.   -  person user1809095    schedule 18.12.2015
comment
Почему вы не хотите использовать внутренности Java (скажем, Календарь)? Обратите внимание, что ваш расчет года неверен из-за високосных лет   -  person    schedule 18.12.2015
comment
Попробуйте еще раз через 14 дней, и я думаю, вы даже ошибетесь в годе.   -  person Tim Bender    schedule 18.12.2015
comment
Это действительно сложная задача. Кроме високосного года, есть переход на летнее время, региональные изменения часового пояса и дополнительные секунды. Идите и доверьтесь стандартным классам Java.   -  person notes-jj    schedule 18.12.2015


Ответы (3)


Мой вариант:

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

}
person Rustam    schedule 18.12.2015

Используйте григорианский календарь.

GregorianCalendar c = new GregorianCalendar();

c.setTimeInMillis(1l);

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

Это возвращает 0, то есть январь. Представьте массив с 12 месяцами года.

person Aronnax    schedule 18.12.2015
comment
Гм... он специально спрашивает, можно ли это сделать БЕЗ внешних библиотек. - person scottb; 18.12.2015
comment
GregorianCalendar не является внешним, а является частью JDK. Обычно мы получаем экземпляр календаря следующим образом: Calendar c = Calendar.getInstance(); - person Laszlo Hirdi; 18.12.2015
comment
Пожалуйста, обратитесь к исходному сообщению: По какой-то причине мне нужно использовать только функцию currentTimeMillis для вычисления, и я не хочу использовать другие функции или внешние библиотеки. - person scottb; 19.12.2015
comment
Как сказал scottb, я специально упомянул об использовании только функции currentTimeMillis. - person user1809095; 19.12.2015

Да, это возможно.

Существуют астрономические алгоритмы, которые позволяют выполнять числовой перевод между номером дня по юлианскому календарю и отметкой даты и времени. Алгоритм, с которым я знаком, был опубликован Дж. Миусом в его Астрономических алгоритмах, 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 (миллис с полуночи 1 января 1970 г.) в число дней по юлианскому календарю (количество дней с 24 ноября 4714 г. до н.э. в пролептическом григорианском календаре). Это тривиально, так как вы просто конвертируете миллисекунды в дни и настраиваете эпоху.

Вот константы:

/** 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, она вернет следующий вектор целых чисел, представляющий полночь 7 сентября 2015 года (день труда) в формате UTC:

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

Редактировать: замечательная вещь в алгоритме Meeus заключается в том, что он правильно учитывает как високосные годы, так и столетние дни (включая исключение столетий). Он использует только целочисленную арифметику и арифметику с плавающей запятой и, скорее всего, будет более производительным, чем решения, требующие создания объектов из Java Calendar или Date-Time API.

person scottb    schedule 18.12.2015