java为什么与UTC偏移相同的时区显示不同的时间?
我今天碰到了这个问题。我已将时钟设置为UTC-6.00(中美洲)时区。我正在将日期“06/01/2015::12:00:00 am”(“MM/dd/yyyy::hh:MM:ss a”格式)转换为java日期对象。然后我将日期对象重新转换为字符串。不过,我做这件事的方式有点扭曲。我在下面列出了重新转换的步骤-
- 计算当前时区的UTC偏移量。(-2160000)
- 获取此偏移量的所有可用时区ID。(都有相同的偏移量)
- 选择第一个时区id。(将具有相同的偏移)
- 将此设置为时区李>
- 使用Java的简单日期格式将日期转换为字符串格式李>
我看到现在渲染的时间是“06/01/2015::01:00:00 AM”
我的问题是:
由于时区偏移在创建和转换期间是相同的,因此我希望显示相同的时间。但我看到的是不同的。为什么会这样
想象一下在服务器中发生的重新转换和在客户机中发生的创建。我需要向客户返回相同的日期和时间。我该怎么做
请帮忙!非常感谢您的帮助
编辑:下面是代码。请注意,我已将当前时区设置为中美洲强>
public class TimeTest {
public static void main (String args[]) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String dateInString = "01/06/2015::12:00:00 AM";
try {
Date date = formatter.parse(dateInString);
System.out.println("Before conversion --> " + formatter.format(date));
System.out.println("After conversion --> " + convertDateValueIntoString(date));
} catch (ParseException e) {
e.printStackTrace();
}
}
private static String convertDateValueIntoString(Date dateValue){
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String date;
int offset = TimeZone.getDefault().getRawOffset();
if (offset == 0) {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
date = dateFormat.format(dateValue);
} else {
String TZ[] = TimeZone.getAvailableIDs(offset);
String timeZone = TZ[0];
if (timeZone == null) {
date = dateFormat.format(dateValue);
} else {
TimeZone tz = TimeZone.getTimeZone(timeZone);
dateFormat.setTimeZone(tz);
date = dateFormat.format(dateValue);
}
}
return date;
}
}
# 1 楼答案
区别似乎在于夏令时的处理。将我的机器设置为不同的时区,然后将时区打印到字符串()我最终得到了:
请注意,这两个时区具有相同的偏移量,但其中一个使用夏令时,另一个不使用。偏移量是代码寻找合适时区的全部内容,但日期格式也使用夏令时偏移量
我参与的每个项目都使用了UTC(或类似的概念)来表示时间。我会让您的客户机在输入时将时间转换为UTC(在发送到服务器之前),让所有服务器存储使用UTC,然后当时间返回到客户机时,将客户机格式转换为默认时区,仅用于向用户输出
这样一来,所有内部时间都是一致的,并且所有显示的时间都是针对客户端的单个实例进行本地化的,因此美国/特古西加尔巴的用户可能会得到12:00的时间,但美国/巴伊亚-班德拉斯的用户会看到1:00。对于用户来说,这两种时间都是正确的
# 2 楼答案
{a1}是正确的。我会补充一些想法
这个问题有很多困惑
时区=偏移+规则/异常/调整
与UTC相比,时区更像是一个偏移量。时区是关于夏令时和其他异常的过去、现在和未来规则的偏移量;调整
因此,只要有可能,就使用named time zone而不仅仅是偏移量。当然,不要只使用时区来混合使用偏移量,并期望得到合理的结果。这似乎是这个问题的核心问题
因此,深入挖掘,以发现设计现有存储数据的程序员的初衷。我怀疑他们确实有一个特定的时区,而不仅仅是一个抵消
使用正确的时区名称
没有“中美洲”这样的时区
正如1337Joe指出的,中美洲各地的偏移量和时区各不相同。例如,
America/Managua
比UTC晚六个小时,而America/Panama
比UTC晚五个小时顺便说一句,避免使用3-4个字母的时区代码,比如“EST”,因为它们既不标准,也不独特。当然,唯一的例外是
UTC
指定预期/期望的时区
当[a]你知道你的输入数据代表一个特定的时区或偏移量,尽管是隐式的,并且[b]你希望应用某个时区时,不要调用默认时区。这是自找麻烦。默认时区可能因主机操作系统设置的不同而有所不同。管理员可以随时更改这两个主机操作系统的设置。第三,JVM的当前默认时区可以在运行时的任何时刻通过同一JVM中任何应用程序的任何线程中的任何代码调用^{} 来更改
因此,与其依赖默认时区,不如指定所需的时区
使用UTC进行逻辑和;储藏室
正如1337joe所说,您的业务逻辑、数据存储、数据通信和数据库都应该使用UTC(几乎总是)。仅在用户/消费者预期时对本地时区进行调整
在评论中,作者说他们的项目已经背负了现有的存储数据,这些数据隐含地代表了某个时区或偏移量
爪哇。util。日期
toString
java上的
toString
方法。util。日期自动应用JVM的当前默认时区。这使得时区调整变得棘手。避免使用java的众多原因之一。util。日期/。日历和;JAVA文本SimpleDataFormat类使用更好的日期时间库
使用Java8及更高版本中的新java.time package(Tutorial)或Joda-Time库(它启发了Java.time)
乔达时间
下面是Joda-Time中的一些示例代码
根据作者的评论,传入字符串隐式表示某个已知时区的日期时间值。这个时区没有说明,所以我会随意使用巴拿马时区。在第一部分中,我们解析一个字符串,同时指定解析期间要使用的时区,并将其分配给结果对象
现在让我们调整到UTC。这是演示用的。在实际代码中,您通常会使用此UTC值进行任何进一步的工作
对于输出,我们的用户/消费者希望字符串表示与输入在同一时区,格式相同
跑步的时候
# 3 楼答案
对于问题#1:不同时区的时区偏移量可能相同,但可能使用或不使用DST,这会导致差异
关于问题#2:
对于未来,您只能在使用UTC时保持安全。(如果你的时间数据是“最近的”,你可以解决这个问题——见下文)
在过去,你无法可靠地提取正确的时间
一般转换建议:
我在一个JDBC驱动程序中处理时区和DST的项目中工作。存储时间值和正确读取时间值时出现问题。我工作/非常努力/试图正确转换,这样我们就可以省去切换到UTC的更大工作。没有UTC,就没有正确的转换。(/real hard/:想想低俗小说,朱尔斯说“我正努力成为牧羊人。”:-)
问题#2/未来:
如果您的客户无法发送UTC时间(可能是因为它是第三方系统):
当服务器从客户机接收到时间数据(非UTC)时,您知道这些数据在几分钟内是最新的(可能更长),您可以尝试使用UTC时间,并将其与客户机的时间相匹配。假设你的客户发送“2015-06-01 15:45”,你知道,现在是“2015-06-01 18:51 UTC”,那么你可以将客户的时间解释为“2015-06-01 18:45 UTC”。如果客户端发送的时间数据可能超过一小时,在某些情况下会失败
或者换句话说:假设你的客户记录温度值。如果客户端发送的数据不超过几分钟,则可以将其与UTC时间匹配。如果你的客户记录了一天的温度,并在一天结束时发送给你,你就无法正确匹配时间
为什么你不能完全(!)正确的转换强>
假设DST变化的夜晚,时钟从03:00变回02:00。切换前有一次02:30,切换后有一次02:30。第一个02:30比第二个02:30有另一个UTC时间。所以有了UTC,你就没事了。但只有在02:30的“本地客户”节目中,你永远不会确定
回到客户机数据时代:如果您的客户机在02:30发送的数据不超过几分钟,然后在第二个02:30发送另一个数据,您可以在服务器上区分这一点。如果在04:00获得了02:30的两条记录,则无法再恢复UTC
问题#2/过去:
能否在数据库中添加一个标志,以便将传输为UTC的新时间标记为“可靠”,而不标记旧值
输出和源:
在我的系统上运行修改后的源代码的输出,其TZ为“Europe/Berlin”。请注意,这台机器正在使用DST,但第一台取回的TZ(“阿尔及尔”)没有使用DST
源代码: