如何在Java中获取没有时间的日期?

279

继续上面这个Stack Overflow问题 Java程序获取当前日期而不带时间戳:

获取一个不包含时间的Date对象,最高效的方法是什么?除了下面这两种方式,还有其他的方法吗?

// Method 1
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dateWithoutTime = sdf.parse(sdf.format(new Date()));

// Method 2
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
dateWithoutTime = cal.getTime();

更新:

  1. 我知道Joda-Time;我只是试图避免为如此简单(我想)的任务使用额外的库。但基于迄今为止的答案,Joda-Time似乎非常受欢迎,所以我可能会考虑它。

  2. 通过“高效”,我指的是要避免像方法1一样使用临时对象String创建,而方法2似乎是一个 hack 而不是一种解决方案。


1
你需要更高效的方法吗?例如,比method1提供的更高效的方法? - Johan Sjöberg
2
“efficient”是什么意思?日期基本上是一个类型为long的变量,你无法使用比这更少的内存。如果你的意思是“方便”,那么JODA时间库是最好的选择。 - millimoose
1
我喜欢第二种方法。在一个实用类中创建一个静态方法,然后直接使用它。我已经使用这种方法多年了。 - Wilson Freitas
1
对于你的“更新1”吹毛求疵:如果这是“如此简单的任务”,我想Sun不会使用如此可怕和低效的API,而你(以及许多其他人)根本不会问那个问题;-) - Flávio Etrusco
1
@RobertoLinares:为什么“不短”很重要呢?每一行都是高效的。我确信这比涉及格式化和解析的方法1 快得多 - ToolmakerSteve
显示剩余3条评论
23个回答

117

你一定要使用java.util.Date吗?我强烈建议你使用Joda Time或Java 8中的java.time包。特别是,虽然Date和Calendar总是表示特定的时间点,没有“只有日期”的概念,但是Joda Time确实有一个表示这一点的类型(LocalDate)。如果您能够使用代表您实际尝试执行的操作的类型,则代码将更加清晰。

使用Joda Time或java.time而不是内置的java.util类型有许多其他原因-它们通常具有更好的API。如果需要,您可以始终在自己代码的边界处将其转换为/从java.util.Date,例如用于数据库交互。


90
在企业应用程序中,我们并不总是有添加/使用其他库的选项。我感谢指向Joda Time的指针,但这并不是使用标准Java解决原始问题的答案。谢谢。我赞成Chathuranga的回答。 - noogrub
5
@noogrub: ChatGPT的回答没有回答原始问题,原问题是关于如何获取“日期”对象 - 那个答案 格式化 日期为字符串,这不是同一件事。基本上,询问一个“日期”在哪一天是没有意义的问题,除非提供更多信息:你要使用的时区和日历系统。 - Jon Skeet
9
在企业应用程序中,我们并不总是有添加/使用其他库的选项。你是否在暗示完全不能使用第三方库? - Brian Agnew
3
@BrianAgnew,noogrub的评论似乎与非技术限制有关,我认为。 - Jose_GD
3
从现在的角度来看:"Joda-Time是Java SE 8之前的事实标准日期和时间库。现在要求用户迁移至java.time(JSR-310)." - Drux
显示剩余6条评论

79

以下是我用来获取今天日期的代码,时间被设置为00:00:00

DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");

Date today = new Date();

Date todayWithZeroTime = formatter.parse(formatter.format(today));

30
这与原问题中已提出的“方法1”有何不同之处? - Chris
不指定时区(由JVM默认时区)会导致此问题发生,但未涉及到具体的时区。 - Darrell Teague

68
你可以使用Apache Commons库中的DateUtils.truncate
例子:
DateUtils.truncate(new Date(), java.util.Calendar.DAY_OF_MONTH)

6
如果你要添加一个新的库,就应该选择Joda而不是Apache Commons。 - Dibbeke
9
区别不仅在于你会添加一个库而不是另一个库。有时候,人们不希望仅仅为了截取日期中的时间部分这种琐碎的任务而学习一个全新的库。或者不想让代码混合使用 Date / Calendar 和 Joda dates,有时使用前者,有时使用后者。或者不能承担或证明将所有良好运行的基于 Date / Calendar 的代码替换为另一个库以执行如此琐碎的任务。虽然建议使用 Joda 肯定是好的,因为它显然优越并且只是正确的事情,但有时现实会打击人。 - SantiBailors

45

简而言之

除了这两种方式,还有其他方法吗?

是的,有:LocalDate.now

LocalDate.now( 
    ZoneId.of( "Pacific/Auckland" ) 
)

java.time

Java 8及更高版本内置了新的java.time package。请参阅Tutorial。大部分java.time功能已经移植到Java 6和7中,具体请查看ThreeTen-Backport,并在ThreeTenABP中进一步适配Android。
类似于Joda-Time,java.time提供了一个LocalDate类来表示仅有日期而没有时间和时区的值。
请注意,时区对于确定特定日期至关重要。例如,在巴黎午夜的钟声响起时,蒙特利尔仍然是“昨天”。
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) ) ;

默认情况下,java.time在生成日期或日期时间值的字符串表示时使用ISO 8601标准。(与Joda-Time的另一个相似之处。)因此,只需调用toString()即可生成类似2015-05-21的文本。
String output = today.toString() ; 

