加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

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

发布时间:2020-12-15 04:22:38 所属栏目:Java 来源:网络整理
导读:我正在与位于与我的不同时区的服务器上开发一个带有 MySQL数据库的Java应用程序,我正在尝试在我的数据库中使用DATETIME或TIMESTAMP. 在阅读了像Should I use field ‘datetime’ or ‘timestamp’?和MySQL documentation这样的问题之后,我认为TIMESTAMP对我
我正在与位于与我的不同时区的服务器上开发一个带有 MySQL数据库的Java应用程序,我正在尝试在我的数据库中使用DATETIME或TIMESTAMP.

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

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

对我来说,这看起来是一个很好的做法:将日期时间存储为UTC时间戳,然后根据当前时区显示它们.

我就是这样做的,但后来我发现这是从Java documentation准备好的陈述,并且非常困惑:

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

但我必须不同意其结论.使用count-since-epoch并不是最好的解决方案.使用count-since-epoch本质上是令人困惑且容易出错并且不兼容.

>人类无法读取长数字并将其解读为日期时间.因此,至少可以说,验证数据和调试变得复杂.
>你会使用什么“数量”? java.util.Date和Joda-Time使用milliseconds? microseconds使用microseconds,MySQL和其他数据库?新的java.time package在Java 8中使用的nanoseconds?
>你会使用哪个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.Datejava.sql.Time类的hacks,这是一个伪装成仅限日期的日期时间值. Joda-Time和java.time都通过提供LocalDate和LocalTime类来纠正它.

另一个具体问题是java.util.Date的分辨率为毫秒,但数据库经常使用微秒或纳秒.在弥合这种差异的不明智的尝试中,早期的Java团队创建了另一个hack,即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.

使用Joda-Time和JDBC与Postgres的示例.Joda-Time使用immutable objects表示thread-safety.因此,我们不是更改实例(“mutate”),而是根据原始值创建一个新实例.

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引用了一个狭隘的技术异常(分区),甚至在保存到数据库之前,他说自己将所有值转换为UTC.

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

方针

更好的日期时间处理的第一步是避免java.util.Date& .Calendar,使用Joda-Time和/或java.time,专注于UTC,并学习特定JDBC驱动程序和特定数据库的行为(数据库在日期时间处理方面差异很大,尽管有SQL标准).

MySQL的

警告:我不使用MySQL(我是Postgres那种人).

根据version 8 documentation,DATETIME和TIMESTAMP这两种类型的不同之处在于第一种类型缺少任何时区概念或从UTC偏移.第二个使用时区的任何指示或伴随输入的UTC偏移指示将该值调整为UTC,然后存储它,并丢弃区域/偏移信息.

所以这两种类型似乎类似于标准的SQL类型:

> MySQLDATETIME≈SQL标准TIMESTAMP没有时区
> MySQLTIMESTAMP≈带有时区的SQL标准TIMESTAMP

对于MySQL DATETIME,使用Java类LocalDateTime.该类与该数据类型一样,故意缺少任何时区概念或从UTC偏移.将此类型和类用于:

>当您指任何区域或所有区域时,例如“圣诞节在2018年12月25日的第一时刻开始”.这可以转化为不同地方的不同时刻,因为东方早些时候比西方早起.
>在未来安排约会或事件时,政治家可能会改变时区的偏差,世界各地的政治家都表现出了倾向.在此用法中,您必须在运行时应用时区来动态计算(但不存储)在日历上显示的时刻.这样,即使政治家们将时钟重新定义为提前几分钟/小时,在8个月内15:00的牙科预约仍然是15:00.

对于MySQL TIMESTAMP,请使用Java类Instant,如上所示.将此类型和类用于时间轴,时间轴上的特定点.

JDBC 4.2

从JDBC 4.2及更高版本开始,我们可以直接与数据库交换java.time对象.使用getObject& setObject方法.

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 4.2或更高版本的JDBC driver.不需要字符串,不需要java.sql.*类.

从哪里获取java.time类?

> Java SE 8,Java SE 9,Java SE 10及更高版本

>内置.
>部分标准Java API,带有捆绑实现.
> Java 9增加了一些小功能和修复.

> Java SE 6Java SE 7

>大部分java.time功能都被反向移植到Java 6& 07年7月7日.

> Android

>更新版本的Android捆绑java.time类的实现.
>对于早期的Android(< 26),ThreeTenABP项目适应ThreeTen-Backport(如上所述).见How to use ThreeTenABP….

ThreeTen-Extra项目使用其他类扩展了java.time.该项目是未来可能添加到java.time的试验场.您可以在这里找到一些有用的类,例如Interval,YearWeek,YearQuarter和more.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读