如何将java.sql.时间戳四舍五入到最近的一天?[已关闭]

xcitsw88  于 2023-03-06  发布在  Java
关注(0)|答案(2)|浏览(123)

4天前关闭。
此帖子已于3天前编辑并提交审核,未能重新打开帖子:
原始关闭原因未解决
Improve this question
我正在尝试将java.sql.timestamp四舍五入到最近的一天。所有的时间戳都是UTC时间。
在这个位置https://stackoverflow.com/a/60100403/9745488,我看到了一种方法来达到小时,但我似乎不能弄清楚。
1800000似乎是30分钟的毫秒,所以对我来说,12小时将是43200000ms。60000 * 60似乎是1小时的毫秒。我试着做(60000 * 60 * 24),但结果就是不正确。如何进行舍入?

public class SqlTimestamp {
 
 public static void main(String...strings) {
  Timestamp[] timestamps = {
    new Timestamp(Timestamp.valueOf("2023-02-19 00:00:00.000000000").getTime()),
    new Timestamp(Timestamp.valueOf("2023-02-25 23:59:59.999999999").getTime()),
    new Timestamp(Timestamp.valueOf("2023-02-25 12:00:00.000000000").getTime()),
    new Timestamp(Timestamp.valueOf("2023-02-25 12:29:00.000000000").getTime()),
    new Timestamp(Timestamp.valueOf("2023-02-25 12:30:00.000000000").getTime()),
    new Timestamp(Timestamp.valueOf("2023-02-25 12:29:59.999999999").getTime()),
  };
  
  for(int i = 0; i < timestamps.length; i++) {
   System.out.println("Original timestamp value=" + timestamps[i].toString());
   System.out.println("round to nearest hour=" +   Timestamp.from(Instant.ofEpochMilli(((timestamps[i].getTime()+1800000)/(60000*60))*(60000*60)))   );
   
   System.out.println("round to nearest day=" +   Timestamp.from(Instant.ofEpochMilli((((timestamps[i].getTime()+43200000)/(60000*60*24))*(60000*60*24))   )  )    );
  }
6ojccjat

6ojccjat1#

核心问题是:java.sql.Timestamp对象表示不带时区信息的瞬间。
这意味着,你想要的是完全不可能的-你的timestamp对象只是跟踪一个时间点,在某个时间点,在一个地区是2022年1月5日,在另一个地区是1月6日。“某个时间点”不能翻译成日期,除非把它本地化到某个时区。
如果它是java.sql.Timestamp,那么有两件事似乎是显而易见的:

  • 你从数据库里找到的。
  • 很可能该数据库表示一些日期/时间计算,其中“四舍五入到最近的一天甚至有意义”(不带时区的时间戳当然不会!!),而您使用.getTimestamp()来获得这个值,这是 * 不正确的 * -这是一个常见的错误,因为类的整个java.util.Date层次结构都是不正确的,而且名称也很糟糕(例如,Date表示时间中的某个时刻,而根本不是日期。只需阅读例如.getYear()上的弃用通知即可获得一些证明)。

这意味着您真正需要做的是正确地从数据库中读取数据。getDategetTimestamp都是您永远不应该使用的过时方法-因为它们返回的类型是损坏的。正确的调用类似于.getObject(idx, LocalDate.class).getObject(idx, ZonedDateTime.class)。JDBC规范(v5)保证这将工作,但这里最好的事情是检查您的数据库类型实际是什么,阅读它们实际表示的内容,然后从java.time包中选择正确的类型,然后试着确认它是否工作。
如果由于某种原因,您被遗留代码卡住了,那么要知道政治时区的变化将会发生,并且会破坏一些东西(比如最新的JDK在时区文件中去掉了1970年代以前的支持,因为tzdata项目决定这么做,或者国家只是简单地切换时区。。所有欧洲大陆都将很快这样做,所以这不是一些'是的,是的,永远不会发生'的假设!)-准备好奇怪的错误,突然你的'最近的日期'四舍五入开始四舍五入上午11:20,当你期望它四舍五入下来,诸如此类的事情。
尽管如此,最好还是尽可能写得干净一些,所以,把过时的瞬时值转换成未过时的数据类型来表示瞬时值,然后从那里开始转换:

Instant i = timestamp.toInstant(); // move away from obsolete types
ZonedDateTime zdt = i.atZone(ZoneId.of("Europe/Amsterdam");

// you'll need to know what the timezone is that is represented
// with the timestamp, as the timestamp type doesn't know.
// That, or, do as I said and avoid java.sql.Timestamp in the first place.

LocalDateTime ldt = zdt.toLocalDateTime();

// now, what does 'round' mean? Days do not neccessarily have 24 hours.
// They can have 23, 25, or in rare cases, even weirder values.
// Perhaps it is more sensible to say, 12 noon and up rounds up,
// instead of going for e.g. 11:30 or 12:30 on daylight savings switch days.

LocalDate answer = ldt.toLocalDate();
if (ldt.getHour() > 11) answer = answer.plusDays(1);
return answer;

如果你真的想要'nearest',尽管这有点奇怪,但没有现成的方法可以做到这一点。在ZonedDateTime范围内,截断到最近的一天,加上一天,取两者的平均值,然后检查是否已经过了中点,以确定是否需要加上一天。

o8x7eapl

o8x7eapl2#

我想这可能是你四舍五入到最近的一天所追求的东西:

import java.sql.Timestamp;
import java.time.*;
import java.time.temporal.*;

public class SqlTimestamp {

    public static void main(String... strings) {
        Timestamp[] timestamps = {
                Timestamp.valueOf("2023-02-19 00:00:00.000000000"),
                Timestamp.valueOf("2023-02-25 23:59:59.999999999"),
                Timestamp.valueOf("2023-02-25 11:00:00.000000000"),
                Timestamp.valueOf("2023-02-25 12:29:00.000000000"),
                Timestamp.valueOf("2023-02-25 12:30:00.000000000"),
                Timestamp.valueOf("2023-02-25 12:29:59.999999999")
        };

        for (int i = 0; i < timestamps.length; i++) {
            Instant inst = Instant.ofEpochMilli(timestamps[i].getTime());
            Instant startDay = inst.truncatedTo(ChronoUnit.DAYS);
            Instant nextDay = startDay.plus(1, ChronoUnit.DAYS);
            Instant roundValue = nextDay;
            if (inst.toEpochMilli() - startDay.toEpochMilli() < nextDay.toEpochMilli() - inst.toEpochMilli()) {
                roundValue = startDay;
            }
            System.out.printf("Start: %s, next: %s. Instant %s rounds to %s%n", startDay, nextDay, inst, roundValue);
        }
    }
}

相关问题