如何创建今天午夜和明天午夜的Java Date对象?


178

在我的代码中,我需要找到我今天发生的所有事情。因此,我需要将今天00:00 am(今早凌晨)至12:00 pm(今晚午夜)之间的日期进行比较。

我知道 ...

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();

3
我认为Joda Time比较容易,请看我回答的结尾。如果要使用java.util.Date/Calendar,则必须这样做,没有比这更简单的方法了。
timaschew 2011年

RE:timaschew使用Joda-Time的注释,知道Joda-Time项目现在处于维护模式,并建议迁移到java.time类。
罗勒·布尔克

1
仅供参考,非常麻烦旧日期,时间类,如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle 教程
罗勒·布尔克

2
很少有人会对此问题提出如此多的错误答案。我推荐Basil Bourque的答案,它是正确的并且知识渊博。
Ole VV

Answers:


344

java.util.Calendar

// 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);

乔达时代

如果您使用的JDK <8,我建议使用Joda Time,因为该API非常好:

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

由于Joda Time 2.3版DateMidnight弃用,因此请使用以下命令:

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.

感谢您的帮助,我刚刚用我当前的方式更新了原始问题。谢谢你的建议。

3
从JodaTime 2.3开始,toDateMidnight不推荐使用。详情请参见此答案:stackoverflow.com/a/19048833/363573
Stephan

为JDK 8添加了解决方案
timaschew

1
@timaschew虽然通常是正确的,但我建议ZonedDateTime像在“ Joda-Time”部分中一样,使用带区域的日期时间值()来执行java.time 。这些Local…类型没有时区信息,这是它们的全部用途,不会丢失/忽略所有偏移量和时区详细信息。尤其是当Question谈论使用这些值进行比较(也许是数据库查询?)时,分区日期时间值可能会更有用。
Basil Bourque

1
@Amalgovinus确保设置Calendar.HOUR_OF_DAY,而不是Calendar.HOUR。
奥斯瓦尔德

42

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

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

如Javadoc所写

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

希望能帮助到你。


1
感谢您的提示。在指定时区获取午夜时间变得更长:Date.from(date.toInstant().atZone(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS).toInstant())
Vadzim


33

查找午夜的最简单方法:

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

我运行了该代码并得到:午夜:CDT 2012
戴夫

3
如果您执行了诸如System.out.print(date)之类的操作,则系统会将日期实例转换为系统时区(CDT)。CDT晚上7点是格林尼治标准时间午夜。如果需要在选定时区的午夜,则必须考虑DST来添加该时区的偏移量。
Andrei Volgin

@Bouncner是的。不论您使用哪种方法,地球上都有许多不同的“午夜”时间,但是Java午夜只有一个。
Andrei Volgin

2
@Andrei Volgin不知道有“ java午夜”。;)我想说的是,此解决方案对imo并没有真正帮助主题创建者。日历解决方案避免了笨拙的第二次计算,并且在正确使用时会注意时区(尤其是夏令时等)。我看不到您的解决方案。
Bouncner 2013年

嗨@AndreiVolgin,我得到了Date date = new Date(time + TimeZone.getDefault()。getRawOffset()-time%(24 * 60 * 60 * 1000));
jrey,

28

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

Calendar c = new GregorianCalendar();

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

现在要第二天午夜,您需要添加一天:

c.add(Calendar.DAY_OF_MONTH, 1);

注意添加 86400由于夏季可能会同时出现,因此秒或24小时不正确。

更新:但是,我最喜欢的处理此问题的方法是使用Commons Lang中的DateUtils类:

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

Calendar在幕后使用...


15
谢谢您的帮助。“日期不被用来代表日期” –我们会非常惊讶,不是吗?;)

@RobertHume坦白地说,Date自Java的第一个发行版就出现了。那个时代的大多数事情都……还没完全想到。特别是因为Java是最早尝试它尝试的东西的人之一,而且关于如何执行它的标准还远远没有达成共识。
基金莫妮卡的诉讼

14

这些方法将帮助您-

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

9

从JodaTime 2.3开始,toDateMidnight()不推荐使用。

2.2升级到2.3

    自2.2起弃用
    ----------------------
    -DateMidnight [#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

2
极好的答案。我还要增加将DateTimeZone实例传递给该DateTime构造函数的作用,以强调指定时区的重要性,而不要无视默认值:DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
Basil Bourque 2014年

8

其他答案是正确的,尤其是arganzheng的java.time答案。如前所述,您应该避免使用旧的java.util.Date/.Calendar类,因为它们的设计不良,令人困惑且麻烦。它们已被java.time类取代。

让我添加有关处理午夜时间跨度的策略的注释。

半开

在日期时间工作中,通常使用“半开放”方法定义时间跨度。在这种方法一开始是包容性的,而结局是独家。这样可以解决问题,如果持续使用,则可以更轻松地进行日期时间处理的推理。

解决的一个问题是定义一天的结束时间。是一天的最后时刻23:59:59.999毫秒)吗?也许是在java.util.Date类中(最早的Java;麻烦的是-避免使用此类!),以及在非常成功的Joda-Time库中。但是在其他软件(例如Postgres这样的数据库)中,最后一刻将是23:59:59.999999微秒)。但是在其他软件中,例如java.time框架(内置在Java 8和更高版本中,是Joda-Time的继承者)和某些数据库(例如H2数据库)中,最后一刻可能是23:59.59.999999999纳秒)。不要一头雾水,而应该只考虑第一刻,而不要考虑最后一刻。

