Як отримати побачення без часу на 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 ; Я просто намагаюся уникати додаткової бібліотеки для такого простого (я думаю) завдання. Але на основі відповідей поки що Joda-Time здається надзвичайно популярним, тому я можу вважати це.

  2. Під ефективним , я маю на увазі, що я хочу уникати Stringстворення тимчасових об'єктів , якими користується method 1, тим часом method 2здається, що хак замість рішення.


1
Ефективний? Вам потрібна більша ефективність, ніж передбачено, наприклад, методом1?
Йохан Шьоберг

2
Що ви маєте на увазі під "ефективним"? Дата в основному набрана довгий, ви не можете реально зробити це за меншої пам’яті, ніж це. Якщо ви маєте на увазі "зручно", час JODA - це шлях.
мільйоз

3
+1 Саме це питання у мене було.
Альба Мендес

1
Мені подобається метод 2. Створіть статичний метод у клас утиліти та просто використовуйте його. Я таким підходом користувався роками.
Вільсон Фрейтас

1
Мимоволі над вашим "оновленням 1": якби це було "таке просте завдання", я думаю, Sun не прийшов би до такого жахливого та неефективного API, і ви (та багато інших людей) не ставили б цього питання взагалі ;-)
Flávio Etrusco

Відповіді:


108

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

Є багато, багато інших причин використовувати Joda Time або java.timeзамість вбудованих java.utilтипів - вони, як правило, набагато кращі API. Ви завжди можете конвертувати в / з а java.util.Dateна межах власного коду, якщо вам потрібно, наприклад, для взаємодії з базою даних.


75
У корпоративних програмах у нас не завжди є можливість додавати / використовувати інші бібліотеки. Я вдячний вказівником на Joda Time, але це справді не відповідь на первісну проблему отримання частини дати за допомогою стандартної Java. Дякую. Оголошення відповіді Чатхуранги.
noogrub

3
@noogrub: Відповідь Chathugranga не відповідає на початкове запитання, яке задається питанням про те, як отримати Dateоб’єкт - ця відповідь форматує дату в рядку, що не те саме. По суті, запитувати, на яку дату Dateвказано дату , є безглуздим запитанням без додаткової інформації: часовий пояс і календарна система, яку ви використовуєте.
Джон Скіт

7
"У корпоративних додатках у нас не завжди є можливість додавати / використовувати інші бібліотеки". Дійсно? Ви припускаєте, що взагалі не можете користуватися сторонніми лібками?
Брайан Агнеу

3
@BrianAgnew noogrub, здається, пов'язаний з
нетехнологічними

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));

28
Чим це відрізняється від вже запропонованого "методу 1" в первісному питанні?
Кріс

Не стосується, в якому часовому поясі це відбувається (залишено довільним часовим поясом JVM за умовчанням).
Darrell Teague

58

Ви можете використовувати DateUtils.truncate з бібліотеки Apache Commons.

Приклад:

DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH)

3
Якщо ви збираєтеся додати нову бібліотеку, нехай це буде Joda замість Apache Commons.
Діббеке

6
+1 @Dibbeke Різниця полягає не лише в тому, що ви додавали б одну бібліотеку замість іншої. Іноді не хочеться вивчити цілком нову бібліотеку лише для тривіального завдання обрізання часу з дати. Або не хоче мати код, який змішує дату / календар з датами Joda, використовуючи іноді перший, а іноді останній. Або не можете собі дозволити / виправдати заміну всіх добре працюючих кодів, що базуються на даті / календарі, на іншу бібліотечну мету. Хоча припускати, що Джода, безумовно, хороший, оскільки він, безумовно, перевершує і просто Правильна річ, іноді починається реальна
ситуація

Геніальний! Допомагав мені багато
Тизен

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

The Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часутакі якjava.util.Date,Calendar, іSimpleDateFormat.

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

Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - 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
    • Пізніші версії реалізації пакетів Android для класів java.time.
    • Для більш раннього Android, то ThreeTenABP проект адаптує ThreeTen-Backport (згаданий вище). Див. Як користуватися ThreeTenABP… .

Проект 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
Це, мабуть, найбільш чіткий і не рекомендований спосіб для інших одно- та дворядкових чітких і стислих методів.
Darrell Teague

