LocalDateTime去除毫秒数

98

我正在开发一个Java应用程序,其中使用的是Java 8。

我已经集成了多个数据库Oracle、Mysql、Postgres,并在数据库中存储了创建日期。

数据库中的日期格式为- 2015-07-29 16:23:28.143

我从数据库中获取这个日期并将其设置为Localdatetime对象。

myObj.setCreated(rs.getTimestamp("created").toLocalDateTime());

所以问题在于我不想在响应中显示/发送毫秒。我想要像2015-07-29 16:23:28这样显示/发送日期。

我尝试了格式化程序,但失败了,因为它会给出一个字符串,而我不想将LocalDateTime对象更改为字符串,因为这将导致所有Java应用程序的重大更改。所以希望找到解决方案。

有人知道任何解决方案吗?


1
不对,这些数据库中的日期时间数据类型并不是以这种格式存储的。数据库中的日期时间值没有固定的格式。你从日期时间值生成的字符串表示具有一种格式。 - Basil Bourque
顺便说一下... (a) 不要使用 java.sql.Timestamp。避免使用那些严重有缺陷的旧日期时间类,它们早已被现代的 java.time 类(JSR 310中定义)所取代。(b) 对于跟踪"创建"时刻,请使用与SQL标准类型 TIMESTAMP WITH TIME ZONE 类似的数据类型列(而不是 WITHOUT)。在JDBC 4.2中,它映射到Java类 java.time.OffsetDateTime。如果使用 LocalDateTime,您将丢失宝贵的信息:与UTC的偏移量。例如:OffsetDateTime odt = myResultSet.getObject("created", OffsetDateTime.class).truncatedTo(ChronoUnit.SECONDS); - Basil Bourque
5个回答

212

如果你尝试这个方法,它仍然会给出毫秒数,但是如果你将其打印/放入字符串中,它就不会显示毫秒数。 - Nitin Murlidhar Gaikwad
2
这个答案对我来说非常有效,当我在测试中创建了一个 LocalDateTime 并将其用于与数据库中的值进行比较时,数据库会将毫秒四舍五入到最近的秒。 - Tim Schimandle
我猜测 OP 不喜欢这个答案,因为你没有明确地写出他们想要的代码行,它应该是这样的: myObj.setCreated(rs.getTimestamp("created").toLocalDateTime().truncatedTo(ChronoUnit.SECONDS)); - T Sloane
4
也适用于“即时”(Instant)的作品。 - Matt Klein
6
我比起被选的答案更喜欢这个。意图更加清晰。 - GabrielBB
显示剩余2条评论

116

只需将它们设置为0

myObj.setCreated(rs.getTimestamp("created").toLocalDateTime().withNano(0));

示例/证明:

import java.time.LocalDateTime;

public class DateTimeSample {

  public static void main(String[] args) {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt);
    System.out.println(ldt.withNano(0));
  }
}

输出:

2015-07-30T16:29:11.684
2015-07-30T16:29:11

作者注:尽管这是被接受的,但我认为Peter Lawrey的答案更可取,因为它使意图更加清晰。


这个解决方案很好,因为它提供了没有毫秒的日期。 - Nitin Murlidhar Gaikwad
2
这里有一个小提示。确保你不需要秒数。如果你在LocalDateTime 2015-07-30T16:29:00.684中有00秒,并且你使用了cutWithNano(0)或者truncatedTo(ChronoUnit.SECONDS),那么你将得到的是2015-07-30T16:29。有时这并不是你期望的结果。 - Mike Menko
有效观点。不过,那只是 LocalDateTime.toString 的问题。LocalDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) 会打印出秒数。 - Marvin

7

为了帮助那些使用STRING方法的人更好地理解,需要在Marvin的答案下提供代码和日志作为评论,

!!!我也掉进了同样的陷阱中!!!

问题

withNano(0)truncatedTo(ChronoUnit.SECONDS) 会在秒钟为:00(时钟上,秒针直立在12点)时同时移除秒钟。

进一步扩展Marvin的示例输出。

2015-07-30T16:29:11.684
2015-07-30T16:29:11
2015-07-30T16:31 // for actual time 2015-07-30T16:31:00.684
2015-07-30T16:31 // for actual time 2015-07-30T16:31:00.888

可能导致错误的上述行为:

当您消除纳秒时,如果秒数显示为 :00,它们将被跳过不打印。

解决方案:

public class WithNanoTest {
    public static void main(String[] args) throws InterruptedException {
        while (true) {
            Thread.sleep(500);

            DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
            System.out.println("truncate :::: " + LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS).format(dtf));
            System.out.println("withNano :::: " + LocalDateTime.now().withNano(0).format(dtf));
        }
    }
}

日志截图

使用DateFormatter的日志

使用DateFormatter的日志

没有使用DateFormatter的日志(仅使用truncatewithNano(0))

注意这里缺少了秒数!

没有使用DateFormatter的日志


1
好的,问题是“我不想将LocalDateTime对象更改为字符串,因为这将导致所有Java应用程序发生重大更改。” 你给我们一个字符串。 - Ole V.V.
@OleV.V. 这更像是一个警告,我在这里反映一下。最近在生产环境中遇到了这个问题。所以认为对于那些使用STRING的人来说是值得注意的 :) - Aditya Rewari
@OleV.V. 这种秒数截断的行为是不被期望的! - Aditya Rewari
@OleV.V. 感谢您的编辑 :)。现在可以吸引更精准的受众了! - Aditya Rewari
FWIW:截断正是相应的“toString”方法的Javadoc所解释的。这是预期的行为。如果您想要特定的格式,请使用格式化程序。 - Marvin

0

这实际上是我们最近遇到的一种有线错误。因此,为了转换目的,我个人更喜欢设置格式,然后使用 DateTimeFormatter 进行相应转换。

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
myObject.setEventStartTimestamp(dateTimeFormatter.format(eventStartTime));

如果您不需要毫秒,可以从模式中去掉它。这将提供您所寻找的确切格式。此外,如果您想设置本地时间,可以选择您的时区,例如ZoneOffset.ESTZoneOffset.MTZoneOffset.PST


ZoneOffset.EST??你提到的这个和其他的在文档中都不存在。这是有充分理由的,因为它们都不表示明确定义的偏移量。 - Ole V.V.
错误在于你的设计:在你的对象内部,你不应该将时间保存为字符串,而是应该使用日期时间对象,例如 InstantLocalDateTime 可能不是最佳选择,因为它不能表示一个时间点)。 - Ole V.V.
它在import java.time.ZoneOffset下面。我认为你是在寻找java.time.ZoneOffset - Ananda G
是的,那就是我查看的地方。那我应该去哪里查找呢?(你是不是说应该使用 java.time.ZoneOffset 而不是 java.util.TimeZone?)无论如何,你提供的偏移量都是无意义的。 - Ole V.V.

0
我发现最简单的方法是:
setDateOfreport(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")));

这将输出一个字符串,所以你只需要将整个字符串解析为一个LocalDate对象即可。
setDateOfreport(
        LocalDateTime.parse(
                LocalDateTime.now()
                        .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))));

这样就解决了所有与截断相关的问题。我没有设置时区,因为我不需要。

1
对我来说,与截断相比,这似乎是一个不必要的绕道(而且需要更多的按键和CPU周期,尽管这些都无关紧要)。此外,你确实需要一个时区。now()方法在不同的时区会产生不同的结果,所以使用哪个时区是很重要的。 - Ole V.V.

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