在半开放式中,一天从一天的第一天开始,一直到(但不包括)第二天的第一天。因此,与其这样思考:

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

……这样想……

从今天的第一刻开始直到但不包括明天的第一刻:
(> = 00:00:00.0今天AND < 00:00:00.0明天)

在数据库工作中,这种方法意味着使用SQL中的BETWEEN运算符

一天的开始

此外,一天的第一时刻并不总是一天中的时间00:00:00.0。某些时区的夏时制(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 );

请注意传递了optional 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-额外的项目,扩展到java.time框架。该项目是将来可能向java.time添加内容的试验场。
    Interval todayMontreal = Interval.of( todayStart.toInstant() , tomorrowStart.toInstant() );

关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

现在处于维护模式Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多


6

java.time

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

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

2
好答案。我建议指定所需/预期的时区。JVM的当前默认时区可能因操作系统设置,更改计算机或JVM的任何线程中的任何应用程序而异,并且在运行时随时更改默认值。
罗勒·布尔克

4

这似乎是一个选择:

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的日期和时间。我需要漫长的第二天开始。我是否仍然可以将Date对象与我的long一起使用,而不是与上面使用的SimpleDateFormat方法一起使用?
AJW

1
只要是正确的毫秒数,就可以将其作为构造函数传递:docs.oracle.com/javase/7/docs/api/java/util/…–
rogerdpack

1

我做这件事的方式与这里的其他人不同。我是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中使用。但是,这些类似乎包含时区信息,这不仅浪费而且容易产生编码错误。


2
嗨,米奇。我认为您的解决方案有效,但好像不赞成使用Date构造函数。Java族早就不赞成使用大多数原始的Date相关方法,而将它们替换为更新的Calendar相关方法。我个人不知道它有什么改进,但是不赞成使用不推荐使用的方法。谢谢!

为弃用而难过。对我来说,指定年份,月份和日期似乎比清除小时,分钟,秒和毫秒要干净得多。清除这些依赖于Millisecs是Date类的最佳粒度,而我的解决方案则不是。我的解决方案也更短,更清晰。Java的新手使我真的很欣赏使用日期和时间时C ++多么干净,便捷和高效。
米奇

@Mitch根据我的经验,您正在学习的最新语言总是被认为是最复杂的。
ArtOfWarfare 2012年

仅供参考:这些血腥的,糟糕的旧日期时间类现在已被遗留,被现代的java.time类所取代。
罗勒·布尔克


0

JodaTime的最简单方法

DateMidnight date = DateMidnight.now();


2
否。Joda-Time中与“午夜”相关的类和方法已全部弃用。它们基于一个有缺陷的概念。一天的第一时刻并不总是如此00:00:00.000。而是使用新withTimeAtStartOfDay方法。更新到当前版本的Joda-Time 2.4,并阅读发行说明。例如:DateTime today = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).withTimeAtStartOfDay();
Basil Bourque

0

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

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

1
此代码仅适用于UTC,不适用于其他时区,并且会忽略诸如夏令时之类的异常。此外,自行拥有日期时间解决方案是不明智的,因为这是一个令人惊讶的棘手主题。请改用出色的java.time类。
罗勒·布尔克

0

与之前的答案大致相同,但没有人提到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);

1
麻烦的Calendar类早在几年前就被java.time类取代了ZonedDateTime
罗勒·布尔克

0
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现在使用。
罗勒·布尔克

0

使用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现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle 教程
罗勒·布尔克

@BasilBourque同意Java8。但是,如果OP使用Java 7,我想仍然可以。
joe4java

ThreeTen-Backport项目中,几乎所有的java.time功能都可用于Java 6和7,并且具有几乎相同的API。在ThreeTenABP项目中进一步适用于Android <26 。因此,无需再使用那些可怜的旧日期时间类。
罗勒·布尔克

-1

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

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

public static Date getCurrentDateWithMidnightTS(){

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

(1000*60 * 330)被减去,即实际上涉及到的时区,例如印度的时区,即加尔各答不同+5:从实际的30小时。因此,将其减去以转换为毫秒为单位。

因此,请根据您的需要更改最后减去的数字。我正在创建产品,即仅基于印度,因此仅使用了特定的时间戳。


1
您为什么建议这个而不是被接受的答案?滚动日期时间值通常会有风险。特别是,此代码永远被硬编码到单个时区的当前偏移量。没有考虑夏时制或其他异常。我只看到没有附加值的缺点。
罗勒·布尔克

@Basil当然我接受答案,是的,我在那儿提到了硬编码值。仅在您在特定时区工作时,这才有用。
拉杰夫2014年

-1
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转换为java.util.Date,反之亦然?


没有一个很好的答案。您正在将现代java.time的使用与它们取代的旧旧类混合在一起。特别是,您使用java.sql日期时间类,该类仅应用于与尚未更新驱动程序以直接处理java.time类的数据库交换数据,而永远不要用于业务逻辑。另一个问题是您忽略了时区这一关键问题。
罗勒·布尔克

@BasilBourque,感谢您的批评。使用一个未标记为不推荐使用的旧类来获取另一个旧类实例是非常安全的。时区问题更为重要。毕竟,我决定坚持riccardo的回答
Vadzim

-1

老式的方式..

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。因此,您将忽略期望/预期时区的问题。例如,印度加尔各答的新天比世界协调时间早得多,比魁北克蒙特利尔的世界协调中心晚几个小时。
罗勒·布尔克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.