22

Стандартною відповіддю на ці питання є використання Joda Time . API кращий, і якщо ви використовуєте формати та парсери, ви можете уникнути неінтуїтивної відсутності безпеки потоку SimpleDateFormat.

Використання Joda означає, що ви можете просто зробити:

LocalDate d = new LocalDate();

Оновлення :: Використовуючи 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

Немає сенсу говорити про дату без позначки часу щодо процедур дати у стандартному режимі виконання Java, оскільки вона по суті відображає конкретну мілісекунду, а не дату. Згаданий мілісекунд по суті прикріплений до нього час доби, який робить його вразливим до проблем часового поясу, таких як літній час та інших коригувань календаря. Дивіться, чому віднімання цих двох разів (у 1927 р.) Дає дивний результат? за цікавий приклад.

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


3
Примітка. Ті підходи, які говорять про "дату опівночі", не справляються з літнім часом та кількома часовими поясами.
Thorbjørn Ravn Andersen

Я підтверджую це, я збільшував дати в один день у своєму додатку, і дізнався про це за допомогою звіту про помилки від користувачів у часовому поясі з DST (моя країна не використовує DST). У день, коли починається DST, моє додаток додає 11 годин замість 12. Можливо, використання полудня як години для дати «немає часу» краще?
Jose_GD

1
@Jose_GD Це в кращому випадку все-таки хак. Ви можете знайти youtube.com/watch?v=-5wpm-gesOY розважальним.
Thorbjørn Ravn Andersen

@ Thorbjorn, ти маєш рацію, просто хотів уникнути JodaTime, тому що додавання бібліотеки лише для однієї функції звуку для мене є надмірною. Я знав про це відео, справді смішно.
Jose_GD

1
@Jose_GD Справа в тому, що існує фактичний випадок використання реального життя, у якому ваш підхід вважатиметься помилкою. Якби ти знайшов його, може бути більше ... Я не знаю, як Джода впорається з цим, але ти можеш подивитися. Також зауважте, що Java 8 пропонує нову бібліотеку з датами (на основі досвіду роботи з Joda), яку ви, можливо, захочете вивчити.
Thorbjørn Ravn Andersen

4

Безумовно, не самий правильний спосіб, але якщо вам просто потрібне швидке рішення, щоб отримати дату без часу, і ви не хочете користуватися сторонній бібліотекою, це слід зробити

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

Мені потрібна була дата лише для візуальних цілей, і не міг Джода, тому я підстрочив.


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

3

Ну, наскільки я знаю, немає простішого способу досягти цього, якщо ви використовуєте лише стандартний JDK.

Звичайно, ви можете ввести цю логіку в method2 в статичну функцію в класі помічників, як це зроблено тут у методі toBeginningOfTheDay

Тоді ви можете скоротити другий метод до:

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

Або, якщо вам справді потрібен поточний день у такому форматі так часто, то ви можете просто загорнути його в інший статичний помічник, таким чином зробивши його однолінійним.


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.


3

Якщо вам просто потрібна поточна дата, без часу, інший варіант:

DateTime.now().withTimeAtStartOfDay()

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);

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();
}

1

Перевірте час Вейдера . Це проста і ефективна альтернатива як java.util, так і Joda-time. Він має інтуїтивно зрозумілий API та класи, які представляють дати самостійно, без часових позначок.


1

Найбільш простий спосіб, який повною мірою використовує величезну базу даних TimeZone Java і є правильним:

long currentTime = new Date().getTime();
long dateOnly = currentTime + TimeZone.getDefault().getOffset(currentTime);

1

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

Він не вимагає сторонніх залежностей, і він ПОТРІБНУЄ ВРЕМУ об'єкта календаря, який передається. Ця функція повертає момент часу о 12 ранку в часовому поясі дати (календаря), яку ви передаєте.

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;
}

Я не дуже впевнений, що це буде поважати літній tme (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)
    );
}

API API рівня 1, немає зовнішньої бібліотеки. Він поважає денне світло та часовий пояс за замовчуванням. Ніяких рядкових маніпуляцій, тому я думаю, що цей спосіб є більш ефективним процесором, ніж ваш, але я не робив жодних тестів.


0

Йо може використовувати час joda.

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.