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

235

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

Каков наиболее эффективный способ получить объект Date без времени? Есть ли другой способ, кроме этих двух?

// Method 1
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dateWithoutTime = sdf.parse(sdf.format(new Date()));

// Method 2
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
dateWithoutTime = cal.getTime();

Обновление :

  1. Я знал о Джода-Тайм ; Я просто пытаюсь избежать дополнительной библиотеки для такой простой (я думаю) задачи. Но, основываясь на ответах, пока Joda-Time кажется чрезвычайно популярным, поэтому я мог бы рассмотреть это.

  2. Под эффективностью я подразумеваю, что хочу избежать Stringсоздания временных объектов в том виде method 1, в method 2каком они используются , а пока кажется взломом вместо решения.

Росди Касим
источник
1
Эффективное? Вам нужно больше эффективности, чем предусмотрено, например, method1?
Йохан Шеберг
2
Что вы подразумеваете под "эффективными"? Дата в основном печатается долго, вы не можете сделать это в меньшем количестве памяти, чем это. Если вы имеете в виду «удобно», время JODA - путь.
миллимус
3
+1 Точно вопрос, который у меня был.
Альба Мендес
1
Мне нравится метод 2. Создайте статический метод в служебном классе и просто используйте его. Я был такой подход в течение многих лет.
Уилсон Фрейтас
1
Не обращая внимания на ваше «обновление 1»: если бы это была «такая простая задача», я думаю, что Sun не пришла бы к такому ужасающему и неэффективному API, и вы (и многие другие люди) не задавали бы этот вопрос вообще ;-)
Флавио Этруско

Ответы:

108

Вы обязательно должны использовать java.util.Date? Я бы тщательно рекомендую вам использовать Joda время или java.timeпакет из Java 8 вместо. В частности, пока Дата и Календарь всегда представляют определенный момент времени, без такого понятия , как «только дата», Joda Время делает иметь тип , представляющий это (LocalDate ). Ваш код будет намного понятнее, если вы сможете использовать типы, которые представляют то, что вы на самом деле пытаетесь сделать.

Существует множество причин использовать Joda Time или java.timeвместо встроенных java.utilтипов - обычно они гораздо лучше API. Вы всегда можете конвертировать в / из a java.util.Dateна границах вашего собственного кода, если вам нужно, например, для взаимодействия с базой данных.

Джон Скит
источник
75
В корпоративных приложениях у нас не всегда есть возможность добавить / использовать другие библиотеки. Я ценю указатель на Joda Time, но на самом деле это не ответ на первоначальную проблему получения части даты с использованием стандартной Java. Спасибо. Отказался от ответа Чатуранги.
noogrub
3
@noogrub: ответ Чатугранги не отвечает на первоначальный вопрос, который спрашивает о том, как получить Dateобъект - этот ответ форматирует дату в строку, что не одно и то же. По сути, спрашивать, на какой дате Dateнаходится a, - это бессмысленный вопрос без дополнительной информации: часовой пояс и система календаря, которую вы используете.
Джон Скит
7
«В корпоративных приложениях у нас не всегда есть возможность добавлять / использовать другие библиотеки». В самом деле ? Вы предлагаете не использовать сторонние библиотеки вообще?
Брайан Агнью
3
Я думаю, что комментарий @BrianAgnew noogrub связан с нетехническими ограничениями
Jose_GD
3
С сегодняшней точки зрения: « Joda-Time является де-факто стандартной библиотекой даты и времени для Java до Java SE 8. Теперь пользователям предлагается перейти на java.time (JSR-310)».
Друкс,
66

Вот что я использовал, чтобы получить сегодняшнюю дату с установленным временем 00:00:00:

DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");

Date today = new Date();

Date todayWithZeroTime = formatter.parse(formatter.format(today));
Shobbi
источник
28
Чем это отличается от уже предложенного «метода 1» в первоначальном вопросе?
Крис
Не указывает, в каком часовом поясе это происходит (оставлен произвольный часовой пояс JVM по умолчанию).
Даррелл Тиг,
58

Вы можете использовать DateUtils.truncate из библиотеки Apache Commons.

Пример:

DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH)
JOGO
источник
3
Если вы собираетесь добавить новую библиотеку, пусть это будет Joda вместо Apache Commons.
Диббеке
6
+1 @Dibbeke Разница не только в том, что вы будете добавлять одну библиотеку вместо другой. Иногда не хочется изучать совершенно новую библиотеку только для тривиальной задачи обрезания времени от даты. Или не хочет иметь код, который смешивает дату / календарь с датами Joda, используя иногда первое и иногда второе. Или не может позволить себе / оправдать замену всего хорошо работающего кода на основе даты / календаря другой библиотекой для такой тривиальной цели. Хотя предположить, что Йода, безусловно, хороша, поскольку она явно превосходит и просто «Правильное дело», иногда
возникает
Genius! Мне очень помогли
Тизен
37

Есть ли другой способ, кроме этих двух?

Да, есть.

LocalDate.now( 
    ZoneId.of( "Pacific/Auckland" ) 
)

java.time

Java 8 и более поздние версии поставляются с новым встроенным пакетом java.time . Смотрите учебник . Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport и дополнительно адаптирована для Android в ThreeTenABP .

Подобно Joda-Time , java.time предлагает LocalDateкласс для представления значения только для даты без времени суток и без часового пояса.

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

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;

По умолчанию java.time использует стандарт ISO 8601 при создании строкового представления даты или значения даты-времени. (Еще одно сходство с Joda-Time.) Так что просто позвоните, toString()чтобы сгенерировать текст, как 2015-05-21.

String output = today.toString() ; 

О java.time

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

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

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

Где взять классы 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

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

Базилик Бурк
источник
1
Спасибо за упоминание часового пояса . Мне не приходило в голову, что «какой сегодня день» не является абсолютной концепцией.
ToolmakerSteve
@ToolmakerSteve Получение даты всегда связано с часовым поясом. Но хитрость заключается в том, что если вы не укажете часовой пояс, текущий часовой пояс по умолчанию вашей JVM будет автоматически применяться при определении даты. Кроме того, текущий часовой пояс JVM по умолчанию может измениться в любой момент ! Любой код в любом потоке любого приложения в JVM может вызываться TimeZone.setDefaultво время выполнения и немедленно влиять на весь другой код, выполняемый в этой JVM. Мораль истории: всегда указывайте часовой пояс.
Базилик Бурк
22

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

long millisInDay = 60 * 60 * 24 * 1000;
long currentTime = new Date().getTime();
long dateOnly = (currentTime / millisInDay) * millisInDay;
Date clearDate = new Date(dateOnly);
Римский
источник
5
Для меня это в настоящее время: «Сб 19 февраля 01 : 00: 00 CET 2011». Если вы хотите использовать это, то только с UTC.
Крис Лерчер
4
Разве строка 3 не long dateOnly = (currentTime / millisInDay) * millisInDay;такая же, как запись long dateOnly = currentTime;?
Крис
5
Это не из-за целочисленной математики. Думайте об этом больше как Math.floor (currentTime / millisInDay) * millisInDay. Таким образом, он фактически устанавливает время на 00:00:00.
Николас Флинт
2
Это решение, вероятно, подходит для большинства приложений, но следует помнить, что предположение о том, что день имеет 60 * 60 * 24 * 1000, не всегда верно. Например, вы можете иметь дополнительные секунды в несколько дней.
Пьер-Антуан
1
Это, возможно, самый тупой и не рекомендуемый способ по сравнению с другими одно- и двухстрочными ясными и лаконичными методами.
Даррелл Тиг
22

Стандартный ответ на эти вопросы - использовать Joda Time . API лучше, и если вы используете средства форматирования и анализаторы, вы можете избежать неинтуитивного отсутствия безопасности потоков SimpleDateFormat.

Использование Joda означает, что вы можете просто сделать:

LocalDate d = new LocalDate();

Update :: Использование Java 8 это может быть достигнуто с помощью

LocalDate date = LocalDate.now();
Брайан Агнью
источник
Новый пакет java.time в Java 8 также предлагает LocalDateкласс, аналогичный Joda-Time.
Василий Бурк
1
В идеале вы должны передать DateTimeZoneэтот конструктор LocalDate. Определение текущей даты зависит от часового пояса, так как Париж начинает новую дату раньше, чем Монреаль. Если вы опустите часовой пояс, часовой пояс вашего JVM будет применяться по умолчанию. Обычно лучше указать, чем полагаться на значение по умолчанию.
Василий Бурк
8

Это простой способ сделать это:

Calendar cal = Calendar.getInstance();
SimpleDateFormat dateOnly = new SimpleDateFormat("MM/dd/yyyy");
System.out.println(dateOnly.format(cal.getTime()));
лощина
источник
4
Этот ответ не отвечает на вопрос. Получение отформатированной строки не было целью.
Василий Бурк
7

