MySQL和Java有一个时区处理的挑战



我正在处理MySQL和Java,并有一个时区处理挑战。

问题:数据库服务器位于美国中部。用户1在墨西哥。用户2在新加坡。

用户1按英国时区输入日期和时间,以提供从伦敦起飞的航班的信息。

用户2应该能够在以下3个选项中看到该文件

  1. 用户1在英国时区输入的日期和时间
  2. 日期和时间按用户的时区
  3. UTC日期和时间

数据库应该如何构建?我们应该将用户提供的日期和时间存储在一列中,而将时区存储在另一列中,还是可以将日期、时间和时区存储在一列中?

争取始终按照UTC约定存储时间信息。它是唯一一个与现实中"发生"的"时刻"1:1对应的。

其余的都是展示/呈现的问题。

即使您认为没有时区问题也要这样做。

这个问题在DB的生命周期中仍然会出现。

Erwin Smout的答案是正确的。

下面是所有三种情况的示例代码,以及一些讨论。

数据库类型TIMESTAMP WITH TIME ZONE

数据库表列应该定义为SQL标准类型TIMESTAMP WITH TIME ZONE(而不是WITHOUT)。或者类似的东西取决于你的数据库。对日期时间的支持在一些较弱的(SQLite)和较强的(Postgres)之间差异很大。

实际上所有的数据库都以UTC存储日期-时间值(如果它们尊重时区的话)。有些保留了与输入值一起传递的时区信息,而有些(如Postgres)在使用它将值调整为UTC后丢弃了该信息。

在UTC中思考、工作和存储

您的业务逻辑和数据存储应该使用UTC。所以只有一个实际的飞行时间,在UTC。对于不同的墨西哥、新加坡和英国表示,我们应用不同的时区来往返UTC值。

<标题>服务器无关的h1> 务器在美国中部的位置应该是无关的。首先,服务器的时区应该几乎总是设置为UTC(或冰岛)。其次,你应该永远不要依赖于这个设置,因为它是你无法控制的外部性,非常容易被改变——甚至在运行时期间 !相反,始终在代码中指定所需/期望的时区作为参数。 <标题> java.time h1> 终使用java。java.util.Date/.Calendar/java.text.SimpleDateFormat类。 <标题> java.sql h1> 数据库中检索飞行时间,作为java.sql.Timestamp,根据定义是UTC。
java.sql.Timestamp ts = myResultSet.getTimestamp( … );

作为旧的麻烦的日期时间类的一部分,尽量减少java的使用。sql类型。立即转换为java。时间类。Instant是UTC时间轴上的时刻,分辨率为纳秒。

Instant instant = ts.toInstant();

请注意,所有移动的部分都在UTC中:数据库存储、JDBC、java.sql.TimestampInstant。完全没有时区问题。

调整m录影带

对于墨西哥的客户,应用他们选择的或者您认为合适的时区。

ZoneId zdt_America_MexicoCity = ZoneId.of( "America/Mexico_City" );
ZonedDateTime zdt_America_MexicoCity = ZonedDateTime.ofInstant( instant , zdt_America_MexicoCity );

搜索Stack Overflow获取有关使用DateTimeFormatter类及其ofLocalized…方法自动本地化为人类语言和文化规范的字符串的信息。

调整新加坡

新加坡客户亦是如此。

ZoneId zdt_Asia_Singapore = ZoneId.of( "Asia/Singapore" );
ZonedDateTime zdt_Asia_Singapore = ZonedDateTime.ofInstant( instant , zdt_Asia_Singapore );

这三个对象,我们的instant和一对ZonedDateTime对象,代表了在时间轴上的同一时刻。

调整为UTC

对于在UTC工作的航空公司工作人员,如果您需要的文本表示是ISO 8601格式,小数位数为0、3、6或9,则使用Instant

String output = instant.toString();

如果您需要其他格式作为文本表示,请在将Instant转换为格式更灵活的OffsetDateTime类之后使用java.time.format包。注意UTC的常量的使用,它存储在ZoneId, ZoneOffset的子类中。

OffsetDateTime odt = OffsetDateTime.ofInstant( instant , ZoneOffset.UTC );

调整数据输入

如果用户输入的是英国时区的飞行时间,随后调整为UTC。顺便说一下,不要把英国时间和UTC时间混淆了。伦敦时间在历史上一直在变化,目前随着夏令时而变化,而UTC时间从未变化(闰秒除外)。

ZoneId zoneId_Europe_London = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = ZonedDateTime( 2016 , 1 , 3 , 9 , 0 , 0 , 0 , zoneId_Europe_London );
Instant instant = zdt.toInstant();
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );
<标题>本地h1> 答案适用于特定时刻,特定日期和时间。如果你指的是"上午9点"的飞行时间,因为墙上的时钟会因为包括日光节约时间(DST)在内的异常情况而移动,那么这将是另一个讨论。问题说用户正在输入一个特定的日期和特定的时间,所以上面的讨论适用。

最新更新