Как создать объект Java Date полночь сегодня и полночь завтра?


178

В моем коде мне нужно найти все мои вещи, которые произошли сегодня. Поэтому мне нужно сравнить даты с сегодняшнего дня в 00:00 утра (полночь рано этим утром) до 12:00 вечера (полночь сегодня вечером).

Я знаю ...

Date today = new Date(); 

... получает меня прямо сейчас. И ...

Date beginning = new Date(0);

... дает мне нулевое время 1 января 1970 года. Но как проще получить нулевое время сегодня и нулевое время завтра?

ОБНОВИТЬ; Я сделал это, но наверняка есть более простой способ?

Calendar calStart = new GregorianCalendar();
calStart.setTime(new Date());
calStart.set(Calendar.HOUR_OF_DAY, 0);
calStart.set(Calendar.MINUTE, 0);
calStart.set(Calendar.SECOND, 0);
calStart.set(Calendar.MILLISECOND, 0);
Date midnightYesterday = calStart.getTime();

Calendar calEnd = new GregorianCalendar();
calEnd.setTime(new Date());
calEnd.set(Calendar.DAY_OF_YEAR, calEnd.get(Calendar.DAY_OF_YEAR)+1);
calEnd.set(Calendar.HOUR_OF_DAY, 0);
calEnd.set(Calendar.MINUTE, 0);
calEnd.set(Calendar.SECOND, 0);
calEnd.set(Calendar.MILLISECOND, 0);
Date midnightTonight = calEnd.getTime();

3
На мой взгляд, Joda Time проще, посмотрите в конце моего ответа. Если вы хотите использовать java.util.Date/Calendar, вы должны сделать это так, более простого способа сделать это не существует.
Тимашев

RE: timaschew комментирует использование Joda-Time, знает, что проект Joda-Time сейчас находится в режиме обслуживания, и советует перейти на классы java.time.
Василий Бурк

1
К вашему сведению, ужасно неприятные старые классы даты и времени, такие как java.util.Date, java.util.Calendarи java.text.SimpleDateFormatтеперь унаследованные , вытеснены классами java.time, встроенными в Java 8 и более поздние версии . Смотрите Учебник по Oracle .
Василий Бурк

2
Редко можно увидеть столько некорректных ответов на вопрос, как на этот. Я рекомендую ответ Василия Бурка , он правильный и знающий.
Оле В.В.

Ответы:


344

java.util.Calendar

// today    
Calendar date = new GregorianCalendar();
// reset hour, minutes, seconds and millis
date.set(Calendar.HOUR_OF_DAY, 0);
date.set(Calendar.MINUTE, 0);
date.set(Calendar.SECOND, 0);
date.set(Calendar.MILLISECOND, 0);

// next day
date.add(Calendar.DAY_OF_MONTH, 1);

JDK 8 - java.time.LocalTime и java.time.LocalDate

LocalTime midnight = LocalTime.MIDNIGHT;
LocalDate today = LocalDate.now(ZoneId.of("Europe/Berlin"));
LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
LocalDateTime tomorrowMidnight = todayMidnight.plusDays(1);

Joda времени

Если вы используете JDK <8, я рекомендую Joda Time , потому что API действительно хорош:

DateTime date = new DateTime().toDateMidnight().toDateTime();
DateTime tomorrow = date.plusDays(1);

Начиная с версии 2.3 Joda времени DateMidnightявляется устаревшим , так что используйте это:

DateTime today = new DateTime().withTimeAtStartOfDay();
DateTime tomorrow = today.plusDays(1).withTimeAtStartOfDay();

Передайте часовой пояс, если вы не хотите использовать текущий часовой пояс JVM по умолчанию.

DateTimeZone timeZone = DateTimeZone.forID("America/Montreal");
DateTime today = new DateTime(timeZone).withTimeAtStartOfDay(); // Pass time zone to constructor.

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

