【问题标题】:How to check if a date Object equals yesterday?如何检查日期对象是否等于昨天?
【发布时间】:2010-06-09 13:21:22
【问题描述】:

现在我正在使用此代码

Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE) - 1, 12, 0, 0); //Sets Calendar to "yeserday, 12am"
if(sdf.format(getDateFromLine(line)).equals(sdf.format(cal.getTime())))                         //getDateFromLine() returns a Date Object that is always at 12pm
{...CODE

必须有一种更流畅的方法来检查 getdateFromLine() 返回的日期是否是昨天的日期。重要的是日期,而不是时间。这就是我使用 SimpleDateFormat 的原因。 提前感谢您的帮助!

【问题讨论】:

    标签: java date calendar java-time


    【解决方案1】:
    Calendar c1 = Calendar.getInstance(); // today
    c1.add(Calendar.DAY_OF_YEAR, -1); // yesterday
    
    Calendar c2 = Calendar.getInstance();
    c2.setTime(getDateFromLine(line)); // your date
    
    if (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)
      && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR)) {
    

    这也适用于 1 月 1 日这样的日期。

    【讨论】:

    • 为什么这在 1 月 1 日有效? 12 月 31 日的年份会不会有所不同?
    • 之所以有效,是因为 Calendar.add 通过根据字段类型和输入金额计算出的偏移量来更改日期的时间戳。因此,无论原始日期如何,它都只使用时间戳(自 1970 年 1 月 1 日以来的毫秒数)并根据该加/减为输入计算的差值计算新日期。这实际上也会改变年份,即使该字段是 DAY_OF_YEAR。
    • 任何看到这个帖子的人都应该参考@Przemek的回答,它使用java.time,应该优先使用joda-time、java.util.Datejava.util.Calendarstackoverflow.com/a/33893147/1322243跨度>
    【解决方案2】:

    java.time

    使用 Java 8 内置的java.time 框架

    LocalDate now = LocalDate.now(); //2015-11-24
    LocalDate yesterday = LocalDate.now().minusDays(1); //2015-11-23
    
    yesterday.equals(now); //false
    yesterday.equals(yesterday); //true
    

    官方 Oracle LocalDate tutorial 状态

    应该使用equals方法进行比较。

    如果您正在使用LocalDateTimeZonedDateTimeOffsetDateTime 等对象,则可以转换为LocalDate

    LocalDateTime.now().toLocalDate(); # 2015-11-24
    

    【讨论】:

    • 我建议始终将可选的ZoneId 传递给LocalDate.now,而不是隐式依赖JVM 当前的默认时区。即使在运行时,该默认值也可以随时更改。像这样:LocalDate.now( ZoneId.of( "America/Montreal" ) )
    【解决方案3】:

    我同意 Ash Kim 的观点,如果您想保持理智,Joda-Time 库是您的最佳选择。

    import org.joda.time.DateTime;
    
    public static boolean dayIsYesterday(DateTime day) {
        DateTime yesterday = new DateTime().withTimeAtStartOfDay().minusDays(1);
        DateTime inputDay = day.withTimeAtStartOfDay();
    
        return inputDay.isEqual(yesterday);
    }
    

    在此示例中,如果日期时间 day 来自昨天,则 dayIsYesterday(day) 将返回 true

    【讨论】:

    • Joda-Time 中与午夜相关的类和方法基于一个有缺陷的想法。从 Joda-Time 2.3 开始,它们现在已被弃用。而是使用方法withTimeAtStartOfDay
    • 另外,您通常应该specify time zone 而不是依赖JVM的默认值,因为这对于确定“今天”和“昨天”至关重要。
    • 现在 = "1/07/2016"。上一个 =“1980 年 12 月 6 日”。您的 previousFromYesteray 将设置为 true,而主题启动者询问您如何检查日期是否为“昨天”。
    • @Dogcat 好点。已编辑。 2011 我有一些解释要做。
    【解决方案4】:

    避免使用 java.util.Date 和 .Calendar

    accepted answer 在技术上是正确的,但不是最优的。 java.util.Date 和 .Calendar 类是出了名的麻烦。避开他们。使用Joda-Time 或新的java.time package(在Java 8 中)。

    时区

    时区在日期时间工作中至关重要。如果您忽略此问题,将应用 JVM 的默认时区。更好的做法是始终指定而不是依赖默认值。即使您想要默认值,也要显式调用getDefault

    一天的开始由时区定义。柏林的新一天比蒙特利尔更早。所以“今天”和“昨天”的定义需要一个时区。

    乔达时间

    Joda-Time 2.3 中的示例代码。

    DateTimeZone timeZone = DateTimeZone.forID( "Europe/Berlin" );
    DateTime today = DateTime.now( timeZone ); 
    

    确定昨天的一种方法是转换为 LocalDate 对象。此处显示的另一种方式是将“昨天”表示为一段时间。我们将该跨度定义为从昨天的第一刻到但不包括今天的第一刻。这种方法称为“半开放”,其中开头是包含,结尾是排他

    减去一天得到昨天(或前一天)。

    DateTime yesterdayStartOfDay = today.minusDays( 1 ).withTimeAtStartOfDay();
    Interval yesterdayInterval = new Interval( yesterdayStartOfDay, today.withTimeAtStartOfDay() );
    

    将您的目标 java.util.Date 对象转换为 Joda-Time DateTime 对象。将时区应用于该新对象,而不是依赖于应用 JVM 的默认时区。从技术上讲,这里的时区在这种情况下是无关紧要的,但包含时区是一个好习惯。

    DateTime target = new DateTime( myJUDate, timeZone );
    

    测试目标是否在昨天的间隔内着陆。

    boolean isYesterday = yesterdayInterval.contains( target );
    

    显然,这种半开放时间跨度的方法不仅仅适用于“昨天”,例如“本周”、“上个月”等。

    更新: Joda-Time 项目现在处于维护模式。该团队建议迁移到 java.time 类。请参阅correct Answer by Przemek 中的 java.time 解决方案。


    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

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

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

    您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

    从哪里获取 java.time 类?

    【讨论】:

      【解决方案5】:

      不要设置日历,试试这个:

      public static void main(String[] args) {
          int DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
          Date date = new Date();
          SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
          String prevDate = dateFormat.format(date.getTime() - DAY_IN_MILLIS);
          String currDate = dateFormat.format(date.getTime());
          String nextDate = dateFormat.format(date.getTime() + DAY_IN_MILLIS);
          System.out.println("Previous date: " + prevDate);
          System.out.println("Current date: " + currDate);
          System.out.println("Next date: " + nextDate);
        }
      

      这应该允许您沿着日历向前和向后移动

      然后您可以简单地将 getDateFromLine(line) 与 prevDate 值或任何您喜欢的值进行比较。

      【讨论】:

        【解决方案6】:

        我用这种方式测试这个方法的时候发现有点迷茫

         Calendar now = new GregorianCalendar(2000, 1, 1);
         now.add(Calendar.DATE, -1);
        
         SimpleDateFormat format= new SimpleDateFormat("yyyy-MM-dd");
         System.out.println(format.format(now.getTime()));
        

        我的期望是打印 1999-12-31,但实际是 2001-1-31 我猜 Calendar.add() 只处理月份中的一天。

        但实际的错误是我创建日历对象的方式。 在日历中,月份以0开头,变量现在的值是2001-2-1,我很自负没有打印它。当我发现有问题时。 创建日历的正确方法是:

         Calendar now = new GregorianCalendar(2000, Calendar.JANUARY, 1);
        

        对于前 C# 程序员来说,日历太奇怪了 :(

        【讨论】:

          【解决方案7】:

          大概是这样的:

                   Calendar c1 = Calendar.getInstance();
                   Date d1 = new Date(/* control time */);
                   c1.setTime(d1);
          
                  //current date
                  Calendar c2 = Calendar.getInstance();
          
                  int day1=c1.get(Calendar.DAY_OF_YEAR);
                  int day2=c2.get(Calendar.DAY_OF_YEAR);
          
                 //day2==day1+1 
          

          【讨论】:

          • 还应该比较年份值。
          • 哦,是的,我忘记了年份。
          【解决方案8】:

          请随意重用这些扩展方法。他们深受这篇帖子答案的影响。顺便谢谢大家!

          fun Date.isSameDay(other: Date): Boolean {
              val thisCalendar = Calendar.getInstance()
              val otherCalendar = Calendar.getInstance()
              thisCalendar.time = this
              otherCalendar.time = other
              return thisCalendar.get(Calendar.YEAR) == otherCalendar.get(Calendar.YEAR)
                      && thisCalendar.get(Calendar.DAY_OF_YEAR) == otherCalendar.get(Calendar.DAY_OF_YEAR)
          }
          
          fun Date.isYesterday(other: Date): Boolean {
              val yesterdayCalendar = Calendar.getInstance()
              yesterdayCalendar.time = other
              yesterdayCalendar.add(Calendar.DAY_OF_YEAR, -1)
          
              return yesterdayCalendar.time.isSameDay(this)
          }
          

          【讨论】:

          • 这些糟糕的日期时间类在几年前被 JSR 310 中定义的现代 java.time 类所取代。
          • 不要讨厌他们。在 Android 上,您必须处理这些可怕的数据时间类,直到您仅支持 Android 26 及更高版本或依赖第三方库。让我们尊重那些可怜的旧的和过时的类。它们仍然有用。
          • 不,根本不需要可怕的遗留类。 Gradle Plug-in 4 现在提供了“API 脱糖”功能,以便在旧版 Android 中追溯使用 java.time 类的子集。如果这不适合您,请使用 ThreeTenABP 库中适用于 Android 的 ThreeTen-Backport 项目。查看my Answer底部的链接。
          • 是的,当我意识到我不想添加另一个第三方库只是为了进行比较时,我正在将 ThreeTenABP 库添加到项目中......关于 Gradle Plugin 4 API 脱糖,也许我试试!但是这段代码通过了我为检查功能而编写的大量测试,所以......也许我没有。但也许在另一个项目中? 非常感谢您的评论!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-11
          • 2014-10-26
          • 2012-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-05
          相关资源
          最近更新 更多