Не имеет смысла говорить о дате без метки времени в отношении процедур Date в стандартной среде выполнения Java, поскольку она, по сути, отображается на определенную миллисекунду, а не на дату. К указанной миллисекунде присуще время суток, что делает ее уязвимой для проблем с часовым поясом, таких как переход на летнее время и другие настройки календаря. См. Почему вычитание этих двух раз (в 1927 году) дает странный результат? за интересный пример.

Если вы хотите работать с датами, а не с миллисекундами, вам нужно использовать что-то еще. Для Java 8 есть новый набор методов, обеспечивающих именно то, что вы просите. Для Java 7 и более ранних версий используйте http://www.joda.org/joda-time/

Турбьерн Равн Андерсен
источник
3
Примечание. Те подходы, которые говорят «дата в полночь», плохо справляются с переходом на летнее время и несколькими часовыми поясами.
Торбьерн Равн Андерсен
Я подтверждаю это, я увеличивал даты на один день в своем приложении и узнал об этом с помощью сообщения об ошибке от пользователей в часовом поясе с DST (моя страна не использует DST). В день начала летнего времени мое приложение добавляет 11 часов вместо 12. Возможно, лучше использовать полдень в качестве часа для даты «без времени»?
Jose_GD
1
@Jose_GD Это в лучшем случае все еще взломать. Вы можете найти youtube.com/watch?v=-5wpm-gesOY занимательным.
Турбьёрн Равн Андерсен
@ Thorbjorn, ты прав, просто хотел избежать JodaTime, потому что добавление библиотеки только для одной функции звучит для меня излишне. Я знал об этом видео, действительно смешно.
Jose_GD
1
@Jose_GD Дело в том, что существует реальный вариант использования в реальной жизни, в котором ваш подход будет считаться ошибкой. Если бы вы нашли один, может быть еще ... Я не знаю, как Джода справляется с этим, но вы могли бы посмотреть. Также обратите внимание, что в Java 8 появилась новая библиотека даты и времени (основанная на опыте работы с Joda), которую вы, возможно, захотите изучить.
Турбьёрн Равн Андерсен
4

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

    Date db = db.substring(0, 10) + db.substring(23,28);

Мне нужно было только свидание для наглядных целей, и я не смог, поэтому я подстригся.

Bamz
источник
4
// 09/28/2015
System.out.println(new SimpleDateFormat("MM/dd/yyyy").format(Calendar.getInstance().getTime()));

// Mon Sep 28
System.out.println( new Date().toString().substring(0, 10) );

// 2015-09-28
System.out.println(new java.sql.Date(System.currentTimeMillis()));

// 2015-09-28
// java 8
System.out.println( LocalDate.now(ZoneId.of("Europe/Paris")) ); // rest zones id in ZoneId class
blueberry0xff
источник
3

Ну, насколько я знаю, нет более простого способа добиться этого, если вы используете только стандартный JDK.

Конечно, вы можете поместить эту логику в method2 в статическую функцию в вспомогательном классе, как это сделано в методе toBeginningOfTheDay.

Затем вы можете сократить второй метод до:

Calendar cal = Calendar.getInstance();
Calendars.toBeginningOfTheDay(cal);
dateWithoutTime = cal.getTime();

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

Enduriel
источник
3

Если все, что вам нужно, это увидеть дату «YYYY-MM-DD» без всякого другого беспорядка, например, «Чт 21 мая 12:08:18 EDT 2015», тогда просто используйте java.sql.Date. Этот пример получает текущую дату:

new java.sql.Date(System.currentTimeMillis());

Также java.sql.Dateявляется подклассом java.util.Date.

BigMac66
источник
3

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

DateTime.now().withTimeAtStartOfDay()
user3037726
источник
1
Вопрос задан без времени суток. Ваш результат - это объект даты и времени, который действительно имеет время суток, которое 00:00:00обычно бывает (может варьироваться в зависимости от часового пояса для таких аномалий, как DST ). Итак, как отмечают другие Ответы, LocalDateкласс более уместен из Joda-Time (при условии, что вы ссылаетесь на Joda-Time - что вы должны явно указать при цитировании классов, не связанных с Java).
Базилик Бурк
3