3
По состоянию на JodaTime 2.3, toDateMidnightустарела. Подробности см. В этом ответе: stackoverflow.com/a/19048833/363573
Стефан,

добавил решение для JDK 8
timaschew

1
@timaschew Хотя в целом правильно, я предлагаю сделать java.time, используя зонированное значение даты-времени ( ZonedDateTime), как вы делали в разделе Joda-Time. Эти Local…типы не имеют никакой информации о часовом поясе - то есть всех их цель, не потерять / игнорировать всю офсетную и часовой пояс деталь. Тем более, что Вопрос говорит об использовании этих значений для сравнения (возможно, запросов к базе данных?), Зонированное значение даты и времени, вероятно, будет более полезным.
Василий Бурк

1
@Amalgovinus Обязательно установите Calendar.HOUR_OF_DAY, а не Calendar.HOUR.
Освальд

43

Для полноты картины, если вы используете Java 8, вы также можете использовать truncatedToметод Instantкласса, чтобы получить полночь в UTC .

Instant.now().truncatedTo(ChronoUnit.DAYS);

Как написано в Javadoc

Например, усечение с помощью модуля MINUTES будет округляться до ближайшей минуты, устанавливая секунды и наносекунды в ноль.

Надеюсь, поможет.


1
Спасибо за подсказку. Получение полуночи в указанной временной зоне становится длительным:Date.from(date.toInstant().atZone(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS).toInstant())
Вадима


33

Самый простой способ найти полночь:

Long time = new Date().getTime();
Date date = new Date(time - time % (24 * 60 * 60 * 1000));

Следующий день:

Date date = new Date(date.getTime() + 24 * 60 * 60 * 1000);

Я запустил этот код и получил: Полночь: Чт 01 ноября 19:00:00 CDT 2012
Дейв

3
Если вы сделали что-то вроде System.out.print (date), система преобразовала экземпляр даты в системный часовой пояс (CDT). 19:00 в CDT - полночь по Гринвичу. Если вам нужна полночь в выбранном часовом поясе, вы должны добавить смещение этого часового пояса, принимая во внимание летнее время.
Андрей Волгин

@Bouncner Это так. На Земле есть много разных «полуночных» времен независимо от того, какой метод вы используете, но есть только одна полночь Java.
Андрей Волгин

2
@ Андрей Волгин Не знал, что там "ява полуночная". ;) То, что я хотел сказать, что это решение действительно не помогает создателю темы. Календарное решение позволяет избежать уродливых повторных вычислений и при правильном использовании заботится о часовых поясах (и особенно при переходе на летнее время и т. Д.). Я не вижу этого в вашем решении.
Bouncner

Привет @AndreiVolgin, я получил с Date date = new Date (time + TimeZone.getDefault (). GetRawOffset () - время% (24 * 60 * 60 * 1000));
Джрей

28

Помните, Dateне используется для представления даты (!). Для представления даты вам нужен календарь. Это:

Calendar c = new GregorianCalendar();

создаст Calendarэкземпляр, представляющий текущую дату в вашем текущем часовом поясе. Теперь вам нужно обрезать каждое поле ниже дня (час, минута, секунда и миллисекунда), установив для него значение 0. У тебя сегодня полночь.

Теперь, чтобы получить полночь на следующий день, нужно добавить один день:

c.add(Calendar.DAY_OF_MONTH, 1);

Обратите внимание, что добавление 86400секунд или 24 часов некорректно из-за летнего времени, которое может произойти в это время.

ОБНОВЛЕНИЕ: Однако мой любимый способ справиться с этой проблемой - использовать класс DateUtils из Commons Lang :

Date start = DateUtils.truncate(new Date(), Calendar.DAY_OF_MONTH))
Date end = DateUtils.addDays(start, 1);

Он использует Calendarза кулисами ...


15
Спасибо за помощь. «Дата не используется для обозначения дат» - мы это чертовски блестяще, не так ли? ;)