关于java.time

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

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

了解更多信息,请参阅Oracle教程。在Stack Overflow上搜索许多示例和解释。规范为JSR 310

如何获取java.time类?

ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是java.time可能未来加入的可靠试验场所。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter以及更多


@ToolmakerSteve 获取日期总是涉及到时区。但诀窍在于,如果您未指定时区,则JVM的当前默认时区会自动应用于确定日期。除此之外,JVM的当前默认时区可以在任何时刻更改!JVM中任何应用程序的任何线程中的任何代码都可以在运行时调用TimeZone.setDefault并立即影响在该JVM中运行的所有其他代码。故事的寓意:始终指定时区。 - Basil Bourque

25

最直接的方法:

long millisInDay = 60 * 60 * 24 * 1000;
long currentTime = new Date().getTime();
long dateOnly = (currentTime / millisInDay) * millisInDay;
Date clearDate = new Date(dateOnly);

6
对我来说,目前的时间是:"2011年2月19日凌晨1点(中欧时间)"。如果您想使用这个时间,请使用协调世界时(UTC)。 - Chris Lercher
5
第三行的 long dateOnly = (currentTime / millisInDay) * millisInDay;long dateOnly = currentTime;是否相同? - Chris
6
这不是由于整数计算引起的。更应该这样想:Math.floor(currentTime / millisInDay)* millisInDay。这样他就有效地将时间设置为 00:00:00。 - Blank
3
这个解决方案对于大多数应用程序来说可能是可以的,但请注意一天有60 * 60 * 24 * 1000毫秒的假设并不总是正确的。例如,在某些日子里会有闰秒。 - Pierre-Antoine
你认为这很简单明了吗? - Elemental
2
这可能是相对于其他1行和2行清晰简洁的方法而言最费解且不建议使用的一种方式。 - Darrell Teague

23

针对这些问题的标准答案是使用Joda Time。该API更好,如果你正在使用格式化程序和解析器,则可以避免SimpleDateFormat缺乏直观的线程安全性的问题。

使用Joda意味着你可以简单地执行以下操作:

LocalDate d = new LocalDate();

更新:使用Java 8,可以使用以下方法实现

LocalDate date = LocalDate.now();

Java 8中的新java.time包还提供了与Joda-Time类似的LocalDate类。 - Basil Bourque
1
理想情况下,你应该将一个 DateTimeZone 传递给那个 LocalDate 构造器。确定当前日期取决于时区,因为巴黎比蒙特利尔更早开始新的一天。如果省略时区,则会应用 JVM 的默认时区。通常最好指定时区而不是依赖于默认值。 - Basil Bourque

10
这是一个简单的做法:
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateOnly = new SimpleDateFormat("MM/dd/yyyy");
System.out.println(dateOnly.format(cal.getTime()));

4
这个答案未能回答问题。获取格式化字符串并不是目标。 - Basil Bourque

7
在标准Java运行时中,有关日期例程的讨论是没有意义的,因为它本质上是将其映射到特定的毫秒而不是日期。所述毫秒固有地附有一天中的时间,这使得它容易受到像夏令时和其他日历调整之类的时区问题的影响。有趣的例子,请参见Why is subtracting these two times (in 1927) giving a strange result? 如果您想使用日期而不是毫秒数进行操作,则需要使用其他工具。对于Java 8,提供了一组新的方法,可提供您需要的内容。对于Java 7及更早版本,请使用http://www.joda.org/joda-time/

3
注意:那些说“午夜日期”的方法在处理夏令时和多个时区方面并不好。 - Thorbjørn Ravn Andersen
1
@Jose_GD 这最多还是一个hack。你可能会发现https://www.youtube.com/watch?v=-5wpm-gesOY很有趣。 - Thorbjørn Ravn Andersen
@ Thorbjorn,你说得对,我只是想避免使用JodaTime,因为为了一个功能添加一个库听起来有些过度。我知道那个视频,确实很有趣。 - Jose_GD
我的第一条评论有勘误:我的应用程序增加了23个小时而不是24个小时。 - Jose_GD
1
@Jose_GD,重点是有一个实际的现实用例,在这种情况下,您的方法将被视为错误。如果您发现了一个,可能会有更多...我不知道Joda如何处理这个问题,但您可以看一下。另外请注意,Java 8带来了一个新的日期时间库(基于对Joda的经验),您可能想要研究一下。 - Thorbjørn Ravn Andersen
显示剩余3条评论

5
// 09/28/2015
System.out.println(new SimpleDateFormat("MM/dd/yyyy").format(Calendar.getInstance().getTime()));

// Mon Sep 28
System.out.println( new Date().toString().substring(0, 10) );

// 2015-09-28
System.out.println(new java.sql.Date(System.currentTimeMillis()));

// 2015-09-28
// java 8
System.out.println( LocalDate.now(ZoneId.of("Europe/Paris")) ); // rest zones id in ZoneId class

5

如果你只需要看到日期,如"YYYY-MM-DD",而不想要其它的杂乱信息例如"Thu May 21 12:08:18 EDT 2015",那么你可以使用java.sql.Date。以下是获取当前日期的示例:

new java.sql.Date(System.currentTimeMillis());

java.sql.Date也是java.util.Date的子类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接