Информация о времени отключения даты Java


121

У меня есть объект Java Date, содержащий информацию о дате и времени. Я хочу написать метод, который отсекает информацию о времени, обрезает часы-минуты-секунды, чтобы у меня оставалась только дата.

Пример ввода:

2008-01-01 13:15:00

Ожидаемый результат:

2008-01-01 00:00:00

У тебя есть чаевые? Я пробовал сделать что-то вроде этого:

(timestamp / (24 * 60 * 60 * 1000)) * (24 * 60 * 60 * 1000)

но у меня возникли проблемы с часовым поясом.

Ответы:


178

Рекомендуется способ сделать манипуляцию дату / время, чтобы использовать Calendarобъект:

Calendar cal = Calendar.getInstance(); // locale-specific
cal.setTime(dateObject);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long time = cal.getTimeInMillis();

2
@cletus, а можно ли убрать время из объекта Date. Я хочу чистую дату, например, 01.01.2008, без временной части.
Прем

2
Я бы не сказал, что рекомендуется использовать java.util.Calendar. Sun / Oracle заменили этот класс в Java 8 новым пакетом java.time. Используйте либо это, либо Joda-Time.
Basil Bourque

3
Календарь больше не рекомендуется. См. Stackoverflow.com/a/28868089/40064, если вы используете Java 8.
Вим Деблауве,

2
К вашему сведению, неприятные старые классы даты и времени, такие как java.util.Calendarтеперь унаследованные , заменены классами java.time . См. Учебник Oracle .
Basil

148

Вы смотрели на метод усечения DateUtils в Apache Commons Lang?

Date truncatedDate = DateUtils.truncate(new Date(), Calendar.DATE);

удалит элемент времени.


9
Снова обновлена ​​ссылка (заходите на Apache! Перестаньте перемещать свои документы!)
Ogre Psalm33

6
Минус этого подхода в том, что часовой пояс, в котором выполняется операция усечения, всегда является локальным часовым поясом ПК. Поэтому, если вы работаете с календарем и датами в часовом поясе UTC или в любом часовом поясе, отличном от местного часового пояса, он может усечь дату не так, как задумано.
Cloud

как отметил @Cloud, тот факт, что я не вижу здесь экземпляра Calendar (не контролирую его), заставляет меня немного нервничать :)
Ярослав

Этот метод на удивление длинный и содержит как минимум два исправления.
Пино

42

Вы смотрели на Джоду ? Это гораздо более простой и интуитивно понятный способ работы с датой и временем. Например, вы можете легко конвертировать между (скажем) объектами LocalDateTime и LocalDate .

например (для иллюстрации API)

LocalDate date = new LocalDateTime(milliseconds).toLocalDate()

Кроме того, он решает некоторые проблемы, связанные с безопасностью потоков при использовании форматеров даты / времени, и его настоятельно рекомендуется использовать для работы с любыми проблемами даты / времени в Java.


12
Я верю, что это так. Рекомендуется использовать лучшую библиотеку, а не бороться со сломанными классами java.util (о чем свидетельствует вопрос)
Брайан Агнью

10
Конечно, это связано с тем, что Joda - это библиотека для управления датой, временем и календарем в Java. Не рекомендовать это было бы нарушением долга.
Джоэл

1
Проблема безопасности потоков очень интересна, я нашел этот пост, исследующий ошибку Oracle / Hibernate «IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)». Здесь говорится о других проблемах Oracle, которые появляются только при большой нагрузке forum.oracle.com/forums/thread.jspa?threadID=325576 Очень сложно даже создать объект с недопустимыми миллисекундами в обычном коде. Что касается меня, я думаю, что я откажусь от своей темы, используя Joda, вместо того, чтобы позволить Oracle использовать потенциально подозрительные вещи, отличные от joda.
Марк Беннетт

4
Хотел бы я проголосовать против всех, кто проголосовал за это. Кто все эти люди, которые добавляют +1 к ответу, даже не пытаясь ответить на вопрос? Ответ с использованием Джоды был бы более чем приветствуем, но это не то ...
Райан,

1
@Ryan Почему этот ответ не актуален? Вопрос запрашивал дату без времени суток. В этом ответе представлен класс, единственная цель которого - представление даты без времени суток.
Basil Bourque

35

Просто быстрое обновление в свете классов java.time, теперь встроенных в Java 8 и новее.

LocalDateTimeесть truncatedToметод, который эффективно обращается к тому, о чем вы здесь говорите:

LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)

Это выразит текущее время с точностью до минут:

2015-03-05T11:47

Вы можете использовать ChronoUnit(или TemporalUnit) меньше ДНЕЙ для выполнения усечения (поскольку усечение применяется только к временной части LocalDateTime, а не к части даты).


