【问题标题】:How can I get UTC Instant of startDate and endDate of today in a specific time zone ("Europe/Paris")如何在特定时区(“欧洲/巴黎”)中获取今天的 startDate 和 endDate 的 UTC 瞬间
【发布时间】:2023-03-27 01:34:02
【问题描述】:

我想从特定时区的 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 时间的开始/结束时间吗?
  • 我期待有2020-04-11 02:00:00.0(Timestamp), 2020-04-12 02:00:00.0(Timestamp)

【问题讨论】:

  • 我在你的代码中没有看到任何老式的Timestamp,所以一定有一些东西你没有向我们展示?但是,是的,鉴于时间戳以 UTC 打印,它们表示巴黎一天的开始和结束。
  • 这能回答你的问题吗? Java Date Time conversion to given timezone
  • 将一天中的小时设置为 0 对于获取一天的开始并不总是正确的。在某些情况下,夏令时 (DST) 在某个时区的午夜开始,因此一天的第一刻是 01:00。您使用atStartOfDay() 是正确且防弹的方式。

标签: java spring date datetime localization


【解决方案1】:

现在,巴黎正处于夏令时:UTC+2。巴黎比世界标准时间“领先”2 小时。

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

【讨论】:

    【解决方案2】:

    这是欧洲/巴黎区一天的开始/结束时间 UTC 时间吗?

    是的。欧洲/巴黎处于夏令时。巴黎的午夜发生在 UTC 时间 22:00。

    我期待有 2020-04-11 02:00:00.0(Timestamp), 2020-04-12 02:00:00.0(Timestamp)

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

    【讨论】:

      【解决方案3】:

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

      这是欧洲/巴黎区一天的开始/结束时间 UTC 时间吗?

      开始一天。

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

      您可能希望将该代码分成更多行,以便于阅读/调试。

      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]
      

      ThreeTen-Extra Interval

      如果您经常在一段时间内进行此类工作,那么我建议将ThreeTen-Extra 库添加到您的项目中。该库包含 Interval 类,用于将时间跨度跟踪为一对 Instant 对象。

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

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

      Timestamp

      永远不要使用java.sql.Timestamp 类。这个类是 Java 最早版本附带的糟糕的日期时间类的一部分。这些类现在是遗留的,完全被 JSR 310 中定义的现代 java.time 类所取代,并内置于 Java 8 及更高版本中。

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

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

      从数据库中检索。

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

      发送到数据库。

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

      【讨论】:

      • ZonedDateTime zdtStartOfDay = instant.atZone( z ).toLocalDate().atStartOfDay( z ) ; 不是返回 ZonedDateTime,而是返回 LocaleDateTime,这会导致线路错误,您对此有何看法?
      • @DimitriKopriwa 见LocalDate.atStartOfDay Javadoc。如果你传递一个ZoneId,你会得到一个ZonedDateTime 对象。如果你pass no arguments,你会得到一个LocalDateTime(它没有时区或偏移的概念,不能代表一个时刻,不是时间轴上的一个点)。
      • 我确实复制了你的片段,它已经传递了一个 zoneId,我在我当前的函数中拥有它,并且它的 toLocalDate 不会返回 ZonedDateTime。它返回一个 LocalDate。
      • @DimitriKopriwa 自己看看,code running live at IdeOne.com。调用LocalDate::atStartOfDay( ZoneId ) 返回ZonedDateTime。运行这个:Instant.now().atZone( ZoneId.of( "Europe/Paris" ) ).toLocalDate().atStartOfDay( ZoneId.of( "Europe/Paris" ) ).getClass().getCanonicalName() ➡ java.time.ZonedDateTime
      • @DimitriKopriwa 我在答案中添加了另一个代码 sn-p,将长行分成多行。这样类类型就更明显了。
      猜你喜欢
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-03
      • 1970-01-01
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多