Как отрезать минуты, часы и секунды от объекта Date?

Мне нужно сделать карту, где Даты - это ключи. 2 объекта даты равны, если они имеют одинаковое значение getTime().

Меня интересуют только год, месяц и день. Как я могу trim лишние часы и минуты получать "четкие" даты?

Ответ 1

Использовать пользовательский Comparator<Date> для TreeMap<Date,V>.

    Comparator<Date> ymdComparator = new Comparator<Date>() {
        @Override public int compare(Date d1, Date d2) {
            return 
                d1.getYear() < d2.getYear() ? -1 :
                d1.getYear() > d2.getYear() ? +1 :
                d1.getMonth() < d2.getMonth() ? -1 :
                d1.getMonth() > d2.getMonth() ? +1 :
                d1.getDay() < d2.getDay() ? -1 :
                d1.getDay() > d2.getDay() ? +1 :
                0;
        }
    };

    SortedMap<Date,V> map = new TreeMap<Date,V>(ymdComparator);

О, java.util.Date отстой, используйте Joda Time и т.д.

Ответ 2

Вы можете создать метод trim:

public static Date trim(Date date) {
      Calendar cal = Calendar.getInstance();
      cal.clear(); // as per BalusC comment.
      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();
 }

И используйте его как:

 map.put( trim( aDate ), xyz() );

...

 map.get( trim( otherDate ));

Здесь полный рабочий образец:

import java.util.Calendar;
import java.util.Date;
import static java.util.Calendar.*;
import static java.lang.System.out;


public class DateTest {
    public static void main( String [] args )  throws InterruptedException {
            Date date = new Date();
            Thread.sleep(1);
            Date other = new Date();
            out.printf("equals? = %s, hashCode? = %s %n", (date.equals(other)), (date.hashCode() == other.hashCode()));

            Date todayeOne = trim( date );
            Date todayTwo  = trim( date );


            out.printf("equals? = %s, hashCode? = %s %n", (todayeOne.equals(todayTwo)), (todayeOne.hashCode() == todayTwo.hashCode()));

    }

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

}

выход:

$ java DateTest 
equals? = false, hashCode? = false 
equals? = true, hashCode? = true 

Ответ 3

long time1 = myDate1.getTime()/(1000*60*60*24);
long time2 = myDate2.getTime()/(1000*60*60*24);
if (time1 == time2)
    // equal!

Это подталкивает незначительные значения ниже десятичной, тогда целочисленное деление усекает его, поэтому остаются только значения, значимые на уровне дня и выше.

Если вы хотите снова сделать эти даты, просто примените смещение к укороченным значениям:

myDate1.setTime(time1 * (1000*60*60*24));
myDate2.setTime(time2 * (1000*60*60*24));

Ответ 4

TL;DR

LocalDate ld =
    myUtilDate.toInstant()
              .atZone( ZoneId.of( "America/Montreal" ) )
              .toLocalDate();

Подробнее

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

Instant усечен

Чтобы более непосредственно задать вопрос:

  • Преобразовать в java.time, от java.util.Date до java.time.Instant.
  • Усечь до даты.

Преобразование с помощью новых методов, добавленных в старые классы.

Instant instant = myUtilDate.toInstant();

Функция усечения встроена в класс Instant. Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунды (до девяти (9) цифр десятичной дроби).

Instant instantTruncated = instant.truncatedTo( ChronoUnit.DAYS );

ZonedDateTime и LocalDate

Но вышеприведенный подход имеет проблемы. Оба java.util.Date и Instant представляют момент на временной шкале в формате UTC, а не в определенном часовом поясе. Поэтому, если вы отбрасываете время суток или устанавливаете его на 00:00:00, вы получаете дату, которая имеет смысл только в UTC. Если вы имели в виду дату для Окленд-НЗ или Монреаль-Квебек, у вас может быть неправильная дата.

Итак, лучший подход - применить желаемый/ожидаемый часовой пояс к Instant, чтобы получить ZonedDateTime.

Другая проблема заключается в том, что мы ненадлежащим образом используем объект даты и времени для представления значения, определенного только по дате. Вместо этого мы должны использовать класс с датой. Из ZonedDateTime следует извлечь a LocalDate, если вы хотите только дату.

Класс LocalDate представляет значение даты только без времени и без часового пояса.

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
LocalDate ld = zdt.toLocalDate();

О java.time

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

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

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

Где получить классы java.time?

  • Java SE 8 и SE 9 и позже
    • Встроенный.
    • Часть стандартного Java API с объединенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функций java.time возвращается на Java 6 и 7 в ThreeTen-Backport.
  • Android

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

Ответ 5

Просто конвертируйте объекты Date равными, если они имеют один и тот же год, месяц и день:

public static Date convertDate(Date oldDate) {
    final long oneDay = 1000 * 60 * 60 * 24;
    long newDate = oldDate.getTime() / oneDay;
    return new Date( newDate * oneDay );
}

Ответ 6

Предлагаемое решение не работает в Android, поскольку оно застряло на Java 7, и поэтому Календарь не работает. У других решений также были ошибки, поскольку они не учитывали смещение TimeZone или имели проблемы с переполнением int до того, как они преобразуются в long.

Это решение работает:

public static final long MILLISECONDS_PER_DAY=(1000L * 60L * 60L * 24L);

public static long convertDate(Date d) {
    TimeZone tz = TimeZone.getDefault();
    long val = d.getTime() + tz.getOffset(d.getTime()); /*local timezone offset in ms*/
    return val / MILLISECONDS_PER_DAY;
}

public static Date convertDate(long date) {
    TimeZone tz = TimeZone.getDefault();
    Date d=new Date();
    d.setTime(date*MILLISECONDS_PER_DAY-tz.getOffset(d.getTime()));
    return d;
}