gpt4 book ai didi

java - Java和MySQL中的时间戳和时区转换

转载 作者:行者123 更新时间:2023-12-01 10:57:39 38 4
gpt4 key购买 nike

我正在与我位于不同时区的服务器上使用MySQL数据库开发Java应用程序,并且我试图决定在数据库上使用DATETIME还是TIMESTAMP。

在阅读了诸如Should I use field 'datetime' or 'timestamp'?MySQL documentation之类的问题之后,我认为TIMESTAMP对我来说更好,因为它将值转换为UTC进行存储,然后返回当前时区进行检索。

另外,正如用户Jesper在this线程中所解释的那样,java.util.Date对象在内部仅是UTC时间戳(即自大纪元以来的毫秒数),并且当您执行toString()时会根据您的当前时区显示它。

对我来说,这似乎是一种好习惯:将日期时间存储为UTC时间戳,然后根据当前时区显示它们。

我本来打算那样做,但是后来我从Java documentation中的Prepared Statements中发现了这一点,并感到非常困惑:

void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException

Sets the designated parameter to the given java.sql.Timestamp value, using the given Calendar object. The driver uses the Calendar object to construct an SQL TIMESTAMP value, which the driver then sends to the database. With a Calendar object, the driver can calculate the timestamp taking into account a custom timezone. If no Calendar object is specified, the driver uses the default timezone, which is that of the virtual machine running the application.



在此之前,我认为时间戳总是按惯例在UTC中进行。为什么在地球上有人会想要本地化的时间戳记而不是本地化的时间戳记?那不是每个人都很困惑吗?

这些转换如何运作?如果Java采用UTC时间戳并将其转换为任意时区,它如何告诉MySQL它处于哪个时区?

MySQL不会假设此时间戳记为UTC,然后获取不正确的本地化值吗?

最佳答案

日期时间处理是一团糟

answer by Teo中的第一段非常有见地和正确:Java中的日期时间处理是一团糟。同上,适用于我所知道的所有其他语言和开发环境。日期时间工作是困难而棘手的,尤其是容易出错和令人沮丧的日期,因为我们直观地认为它是日期时间。但是当涉及到数据类型,数据库,序列化,本地化,跨时区调整以及计算机编程附带的所有其他形式时,“直观地”并没有减少它。

不幸的是,计算机行业基本上选择忽略了日期时间工作的这一问题。鉴于有明显的需求,Unicode的发明时间太长,因此业界也为解决日期时间处理问题开辟了道路。

世代以来不依赖计数