Используйте LocalDate.now()и конвертируйте Dateкак ниже:

Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
Сахил Чхабра
источник
1
Текущий часовой пояс JVM по умолчанию может измениться в любой момент во время выполнения. Любой код в любом потоке любого приложения в JVM может вызывать TimeZone.setDefault. Ваш код вызывает ZoneId.systemDefaultдважды, первый неявный в, LocalDate.now()а второй явный с atStartOfDay. Между этими двумя моментами во время выполнения текущая зона по умолчанию может измениться. Я предлагаю записать зону в переменную и перейти к обоим методам.
Василий Бурк
2

Если вам нужна часть даты только для повторения цели, то

Date d = new Date(); 
String dateWithoutTime = d.toString().substring(0, 10);
manish_s
источник
1
Я не думаю, что это i18n дружественный
Марк Лапаса
2

Как насчет этого?

public static Date formatStrictDate(int year, int month, int dayOfMonth) {
    Calendar calendar = Calendar.getInstance();
    calendar.set(year, month, dayOfMonth, 0, 0, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
}
vitiello.antonio
источник
1

Проверьте Вейдер-время . Это простая и эффективная альтернатива java.util и Joda-time. Он имеет интуитивно понятный API и классы, которые представляют даты самостоятельно, без отметок времени.

lonerook
источник
1

Самый верный способ, который в полной мере использует огромную базу данных TimeZone Java и является правильным:

long currentTime = new Date().getTime();
long dateOnly = currentTime + TimeZone.getDefault().getOffset(currentTime);
TomWolk
источник
1

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

Он не требует никаких сторонних зависимостей и УЗНАЕТ ВРЕМЯ ЗОНЫ переданного объекта Calender. Эта функция возвращает момент времени в 12:00 в часовом поясе даты (Календарь), в которую вы передаете.

public static Calendar date_only(Calendar datetime) {
    final long LENGTH_OF_DAY = 24*60*60*1000;
    long millis = datetime.getTimeInMillis();
    long offset = datetime.getTimeZone().getOffset(millis);
    millis = millis - ((millis + offset) % LENGTH_OF_DAY);
    datetime.setTimeInMillis(millis);
    return datetime;
}
Брент Ларсен
источник
Я не очень уверен, что он будет уважать лето (DST)?
Оле В.В.
0

Предпочитаю не использовать сторонние библиотеки в максимально возможной степени. Я знаю, что этот способ упоминался ранее, но вот хороший чистый способ:

  /*
    Return values:
    -1:    Date1 < Date2
     0:    Date1 == Date2
     1:    Date1 > Date2

    -2:    Error
*/
public int compareDates(Date date1, Date date2)
{
    SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy");

    try
    {
        date1 = sdf.parse(sdf.format(date1));
        date2 = sdf.parse(sdf.format(date2));
    }
    catch (ParseException e) {
        e.printStackTrace();
        return -2;
    }

    Calendar cal1 = new GregorianCalendar();
    Calendar cal2 = new GregorianCalendar();

    cal1.setTime(date1);
    cal2.setTime(date2);

    if(cal1.equals(cal2))
    {
        return 0;
    }
    else if(cal1.after(cal2))
    {
        return 1;
    }
    else if(cal1.before(cal2))
    {
        return -1;
    }

    return -2;
}

Ну, возможно, не использовать GregorianCalendar!

хе-хе
источник
0

Я только что сделал это для моего приложения:

public static Date getDatePart(Date dateTime) {
    TimeZone tz = TimeZone.getDefault();
    long rawOffset=tz.getRawOffset();
    long dst=(tz.inDaylightTime(dateTime)?tz.getDSTSavings():0);
    long dt=dateTime.getTime()+rawOffset+dst; // add offseet and dst to dateTime
    long modDt=dt % (60*60*24*1000) ;

    return new Date( dt
                    - modDt // substract the rest of the division by a day in milliseconds
                    - rawOffset // substract the time offset (Paris = GMT +1h for example)
                    - dst // If dayLight, substract hours (Paris = +1h in dayLight)
    );
}

Android API уровня 1, без внешней библиотеки. Он учитывает дневной свет и часовой пояс по умолчанию. Никаких манипуляций со строками, так что я думаю, что этот способ более эффективен, чем ваш, но я не проводил никаких тестов.

Elloco
источник
0

Йо может использовать время йода.

private Date dateWitoutTime(Date date){
 return new LocalDate(date).toDate()
}

и вы звоните с:

Date date = new Date();
System.out.println("Without Time = " + dateWitoutTime(date) + "/n  With time = " + date);
Хесус Санчес
источник