Інформація про час відключення Java


120

У мене є об’єкт дати Java, який містить інформацію про дату та час. Я хочу написати метод, який скорочує інформацію про час, скорочує години-хвилини-секунди, тому у мене залишилася лише дата.

Приклад введення:

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.
Василь Бурк

3
Календар більше не рекомендується. Дивіться stackoverflow.com/a/28868089/40064, якщо ви використовуєте Java 8.
Wim Deblauwe

2
FYI, старі класи, java.util.Calendarякі зараз неспокійні, такі, як зараз, застарілі , замінені класами java.time . Дивіться Підручник від Oracle .
Василь Бурк

147

Ви переглянули метод урізання DateUtils в Apache Commons Lang?

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

видалить елемент часу.


9
Оновлене посилання ще раз (заходьте на Apache! Вийдіть, перекажіть свої документи!)
Огр Псалм33,

6
Мінусом цього підходу є те, що часовий пояс, в якому виконується операція усікання, - це завжди місцевий часовий пояс ПК. Тож якщо ви працюєте з календарем і датами в часовому поясі UTC або в будь-якому часовому поясі, окрім місцевого часового поясу - це може скоротити дату не за призначенням.
Хмара

як зазначав @Cloud, той факт, що я не бачу тут екземпляра календаря (не маю над ним контролю), змушує мене трохи нервувати :)
Jaroslav Záruba

Цей метод напрочуд довгий і мав щонайменше два виправлення.
Піно

42

Ви подивилися на Джоду ? Це набагато простіший та інтуїтивніший спосіб роботи з датами та часом. Наприклад, ви можете тривіально перетворити між (скажімо) LocalDateTime та LocalDate об'єктами.

наприклад (для ілюстрації API)

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

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


12
Я вірю, що так і є. Це рекомендація використовувати кращу бібліотеку, а не боротися зі зламаними класами java.util (про що свідчить запитання)
Brian Agnew

10
Звичайно, це стосується, Joda - БІБЛІОТЕКА для дати, часу та маніпуляцій календарем на Java. Не рекомендуватимуть обов'язок не рекомендувати це.
Джоель

1
Питання безпеки потоку є дуже цікавим, я знайшов цю публікацію, яка досліджує помилку Oracle / Hibernate "IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)". Це говорить про ще одну проблему Oracle, яка з’являється лише з великим навантаженням forums.oracle.com/forums/thread.jspa?threadID=325576 Навіть важко навіть створити об’єкт з недійсними мілісекундами у звичайному коді. Для мене, я думаю, я перекинувся в мою тему за допомогою Joda, а не дозволю Oracle використовувати речі, які можуть підозрювати, що не стосуються joda.
Марк Беннетт

4
Я хотів би, щоб я міг зняти голос усіх, хто за це проголосував. Хто всі ці люди, які ставлять +1, що навіть не намагаються відповісти на запитання? Відповідь, що використовує Джоду, буде більш ніж вітаю, але це не все ...
Райан

1
@Ryan Як ця відповідь не актуальна? Питання запитувало дату без періоду дня. У цій відповіді представлений клас, єдиною метою якого є подання дати без часу дня.
Василь Бурк

35

Просто швидке оновлення у світлі класів java.time, тепер вбудованих у Java 8 та новіших версій.

LocalDateTimeє truncatedToметод, який ефективно вирішує те, про що ви тут говорите:

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

Це виразить поточний час лише до хвилин:

2015-03-05T11:47