但是我必须不同意它的结论。从计数开始的时代并不是最好的解决方案。使用count-since-epoch本质上是令人困惑的,并且容易出错并且不兼容。

  • 人类无法读取long号并将其解密为日期时间。至少可以这样说,因此验证数据和调试变得很复杂。
  • 您将使用什么“计数”? millisecondsjava.util.Date使用的Joda-Time吗? microseconds,MySQL和其他数据库使用的Postgres? Java 8中新的nanoseconds使用的java.time package
  • 您将使用哪种epoch? UTC 1970年初的Unix epoch很常见,但远非单数。几乎two dozen epochs已被各种计算机系统使用。

  • 我们创建用于执行数学运算的数字数据类型,而不是使用位。我们创建字符串类来处理处理文本的细节,而不是裸字节。因此,我们也应该创建数据类型和类来处理日期时间值。

    早期的Java团队(以及之前的IBM和 Taligent)尝试使用java.util.Date和java.util.Calendar及其相关类。不幸的是,这一尝试是不充分的。尽管日期时间本来就是令人困惑的,但是这些类却增加了更多的困惑。

    乔达时代

    据我所知, Joda-Time项目是第一个以彻底,称职和成功的方式进行日期时间的项目。即便如此,Joda-Time的创造者仍不完全满意。他们继续在Java 8中创建 java.time package,并使用 threeten-extra project扩展了该功能。 Joda-Time和java.time共享相似的概念,但是截然不同,每个都有一些优点。

    数据库问题

    具体来说,java.util.Date和.Calendar类缺少仅日期的值,而没有日期和时区。而且它们缺少没有日期和时区的仅时间值。在Java 8之前,Java团队添加了称为 java.sql.Date java.sql.Time 类的黑客,这是一个伪装为仅日期的日期时间值。 Joda-Time和java.time都通过提供 LocalDateLocalTime类进行了纠正。

    另一个特定的问题是java.util.Date的分辨率为毫秒,但是数据库经常使用微秒或纳秒。为了弥补这种差异,不明智的尝试是,早期的Java团队创建了另一个黑客 java.sql.Timestamp类。从技术上讲,它是一个java.util.Date子类,但它也跟踪小数秒至纳秒级的分辨率。因此,在转换为这种类型时,您可能会丢失或获得更精细的小数秒粒度,而不会意识到这一事实。因此,这可能意味着您期望相等的值不相等。

    另一个令人困惑的原因是SQL数据类型 TIMESTAMP WITH TIME ZONE。该名称使用不当,因为未存储时区信息。将名称视为 TIMESTAMP WITH RESPECT FOR TIME ZONE,因为在将日期时间值转换为 UTC时会使用任何传递的时区偏移量信息。

    纳秒级分辨率的java.time软件包具有一些特定功能,可以更好地与数据库通信日期时间数据。

    我可以写更多的东西,但是可以通过在StackOverflow中搜索诸如joda,java.time,sql timestamp和JDBC之类的单词来收集这些信息。

    将JDBC与 Postgres一起使用Joda-Time的示例。 Joda-Time将 immutable objects用作 thread-safety。因此,我们无需更改实例(“变异”),而是根据原始值创建一个新实例。
    String sql = "SELECT now();";

    java.sql.Timestamp now = myResultSet.getTimestamp( 1 );
    DateTime dateTimeUtc = new DateTime( now , DateTimeZone.UTC );
    DateTime dateTimeMontréal = dateTimeUtc.withZone( DateTimeZone.forID( "America/Montreal" ) );

    专注于UTC

    Before this, I thought timestamps were by convention always in UTC. Why on earth would anyone want a localized timestamp instead of a localized representation of it? Wouldn't that be very confusing for everyone?



    的确。 SQL标准定义了一个 TIMESTAMP WITHOUT TIME ZONE,它会忽略并剥离所有包含的时区数据。我无法想象这样做的用处。这位Postgres专家David E. Wheeler的 says as much in recommending始终使用 TIMESTAMP WITH TIME ZONE。 Wheeler引用了一个狭义的技术异常(exception)(分区),甚至说要在保存到数据库之前将所有值自己转换为UTC。

    最佳实践是在UTC中工作和存储数据,同时调整到本地时区以呈现给用户。有时您可能想记住其本地化时区中的原始日期时间数据。如果是这样,除了转换为UTC之外,还要保存该值。

    指导方针

    更好地处理日期时间的第一步是避免使用java.util.Date和.Calendar,使用Joda-Time和/或java.time,着重于UTC,并了解特定JDBC驱动程序和特定数据库(数据库)的行为。尽管采用了SQL标准,但它们在日期时间处理上的差异也很大。

    的MySQL

    警告:我不使用MySQL(我是 Postgres类人)。

    根据 version 8 documentation,两种类型的 DATETIMETIMESTAMP的不同之处在于,第一种缺乏时区或从UTC偏移的概念。第二个参数使用输入的时区或UTC偏移量的任何指示来将该值调整为UTC,然后将其存储,并丢弃时区/偏移量信息。

    因此,这两种类型似乎类似于标准SQL类型:
  • MySQL DATETIME≈SQL标准的TIMESTAMP WITHOUT TIME ZONE
  • MySQL TIMESTAMP≈SQL标准的TIMESTAMP WITH TIME ZONE

  • 对于MySQL DATETIME,请使用Java类 LocalDateTime 。该类与该数据类型一样,故意缺少任何时区或从UTC偏移的概念。将此类型和类用于以下任何一种:
  • 是指任何一个区域或所有区域,例如“圣诞节始于2018年12月25日的第一刻”。随着新的一天的到来,东方比西方早到了不同的时刻,这在不同的地方转化为不同的时刻。
  • 如果将来安排约会或事件的时间足够远,以至于政客可能会更改时区的偏移量,而世界各地的政客都对时区表示偏爱。在这种用法中,您必须在运行时应用时区来动态计算(但不存储)要在日历上显示的时刻。这样,即使政客将时钟重新定义为提前或落后几分钟/几小时,在8个月内仍将在15:00进行一次 dentry 预约。

  • 对于MySQL TIMESTAMP,使用Java类 Instant,如上所示。将这种类型和类用于时刻,即时间轴上的特定点。

    JDBC 4.2

    从JDBC 4.2及更高版本开始,我们可以直接与数据库交换java.time对象。使用 getObjectsetObject方法。
    myPreparedStatement.setObject( … , Instant.now() ) ;

    恢复。
    Instant instant = myResultSet.getObject( … , Instant.class ) ;

    然后,您可以将 Instant中的UTC值调整为特定时区,以呈现给用户。
    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
    ZonedDateTime zdt = instant.atZone( z ) ;

    关于java.time

    java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧 legacy日期时间类,例如 java.util.Date Calendar SimpleDateFormat

    现在位于 Joda-Time中的 maintenance mode项目建议迁移到 java.time类。

    要了解更多信息,请参见 Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规范为 JSR 310

    您可以直接与数据库交换java.time对象。使用兼容 JDBC driver或更高版本的 JDBC 4.2。不需要字符串,不需要 java.sql.*类。

    在哪里获取java.time类?
  • Java SE 8Java SE 9Java SE 10和更高版本
  • 内置。
  • 是标准Java API的一部分,具有 bundle 的实现。
  • Java 9添加了一些次要功能和修复。
  • Java SE 6Java SE 7
  • 许多java.time功能都已在ThreeTen-Backport中反向移植到Java 6和7。
  • Android
  • 更高版本的java.time类的Android bundle 实现。
  • 对于早期的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。参见How to use ThreeTenABP…

  • ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如 Interval YearWeek YearQuarter more

    关于java - Java和MySQL中的时间戳和时区转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26065928/

    38 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com