3
Вы можете использовать ChronoUnits только до DAYS - более высокие единицы выдадут исключение.
assylias

26
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
date = cal.getTime();

22

С Joda вы легко можете получить ожидаемое свидание.

Начиная с версии 2.7 (возможно, с тех пор, как какая-то предыдущая версия выше, чем 2.2), как отмечает комментатор, toDateMidnightустарела в пользу или удачно названа withTimeAtStartOfDay(), что делает удобный

DateTime.now().withTimeAtStartOfDay()

возможно.

Добавлена ​​выгода в виде более приятного API.

Со старыми версиями вы можете сделать

new DateTime(new Date()).toDateMidnight().toDate()

6
Методы и классы, связанные с полуночью, в Joda-Time теперь устарели. Разработчики не рекомендуют их использовать. Вместо этого используйте метод withTimeAtStartOfDay.
Basil Bourque

Спасибо, что указали. Обновил ответ, чтобы учесть это.
h7r

Вы также можете использовать toLocalDateдля получения объекта, у которого даже нет компонента времени.
Чарльз Вуд

К вашему сведению, проект Joda-Time сейчас находится в режиме обслуживания , и команда советует перейти на классы java.time . См. Учебник Oracle .
Basil Bourque

8

Я сделал усечение с новым API java8. Столкнулся с одной странной вещью но в целом обрез ...

Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.DAYS);
date = Date.from(instant);

ключевые слова: java.time, Java 8, LocalDateTime.truncatedTo ()
Александр Тейлор

1
Этот ответ дает даты только для контекста часового пояса UTC.
Basil Bourque

6

Используйте DateUtils из Apache с усечением, например:

DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DATE);

Это будет усекаться в локальном часовом поясе, а не в формате UTC, который представляет Date (getTime).
эпокс

6

Для отметок времени:

timestamp -= timestamp % (24 * 60 * 60 * 1000)

3
В этом коде есть крошечная ошибка - левая скобка размещена неправильно, это должна быть отметка времени - = отметка времени% (24 * 60 * 60 * 1000). Это отрежет временную часть. И я уверен, что этот способ более достаточен, чем создание объекта Caledar и игра с его методами или даже использование любых других сторонних библиотек
Стэн

2
Нет, это НЕ правильное решение, ОСТОРОЖНО. Это то же самое, что и округление значения даты до пола, то есть вы можете получить 28 марта для отметки времени 29 марта (в вашем регионе). Таким образом, решения на основе календаря (Apache DateUtils также используют Calendar) - единственный способ,
Дрю

Это также не будет работать, когда время меняется из-за перехода на летнее время.
Александру Северин

5

Из java.util.Date JavaDocs:

Класс Date представляет определенный момент времени с точностью до миллисекунды.

и из java.sql.Date JavaDocs:

Чтобы соответствовать определению SQL DATE, значения миллисекунды, заключенные в экземпляр java.sql.Date, должны быть «нормализованы» путем установки часов, минут, секунд и миллисекунд на ноль в конкретном часовом поясе, с которым связан экземпляр. ,

Итак, лучший подход - использовать java.sql.Date, если вам не нужна временная часть.

java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());

и вывод:

java.util.Date : Thu Apr 26 16:22:53 PST 2012
java.sql.Date  : 2012-04-26

4

ТЛ; др

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.toString()                                     // Generate a String in standard ISO 8601 format, wisely extended to append the name of the time zone in square brackets.

2008-01-01T00: 00 + 01: 00 [Европа / Париж]

Чтобы сгенерировать строку в желаемом формате, передайте DateTimeFormatter.

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.format(                                        // Generate a String representing the object’s value.
    DateTimeFormatter.ISO_LOCAL_DATE_TIME       // Built-in predefined formatter close to what you want. 
)
.replace( "T" , " " )                           // Replace the standard’s use of a 'T' in the middle with your desired SPACE character.

2008-01-01 00:00:00

подробности

Другие ответы верны, но используйте старые классы даты и времени, которые теперь устарели фреймворком java.time.

java.time

Фреймворк java.time встроен в Java 8 и новее. Большая часть функциональности java.time перенесена на Java 6 и 7 ( ThreeTen-Backport ) и дополнительно адаптирована для Android ( ThreeTenABP ).

Сначала измените входную строку, чтобы она соответствовала канонической версии формата ISO 8601. Стандартные форматы ISO 8601 используются по умолчанию в классах java.time для синтаксического анализа / генерации строк, представляющих значения даты и времени. Нам нужно заменить это ПРОБЕЛ посередине на T.

String input = "2008-01-01 13:15:00".replace( " " , "T" );  // → 2008-01-01T13:15:00

