我正在处理MySQL和Java,并有一个时区处理挑战。
问题:数据库服务器位于美国中部。用户1在墨西哥。用户2在新加坡。
用户1按英国时区输入日期和时间,以提供从伦敦起飞的航班的信息。
用户2应该能够在以下3个选项中看到该文件
- 用户1在英国时区输入的日期和时间
- 日期和时间按用户的时区
- UTC日期和时间
数据库应该如何构建?我们应该将用户提供的日期和时间存储在一列中,而将时区存储在另一列中,还是可以将日期、时间和时区存储在一列中?
争取始终按照UTC约定存储时间信息。它是唯一一个与现实中"发生"的"时刻"1:1对应的。
其余的都是展示/呈现的问题。
即使您认为没有时区问题也要这样做。
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.Timestamp
和Instant
。完全没有时区问题。
调整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)在内的异常情况而移动,那么这将是另一个讨论。问题说用户正在输入一个特定的日期和特定的时间,所以上面的讨论适用。
标题>标题>标题>标题>