【问题标题】:How to create a Java Date object of midnight today and midnight tomorrow?如何创建今天午夜和明天午夜的 Java Date 对象?
【发布时间】:2011-10-14 14:51:05
【问题描述】:

在我的代码中,我需要找到我今天发生的所有事情。所以我需要比较从今天上午 00:00(今早午夜)到下午 12:00(今晚午夜)的日期。

我知道...

Date today = new Date(); 

...现在就抓住我。还有……

Date beginning = new Date(0);

...让我在 1970 年 1 月 1 日获得零时间。但是,有什么简单的方法可以让今天的时间为零,明天的时间为零?


更新

我这样做了,但肯定有更简单的方法吗?

Calendar calStart = new GregorianCalendar();
calStart.setTime(new Date());
calStart.set(Calendar.HOUR_OF_DAY, 0);
calStart.set(Calendar.MINUTE, 0);
calStart.set(Calendar.SECOND, 0);
calStart.set(Calendar.MILLISECOND, 0);
Date midnightYesterday = calStart.getTime();
            
Calendar calEnd = new GregorianCalendar();
calEnd.setTime(new Date());
calEnd.set(Calendar.DAY_OF_YEAR, calEnd.get(Calendar.DAY_OF_YEAR)+1);
calEnd.set(Calendar.HOUR_OF_DAY, 0);
calEnd.set(Calendar.MINUTE, 0);
calEnd.set(Calendar.SECOND, 0);
calEnd.set(Calendar.MILLISECOND, 0);
Date midnightTonight = calEnd.getTime();

【问题讨论】:

  • 在我看来 Joda Time 比较容易,看我回答的结尾。如果你想使用 java.util.Date/Calendar 你必须这样做,没有更简单的方法。
  • RE:使用 Joda-Time 的 timaschew 注释,知道 Joda-Time 项目现在处于维护模式,并建议迁移到 java.time 类。
  • 仅供参考,非常麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle
  • 很少会看到这么多有缺陷的问题答案。我推荐the answer by Basil Bourque,它是正确的和知识渊博的。
  • 尝试将hh:mm aa作为11:59 pm连接到SimpleDateFormat对象

标签: java date


【解决方案1】:

JDK8 - Java 时间模块方式:

LocalDateTime todayMidnight = LocalDate.now().atStartOfDay();

同样有效:

LocalDateTime todayMidnight = LocalDateTime.now().with(LocalTime.MIDNIGHT);

【讨论】:

  • 通常使用ZonedDateTime 比使用LocalDateTime 更好。无论如何,我建议指定时区,即使它只是LocalDate.now(ZoneId.systemDefault())。无论如何,感谢您提到 java.time,现代 Java 日期和时间 API,它是这项工作的不错选择。
【解决方案2】:

其他答案是正确的,尤其是java.time answer by arganzheng。正如一些人所提到的,您应该避免使用旧的 java.util.Date/.Calendar 类,因为它们设计不佳、混乱且麻烦。它们已被 java.time 类取代。

让我补充一下关于处理午夜时间跨度的策略。

半开

在日期时间工作中,时间跨度通常使用“半开放”方法定义。在这种方法中,开头是inclusive,而结尾是exclusive。这可以解决问题,如果始终使用,可以更轻松地推理您的日期时间处理。

解决的一个问题是定义一天的结束。一天的最后一刻是23:59:59.999 (milliseconds) 吗?也许,在 java.util.Date 类(来自最早的 Java;麻烦——避免这个类!)和非常成功的 Joda-Time 库中。但是在其他软件中,例如像 Postgres 这样的数据库,最后时刻将是23:59:59.999999 (microseconds)。但在其他软件中,例如 java.time 框架(内置于 Java 8 及更高版本,Joda-Time 的继任者)和某些数据库中,例如 H2 Database,最后时刻可能是 23:59.59.999999999 (nanoseconds)。与其分裂头发,不如只考虑第一时刻,而不是最后时刻。

在半开中,一天从一天的第一个时刻开始,一直到但不包括第二天的第一个时刻。所以与其这样想:

…从今天上午 00:00(今天凌晨)到中午 12:00(今晚午夜)。

……这样想……

从今天的第一刻到但不包括明天的第一刻:
( >= 00:00:00.0 today AND 00:00:00.0 tomorrow )

在数据库工作中,这种方法意味着在 SQL 中使用BETWEEN operator

一天的开始