Теперь мы можем разобрать его как a LocalDateTime, где «Local» означает отсутствие конкретной местности. На входе отсутствует информация о смещении от UTC или часовом поясе.

LocalDateTime ldt = LocalDateTime.parse( input );

ldt.toString ()… 2008-01-01T13: 15: 00

Если вам не важны ни время суток, ни часовой пояс, конвертируйте в формат LocalDate.

LocalDate ld = ldt.toLocalDate();

ld.toString ()… 01.01.2008

Первый момент дня

Если вместо этого вы хотите установить время дня на первый момент дня, используйте ZonedDateTimeкласс, а затем преобразуйте его в LocalDateобъект для вызова его atStartOfDayметода. Имейте в виду, что первым моментом может быть не время 00:00:00из-за летнего времени или, возможно, других аномалий.

Часовой пояс имеет решающее значение, потому что в любой момент дата меняется в зависимости от зоны в мире. Например, несколько мгновений после полуночи в Париже - это новый день для парижан, но для канадцев это все еще «вчерашний день» в Монреале.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
LocalDate ldFromZdt = zdt.toLocalDate();
ZonedDateTime zdtStartOfDay = ldFromZdt.atStartOfDay( zoneId );

zdtStartOfDay.toString ()… 2008-01-01T00: 00: 00-05: 00 [Америка / Монреаль]

универсальное глобальное время

Чтобы увидеть этот момент сквозь призму часового пояса UTC , извлеките Instantобъект. Оба символа ZonedDateTimeи Instantбудут представлять один и тот же момент на шкале времени, но будут отображаться как два разных времени настенных часов .

An Instant- это базовый класс строительных блоков в java.time, всегда по определению в формате UTC. Используйте этот класс часто, поскольку, как правило, вы должны выполнять свою бизнес-логику, хранение данных и обмен данными в формате UTC.

Instant instant = zdtStartOfDay.toInstant();

Instant.toString ()… 2008-01-01T05: 00: 00Z

Мы видим 5 утра, а не полночь. В стандартном формате, Zконец является сокращением Zuluи означает «UTC».


О java.time

Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как java.util.Date, Calendar, и SimpleDateFormat.

Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial . И поищите в Stack Overflow множество примеров и объяснений. Спецификация - JSR 310 .

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, совместимый с JDBC 4.2 или новее. Нет необходимости в строках, нет необходимости в java.sql.*занятиях.

Где взять классы java.time?

  • Java SE 8 , Java SE 9 и более поздние версии
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • В Java 9 добавлены некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии Android связывают реализации классов java.time.
    • Для более ранней версии Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). См. Как использовать ThreeTenABP… .

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является испытательной площадкой для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как Interval, YearWeek, YearQuarter, и более .



2

Вопрос противоречивый. Он запрашивает дату без времени суток, но отображает пример со временем 00:00:00.

Joda времени

UPDATE: Joda время проект сейчас находится в режиме технического обслуживания , с командой советуя миграции в java.time классы. См. Мой другой ответ для решения java.time.

Если вместо этого вы хотите установить время дня на первый момент дня, используйте DateTimeобъект в библиотеке Joda-Time и вызовите его withTimeAtStartOfDayметод. Имейте в виду, что первым моментом может быть не время 00:00:00из-за летнего времени или, возможно, других аномалий.


Это окончательный ответ (при условии, что можно и нужно использовать Joda Time)
Клинт Иствуд

2

Просто clear()лишние поля.

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Date truncatedDate = calendar.getTime();

Из Java 8 лучшим вариантом является использование truncatedToметода LocalDateTime, например:

LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)

Это не работает, потому calendar.clear(Calendar.HOUR_OF_DAY);что не очищаются часы. Решение от @cletus работает нормально.
Arcao

Извините, вы правы, поправил, часами стоит выставлять значение. В общем, это почти так же, как
обрезать

1

Меня действительно раздражало, что новый «улучшенный» конструктор календаря не принимает int для миллисекунд, как «ужасный» старый конструктор Date. Я тогда очень рассердился и написал это:

long stuffYou = startTime.getTimeInMillis() % 1000;
startTime.setTimeInMillis(startTime.getTimeInMillis() - stuffYou);

В то время я не использовал слово «материал», но потом я обнаружил, что такое счастье:

startTime.set(Calendar.MILLISECOND, 0);

Но я все еще очень зол на это.


1

Я исправил такую ​​проблему (в зоне Восточной восьмерки (по пекинскому времени)):

private Date getTruncatedDate(Date d) {
    if (d == null) {
        return null;
    }
    long h = 60 * 60 * 1000L;
    long dateStamp = d.getTime() - (d.getTime() + 8 * h) % (24 * h);
    return new Date(dateStamp);
}

