如何在特定时区 ("Europe/Paris") 获取今天的开始日期和结束日期的 UTC Instant

How can I get UTC Instant of startDate and endDate of today in a specific time zone ("Europe/Paris")

我想从特定时区的 Java(也是 UTC)获取 UTC 即时(因为我的数据库存储在 UTC),这是我到目前为止尝试过的方法:

    public static Instant getStartOfTheDayDateTime(Instant instant, String zoneId) {

        ZonedDateTime zoned = instant.atZone(ZONE_ID_TO_ZONE_MAP.get(zoneId));
        ZoneId zone = ZoneId.of(zoneId);

        return zoned.toLocalDate().atStartOfDay(zone).toInstant();
//        ZonedDateTime startOfTheDay = zoned.withHour(0)
//                .withMinute(0)
//                .withSecond(0)
//                .withNano(0);
//
//        return startOfTheDay.toInstant();
    }

    public static Instant getEndOfTheDayDateTime(Instant instant, String zoneId) {
        ZonedDateTime zoned = instant.atZone(ZONE_ID_TO_ZONE_MAP.get(zoneId));
        ZonedDateTime endOfTheDay = zoned.withHour(0)
                .withMinute(0)
                .withSecond(0)
                .withNano(0)
                .plusDays(1);
        return endOfTheDay.toInstant();

    }

每次尝试显示:

2020-04-10 22:00:00.0(Timestamp), 2020-04-11 22:00:00.0(Timestamp)

现在,巴黎是夏令时:UTC+2。巴黎 'ahead' UTC 时差 2 小时。

所以 00:00:00 在巴黎当地时间是 22:00:00 UTC。

Is this the start/end of the day UTC time in Europe/Paris zone ?

是的。 Europe/Paris 现在是夏令时。巴黎的午夜发生在 22:00 UTC 时间。

I was expecting to have 2020-04-11 02:00:00.0(Timestamp), 2020-04-12 02:00:00.0(Timestamp)

不对,02:00 UTC 在巴黎时间应该是 04:00。

以编程方式询问时刻是否在夏令时

Is this the start/end of the day UTC time in Europe/Paris zone ?

获取一天的开始。

ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtStartOfDay = instant.atZone( z ).toLocalDate().atStartOfDay( z ) ; 

询问该时刻是否在该区域的夏令时。

ZoneRules rules = z.getRules();
boolean isDst = rules.isDaylightSavings( zdtStartOfDay.toInstant() );

传递日期时间对象而不仅仅是字符串

public static Instant getStartOfTheDayDateTime(Instant instant, String zoneId)

我建议您要求调用程序的程序员传递一个有效的 ZoneId 对象,而不仅仅是一个字符串。验证字符串输入不应该是此方法的工作。如果期望 Instant 是合理的,那么期望 ZoneId 也是合理的。

 public static Instant getStartOfTheDayDateTime(Instant instant, ZoneID zoneId )

半开

public static Instant getEndOfTheDayDateTime(Instant instant, String zoneId) {

由于最后一秒可以无限整除,因此无法确定当天的最后时刻。

这种定义时间跨度的方法也很笨拙。它使邻接多个跨度变得棘手。各种软件系统和协议在最后一个小数秒的分辨率上有所不同,使用毫秒、微秒、纳秒或其他分数。

日期时间处理的常见做法是使用半开方法跟踪时间跨度。在 Half-Open 中,开头是 inclusive,结尾是 exclusive

所以一整天从一天的第一刻开始,一直到但不包括 下一个 天的第一刻。

ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtStartOfDay = instant.atZone( z ).toLocalDate().atStartOfDay( z ) ; 
ZonedDateTime zdtStartOfNextDay = instant.atZone( z ).toLocalDate().plusDays( 1 ).atStartOfDay( z ) ;

您可能希望将该代码分成更多行,以便更容易 reading/debugging。

Instant instant = Instant.now() ;  // Or passed in. 
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
LocalDate ld = zdt.toLocalDate() ;
LocalDate ldNextDay = ld.plusDays( 1 ) ;
ZonedDateTime zdtStartOfNextDay = ldNextDay.atStartOfDay( z ) ;

看到这个 code run live at IdeOne.com。例如:

System.out.println( instant ) ;             // 2020-04-13T00:15:25.235341Z
System.out.println( zdt ) ;                 // 2020-04-13T02:15:25.235341+02:00[Europe/Paris]
System.out.println( ld ) ;                  // 2020-04-13
System.out.println( ldNextDay ) ;           // 2020-04-14
System.out.println( zdtStartOfNextDay ) ;   // 2020-04-14T00:00+02:00[Europe/Paris]

三十加 Interval

如果你经常用时间跨度做这种工作,那么我建议添加 ThreeTen-Extra library to your project. That library includes the Interval class 来跟踪时间跨度作为一对 Instant对象。

Interval interval = Interval.of( zdtStartOfDay.toInstant() , zdtStartOfNextDay.toInstant() ) ;

然后您可以使用几种方便的比较方法,例如 abutscontainsenclosesintersectionoverlapsunion

Timestamp

切勿使用 java.sql.Timestamp class。这个 class 是 Java 最早版本附带的可怕的日期时间 classes 的一部分。这些 classes 现在是遗留的,完全被现代 java.time classes 所取代,在 JSR 310 中定义并内置于 Java 8然后。

从 JDBC 4.2 开始,我们可以与数据库交换 java.time 对象。使用 getObjectsetObject 以及 updateObject

JDBC 规范奇怪地要求支持 OffsetDateTime 而不是更常用的 InstantZonedDateTime。您的特定驱动程序 可能 支持这些其他类型。如果没有,请转换。

从数据库中检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;

正在发送到数据库。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;