此外,一天中的第一个时刻始终是一天中的时间00:00:00.0Daylight Saving Time (DST) 在某些时区,可能还有其他异常情况,可能意味着一天的开始时间不同。

因此,让java.time 类通过调用LocalDate::atStartOfDay( ZoneId ) 来确定一天的开始。所以我们必须绕道LocalDate回到ZonedDateTime,正如你在这个示例代码中看到的那样。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( zoneId );
ZonedDateTime todayStart = now.toLocalDate().atStartOfDay( zoneId );
ZonedDateTime tomorrowStart = todayStart.plusDays( 1 );

注意可选的ZoneId 的传递。如果省略,则隐式应用 JVM 当前的默认时区。最好是明确的。

时区对于日期时间工作至关重要。问题和其他一些答案可能存在缺陷,因为它们没有有意识地处理时区。

转换

如果您必须使用 java.util.Date 或 .Calendar,请寻找添加到这些旧类的新转换方法。

java.util.Date utilDate = java.util.Date.from( todayStart.toInstant() );
java.util.GregorianCalendar gregCal = java.util.GregorianCalendar.from( todayStart );

时间跨度

顺便说一句,如果您在一段时间内做大量工作,请查看:

  • Duration
  • Period
  • Interval
    Interval 类位于 ThreeTen-Extra 项目中,它是 java.time 框架的扩展。该项目是 java.time 未来可能添加的试验场。
    Interval todayMontreal = Interval.of( todayStart.toInstant() , tomorrowStart.toInstant() );

关于java.time

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

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

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

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

从哪里获得 java.time 类?

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