@RobertHume Чтобы быть честным, Дата существует с момента первого выпуска Java. Большинство вещей той эпохи ... не совсем продумано. Тем более , что Java была в числе первых , чтобы попробовать разные вещи он пытается, и там было далеко от согласованного стандарта , как это сделать.
Фонд Моника иск

14

эти методы помогут вам

public static Date getStartOfDay(Date date) {
     Calendar calendar = Calendar.getInstance();
     calendar.setTime(date);
     calendar.set(Calendar.HOUR_OF_DAY, 0);
     calendar.set(Calendar.MINUTE, 0);
     calendar.set(Calendar.SECOND, 0);
     calendar.set(Calendar.MILLISECOND, 0);
     return calendar.getTime();
 }

и

public static Date getEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);
    calendar.set(Calendar.MILLISECOND, 999);
    return calendar.getTime();
}

9

Начиная с JodaTime 2.3, toDateMidnight()он устарел.

С обновления с 2.2 до 2.3

    Амортизация с 2.2
    ----------------------
    - DateMidnight [# 41]
     Этот класс имеет недостатки в концепции
     Время полуночи иногда не происходит в некоторых часовых поясах
     Это результат перехода на летнее время с 00:00 до 01:00.
     DateMidnight - это, по сути, DateTime, время которого привязано к полуночи
     Такая концепция, как правило, является плохой для использования, учитывая LocalDate
     Замените DateMidnight на LocalDate
     Или замените его на DateTime, возможно, используя метод withTimeAtStartOfDay ()

Вот пример кода без toDateMidnight()метода.

Код

DateTime todayAtMidnight = new DateTime().withTimeAtStartOfDay();
System.out.println(todayAtMidnight.toString("yyyy-MM-dd HH:mm:ss"));

Выход ( может отличаться в зависимости от вашего местного часового пояса )

2013-09-28 00:00:00

2
Отличный ответ. Я хотел бы добавить передачу DateTimeZoneэтого DateTimeконструктора, чтобы подчеркнуть важность указания часового пояса, а не случайно, в зависимости от значения по умолчанию:DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
Бэзил Бурк

9

Другие ответы верны, особенно ответ java.time от arganzheng . Как уже упоминалось, вам следует избегать старых классов java.util.Date/.Calendar, поскольку они плохо спроектированы, сбивают с толку и создают проблемы. Они были вытеснены классами java.time.

Позвольте мне добавить заметки о стратегии обработки полуночи и промежутков времени .

Полуоткрытый

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

Одна решаемая проблема - определение конца дня. Последний момент дня 23:59:59.999( миллисекунды )? Возможно, в классе java.util.Date (с самой ранней Java; хлопотно - избегайте этого класса!) И в весьма успешной библиотеке Joda-Time . Но в других программах, таких как базы данных, такие как Postgres, последний момент будет 23:59:59.999999( микросекунды ). Но в других программах, таких как инфраструктура java.time (встроенная в Java 8 и более поздние версии, преемница Joda-Time) и в некоторых базах данных, таких как база данных H2 , последний момент может быть 23:59.59.999999999( наносекунды ). Вместо того, чтобы раскалывать волосы, думайте только в первый момент, а не в последний момент.

В Half-Open день начинается с первого момента одного дня и идет вверх, но не включает первый момент следующего дня. Так что вместо того, чтобы думать так:

… С сегодняшнего дня в 00:00 (полночь рано этим утром) до 12:00 (полночь сегодня вечером).

... думать так ...

с первого момента сегодняшнего дня, но не включая первый момент завтрашнего дня:
(> = 00:00:00.0сегодня И < 00:00:00.0завтра)

В работе с базой данных этот подход означает, что оператор SQL не используется .BETWEEN

Начало дня

Кроме того, первый момент дня не всегда время суток 00:00:00.0. Переход на летнее время (DST) в некоторых часовых поясах и, возможно, другие аномалии могут означать, что день начинается в другое время.

Так что пусть классы java.time определяют начало дня с помощью вызова LocalDate::atStartOfDay( ZoneId ). Таким образом, мы должны объехать LocalDateи вернуться, ZonedDateTimeкак вы можете видеть в этом примере кода.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( zoneId );
ZonedDateTime todayStart = now.toLocalDate().atStartOfDay( zoneId );
ZonedDateTime tomorrowStart = todayStart.plusDays( 1 );

Обратите внимание на прохождение необязательно ZoneId. Если этот параметр опущен, текущий часовой пояс JVM по умолчанию применяется неявно. Лучше быть явным.

Часовой пояс имеет решающее значение для работы даты и времени. Вопрос и некоторые другие ответы могут быть ошибочными, поскольку они не обрабатывают сознательно часовой пояс.

Перерабатывать

Если вы должны использовать java.util.Date или .Calendar, ищите новые методы преобразования, добавленные к этим старым классам.

java.util.Date utilDate = java.util.Date.from( todayStart.toInstant() );
java.util.GregorianCalendar gregCal = java.util.GregorianCalendar.from( todayStart );

Промежуток времени

Кстати, если вы делаете много работы с промежутками времени, взгляните на:

  • Duration
  • Period
  • Interval
    IntervalКласс находится в ThreeTen-Extra проекта, расширение в рамках java.time. Этот проект является полигоном для возможных будущих дополнений к java.time.
    Interval todayMontreal = Interval.of( todayStart.toInstant() , tomorrowStart.toInstant() );

О java.time

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

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

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

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

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

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


6

java.time

Если вы используете Java 8 и более поздние версии , вы можете попробовать пакет java.time ( Tutorial ):

LocalDate tomorrow = LocalDate.now().plusDays(1);
Date endDate = Date.from(tomorrow.atStartOfDay(ZoneId.systemDefault()).toInstant());

2
Хороший ответ. Я бы предложил указать желаемый / ожидаемый часовой пояс. Текущий часовой пояс JVM по умолчанию может варьироваться в зависимости от настроек ОС, смены компьютеров или любого приложения в любом потоке JVM, изменяя настройки по умолчанию в любой момент времени выполнения.
Базилик Бурк

4

Похоже, это вариант:

DateFormat justDay = new SimpleDateFormat("yyyyMMdd");
Date thisMorningMidnight = justDay.parse(justDay.format(new Date()));

добавить день к нему, либо

Date tomorrow = new Date(thisMorningMidnight.getTime() + 24 * 60 * 60 * 1000);

или

Calendar c = Calendar.getInstance();
c.setTime(thisMorningMidnight);
c.add(Calendar.DATE, 1);
Date tomorrowFromCalendar = c.getTime();

У меня есть предчувствие, что последнее предпочтительнее в случае чего-то странного, например, перехода на летнее время, в результате которого добавления 24 часов недостаточно (см. Https://stackoverflow.com/a/4336131/32453 и другие ответы).


У меня есть дата и время дня, представленные как длинные в моей базе данных sqlite. Мне нужно получить длинный для начала следующего дня. Могу ли я по-прежнему использовать объект Date с моим long, а не с методом SimpleDateFormat, который вы использовали выше?
AJW

1
Пока это правильные миллисекунды, вы можете передать его как конструктор: docs.oracle.com/javase/7/docs/api/java/util/…
rogerdpack

1

Я сделал это иначе, чем все остальные здесь. Я новичок в Java, так что, возможно, мое решение плохое.

Date now = new Date();
Date midnightToday = new Date(now.getYear(), now.getMonth(), now.getDate());

Я не уверен, что это работает, но в любом случае, я был бы признателен за любые отзывы об этом решении.

Я смущен заявлением выше, что вы можете рассчитать завтра, позвонив:

c.add(Calendar.DAY_OF_MONTH, 1);

Если вы добавите 1 к дню месяца, а это 31-й день, разве вы не получите 32-й день месяца?

Почему времена / даты не все основаны на UTC в Java? Я думаю, что часовые пояса должны быть необходимы только при использовании ввода-вывода, но внутренне должны всегда использоваться в UTC. Однако классы, похоже, содержат информацию о часовом поясе, которая кажется не только расточительной, но и подвержена ошибкам кодирования.


2
Привет Митч. Я думаю, что ваше решение работает, но похоже, что конструктор Date устарел. Некоторое время назад Java-разработчики устарели большинство оригинальных методов, связанных с датой, и заменили их новыми методами, связанными с календарем. Лично я не вижу, как это улучшение, но его не одобряют использование устаревших методов. Спасибо!

Грустно об устаревании. Мне кажется, гораздо понятнее указывать год, месяц и дату, а не очищать часы, минуты, секунды и миллисекунды. Очистка этих данных основывается на том, что миллисекунды являются лучшей гранулярностью класса Date, а мое решение - нет. Мое решение также короче и понятнее. Будучи новичком в Java, я действительно ценю то, насколько чист, прост и эффективен C ++, когда дело доходит до использования даты и времени.
Митч

@ По моему опыту, самый последний язык, который вы изучаете, всегда воспринимается как самый сложный.
ArtOfWarfare

К вашему сведению: эти чертовски ужасные старые классы даты и времени теперь унаследованы, заменены современными классами java.time.
Василий Бурк


0

Самый простой способ с JodaTime

DateMidnight date = DateMidnight.now();


2
Нет. Классы и методы, относящиеся к «полуночи» в Joda-Time, устарели. Они были основаны на ошибочной концепции. Первый момент дня не всегда 00:00:00.000. Вместо того, чтобы использовать новый withTimeAtStartOfDayметод. Обновите Joda-Time до текущей версии 2.4 и прочтите примечания к выпуску. Пример:DateTime today = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).withTimeAtStartOfDay();
Василий Бурк

0

Поскольку один день равен 24 * 60 * 60 * 1000мс, полночь этого дня можно рассчитать как ...

long now = System.currentTimeMillis();
long delta = now % 24 * 60 * 60 * 1000;
long midnight = now - delta;
Date midnightDate = new Date(midnight);`

1
Этот код применяется только к UTC и никаким другим часовым поясам и игнорирует аномалии, такие как переход на летнее время. Кроме того, решение «сворачивать свои собственные даты и время» неразумно, поскольку это удивительно сложная тема. Вместо этого используйте отличные классы java.time.
Василий Бурк

0

Почти как ответы раньше, но никто не упомянул параметр AM_PM:

    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.HOUR, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    cal.set(Calendar.AM_PM, Calendar.AM);

1
Хлопотно Calendarкласс был вытеснен лет назад в java.time классов, в частности ZonedDateTime.
Василий Бурк

0
Date now= new Date();
// Today midnight
Date todayMidnight = new Date(endTime.getTime() -endTime.getTime()%DateUtils.MILLIS_PER_DAY);

// tomorrow midnight
Date tomorrowMidnight = new Date(endTime.getTime() -endTime.getTime()%DateUtils.MILLIS_PER_DAY + DateUtils.MILLIS_PER_DAY);

Это работает только для UTC и не относится к часовым поясам. Кроме того, ужасный Dateкласс был вытеснен несколько лет назад java.time.Instant. Избегайте использования в Dateнастоящее время.
Василий Бурк

0

Используя Apache Commons ..

//For midnight today 
Date today = new Date(); 
DateUtils.truncate(today, Calendar.DATE);

//For midnight tomorrow   
Date tomorrow = DateUtils.addDays(today, 1); 
DateUtils.truncate(tomorrow, Calendar.DATE);

К вашему сведению, ужасно неприятные старые классы даты и времени, такие как java.util.Date, java.util.Calendarи java.text.SimpleDateFormatтеперь унаследованные , вытеснены классами java.time, встроенными в Java 8 и более поздние версии . Смотрите Учебник по Oracle .
Василий Бурк

@BasilBourque Согласен для Java 8. Но если OP использует Java 7, я думаю, что это все равно будет работать.
joe4java

Почти все функциональные возможности java.time доступны для Java 6 & 7 в проекте ThreeTen-Backport с практически идентичным API. Далее адаптирован для Android <26 в проекте ThreeTenABP . Так что нет необходимости снова использовать эти ужасные старые классы даты и времени.
Василий Бурк

-1

Я знаю, что это очень старый пост. Я думал поделиться своими знаниями здесь!

Для даты Середина ночи сегодня с точным часовым поясом вы можете использовать следующие

public static Date getCurrentDateWithMidnightTS(){

    return new Date(System.currentTimeMillis() - (System.currentTimeMillis()%(1000*60*60*24)) - (1000*60 * 330));
}

куда (1000*60 * 330) вычитается, т. Е. Фактически относится к часовому поясу, например, индийский часовой пояс, т. Е. Калькутта отличается на +5: 30 часов от фактического. Так вычитая это с преобразованием в миллисекунды.

Так что измените последнее вычтенное число согласно вам. Я создаю продукт, т. Е. Базируюсь только в Индии, поэтому просто использовал конкретную метку времени.


1
Почему вы предлагаете это вместо принятого ответа? Свернуть ваши значения даты и времени в целом рискованно. В частности, этот код жестко запрограммирован на текущее смещение одного часового пояса навсегда. Нет учета летнего времени или других аномалий. Я вижу только недостатки без добавленной стоимости.
Василий Бурк

@Basil, конечно, я принимаю ответ, и да, я упомянул там жестко закодированное значение. Это полезно только в том случае, если вы работаете в определенном часовом поясе.
Раджив

-1
Date todayMidnightUTC = java.sql.Date.valueOf(LocalDate.now());
Date tomorrowMidnightUTC = java.sql.Date.valueOf(LocalDate.now().plusDays(1));
Date anyMidnightLocal = java.sql.Date.valueOf(LocalDate.from(dateTime.toInstant().atZone(ZoneId.systemDefault())));

Но будьте осторожны, что java.sql.Date.toInstant()всегда бросаетUnsupportedOperationException .

Через LocalDate в java.util.Date и наоборот самое простое преобразование?


Не очень хороший ответ. Вы смешиваете использование современного java.time со старыми классами, которые они вытесняют. И, в частности, вы используете классы даты и времени java.sql, которые должны использоваться только для обмена данными с базой данных, драйвер которой еще не обновлен, для непосредственного взаимодействия с классами java.time, которые никогда не будут использоваться для бизнес-логики. Еще одна проблема заключается в том, что вы игнорируете важнейшую проблему часового пояса.
Василий Бурк

@BasilBourque, спасибо за критику. Использование одного унаследованного класса, который не помечен как устаревший, для получения другого экземпляра унаследованного класса довольно безопасно. Вопрос о часовом поясе является более важным. Я решил придерживаться ответа Риккардо в конце концов.
Вадим

-1

Старомодный способ ..

private static Date getDateWithMidnight(){
    long dateInMillis = new Date().getTime();
    return new Date(dateInMillis - dateInMillis%(1000*60*60*24) - TimeZone.getDefault().getOffset(dateInMillis));
}

Этот неприятный класс был вытеснен несколько лет назад классом java.time.Instant . Нет необходимости когда-либо использовать Date.
Василий Бурк

Этот Dateкласс всегда в UTC. Таким образом, вы игнорируете проблему желаемого / ожидаемого часового пояса. Например, в Калькутте в Индии наступает новый день на много часов раньше, чем в UTC, и на несколько часов позже, чем в UTC в Монреале-Квебеке.
Василий Бурк
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.