TimeUnit преобразование из Миллисекунд в Дни не работает для меня
Я пытаюсь получить разницу двух временных меток в днях, и TimeUnit возвращает совершенно неверные результаты для меня.
Вот мой код:
long ts = 1522242239952L;
long now = 1527274162820L;
long difference = now - ts;
int ageInDays = (int) TimeUnit.MILLISECONDS.convert(difference, TimeUnit.DAYS);
int ageInDays2 = (int) (difference/(1000 * 60 * 60 * 24));
System.out.println(ageInDays);
System.out.println(ageInDays2);
Выход:
-1756844032
58
Почему расчет TimeUnit такой неправильный?
3 ответа
Потому что вы используете TimeUnit.convert в обратном направлении. Пытаться
TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);
или просто
TimeUnit.MILLISECONDS.toDays(difference);
Ссылка: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/TimeUnit.html
Вы неправильно поняли документацию TimeUnit:
длинное преобразование (long sourceDuration, TimeUnit sourceUnit)
Преобразовать данное время в данной единице в эту единицу.
Ваши исходные единицы - МИЛЛИСЕКУНДЫ, и вы хотите ДНИ, чтобы строка читалась
int ageInDays = (int) TimeUnit.DAYS.convert(difference, TimeUnit.MILLISECONDS);
Почему расчет TimeUnit такой неправильный?
Другие ответы верны: вы выполняли обращение, противоположное тому, которое вы намеревались. TimeUnit.MILLISECONDS.convert(difference, TimeUnit.DAYS);
конвертируется из дней в миллисекунды.
Но как получилось, что вы получили отрицательное число? Это из-за int
переполнение. Обратное преобразование было выполнено правильно и дало 434 758 135 795 200 000. Это намного больше, чем 32-битный int
может держать. Поэтому, когда вы бросаете int
, самые значимые биты отрублены. Случайно, бит, который закончил тем, что был битом знака int
был 1, что означает отрицательный. Я обычно избегаю делать такой бросок без проверки дальности. Вы можете приобрести ту же привычку. Math.toIntExact()
здесь очень полезно
Как исправить: java.time
Другие ответы в основном верны. Тем не менее, я хотел бы предоставить пару предложений.
Если вы считаете, что время, скажем, с 16:04 один день до 16:04 следующего дня составляет 1 день, то вам нужно принять во внимание часовой пояс, чтобы получить правильный результат. Например:
ZoneId zone = ZoneId.of("Africa/Mogadishu");
ZonedDateTime birth = Instant.ofEpochMilli(ts).atZone(zone);
long ageInDays = ChronoUnit.DAYS.between(birth, ZonedDateTime.now(zone));
System.out.println(ageInDays);
В этом случае результат запуска кода прямо сейчас также тот, который вы ожидали:
58
Если, с другой стороны, вы определяете 1 день как 24 часа, независимо от того, что показывают настенные часы, вот способ получить возраст с точностью до секунды. Вы всегда можете преобразовать в дни после.
Instant birth = Instant.ofEpochMilli(ts);
Duration age = Duration.between(birth, Instant.now());
System.out.println(age);
System.out.println(age.toDays());
Выход:
PT1398H52M13.994841S
58
Первая из двух строк гласит, что возраст составляет 1398 часов 52 минуты 13,994841 секунды. Вторая строка согласуется с результатом ранее.
Мысли о двух фрагментах также могут быть объединены и смешаны. В общем, я думаю, что java.time
дает некоторые возможности, которые было бы сложнее получить с TimeUnit
и некоторый довольно ясный и понятный код, в котором вы не могли бы легко совершить ту же ошибку, что и в вопросе.
Ссылки:
- Учебник Oracle: Дата и время, объясняющие, как использовать
java.time
, - Документация
Math.toIntExact