Ви можете використовувати ChronoUnit(або a TemporalUnit) менше DAYS для виконання усікання (оскільки усічення застосовується лише до часової частини 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.
Василь Бурк

Дякую, що ви вказали. Оновлено відповідь, щоб врахувати це.
h7r

Ви також toLocalDateможете отримати об'єкт, який навіть не має компонента часу.
Чарльз Вуд

FYI, проект Joda-Time зараз перебуває в режимі обслуговування , команда радить перейти до класів java.time . Дивіться Підручник від Oracle .
Василь Бурк

8

Я робив усічення за допомогою нового API java8. Я зіткнувся з однією дивною річчю, але взагалі це усічення ...

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

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

1
Ця відповідь дає лише дати для контексту часового поясу UTC.
Василь Бурк

6

Використовуйте DateUtils від Apache зі скороченням, як це:

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

Це буде скорочено в локальній часовій зоні, а не в UTC, яку представляє дата (getTime).
епокс

6

Для часових позначок:

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

3
У цьому коді є невелика помилка - ліва дужка розміщена неправильно, вона повинна бути часовою позначкою - = часова мітка% (24 * 60 * 60 * 1000). Це відріже часову частину. І я впевнений, що цей спосіб є більш достатнім, ніж створення об’єкта Caledar і гра з його методами або навіть з використанням будь-яких інших бібліотек сторонніх організацій
Стан

2
Ні, це НЕ правильне рішення. Це те саме, що округлення значення дати до поверху, тобто - ви можете отримати 28 березня для позначки часу 29 березня (у вашій місцевості). Тому рішення на основі календаря (Apache's DateUtils теж використовує Календар) - єдиний спосіб
Дрю

Це також не буде працювати, коли час змінюється через літній час.
Олександру Северину

5

Від java.util.Date JavaDocs:

Дата класу являє собою конкретний момент у часі, з мілісекундною точністю

і з 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

Тепер ми можемо проаналізувати це як LocalDateTime«місцевий», який означає відсутність конкретної місцевості. На вході відсутня будь - яка інформація про зміщення з-за 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

Щоб побачити цей момент через об'єктив часового поясу UTC , витягніть Instantоб'єкт. Обидва ZonedDateTimeта і Instantбудуть представляти один і той же момент на шкалі часу, але відображаються як два різних часових годинника .

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 . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - 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-Time

ОНОВЛЕННЯ: Проект Joda-Time зараз знаходиться в режимі обслуговування , команда радить перейти до класів 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 працює добре.
Аркао

Вибачте, ви праві, я виправив це, протягом годин ви повинні встановити значення. Загалом, це майже так само, як урізати дату ...
m190

1

Мене справді дратувало те, що новий "вдосконалений" конструктор календаря не займає ініцію протягом мілісекунд, як "жахлива" стара дата. Потім я справді перехрестився і написав це:

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 GMT (те саме, що UTC), або Чт 1 січня 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 відображає інформацію про дату та час у різних часових поясах, як легко перемістити інформацію про час. Вам слід отримати позначку часу та врахувати часовий пояс, коли буде відключена інформація про час. Тоді ви можете створити новий об’єкт дати за допомогою відрізання штампів часу (ми можемо називати це печаткою дати), java компенсує часовий пояс, коли відображатиметься інформація про дату.

Як і у восьми зоні Східної (пекінський час), час у Пекіні раніше на 8 годин, ніж GMT, тому вам слід відняти більше 8 годин, коли ви робите модульну операцію. Тобто, спочатку слід отримати GMT-час, тоді 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();

}


FYI, у цій відповіді використовуються тривожні старі класи, які були витіснені років тому класами java.time .
Василь Бурк

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 році - це погана порада.
Василь Бурк

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] Ваш код неправильний, оскільки він ігнорує часовий пояс, саме питання, порушене в питанні.
Василь Бурк

Може бути зайвим, але я просто наведу простіший приклад, тому що я визнав, що відповідь не повинна бути в коментарях. Так, java.util.Date ігнорує TimeZone, але початкове питання, a Java Date objectтаким чином, я використовував java.util.Date. Більше того, це не пояснює, наскільки його часовий пояс проблематичний з java.util.Date (коли? Куди?), Хоча він не може не використати іншого Date.
lamusique

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. Тож насправді не підходить рішення питання.
Василь Бурк

0

Ось простий спосіб отримати дату без часу, якщо ви використовуєте Java 8+: Використовуйте тип java.time.LocalDate замість дати.

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.