当前位置: 首页 > 知识库问答 >
问题:

将约会存储在SQL数据库中,例如Postgres,以便与java.time框架一起使用

农存
2023-03-14

假设我们在意大利米兰有一个约会,时间是2021 1月23日21:00“欧洲/罗马”。此约会以UTC格式保存到数据库的一列中,该列的类型类似于带时区的SQL标准类型时间戳。

现在,居住在美国纽约的用户需要了解此约会何时进行。我们可以向该用户显示转换为“美国/New_York”时区的日期时间,或者取而代之的是“欧洲/罗马”TZ。一旦用户从纽约飞往米兰,他会发现这两个信息都很有用。

关键是存储转换为相同TZ引用(UTC)的所有内容,并根据您使用与现代Java捆绑的java.time框架的目标操作日期时间。

有道理还是有问题/遗漏?

共有2个答案

李鸿
2023-03-14

一个非常简单的解决方案是将转换留给PostgreSQL。如果为每个会话正确设置时区参数,并将时间戳与时区一起使用,PostgreSQL将在纽约时间自动向纽约用户显示时间戳。

赵炯
2023-03-14

有道理还是有问题/遗漏?

这取决于约会的类型。

有两种预约:

  • 时间线上的一个时刻,一个特定的点,忽略对时区规则的任何更改。
    示例:火箭发射。
  • 应根据时区规则的变化进行调整的日期和时间。
    示例:医疗/牙科访问。

例如,如果我们预定火箭的发射,我们就不在乎一天中的日期和时间。我们只关心(a)天齐的时刻,以及(b)我们期待好天气的时刻。

如果在此期间,政客们改变了我们发射场或办公室使用的时区规则,这对我们的发射任命没有影响。如果管理我们发射场的政客们采用夏时制(DST),我们发射的时刻将保持不变。如果管理我们办公室的政客们因为与邻国的外交关系决定提前半小时改变时钟,我们发射的时刻将保持不变。

对于这样的约会,是的,您的方法是正确的。您将使用TIMESTAMP AND TIME ZONE类型的列在UTC中记录约会。检索后,调整到用户喜欢的任何时区。

Postgres等数据库使用随输入一起的任何时区信息来调整到UTC,然后处理该时区信息。当您从Postgres中检索值时,它将始终表示在UTC中看到的一天中的时间的日期。请注意,某些工具或中间件可能具有在从数据库检索到程序员交付给您之间应用一些默认时区的反功能。但要明确:Postgres始终在UTC中保存和检索TIMESTAMP with TIME ZONE类型的值,始终为UTC,并且从UTC偏移0小时-分钟-秒。

下面是一些示例Java代码。

LocalDate launchDateAsSeenInRome = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime launchTimeAsSeenInRome = LocalTime.of( 21 , 0 ) ;
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
// Assemble those three parts to determine a moment.
ZonedDateTime launchMomentAsSeenInRome = ZonedDateTime.of( launchDateAsSeenInRome , launchTimeAsSeenInRome , zoneEuropeRome ) ;

LaunchMomentAsSeenInRome.toString(): 2021-01-23T21:00 01:00[欧洲/罗马]

要在UTC中查看同一时刻,请转换为InstantInstant对象始终表示在UTC中看到的时刻。

Instant launchInstant = launchMomentAsSeenInRome.toInstant() ;  // Adjust from Rome time zone to UTC.

启动Instant。toString():2021-01-23T20:00:00Z

上面字符串示例末尾的Z是UTC的标准表示法,发音为“Zulu”。

不幸的是,JDBC 4.2团队忽略了需要对InstantZonedDateTime类型的支持。因此,您的JDBC驱动程序可能无法或可能无法将此类对象读取/写入您的数据库。如果没有,只需转换为OffsetDateTime。所有三种类型都代表一个时刻,时间线上的一个特定点。但是OffsetDateTime具有JDBC 4.2所需的支持,原因我无法理解。

OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

写入数据库。

myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

从数据库检索。

OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;

调整到用户所需的纽约时区。

ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;

纽约州发射基地。toString():2021-01-23T15:00-05:00[美国/纽约]

您可以看到以上所有代码在IdeOne上实时运行。通用域名格式。

顺便说一下,追踪过去的事件也被视为一个瞬间。患者实际何时到达预约,客户何时支付发票,新员工何时签署文档,服务器何时崩溃……所有这些都在UTC中作为一个瞬间进行跟踪。如上所述,这通常是一个即时的,但分区的最新时间

我预计大多数面向商业的应用程序都集中在其他类型的约会上,我们的约会目标是一天中的某个时间,而不是某个特定的时刻。

如果用户与他们的医疗保健提供者预约检查测试结果,他们会在当天的特定时间进行检查。如果与此同时,政客们改变了他们的时区规则,将时钟向前或向后移动一个小时或半个小时或任何其他时间,那么医疗预约的日期和时间保持不变。事实上,在政客改变时区后,最初任命的时间点将发生变化,转移到时间线上的较早/较晚点。

对于此类任命,我们不存储UTC中显示的日期和时间。我们不使用带有时区的数据库列类型时间戳。

