Java日期截止时间信息


120

我有一个包含日期和时间信息的Java Date对象。我想编写一种方法来截断时间信息,将小时-分钟-秒数截断,所以我只剩下日期了。

输入示例:

2008-01-01 13:15:00

预期产量:

2008-01-01 00:00:00

你有小费吗?我试图做这样的事情:

(timestamp / (24 * 60 * 60 * 1000)) * (24 * 60 * 60 * 1000)

但我遇到了时区问题。

Answers:


178

建议的方式做日期/时间操作是使用Calendar对象:

Calendar cal = Calendar.getInstance(); // locale-specific
cal.setTime(dateObject);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long time = cal.getTimeInMillis();

2
@cletus,是否可以从Date对象中删除时间。我想要像2008-01-01这样的纯日期没有时间部分。
2012年

2
我不会说java.util.Calendar是推荐的。Sun / Oracle用新的java.time软件包取代了Java 8中的该类。使用那个或乔达时间。
罗勒·布尔克2014年

3
不再建议使用日历。见stackoverflow.com/a/28868089/40064如果您使用的是Java 8
维姆Deblauwe

2
仅供参考,麻烦的旧日期时间类(例如java.util.Calendar现在已遗留)已由java.time类取代。请参见Oracle教程
罗勒·布尔克

147

您是否看过Apache Commons Lang 中的DateUtils截断方法?

Date truncatedDate = DateUtils.truncate(new Date(), Calendar.DATE);

将删除时间元素。


9
再次更新了链接(在Apache上发布!退出所有文档!)
Ogre Psalm33,2014年

6
截断操作执行时区的这种方法的缺点始终是本地PC时区。因此,如果您在UTC时区或本地时区以外的任何其他时区中使用日历和日期,则可能会截断日期。
2015年

正如@Cloud指出的那样,我在这里看不到Calendar实例(无法控制它)使我有点紧张:)
JaroslavZáruba17年

该方法出奇的长,并且至少有两个错误修正。
皮诺,

42

你看乔达了吗?这是一种处理日期和时间的容易,更直观的方法。例如,您可以在(例如)LocalDateTimeLocalDate对象之间轻松转换。

例如(以说明API)

LocalDate date = new LocalDateTime(milliseconds).toLocalDate()

另外,它解决了日期/时间格式化程序的一些线程安全问题,强烈建议在Java中处理任何日期/时间问题。


12
我相信是的。建议使用更好的库,而不要与损坏的java.util类作斗争(由问题证明)
Brian Agnew

10
当然,Joda是Java中用于日期,时间和日历处理的库。不推荐它是失职。
乔尔

1
线程安全性问题非常有趣,我发现这篇文章正在研究Oracle / Hibernate错误“ IllegalArgumentException,sun.util.calendar.ZoneInfo.getOffset(ZoneInfo),oracle.jdbc.driver.DateCommonBinder.zoneOffset(OraclePreparedStatement)”。这讨论了另一个Oracle问题,仅在负载很重的Forums.oracle.com/forums/thread.jspa?threadID=325576出现时才出现,即使在常规代码中创建具有无效毫秒数的对象也非常困难。对我而言,我认为我将使用Joda在我的话题中贬低,而不是让Oracle使用可能会怀疑的非Joda东西。
Mark Bennett

4
我希望我可以对投票赞成的所有人投反对票。这些为+1甚至都没有尝试回答问题的人中的谁?使用Joda的答案将非常受欢迎,但这不是...
Ryan

1
@Ryan这个答案不相关吗?该问题要求提供日期,但无日期。该答案提供了一个班级,其唯一目的是代表没有日期的日期。
罗勒·布尔克2014年

35

根据Java 8及更高版本中内置的java.time类进行快速更新。

LocalDateTime有一种truncatedTo方法可以有效解决您在此处所说的问题:

LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)

这将仅将当前时间表示为分钟:

2015-03-05T11:47

