2012-02-04 3 views
19

Я делаю некоторые вычисления даты в Java, используя миллисекунды. У меня нет большого опыта работы с миллисекундами, и я не могу даже определить, сколько миллисекунд в год. Вот моя попытка:Java Миллисекунды в году

private static final int MILLIS_IN_SECOND = 1000; 
    private static final int SECONDS_IN_MINUTE = 60; 
    private static final int MINUTES_IN_HOUR = 60; 
    private static final int HOURS_IN_DAY = 24; 
    private static final int DAYS_IN_YEAR = 365; //I know this value is more like 365.24... 
    private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; 


System.out.println(MILLISECONDS_IN_YEAR); //Returns 1471228928 

Я знаю, что 1 год = 31556952000 миллисекунды, поэтому мое умножение выключен каким-то образом.

Может ли кто-нибудь указать, что я делаю неправильно? Должен ли я использовать длинный?

+2

Почему бы просто не попробовать? Или посмотрите, какое максимальное значение имеет 32-разрядное целое число со знаком? –

+0

@Dave Newton Я пробовал, и код возвращает значение, которое, как я знаю, неверно. Int работает в миллисекундах в год, однако я вычисляю неправильное значение. –

+0

Предположительно високосные годы не имеют значения ... –

ответ

28

Должен ли я использовать длинный?

Да. Проблема в том, что с MILLIS_IN_SECOND и т. Д. Все int s, когда вы их умножаете, вы получаете int. Вы конвертируете это int в long, но только после умножение int уже привело к неправильному ответу.

Чтобы это исправить, вы можете бросить первый один к long:

private static final long MILLISECONDS_IN_YEAR = 
     (long)MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR 
     * HOURS_IN_DAY * DAYS_IN_YEAR; 
+1

Отличное объяснение. Так ли броски превращают ints задолго до их умножения? –

+1

@ kmb385 Когда вы умножаете что-то большее, ваш результат всегда будет более крупным. – kechapito

+5

@ kmb385: Да, точно. Или, скорее, я полагаю, что сам cast * просто превращает * first * 'int' в' long', но после этого Java будет преобразовывать каждый 'int' в' long', прежде чем умножать его на 'long '. – ruakh

4

Вам нужно долго. Интс обертывает около 2 миллиардов.

7
private static final long MILLISECONDS_IN_YEAR = MILLIS_IN_SECOND * ... 

Все операнды с правой стороны являются int s, поэтому умножение выполняется с 32-разрядными целыми числами, которые переполняется. Перенесите первый на long, и вы получите ожидаемое значение.

private static final long MILLISECONDS_IN_YEAR = (long)MILLIS_IN_SECOND * ... 
+0

Или просто используйте 'Long' для всех остальных, а не' int', 16 лишних байтов будут прокляты! –

+0

Спасибо за помощь. Я не знал, что int переполнен, я думал, что получаю ошибку времени выполнения. –

+0

@BrianRoach: Я не знаю, мне было бы немного досадно, что я должен был бы наложить 'HOURS_IN_DAY' на' int', чтобы я мог передать его в функцию 'int'-expecting, только потому, что у кого-то было ошибочное представление о сохранении своего типа в соответствии с типом 'MILLISECONDS_IN_YEAR'. – ruakh

4

Вы переполняете тип int. В Java результат примитивной арифметической операции над двумя int s - это int. Тип операндов решает это, а не тип переменной результата. Попробуйте:

private static final int MILLIS_IN_SECOND = 1000; 
private static final int SECONDS_IN_MINUTE = 60; 
private static final int MINUTES_IN_HOUR = 60; 
private static final int HOURS_IN_DAY = 24; 
private static final int DAYS_IN_YEAR = 365; //I know this value is more like 365.24... 
private static final long MILLISECONDS_IN_YEAR = (long) MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; 
+0

Это сработало. Спасибо. –

0

попробовать это

int MILLIS_IN_SECOND = 1000; 
    int SECONDS_IN_MINUTE = 60; 
    int MINUTES_IN_HOUR = 60; 
    int HOURS_IN_DAY = 24; 
    int DAYS_IN_YEAR = 365; 

    long MILLISECONDS_IN_YEAR = (long) MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR; 

    System.out.println(MILLISECONDS_IN_YEAR); // Returns 31536000000 
8

В то время как другие уже указывали arithmetic overflow, вы также можете попробовать TimeUnit, чтобы решить проблема:

Calendar calendar = Calendar.getInstance(); 
calendar.set(Calendar.YEAR, year); 
int daysInYear = calendar.getActualMaximum(Calendar.DAY_OF_YEAR); 
System.out.println(TimeUnit.DAYS.toMillis(daysInYear)); 
2

К fi x это, вы можете положить письмо L после первого: например.1000L

long MILLS_IN_YEAR = 1000L * 60 * 60 * 24 * 365; // Returns 31536000000 
19

Если на андроид, я предлагаю:

android.text.format.DateUtils

DateUtils.SECOND_IN_MILLIS 
DateUtils.MINUTE_IN_MILLIS 
DateUtils.HOUR_IN_MILLIS 
DateUtils.DAY_IN_MILLIS 
DateUtils.WEEK_IN_MILLIS 
DateUtils.YEAR_IN_MILLIS 
+6

В документах класса для 'DateUtils.YEAR_IN_MILLIS' говорится:« Эта константа на самом деле составляет 364 дня, а не год! » –

1

31.536.000.000

Учитывая обычный год 365 дней с 24-часовым рабочим днем ​​и никаких аномалий для учета, таких как Daylight Savin g Время (DST), год 31,536,000,000 миллисекунд. Не 31,556,952,000, как вы предлагаете в своем Вопросе.

31.536.000.000 = (365 * 24 * 60 * 60 * 1000)

високосный год с 366 дней будет 31,622,400,000 миллисекунды.

31.622.400.000 = (366 * 24 * 60 * 60 * 1000)

java.time

Современные классы java.time вытеснять старые классы даты и время в комплект с самыми ранними версиями Java. Те старые классы оказались запутанными и хлопотными.

ChronoUnit

Високосный год и другие аномалии могут означать неожиданное количество миллисекунд, в течение года. Поэтому вы должны позволить java.time делать фактический расчет, если точность важна в вашей ситуации.

Класс ChronoUnit может рассчитать истекшее время в определенной единице.

long millisInYear = ChronoUnit.MILLIS.between(start , stop); 

Нам необходимо определить точный момент начала первого дня года и следующего года. Мы делаем это, пройдя класс LocalDate, который представляет значение только для даты без времени и без часового пояса.

LocalDate startLd = LocalDate.of (2015 , 1 , 1); 
LocalDate stopLd = startLd.plusYears (1); 

Назначая часовой пояс (ZoneId) мы получаем ZonedDateTime объекты для конкретных моментов на временной шкале.

ZoneId z = ZoneId.of ("America/Montreal"); 
ZonedDateTime start = startLd.atStartOfDay (z); 
ZonedDateTime stop = stopLd.atStartOfDay (z); 

Наконец, вычислите прошедшее время в миллисекундах.

long millisInYear = ChronoUnit.MILLIS.between (start , stop); 

старт: 2015-01-01T00: 00-05: 00 [Америка/Монреаль] | остановка: 2016-01-01T00: 00-05: 00 [Америка/Монреаль] | millisInYear: 31536000000

Смежные вопросы