- Где я ошибаюсь в своих расчетах 41 год и отрицательное число?
Помимо использования общеизвестно проблемного и давно устаревшего класса SimpleDateFormat
и столь же устаревшего класса Date
, в вашем коде есть следующие ошибки:
- Вы анализируете
08.0
как 8 секунд 0 секунд. На моем JDK-11 SimpleDateFormat
выбирает 0 секунд и отбрасывает 8 секунд, которые я считаю правильными. SimpleDateFormat
не может разобрать один десятичный знак в секундах (только ровно три десятичных знака), поэтому решение этой ошибки полностью отбрасывает SimpleDateFormat
.
- Как уже говорили другие, у вас есть переполнение
int
в ваших умножениях. Например, 30 * 24 * 60 * 60 * 1000
должно дать 2 592 000 000, но поскольку int
не может содержать это число, вместо этого вы получите -1 702 967 296
. Поскольку это отрицательное число, следующее деление дает отрицательное число месяцев.
- Как указал Соломон Слоу в комментарии, в месяце может быть 28, 29, 30 или 31 день. Устанавливая для всех месяцев значение 30 дней, вы рискуете получить неверное количество дней и месяцев, а в конце также и годы. Когда я запустил ваш код сегодня, правильным ответом было бы 1 год, 4 месяца, 13 дней, но вместо этого я получил 19 дней, что на 6 дней больше.
- Вы не учитываете летнее время (DST) и другие временные аномалии. Это может привести к тому, что в сутках будет, например, 23 или 25 часов, что приведет к ошибке.
Или резюмируя: Ваша ошибка заключалась в том, что вы пытались сделать расчет «вручную». Математика даты и времени слишком сложна и подвержена ошибкам, чтобы сделать это. Вместо этого вы всегда должны оставлять это хорошо зарекомендовавшим себя библиотечным классам.
- Есть ли лучший способ сделать это? Моя текущая настройка не учитывает високосные годы или 365-дневный год, и мне нужно это учитывать.
Да, есть гораздо лучший способ. Лучшим способом может быть использование класса PeriodDuration
из проекта ThreeTen Extra, см. ссылку ниже. Я не собираюсь устанавливать эту библиотеку на свой компьютер прямо сейчас, поэтому просто покажу хорошее и современное решение с использованием встроенных классов:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
LocalDateTime currentDateTime = LocalDateTime.now(ZoneId.of("Australia/Sydney"));
String earliestRunTime = "2017-12-16 01:30:08.0";
LocalDateTime earliestDateTime = LocalDateTime.parse(earliestRunTime, dtf);
// We want to find a period (years, months, days) and a duration (hours, minutes, seconds).
// To do that we cut at the greatest possible whole number of days
// and then measure the period before the cut and the duration after it.
LocalDateTime cut = earliestDateTime.plusDays(
ChronoUnit.DAYS.between(earliestDateTime, currentDateTime));
Period p = Period.between(earliestDateTime.toLocalDate(), cut.toLocalDate());
Duration d = Duration.between(cut, currentDateTime);
String result = String.format("%s years, %s months, %s days, %s hours, %s minutes, %s seconds",
p.getYears(), p.getMonths(), p.getDays(),
d.toHours(), d.toMinutesPart(), d.toSecondsPart());
System.out.println(result);
Когда я только что запустил код, я получил:
1 год, 4 месяца, 13 дней, 19 часов, 26 минут, 7 секунд
В java.time, современном API даты и времени Java, Period
— это количество лет, месяцев и дней, а Duration
— это количество часов, минут, секунд и долей секунды (вплоть до наносекунд). Поскольку вы хотели оба, я использую оба класса.
Методы toXxxPart
Duration
, которые я использую, были введены в Java 9. Если вы используете Java 8 (или ThreeTen Backport), печать минут и секунд немного сложнее. Найдите java format duration
или аналогичный, чтобы узнать, как это сделать.
Я все еще не учитываю летнее время. Для этого нам нужно знать часовой пояс самой ранней строки времени выполнения, а затем использовать ZonedDateTime
вместо LocalDateTime
. В противном случае код был бы очень похож.
Ссылки
person
Ole V.V.
schedule
29.04.2019
Date
. - person Basil Bourque   schedule 25.04.2019