您可以使用小于DAYS 的ChronoUnit(或TemporalUnit)来执行截断(因为截断仅应用于LocalDateTime的时间部分,而不应用于日期部分)。


3
您最多只能在DAYS内使用ChronoUnits-更高的单位将引发异常。
2015年

26
Date date = new Date();
Calendar cal = Calendar.getInstance();
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);
date = cal.getTime();

22

使用Joda,您可以轻松获得预期的日期。

如评论者所述,从2.7版开始(可能是从2.2以前的某个版本开始的),toDateMidnight已被赞成或以适当的名称使用withTimeAtStartOfDay(),以方便使用。

DateTime.now().withTimeAtStartOfDay()

可能。

增加了更好的API的好处。

使用旧版本,您可以

new DateTime(new Date()).toDateMidnight().toDate()

6
现在不建议使用Joda-Time中与午夜相关的方法和类。开发人员建议不要使用它们。而是使用方法withTimeAtStartOfDay
罗勒·布尔克

感谢您指出。更新了答案以考虑这一点。
h7r 2015年

您还可以toLocalDate用来获取甚至没有时间成分的对象。
查尔斯·伍德

仅供参考,Joda-Time项目现在处于维护模式,团队建议迁移到java.time类。请参见Oracle教程
罗勒·布尔克


6

将Apache的DateUtils与truncate一起使用,如下所示:

DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DATE);

这将在本地TimeZone中截断,而不是在Date(getTime)表示的UTC中截断。
epox

6

对于时间戳:

timestamp -= timestamp % (24 * 60 * 60 * 1000)

3
此代码中有一个小错误-左括号未正确放置,必须为时间戳-=时间戳%(24 * 60 * 60 * 1000)。这将切断时间部分。而且我确定,这种方法比创建Caledar对象,使用其方法甚至使用任何其他第三方库都足够了
Stan

2
不,请注意,这不是正确的解决方案。这与将日期值四舍五入到下限是相同的-也就是说,对于3月29日的时间戳记,您可能会在3月28日得到(在您的区域设置中)。因此,基于日历的解决方案(Apache的DateUtils也使用日历)是唯一的方法
Drew

当时间由于夏令时而改变时,这也将不起作用。
亚历山德鲁·塞韦林

5

从java.util.Date JavaDocs:

Date类表示特定的时间瞬间,精度为毫秒

并从java.sql.Date JavaDocs中获取:

为了符合SQL DATE的定义,必须通过将与实例相关联的特定时区中的小时,分​​钟,秒和毫秒设置为零,来“标准化” java.sql.Date实例包装的毫秒值。 。

因此,最好的方法是在不需要时间的情况下使用java.sql.Date

java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());

输出为:

java.util.Date : Thu Apr 26 16:22:53 PST 2012
java.sql.Date  : 2012-04-26

4

tl; dr

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.toString()                                     // Generate a String in standard ISO 8601 format, wisely extended to append the name of the time zone in square brackets.

2008-01-01T00:00 + 01:00 [欧洲/巴黎]

要生成所需格式的字符串,请传递DateTimeFormatter

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.format(                                        // Generate a String representing the object’s value.
    DateTimeFormatter.ISO_LOCAL_DATE_TIME       // Built-in predefined formatter close to what you want. 
)
.replace( "T" , " " )                           // Replace the standard’s use of a 'T' in the middle with your desired SPACE character.

2008-01-01 00:00:00

细节

其他答案是正确的,但是使用java.time框架现在已过时的旧日期时间类。

java.time

java.time框架内置于Java 8及更高版本中。许多java.time功能都向后移植到Java 6和7(ThreeTen-Backport),并进一步适应了Android(ThreeTenABP)。

首先,更改输入字符串以符合ISO 8601格式的规范版本。缺省情况下,java.time类中使用标准ISO 8601格式来解析/生成表示日期时间值的字符串。我们需要将中间的SPACE替换为T