对于此类约会,我们使用时间存储日期,而不考虑时区。我们使用的数据库列类型为不带时区的时间戳(注意,不带时间戳,而不是带时间戳)。Java中的匹配类型是LocalDateTime。

LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;

将其写入数据库。

myPreparedStatement.setObject( … , medicalApptDateTime ) ;

要清楚这一点:LocalDateTime对象不代表一个时刻,也不是时间线上的特定点。LocalDateTime对象表示时间线上大约26-27小时的可能时刻范围(全球时区范围)。要赋予LocalDateTime真正的意义,我们必须关联一个预期的时区。

对于预期的时区,使用第二列存储区域标识符。例如,字符串是欧洲/罗马或美国/纽约。请参见区域名称列表。

ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;

将其作为文本写入数据库。

myPreparedStatement.setString( … , zoneEuropeRome ) ;

检索以文本形式检索分区名称,并实例化ZoneId对象。

LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;

将这两个部分放在一起以确定表示为ZonedDateTime对象的时刻。当您需要安排日历时动态执行此操作。但不要存储时刻。如果政客将来重新定义时区,则必须计算不同的时刻。

ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;

用户正在前往美国纽约。他们需要根据他们临时所在地纽约墙上的时钟,知道何时给意大利米兰的医疗服务提供商打电话。因此,从一个时区调整到另一个时区。相同的时刻,不同的挂钟时间。

ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;

请注意,如果所需时区的规则可能正在更改,则必须更新计算机上的时区定义副本。

Java包含自己的tzdata副本,Postgres数据库引擎也是如此。以及您的主机操作系统。这里显示的这段特定代码只需要Java是最新的。如果您使用Postgres进行时区调整,其tzdata也必须是最新的。对于日志记录等,您的主机操作系统应该保持最新。为了让用户正确地观察时钟,他们的客户端机器的操作系统也必须是最新的。

注意:世界各地的政治家都表现出了以惊人的频率改变时区的倾向,而且往往没有什么预警。

java。Java 8及更高版本内置了时间框架。这些类取代了麻烦的旧遗留日期时间类,如java。util。日期、日历,

要了解更多信息,请参阅Oracle教程。并搜索堆栈溢出以获得许多示例和解释。规范为JSR 310。

现在处于维护模式的Joda Time项目建议迁移到java。时间类。

您可以交换java。直接使用数据库创建时间对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要html" target="_blank">字符串,也不需要java。sql* 类。Hibernate5

从何处获取java。时间课程?

  • Java SE 8、Java SE 9、Java SE 10、Java SE 11及更高版本—标准Java API的一部分,带有捆绑实现。
    • Java 9带来了一些次要功能和修复
    • 大部分java。时间功能后移植到Java 6
    • 更高版本的Android(26)捆绑了java.time类的实现。
    • 对于早期的Android(

 类似资料:
  • 问题内容: 我已经阅读了有关键/值存储(例如Redis)的很棒的文章,但是我似乎无法弄清楚何时该在应用程序中使用它。 假设我正在设计一个基于Web的应用程序;我知道前端,后端,数据库等将使用什么堆栈。在某些情况下,我会使用“哦,我们还需要Redis用于X,Y或Z”。 我会喜欢node.js示例以及非node.js示例。 问题答案: 我似乎无法弄清楚何时在应用程序中使用它。 我建议您阅读本教程,其中

  • 我刚开始冬眠。我需要在postgres中存储文件,列的数据类型是。我在互联网上四处寻找这个案例的工作演示,但我找不到任何。 Hibernate映射:

  • 本文向大家介绍如何临时存储数据以便以后在Oracle中使用?,包括了如何临时存储数据以便以后在Oracle中使用?的使用技巧和注意事项,需要的朋友参考一下 问题: 您要临时存储SQL的结果。 解 我们可以使用CREATE GLOBAL TEMPORARY TABLE语句创建一个表,该表临时存储会话的数据。此外,您可以指定是保留会话的临时表数据还是事务提交之前。我们可以进一步使用ON COMMIT

  • 问题内容: 我在Django中有一些REST API端点,我想对Graphene使用相同的身份验证。该文档不提供任何指导。 问题答案: 例如,如果在API视图中使用,则可以将端点添加到以这种方式装饰的GraphQLView中: urls.py: 请注意,我们添加了一个新的端点,并保留了GraphiQL工具使用的原始端点。 然后,您应该在GraphQL客户端中设置标头并指向端点。 更新:请参阅此Gi

  • 问题内容: 如何在SQL数据库中存储HashMap?另外,您如何将HashMap从SQL数据库加载回HashMap实例? 好的,这就是我正在做的。我有一个数据库来存储我的游戏的玩家数据。它有一个包含用户名和密码的表。每个玩家都有一个存储其属性的HashMap。我需要将该HashMap及其各自的用户存储在数据库中。 问题答案: 您需要一个三列表 用户, 键, 值 然后看起来像 “用户1”,“属性1”

  • 我有一个名为UserRepository的通用存储库接口。然后我有一个接口,它从MyUserRepository扩展而来。它处理一个MyUser类,该类扩展了User。 我还有一个名为UserService的服务接口和一个名为MyUserServiceImpl的类。 该服务需要UserRepository的实例,我虽然可以使用某种注释,如@Qualifer,但它不起作用。 应用程序无法启动 说明: