截止日期时间信息

我有一个包含日期和时间信息的 JavaDate 对象。我想写一个方法来截断时间信息,截断小时-分钟-秒,这样我就只剩下日期了。

示例输入:

2008-01-01 13:15:00

预期产出:

2008-01-01 00:00:00

你有什么建议吗? 我试过这样做:

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

但我遇到了时区问题。

241985 次浏览

建议进行日期/时间操作的方法是使用 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();

使用 日历类的 set()方法将 HOUR_OF_DAY, MINUTE, SECONDMILLISECOND字段设置为零。

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

你看过 Joda吗?这是一个 很多更容易和更直观的方式与日期和时间的工作。例如,您可以在(比如说) 本地日期时间LocalDate对象之间进行简单的转换。

(以说明空气污染指数)

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

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

您看过 Apache Commons Lang 中的 日期工具截断方法吗?

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

将删除时间元素。

来自 java.util 日期 JavaDocs:

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

以及 java.sql。日期 JavaDocs:

为了符合 SQL DATE 的定义,使用 java.SQL 包装的毫秒值。日期实例必须通过在与实例关联的特定时区中将小时、分钟、秒和毫秒设置为零来“规范化”。

因此,如果不需要 time 部分,最好的方法是使用 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

使用来自 Apache 的 DateUtils,像下面这样截断:

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

时间戳:

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

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

在 Java8中,一个更好的选择是使用 LocalDateTimetruncatedTo方法,例如:

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

有了 Joda,你可以很容易地得到预期的日期。

至于版本2.7(可能是因为以前的一些版本大于2.2) ,正如一位评论者指出的那样,toDateMidnight已经不被支持,或者适当地命名为 withTimeAtStartOfDay(),这使得方便

DateTime.now().withTimeAtStartOfDay()

有可能。

增加了一种更好的 API 的好处。

使用旧版本,你可以做到

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

新的“改进的”日历构造函数没有像“糟糕的”旧的 Date 函数那样在毫秒内使用 int,这真的让我很恼火。 然后我很生气,写道:

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

我当时没有用“东西”这个词,但是 然后我发现了这样的幸福:

startTime.set(Calendar.MILLISECOND, 0);

但我还是很生气。

可能有点晚了,但这里有一种方法可以在不使用任何库的情况下一行完成:

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

这个问题自相矛盾。它要求一个没有时间的日期,但显示一个时间为 00:00:00的示例。

Joda 时间

更新: Joda 时间项目现在在 维修模式,团队建议迁移到 爪哇时间类。有关 java.time 解决方案,请参见 我的另一个答案

如果你想把一天中的时间设置为一天中的第一个时刻,那么在 Joda-Time 库中使用一个 DateTime对象并调用它的 withTimeAtStartOfDay方法。请注意,由于夏时制或其他异常情况,第一个时刻可能不是 ABc2时刻。

在2.0版以后的 Joda 时间中,您可以使用 LocalDate.toDate()

很简单

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

我用新的 java8 API 做了截断。

Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.DAYS);
date = Date.from(instant);

只是根据现在内置到 Java8及更高版本中的 爪哇时间类进行了一个快速更新。

LocalDateTime 有一个 truncatedTo方法,可以有效地解决您在这里谈论的问题:

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

这将表示当前的时间只有几分钟:

2015-03-05T11:47

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

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

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

博士

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-01 T00:00 + 01:00[欧洲/巴黎]

若要生成所需格式的 String,请传递一个 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-0100:00:00

细节

其他的解答是正确的,但是使用旧的日期时间类已经被 java.time 框架过时了。

爪哇时间

Time 框架内置于 Java8及更高版本中。许多 Java.time 功能被移植回 Java6 & 7(310-后端口) ,并进一步适应 Android (三个十分)。

首先修改输入字符串以符合 ISO8601格式的规范版本。默认情况下,java.time 类使用标准的 ISO 8601格式来解析/生成表示日期时间值的字符串。我们需要用 T代替中间的那个空格。

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

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

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方法。请注意,由于夏时制或其他异常情况,第一个时刻可能不是 ABc3时刻。

时区是至关重要的,因为在任何给定的时刻,日期在世界各地的地区不同。例如,对于巴黎人来说,午夜过后几分钟是新的一天,但对于加拿大人来说,在蒙雷阿勒仍然是“昨天”。

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

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

协调世界时

要通过 协调世界时时区的透镜看到那个时刻,提取一个 Instant物体。无论是 ZonedDateTimeInstant将代表相同的时刻在时间线上,但出现作为两个不同的 挂钟时间

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

Instant instant = zdtStartOfDay.toInstant();

ToString () ... 2008-01-01 T05:00:00Z

我们看到的是凌晨5点,而不是午夜的钟声。在标准格式中,末端的 ZZulu的缩写,意思是“ UTC”。


关于 爪哇时间

< em > java.time 框架内置于 Java8及更高版本中。这些类取代了令人讨厌的旧的 遗产日期时间类,如 java.util.DateCalendarSimpleDateFormat

现在在 维修模式中的 尤达时间项目建议迁移到 爪哇时间类。

要了解更多,请参阅 < em > Oracle 教程 。并搜索堆栈溢出许多例子和解释。规范是 JSR 310

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

从哪里获得 java.time 类?

  • Java SE 8 < strong > JavaSE9 和更高版本
    • 内置的。
    • 带有捆绑实现的标准 JavaAPI 的一部分。
    • Java9添加了一些次要的特性和修复。
  • Java SE 6 和 < a href = “ https://en.wikipedia.org/wiki/Java _ version _ history # Java _ SE _ 7”rel = “ noReferrer”> < strong > Java SE 7
  • 仿生人
    • Java.time 类的 Android 捆绑包实现的后续版本。
    • 对于早期的 Android (< 26) ,项目适用于 310-后端口(上面提到过)。

< strong > Three Ten-Ultra 项目使用其他类扩展 java.time。这个项目是将来可能添加 java.time 的试验场。您可以在这里找到一些有用的类,比如 IntervalYearWeekYearQuarter更多

我是这样解决问题的(在东八区(北京时间)) :

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

在不同的时区打印不同的时间信息: 如果将个人电脑时区设置为 UTC,则会得到

Thu Jan 01 00:00:00 UTC 1970

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

Thu Jan 01 08:00:00 CST 1970

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

对于介绍 Java 如何在不同的时区显示日期和时间信息,如何翻译时间信息是很容易的。你应该得到的时间戳,并考虑到时区时,切断了时间信息。然后您可以创建一个新的日期对象与削减时间戳(我们可以称之为日期戳) ,Java 将补偿时区时显示日期信息。

在东八区(北京时间) ,北京时间比格林尼治时间早8小时,所以你应该减去更多的8小时,当你做模除。 也就是说,你应该得到的 GMT 时间第一,然后 Java 将增加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();

}

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

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

虽然 JavaDate对象是时间戳值,但是在截断期间,它将被转换为本地时区,所以如果您期望从 UTC 时区获得值,那么您将获得令人惊讶的值。