在了解 ZonedDateTime、LocalDateTime 以及如何传递此信息时。
日期时间处理非常棘手。我们对日期时间的日常直观理解对我们是程序员。
在 Stack Overflow 上已经处理了很多次预约,所以我会尽量简短。搜索以了解更多信息,并查看更多示例。
Instant、OffsetDateTime 和 ZonedDateTime 类各自代表一个时刻,即时间线上的一个特定点。例如,记录时,使用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。