【问题标题】:Java Time Zones [closed]Java 时区 [关闭]
【发布时间】:2022-01-06 20:57:05
【问题描述】:

我正在使用 Java FX 表单和控制器。它也连接到 MySQL 数据库。我在屏幕上输入数据,其中一个字段是指定日期的“开始”和“结束”时间。在数据库中,有一个开始时间戳,一个结束时间戳。

问题是,此应用程序设置为在多个时区运行。它需要验证。所以,我在想 - 也许我可以根据最终用户的时区以 1 小时的增量填充组合框的开始和结束时间。

营业时间为 8a-5p EST

所以我的想法是我可以获取时区以找出最终用户时区中的 8a 是什么,并找出最终用户时区中的 5p 是什么,然后编写代码来填充组合带有此时间信息的框,以一小时为增量。因此它会绕过验证。

如果我不这样做,我必须填充具有非常宽的时间范围的组合框,并验证时间在 8a-5p EST 范围内。在理解 ZonedDateTime、LocalDateTime 以及如何传递这些信息时,我也是完全愚蠢的。非常感谢一个简短的解释或我如何在不失去理智的情况下做到这一点的例子。当我从数据库中推送或拉取数据时,这会即时发生,但在程序内部并没有那么多。谢谢!

【问题讨论】:

  • 指定您的特定数据库和 JDBC 驱动程序、它们的版本以及数据库列的确切数据类型。

标签: java timezone


【解决方案1】:

在了解 ZonedDateTime、LocalDateTime 以及如何传递此信息时。

日期时间处理非常棘手。我们对日期时间的日常直观理解我们是程序员。

在 Stack Overflow 上已经处理了很多次预约,所以我会尽量简短。搜索以了解更多信息,并查看更多示例。

InstantOffsetDateTimeZonedDateTime 类各自代表一个时刻,即时间线上的一个特定点。例如,记录时,使用Instant

LocalDateTime 类确实代表片刻。仅包含日期和时间,此类缺少时区或偏移量的上下文。例如,“今年 1 月 23 日中午”并不能告诉我们您是指日本东京的中午还是美国俄亥俄州托莱多的中午,两个截然不同的时刻相隔几个小时。

预订约会通常侧重于一天中的特定时间,而不是某个时刻。世界各地的政客都表现出改变其管辖范围内时区规则的嗜好。他们这样做几乎没有或没有预先警告。所以我们必须在数据库中将约会记录为两列,约会开始的日期和时间在TIMESTAMP WITHOUT TIME ZONE 类型的列中,文本类型的列保留预期时区的名称,第三列跟踪任命期限。如果数据库没有时间跨度类型,则使用文本类型存储ISO 8601 format for durations

您似乎还想提供一个用户界面,该界面按用户的时区而不是供应商的时区显示可能的约会范围。

因此,您需要使用供应商区域在这两列中记录约会,但将显示调整为用户区域。使用的时区将仅用于显示,不用于存储。

你说:

营业时间为 8a-5p EST

LocalTime open = LocalTime.of( 8 , 0 ) ;
LocalTime close = LocalTime.of( 17 , 0 ) ;

没有EST 这样的时区。这些 2-4 个字母的缩写是预期时区的非标准模糊指标,以及夏令时 (DST) 是否有效。切勿在工作中使用这些伪区域。用户实时时区名称,格式为Continent/Region

ZoneId zoneOfBusiness = ZoneId.of( "America/New_York" ) ;

你说:

开始和结束时间以 1 小时为增量

List< LocalTime > hoursOfBusiness = new ArrayList<>() ;
LocalTime lt = open ; 
while( lt.isBefore( close ) ) 
{
    hoursOfBusiness.add( lt ) ;
    // Set up next loop.
    lt = lt.plusHours( 1 ) ;
}

[08:00, 09:00, 10:00, 11:00, 12:00, 13:00, 14:00, 15:00, 16:00]

要获取时刻,请指定日期。请记住,日期不明确。对于任何给定的时刻,日期在全球范围内都会因时区而异。

LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;

确定每个小时的时刻。

LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;
ArrayList< ZonedDateTime > hoursOfBusinessOnDate = new ArrayList<>() ;
for( LocalTime localTime : hoursOfBusiness )
{
    ZonedDateTime zdt = ZonedDateTime.of( ld , localTime , zoneOfBusiness ) ;
    hoursOfBusinessOnDate.add( zdt ) ;
}

[2022-01-23T08:00-05:00[America/New_York], 2022-01-23T09:00-05:00[America/New_York], 2022-01-23T10:00-05:00[ America/New_York], 2022-01-23T11:00-05:00[America/New_York], 2022-01-23T12:00-05:00[America/New_York], 2022-01-23T13:00-05:00 [美国/纽约], 2022-01-23T14:00-05:00[美国/纽约], 2022-01-23T15:00-05:00[美国/纽约], 2022-01-23T16:00-05: 00[美国/纽约]]

