【问题标题】:How to trim minutes and hours and seconds from Date object?如何从 Date 对象中修剪分钟和小时和秒?
【发布时间】:2010-05-05 17:29:05
【问题描述】:

我需要制作以日期为键的地图。如果两个日期对象的getTime() 方法的值相同,则它们是相等的。

我只对年、月和日感兴趣。我如何trim 不必要的小时和分钟来获得“明确”的日期?

【问题讨论】:

    标签: java date


    【解决方案1】:

    tl;博士

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

    详情

    问题和其他答案使用过时的旧日期时间类,这些类已被证明设计不佳、令人困惑且麻烦。现在是遗留的,被 java.time 类所取代。

    Instant 被截断

    为了更直接地解决这个问题:

    1. 转换为 java.time,从 java.util.Datejava.time.Instant
    2. 截断日期。

    通过添加到旧类的新方法进行转换。

    Instant instant = myUtilDate.toInstant();
    

    截断功能内置于Instant 类中。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

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

    ZonedDateTime & LocalDate

    但上述方法存在问题。 java.util.DateInstant 都代表 UTC 时间线上的时刻,而不是特定时区。因此,如果您放弃一天中的时间,或将其设置为 00:00:00,您将获得一个仅在 UTC 中有意义的日期。如果您指的是奥克兰 NZ 或蒙特利尔魁北克的日期,您可能打错了日期。

    因此,更好的方法是将您想要/预期的时区应用于Instant 以获得ZonedDateTime

    另一个问题是我们不恰当地使用日期时间对象来表示仅日期值。相反,我们应该使用仅日期类。如果您想要的只是日期,我们应该从ZonedDateTime 中提取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.DateCalendarSimpleDateFormat

    Joda-Time 项目现在位于maintenance mode,建议迁移到 java.time。

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

    从哪里获得 java.time 类?

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

      【解决方案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 
      

      【讨论】:

      • 我会在setTime() 之前添加一个clear() 以避免时区问题。 OT:账户合并?是时候了:)
      • @BalusC 添加。是的,我已经有两个 10k 帐户了,不过我还是按顺序合并了。
      • @BalusC 我不明白 clear() 的必要性吗? setTime() 不会重置所有内容吗?
      • @james:这是几年前教给我的“练习”。该日历之前充满了错误,其中大部分是通过在getInstance() 之后显式调用clear() 来修复的。它还取决于所使用的 JVM。
      【解决方案3】:

      TreeMap<Date,V> 使用自定义Comparator<Date>

          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 等等。

      【讨论】:

      • +1 表示比较器,-1 表示不推荐使用的方法,+1 表示吸吮。
      • 我在比较器中使用了来自 commons-lang 的 DateUtils.isSameDay(d1, d2) 而不是所有这些三元运算符。
      【解决方案4】:
      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));
      

      【讨论】:

      • 这个解决方案有点错误,因为它没有考虑 TimeZone 偏移量。例如,5 月 21 日 0:35 UTC+1 转换为 5 月 20 日 1:00 UTC+1。
      【解决方案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 上,因此 Calendar 存在错误。其他解决方案也存在错误,因为它们没有考虑 TimeZone 偏移,或者在转换为 long 之前存在 int 溢出问题。

        这个解决方案似乎有效:

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

        【讨论】:

          猜你喜欢
          • 2011-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-19
          • 2012-06-08
          • 1970-01-01
          • 2020-03-13
          • 1970-01-01
          相关资源
          最近更新 更多