【讨论】:

    【解决方案3】:

    使用 apache commons..

    //For midnight today 
    Date today = new Date(); 
    DateUtils.truncate(today, Calendar.DATE);
    
    //For midnight tomorrow   
    Date tomorrow = DateUtils.addDays(today, 1); 
    DateUtils.truncate(tomorrow, Calendar.DATE);
    

    【讨论】:

    • 仅供参考,非常麻烦的旧日期时间类,例如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle
    • @BasilBourque 同意 Java 8。但如果 OP 使用 Java 7,我想这仍然可行。
    • 几乎所有的 java.time 功能都可用于 ThreeTen-Backport 项目中的 Java 6 和 7,并具有几乎相同的 API。 ThreeTenABP 项目中进一步适用于 Android
    【解决方案4】:

    老式的方式..

    private static Date getDateWithMidnight(){
        long dateInMillis = new Date().getTime();
        return new Date(dateInMillis - dateInMillis%(1000*60*60*24) - TimeZone.getDefault().getOffset(dateInMillis));
    }
    

    【讨论】:

    • 那个麻烦的类在几年前被 java.time.Instant 类取代了。无需使用Date
    • Date 类始终采用 UTC。因此,您忽略了所需/预期时区的问题。例如,新的一天在印度加尔各答比 UTC 早几个小时,在魁北克蒙特利尔比 UTC 晚几个小时。
    【解决方案5】:
    Date now= new Date();
    // Today midnight
    Date todayMidnight = new Date(endTime.getTime() -endTime.getTime()%DateUtils.MILLIS_PER_DAY);
    
    // tomorrow midnight
    Date tomorrowMidnight = new Date(endTime.getTime() -endTime.getTime()%DateUtils.MILLIS_PER_DAY + DateUtils.MILLIS_PER_DAY);
    

    【讨论】:

    • 这仅适用于 UTC,无法解决时区问题。此外,可怕的Date 类在几年前被java.time.Instant 取代。现在避免使用Date
    【解决方案6】:

    和之前的答案差不多,但是没有人提到 AM_PM 参数:

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        cal.set(Calendar.AM_PM, Calendar.AM);
    

    【讨论】:

    • 麻烦的 Calendar 类在几年前被 java.time 类取代,特别是 ZonedDateTime
    【解决方案7】:
    Date todayMidnightUTC = java.sql.Date.valueOf(LocalDate.now());
    Date tomorrowMidnightUTC = java.sql.Date.valueOf(LocalDate.now().plusDays(1));
    Date anyMidnightLocal = java.sql.Date.valueOf(LocalDate.from(dateTime.toInstant().atZone(ZoneId.systemDefault())));
    

    但要注意java.sql.Date.toInstant() 总是抛出UnsupportedOperationException

    通过LocalDate to java.util.Date and vice versa simpliest conversion?

    【讨论】:

    • 不是一个好的答案。您正在将现代 java.time 的使用与它们取代的旧遗留类混合在一起。具体来说,您使用 java.sql 日期时间类,该类仅应用于与尚未更新驱动程序以直接处理 java.time 类的数据库交换数据,永远不能用于业务逻辑。还有一个问题是你忽略了时区这个关键问题。
    • @BasilBourque,感谢您的批评。使用一个未标记为已弃用的遗留类来获取另一个遗留类实例是非常安全的。时区问题更为重要。毕竟我决定坚持使用 riccardo 的answer
    【解决方案8】:

    为了完整起见,如果您使用的是Java 8,您还可以使用Instant 类的truncatedTo 方法在UTC 中获取午夜。

    Instant.now().truncatedTo(ChronoUnit.DAYS);
    

    如 Javadoc 中所写

    例如,使用 MINUTES 单位截断将向下舍入到最接近的分钟,将秒和纳秒设置为零。

    希望对你有帮助。

    【讨论】:

    【解决方案9】:

    因为一天是24 * 60 * 60 * 1000ms, 这一天的午夜可以计算为...

    long now = System.currentTimeMillis();
    long delta = now % 24 * 60 * 60 * 1000;
    long midnight = now - delta;
    Date midnightDate = new Date(midnight);`
    

    【讨论】:

    • 此代码仅适用于UTC,不适用于其他时区,并忽略夏令时等异常情况。此外,滚动你自己的日期时间解决方案是不明智的,因为这是一个非常棘手的话题。改用优秀的 java.time 类。
    【解决方案10】:

    我做这件事的方式与这里的其他人不同。我是 Java 新手,所以我的解决方案可能很差。

    Date now = new Date();
    Date midnightToday = new Date(now.getYear(), now.getMonth(), now.getDate());
    

    我不确定这是否可行,但无论哪种方式,我都非常感谢有关此解决方案的任何反馈。

    我对上面的陈述感到困惑,你可以通过调用明天来计算:

    c.add(Calendar.DAY_OF_MONTH, 1);
    

    如果你将一个月的日期加 1 并且是第 31 天,你不会得到这个月的第 32 天吗?

    为什么时间/日期并非全部基于 Java 中的 UTC?我认为只有在与 i/o 一起使用时才需要时区,但在内部应该始终在 UTC 中使用。但是,这些类似乎包含时区信息,这似乎不仅浪费,而且容易出现编码错误。

    【讨论】:

    • 嗨,米奇。我认为您的解决方案有效,但看起来 Date 构造函数已被弃用。 Java 人员不久前弃用了大多数与 Date 相关的原始方法,并用较新的 Calendar 相关方法取而代之。就个人而言,我不认为这是一种改进,但不赞成使用已弃用的方法。谢谢!
    • 对弃用感到难过。对我来说,指定年、月和日期似乎比清除小时、分钟、秒和毫秒要干净得多。清除这些依赖于毫秒是 Date 类的最精细粒度,而我的解决方案没有。我的解决方案也更短更清晰。作为 Java 新手,我真的很欣赏 C++ 在使用日期和时间方面的简洁、简单和高效。
    • @Mitch 以我的经验,你最近学习的语言总是被认为是最复杂的。
    • 仅供参考:这些该死的可怕的旧日期时间类现在已被现代 java.time 类所取代。
    【解决方案11】:

    java.util.日历

    // today    
    Calendar date = new GregorianCalendar();
    // reset hour, minutes, seconds and millis
    date.set(Calendar.HOUR_OF_DAY, 0);
    date.set(Calendar.MINUTE, 0);
    date.set(Calendar.SECOND, 0);
    date.set(Calendar.MILLISECOND, 0);
    
    // next day
    date.add(Calendar.DAY_OF_MONTH, 1);
    

    JDK 8 - java.time.LocalTime 和 java.time.LocalDate

    LocalTime midnight = LocalTime.MIDNIGHT;
    LocalDate today = LocalDate.now(ZoneId.of("Europe/Berlin"));
    LocalDateTime todayMidnight = LocalDateTime.of(today, midnight);
    LocalDateTime tomorrowMidnight = todayMidnight.plusDays(1);
    

    乔达时间

    如果您使用的是 Joda Time,因为 API 非常好: 罢工>

    DateTime date = new DateTime().toDateMidnight().toDateTime();
    DateTime tomorrow = date.plusDays(1);
    

    由于 Joda Time DateMidnight 的 2.3 版已弃用,因此请使用以下代码:

    DateTime today = new DateTime().withTimeAtStartOfDay();
    DateTime tomorrow = today.plusDays(1).withTimeAtStartOfDay();
    

    如果您不想要 JVM 的当前默认时区,请传递时区。

    DateTimeZone timeZone = DateTimeZone.forID("America/Montreal");
    DateTime today = new DateTime(timeZone).withTimeAtStartOfDay(); // Pass time zone to constructor.
    

    【讨论】:

    • 感谢您的帮助,我刚刚用我目前的做法更新了我原来的问题。感谢您的建议。
    • 从 JodaTime 2.3 开始,toDateMidnight 已弃用。有关详细信息,请参阅此答案:stackoverflow.com/a/19048833/363573
    • 添加了 JDK 8 的解决方案
    • @timaschew 虽然通常是正确的,但我建议使用分区日期时间值 (ZonedDateTime) 来执行 java.time,就像您在 Joda-Time 部分中所做的那样。 Local… 类型没有时区信息——这是它们的全部目的,不会丢失/忽略所有偏移量和时区详细信息。特别是当问题谈到使用这些值进行比较时(可能是数据库查询?),分区日期时间值可能更有用。
    • @Amalgovinus 确保设置 Calendar.HOUR_OF_DAY,而不是 Calendar.HOUR。
    【解决方案12】:

    这些方法对你有帮助-

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

    public static Date getEndOfDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }
    

    【讨论】:

      【解决方案13】:

      java.time

      如果您使用的是 Java 8 及更高版本,您可以尝试java.time package (Tutorial):

      LocalDate tomorrow = LocalDate.now().plusDays(1);
      Date endDate = Date.from(tomorrow.atStartOfDay(ZoneId.systemDefault()).toInstant());
      

      【讨论】:

      • 好答案。我建议指定所需/预期的时区。 JVM 当前的默认时区可能因操作系统设置、更改计算机或 JVM 的任何线程中的任何应用程序在运行时随时更改默认值而有所不同。
      【解决方案14】:

      我知道这是一个很老的帖子。我想在这里分享我的知识!

      对于今天午夜的日期,您可以使用以下确切时区

      public static Date getCurrentDateWithMidnightTS(){
      
          return new Date(System.currentTimeMillis() - (System.currentTimeMillis()%(1000*60*60*24)) - (1000*60 * 330));
      }
      

      其中 (1000*60 * 330) 被减去,即实际上与时区相关,例如印度时区,即 kolkata 与实际时区不同 +5:30hrs。因此,通过转换为毫秒来减去它。

      所以根据你改变最后一个减去的数字。我正在创建一个产品,即仅位于印度所以只使用了特定的时间戳。

      【讨论】:

      • 你为什么建议这个而不是接受的答案?滚动您的日期时间值通常是有风险的。特别是,此代码永久硬编码为单个时区的当前偏移量。没有考虑夏令时或其他异常情况。我只看到没有附加价值的缺点。
      • @Basil 我当然接受答案,是的,我在那里提到了硬编码值。这仅在您在特定时区工作时才有用。
      【解决方案15】:

      这似乎是一个选项:

      DateFormat justDay = new SimpleDateFormat("yyyyMMdd");
      Date thisMorningMidnight = justDay.parse(justDay.format(new Date()));
      

      添加一天

      Date tomorrow = new Date(thisMorningMidnight.getTime() + 24 * 60 * 60 * 1000);
      

      Calendar c = Calendar.getInstance();
      c.setTime(thisMorningMidnight);
      c.add(Calendar.DATE, 1);
      Date tomorrowFromCalendar = c.getTime();
      

      我有一种预感,如果出现夏令时等奇怪的事情会导致增加 24 小时不够用(请参阅https://stackoverflow.com/a/4336131/32453 及其其他答案),则首选后者。

      【讨论】:

      • 我在我的 sqlite 数据库中有一个日期和时间表示为 long。我需要为第二天的开始做很长时间。我仍然可以使用我的 long 对象而不是上面使用的 SimpleDateFormat 方法吗?
      • 只要是正确的毫秒,您就可以将其作为构造函数传递:docs.oracle.com/javase/7/docs/api/java/util/…
      【解决方案16】:

      使用 JodaTime 的最简单方法

      DateMidnight date = DateMidnight.now();

      【讨论】:

      • 没有。 Joda-Time 中与“午夜”相关的类和方法都已被弃用。它们基于一个有缺陷的概念。一天中的第一刻并不总是00:00:00.000。而是使用新的withTimeAtStartOfDay 方法。更新到 Joda-Time 的当前版本 2.4 并阅读发行说明。示例:DateTime today = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).withTimeAtStartOfDay();
      【解决方案17】:

      从 JodaTime 2.3 开始,toDateMidnight() 已弃用。

      来自Upgrade from 2.2 to 2.3

      自 2.2 起的弃用 ---------------------- - 约会午夜 [#41] 这个类在概念上有缺陷 在某些时区偶尔不会出现午夜时间 这是夏令时从 00:00 到 01:00 的结果 DateMidnight 本质上是一个时间锁定到午夜的 DateTime 考虑到 LocalDate,这样的概念通常不太好用 用 LocalDate 替换 DateMidnight 或者用 DateTime 替换它,也许使用 withTimeAtStartOfDay() 方法

      这是一个没有toDateMidnight() 方法的示例代码。

      代码

      DateTime todayAtMidnight = new DateTime().withTimeAtStartOfDay();
      System.out.println(todayAtMidnight.toString("yyyy-MM-dd HH:mm:ss"));
      

      输出可能因您当地的时区而异

      2013-09-28 00:00:00
      

      【讨论】:

      • 优秀的答案。我会添加一个 DateTimeZone 实例到 DateTime 构造函数,以强调指定时区的重要性,而不是无意中依赖于默认值:DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
      【解决方案18】:

      【讨论】:

        【解决方案19】:

        找到午夜的最简单方法:

        Long time = new Date().getTime();
        Date date = new Date(time - time % (24 * 60 * 60 * 1000));
        

        第二天:

        Date date = new Date(date.getTime() + 24 * 60 * 60 * 1000);
        

        【讨论】:

        • 我运行该代码并得到:午夜:Thu Nov 01 19:00:00 CDT 2012
        • 如果您执行了 System.out.print(date) 之类的操作,系统会将日期实例转换为系统时区 (CDT)。 CDT 晚上 7 点是 GMT 午夜。如果您需要选定时区的午夜,则必须添加该时区的偏移量,同时将 DST 考虑在内。
        • @Bouncner 是的。无论您使用哪种方法,地球上都有许多不同的“午夜”时间,但 Java 午夜只有一个。
        • @Andrei Volgin 不知道有一个“java午夜”。 ;) 我想说的是,这个解决方案并没有真正帮助主题创建者。日历解决方案可以避免丑陋的二次计算,并在正确使用时处理时区(尤其是夏令时等)。我在您的解决方案中没有看到这一点。
        • 嗨@AndreiVolgin,我得到了 Date date = new Date(time+ TimeZone.getDefault().getRawOffset() - time % (24 * 60 * 60 * 1000));
        【解决方案20】:

        请记住,Date 不用于表示 日期 (!)。要表示日期,您需要一个日历。这个:

        Calendar c = new GregorianCalendar();
        

        将创建一个 Calendar 实例,表示您当前时区的当前日期。现在您需要通过将其设置为0 来截断低于天(小时、分钟、秒和毫秒)的每个字段。你今天有一个午夜。

        现在要到第二天的午夜,你需要加一天:

        c.add(Calendar.DAY_OF_MONTH, 1);
        

        请注意,添加 86400 秒或 24 小时是不正确的,因为此时可能会出现夏令时。

        更新:但是我最喜欢的处理这个问题的方法是使用来自Commons LangDateUtils 类:

        Date start = DateUtils.truncate(new Date(), Calendar.DAY_OF_MONTH))
        Date end = DateUtils.addDays(start, 1);
        

        它在幕后使用Calendar...

        【讨论】:

        • 感谢您的帮助。 “日期不用于表示日期”——我们会觉得这真是太棒了,不是吗? ;)
        • @RobertHume 公平地说,Date 自从 Java 的第一个版本就已经存在。那个时代的大多数事情……都没有经过深思熟虑。尤其是因为 Java 是第一个尝试它正在尝试的各种事情的人之一,而且距离达成一致的标准如何做到这一点。
        猜你喜欢
        • 1970-01-01
        • 2020-09-30
        • 2021-09-23
        • 2012-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-19
        相关资源
        最近更新 更多