String input = "2008-01-01 13:15:00".replace( " " , "T" );  // → 2008-01-01T13:15:00

现在我们可以将其解析为LocalDateTime,其中“ Local”表示没有特定的位置。输入缺少任何UTC偏移量或时区信息。

LocalDateTime ldt = LocalDateTime.parse( input );

ldt.toString()…2008-01-01T13:15:00

如果您不在乎时间和时区,请转换为LocalDate

LocalDate ld = ldt.toLocalDate();

ld.toString()…2008-01-01

一天的第一时刻

相反,如果您希望将时间设置为一天的第一时刻,请使用一个ZonedDateTime类,然后转换为一个LocalDate对象以调用其atStartOfDay方法。请注意,00:00:00由于夏时制或其他异常情况,第一刻可能不是时间。

时区至关重要,因为在任何给定的时刻,日期在世界各地都会有所不同。例如,巴黎人午夜过后的片刻对巴黎人来说是新的一天,但对于加拿大人来说,在蒙特利尔仍然是“昨天”。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
LocalDate ldFromZdt = zdt.toLocalDate();
ZonedDateTime zdtStartOfDay = ldFromZdt.atStartOfDay( zoneId );

zdtStartOfDay.toString()…2008-01-01T00:00:00-05:00 [美国/蒙特利尔]

世界标准时间

要通过UTC时区的镜头查看该时刻,请提取一个Instant对象。无论是ZonedDateTimeInstant将代表时间轴上同一时刻,但显示为两个不同的挂钟时间

An Instant是java.time中的基本构建块类,根据定义始终是UTC。经常使用此类,因为通常应该在UTC中进行业务逻辑,数据存储和数据交换。

Instant instant = zdtStartOfDay.toInstant();

Instant.toString()…2008-01-01T05:00:00Z

我们看到的是凌晨5点,而不是午夜。在标准格式中,Z结尾处的缩写Zulu,表示“ UTC”。


关于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类?

  • Java SE 8 Java SE 9和更高版本
    • 内置的
    • 标准Java API的一部分,具有捆绑的实现。
    • Java 9添加了一些次要功能和修复。
  • Java SE 6 Java SE 7
    • java.time的许多功能在ThreeTen- Backport中都被反向移植到Java 6和7 。
  • 安卓
    • 更高版本的Android捆绑了java.time类的实现。
    • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。请参阅如何使用ThreeTenABP…

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



2

这个问题是矛盾的。它要求的日期不包含一天中的某个时间,但会显示一个时间为的示例00:00:00

乔达时代

UPDATE:乔达时间的项目现在处于维护模式,与团队的建议迁移java.time类。请参阅我的其他关于java.time的答案

相反,如果要将时间设置为一天中的第一时间,请使用DateTimeJoda-Time库上的对象并调用其withTimeAtStartOfDay方法。请注意,00:00:00由于夏时制或其他异常情况,第一刻可能不是时间。


这是一个明确的答案(假设一个人可以并且想使用Joda Time)
克林特·伊斯特伍德

2

只是clear()多余的字段。

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Date truncatedDate = calendar.getTime();

在Java 8中,更好的选择是使用truncatedTo方法LocalDateTime,例如:

LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)

这不起作用,因为calendar.clear(Calendar.HOUR_OF_DAY);无法清除时间。@cletus的解决方案工作正常。
Arcao 2013年

抱歉,您说得对,我已更正,您应该在几个小时内设置该值。通常,截断日期的方式几乎相同...
m190 2013年

1

真的让我感到恼火的是,新的“改进”日历构造函数没有像“糟糕的”旧Date Date这样的毫秒数来获取整数。然后我真的很交叉,并写了这个:

long stuffYou = startTime.getTimeInMillis() % 1000;
startTime.setTimeInMillis(startTime.getTimeInMillis() - stuffYou);

当时我没有使用“东西”这个词,但是后来我发现了其中的快乐之处:

startTime.set(Calendar.MILLISECOND, 0);

但是我仍然对此持怀疑态度。


1

我修复了这样的问题(在北京东部时间八区):

private Date getTruncatedDate(Date d) {
    if (d == null) {
        return null;
    }
    long h = 60 * 60 * 1000L;
    long dateStamp = d.getTime() - (d.getTime() + 8 * h) % (24 * h);
    return new Date(dateStamp);
}

首先,您应该清楚什么是时间戳。时间戳记是格林尼治标准时间1970年1月1日00:00:00(与UTC相同)或夏令时1970年1月1日08:00:00到现在的总毫秒数。

请记住:时间戳记与时区无关。

因此,在不同的时区使用以下语句可以获得相同的结果:

System.out.println(new Date().getTime());

System.out.println(new Date(0));

在不同的时区中打印不同的时间信息:如果将您的pc时区设置为UTC,则会得到

Thu Jan 01 00:00:00 UTC 1970

但是,如果您将时区设置为(UTC +8:00)北京,重庆,香港,乌鲁木齐,则会得到:

Thu Jan 01 08:00:00 CST 1970

Java获取时间戳,然后根据时区显示日期和时间信息。

为了介绍Java如何在不同时区显示日期和时间信息,如何转换时间信息很容易。您应该获取时间戳记,并在切断时间信息时将时区考虑在内。然后,您可以使用剪切的时间戳创建一个新的Date对象(我们可以将其称为date stamp),java在显示日期信息时将补偿时区。

与东部八区(北京时间)一样,北京时间比格林尼治标准时间早8小时,因此在进行模运算时应减去8小时。也就是说,您应该首先获取GMT时间,然后Java将根据您的PC的时区设置增加8个小时的显示时间。


时区问题晦涩难懂,也困扰了我很长时间。现在我说清楚了。希望会有所帮助。


2018-01-04以下方法也适用。

private Date getTruncatedDate2(Date d) {
    Calendar cal = Calendar.getInstance(); // locale-specific
    cal.setTime(d);
    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();

}


仅供参考,此答案使用java.time类取代了麻烦的旧日期时间类。
罗勒·布尔克

1

您可以这样做以避免时区问题:

public static Date truncDate(Date date) {
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    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();
}

尽管Java Date对象是时间戳值,但是在截断过程中,它将被转换为本地时区,因此,如果您期望UTC时区的值,那么您将获得令人惊讶的值。


麻烦CalendarDate类成为遗留年前的java.time在Java中8和更高版本实现JSR 310班。建议在2018年使用它们是不好的建议。
罗勒·布尔克

0

可能是一个较晚的响应,但是这是一种无需使用任何库即可在一行中完成此操作的方法:

new SimpleDateFormat("yyyy-MM-dd").parse(new SimpleDateFormat("yyyy-MM-dd").format(YOUR_TIMESTAMP))

0

随着乔达时间从2.0版本开始,你可以使用LocalDate.toDate()

只是

// someDatetime is whatever java.util.Date you have.
Date someDay = new LocalDate(someDatetime).toDate();

[A]您的答案是多余的。已经由Brian Agnew发布。[B]您的代码不正确,因为它忽略了时区,即问题中提出的问题。
罗勒·布尔克

可能是多余的,但我在这里仅举了一个简单的示例,因为我认识到答案不应该包含在注释中。是的,java.util.Date忽略了TimeZone,但是最初的问题是a Java Date object我使用的java.util.Date。而且,尽管他不能帮助使用以外的其他语言,但这并不能说明他的时区如何与java.util.Date产生问题(何时?在哪里?)Date
lamusique

0

对于使用日历的所有答案,您应该这样使用它

public static Date truncateDate(Date date) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
    return c.getTime();
}

但是我更喜欢这样:

public static Date truncateDate(Date date) {
    return new java.sql.Date(date.getTime());
}

java.sql.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.