Прежде всего, вы должны четко понимать, что такое метка времени. Отметка времени - это общее количество миллисекунд с 01 января 00:00:00 1970 по Гринвичу (то же самое, что и UTC) или четверг 01 января с 08:00:00 CST 1970 по настоящее время.

Помните: отметка времени не зависит от часового пояса.

Таким образом, вы получите тот же результат с помощью следующего оператора в разных часовых поясах:

System.out.println(new Date().getTime());

И

System.out.println(new Date(0));

печатает разную информацию о времени в разных часовых поясах: если вы установите часовой пояс компьютера как UTC, вы получите

Thu Jan 01 00:00:00 UTC 1970

Но если вы установите часовой пояс как (UTC +8: 00) Пекин, Чунцин, Гонконг, Урумк, вы получите:

Thu Jan 01 08:00:00 CST 1970

Java получает отметку времени, а затем отображает информацию о дате и времени в соответствии с часовым поясом.

Для введения того, как Java отображает информацию о дате и времени в разных часовых поясах, легко понять, как транслировать информацию о времени. Вы должны получить отметку времени и принять во внимание часовой пояс при отключении информации о времени. Затем вы можете создать новый объект Date с вырезанной отметкой времени (мы можем назвать это отметкой даты), java компенсирует часовой пояс при отображении информации о дате.

Как и в зоне Восточной восьмерки (по пекинскому времени), пекинское время на 8 часов раньше, чем по Гринвичу, поэтому вам следует вычесть больше 8 часов при выполнении операции по модулю. То есть сначала вы должны получить время по Гринвичу, затем Java добавит 8 часов при отображении времени на основе настройки часового пояса вашего компьютера.


Вопрос о часовом поясе неясен и долгое время меня озадачивает. Теперь я проясняю. Надежда помогает.


2018-01-04 Метод ниже также работает.

private Date getTruncatedDate2(Date d) {
    Calendar cal = Calendar.getInstance(); // locale-specific
    cal.setTime(d);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);


return cal.getTime();

}


К вашему сведению, в этом ответе используются неприятные старые классы даты и времени, вытесненные много лет назад классами java.time .
Basil

1

Вы можете сделать это, чтобы избежать проблем с часовым поясом:

public static Date truncDate(Date date) {
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    return cal.getTime();
}

Хотя Dateобъект Java является значением метки времени, но во время усечения он будет преобразован в местный часовой пояс, поэтому вы получите удивительное значение, если ожидаете значение из часового пояса UTC.


Проблемы Calendarи Dateклассы стали унаследованными много лет назад с классами java.time, реализующими JSR 310 в Java 8 и более поздних версиях. Предлагать их использование в 2018 году - плохой совет.
Basil Bourque

0

Возможно, ответ запоздал, но вот способ сделать это одной строкой без использования каких-либо библиотек:

new SimpleDateFormat("yyyy-MM-dd").parse(new SimpleDateFormat("yyyy-MM-dd").format(YOUR_TIMESTAMP))

0

С Joda-Time, начиная с версии 2.0, вы можете использовать LocalDate.toDate().

Просто

// someDatetime is whatever java.util.Date you have.
Date someDay = new LocalDate(someDatetime).toDate();

[A] Ваш ответ избыточен. Уже отправлено Брайаном Агнью . [B] Ваш код неверен, так как он игнорирует часовой пояс, как раз проблема, поднятая в вопросе.
Basil Bourque

Может быть избыточным, но я просто привел здесь более простой пример, потому что понял, что ответ не должен быть в комментариях. Да, java.util.Date игнорирует TimeZone, но исходный вопрос был a Java Date objectтаким, что я использовал java.util.Date. Более того, он не объясняет, почему его часовой пояс проблематичен с java.util.Date (когда? Где?), Хотя он не может помочь, используя что-то, кроме Date.
lamusique 08

0

Для всех ответов, использующих Календарь, вы должны использовать его следующим образом

public static Date truncateDate(Date date) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
    return c.getTime();
}

Но я предпочитаю это:

public static Date truncateDate(Date date) {
    return new java.sql.Date(date.getTime());
}

java.sql.DateКласс притворяется , чтобы представлять дату только значение , но на самом деле действительно есть время-день с поправкой на часовой пояс UTC. Так что не совсем подходящее решение вопроса.
Basil Bourque

0

Вот простой способ получить дату без времени, если вы используете Java 8+: используйте тип java.time.LocalDate вместо Date.

LocalDate now = LocalDate.now();
 System.out.println(now.toString());

Выход:

2019-05-30

https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html


1
Использование LocalDate- хорошая идея для большинства целей, но неясно, как это будет работать с вводными данными из вопроса 2008-01-01 13:15:00.
Оле В.В.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.