ZoneId zoneOfUser = ZoneId.of( "America/Los_Angeles" ) ;
ArrayList< ZonedDateTime > hoursOfUserOnDate = new ArrayList<>() ;
for( ZonedDateTime zdtBusiness : hoursOfBusinessOnDate )
{
    ZonedDateTime zdtUser = zdtBusiness.withZoneSameInstant( zoneOfUser ) ;
    hoursOfUserOnDate.add( zdtUser ) ;
}

[2022-01-23T05:00-08:00[美国/洛杉矶], 2022-01-23T06:00-08:00[美国/洛杉矶], 2022-01-23T07:00-08:00[ America/Los_Angeles], 2022-01-23T08:00-08:00[America/Los_Angeles], 2022-01-23T09:00-08:00[America/Los_Angeles], 2022-01-23T10:00-08:00 [美国/洛杉矶], 2022-01-23T11:00-08:00[美国/洛杉矶], 2022-01-23T12:00-08:00[美国/洛杉矶], 2022-01-23T13:00-08: 00[美国/洛杉矶]]

在自动本地化后将该列表呈现给用户。假设我们的用户来自魁北克。

Locale locale = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( locale ) ;
List< String > choices = new ArrayList<>() ;
for( ZonedDateTime zdt : hoursOfUserOnDate ) 
{
    String choice = zdt.format( f ) ;
    choices.add( choice ) ;
}

选择:[1 月 23 日。 2022 年 05 时 00 分 00 秒,1 月 23 日。 2022 年 06 时 00 分 00 秒,1 月 23 日。 2022 年 07 时 00 分 00 秒,1 月 23 日。 2022 年 8 时 00 分 00 秒,1 月 23 日。 2022 年 09 时 00 分 00 秒,1 月 23 日。 2022 年 10 时 00 分 00 秒,1 月 23 日。 2022 年 11 时 00 分 00 秒,1 月 23 日。 2022 年 12 时 00 分 00 秒,1 月 23 日。 2022 13 时 00 分 00 秒]

当用户选择一个时,转换回业务区。

ZonedDateTime userChoice = hoursOfUserOnDate.get( 1 ) ; // Get second element, simulating user's choice.
ZonedDateTime userChoiceForBusiness = userChoice.withZoneSameInstant( zoneOfBusiness ) ;

用户选择:2022-01-23T06:00-08:00[America/Los_Angeles]

userChoiceForBusiness:2022-01-23T09:00-05:00[America/New_York]

仅提取日期和时间,省略时区。

LocalDateTime appointmentStart = userChoiceForBusiness.toLocalDateTime() ;

约会开始:2022-01-23T09:00

在数据库中,记录开始、预期时区和持续时间。

myPreparedStatement.setObject( … , appointmentStart ) ;
myPreparedStatement.setString( … , zoneOfBusiness.toString() ) ;
myPreparedStatement.setString( … , Duration.ofHours( 1 ).toString() ) ;

从数据库中检索。

LocalDateTime appointmentStart = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId zoneOfBusiness = ZoneId.of( myResultSet.getString( … ) ) ;
Duration duration = Duration.parse( myResultSet.getString( … ) ) ;

在制定时间表时,您需要确定一个时刻。 永远不要保存这一刻,因为这不是您真正的约会。鉴于当前的时区规则集,它代表您的约会时刻现在看起来

ZonedDateTime begin = appointmentStart.atZone( zoneOfBusiness ) ;
ZonedDateTime end = begin.plus( duration ) ;

开始:2022-01-23T09:00-05:00[America/New_York]

结束:2022-01-23T10:00-05:00[美国/纽约]

要适应 UTC,请提取 Instant

Instant beginUtc = begin.toInstant() ;

beginUtc: 2022-01-23T14:00:00Z

查看大部分内容code run live at IdeOne.com


确保所有tzdata 数据文件保持最新。您的服务器的主机操作系统有一个。你的JVM 有一个。如果你的数据库很复杂,比如Postgres,你也会在那里找到一个tzdata

【讨论】:

  • 哇,非常感谢,谢谢!这给了我大量的信息,我可以用这些信息以一种简单易懂的方式开始。我使用的是 JDBC 版本 8.0.25,因此在从数据库写入或检索时会即时进行转换,就在我需要检查程序信息时遇到麻烦。
  • @axk5216 没有 JDBC 8。JDBC 4.3 是最新的。
猜你喜欢
  • 2017-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2011-01-28
  • 2019-04-09
相关资源
最近更新 更多