Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/398.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从postgres检索带有时区的时间戳后,日期增加1。为什么?_Java_Postgresql_Date_Timestamp - Fatal编程技术网

Java 从postgres检索带有时区的时间戳后,日期增加1。为什么?

Java 从postgres检索带有时区的时间戳后,日期增加1。为什么?,java,postgresql,date,timestamp,Java,Postgresql,Date,Timestamp,我从postgres获取带有时区值的时间戳,在java中,我将结果存储在timestamp变量中。但是日期和时间会有一些变化。我没有得到原始值 表格 CREATE TABLE log_fail ( user_name character varying(99) NOT NULL, date_time timestamp with time zone, CONSTRAINT pk_log_failed_login PRIMARY KEY (user_name) ) 表格数据 用户名=

我从postgres获取带有时区值的时间戳,在java中,我将结果存储在timestamp变量中。但是日期和时间会有一些变化。我没有得到原始值

表格

CREATE TABLE log_fail
(
  user_name character varying(99) NOT NULL,
  date_time timestamp with time zone,
  CONSTRAINT pk_log_failed_login PRIMARY KEY (user_name)
)
表格数据

用户名=超人

日期时间=2016-12-12 10:06:39.582-08

sql

字符串uname=“超人”

String sql1=“从日志中选择日期时间”登录失败,其中用户名=“+uname+”

我得到的结果

尝试日期时间=2016-12-12 23:36:39.582

对于带有时区的时间戳,内部存储的值始终为 UTC(世界协调时间,传统上称为格林威治标准时间) 时间(格林尼治标准时间)。指定了具有显式时区的输入值 使用该时区的适当偏移量转换为UTC。如果 输入字符串中未说明时区,则假定为 在系统时区参数指示的时区中,并且 使用时区的偏移量转换为UTC

当输出带有时区值的时间戳时,它总是 从UTC转换为当前时区,并显示为 该区域的当地时间

检查客户端的时区:

select setting from pg_settings where name = 'TimeZone';
这将让您了解为什么不同客户的时间不同

对于带有时区的时间戳,内部存储的值始终为 UTC(世界协调时间,传统上称为格林威治标准时间) 时间(格林尼治标准时间)。指定了具有显式时区的输入值 使用该时区的适当偏移量转换为UTC。如果 输入字符串中未说明时区,则假定为 在系统时区参数指示的时区中,并且 使用时区的偏移量转换为UTC

当输出带有时区值的时间戳时,它总是 从UTC转换为当前时区,并显示为 该区域的当地时间

检查客户端的时区:

select setting from pg_settings where name = 'TimeZone';

这将给您一个想法,为什么不同客户端的时间不同

问题是Java代码运行的机器上的时区是
Asia/Calcutta
,时间戳在转换为字符串时会转换为该时区

您可以按如下方式在Java中更改时区:

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone(“欧洲/维也纳”);

问题在于,运行Java代码的机器上的时区是
Asia/Calcutta
,时间戳转换为字符串时会转换为该时区

您可以按如下方式在Java中更改时区:

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone(“欧洲/维也纳”);
三个偏移 您没有向我们展示所有代码,因此我们无法肯定地回答。但是通过一些推论,我有一个理论,一个三个偏移的理论

以比UTC晚八小时的值开始→ <代码>2016-12-12 10:06:39.582-08

调整到UTC→ <代码>2016-12-12 18:06:39.582Z

调整到偏移量
+05:30
→ <代码>2016-12-12 23:36:39.582+05:30

笨拙地报告SAN偏移量→ <代码>2016-12-12 23:36:39.582

感谢您解决了核心问题;我只是在这里添加了一个冗长的叙述

几个术语:

  • 是从UTC算起的小时数和分秒数,例如
    +05:00
  • 时区是一个偏移量加上一组处理异常的规则,如夏令时(DST)。正确命名为
    大陆/地区
    ,如
    美洲/蒙特利尔
您的输入
2016-12-12 10:06:39.582-08
代表了比UTC晚8小时的时刻,在北美西海岸的大部分地区都有使用

您已将该值提交给Postgres存储。您说的不准确,但我假设表中的列是带时区的时间戳类型

在Postgres中,这种类型的意思是:“对于任何伴随的偏移量/区域信息,将传入值调整为UTC”。Postgres然后放弃附带的偏移量/区域信息。我重复一遍,Postgres不存储或记住偏移量/区域,不存储在任何日期时间类型中。带类型的
与不带时区的
时间戳
之间的区别在于,在不带
类型的
中,将完全忽略任何附带的偏移量/区域信息,不进行任何调整

因此,当您向Postgres提交
2016-12-12 10:06:39.582-08
时,该值自动调整为
2016-12-12 18:06:39.582Z
。这里的
Z
是祖鲁语的缩写,意思是UTC。另外,Postgres并没有按字面意思存储这些字符串,而是使用它自己的内部二进制存储格式。这些字符串是供我们人类在讨论中阅读的

无论如何,回到UTC的调整。请注意时间是如何从
10
小时更改为
18
小时的,因为从UTC后8小时调整为UTC意味着增加8小时

很好,存储这个值的工作已经完成了。我们在时间上记录了完全相同的时刻,
2016-12-12 10:06:39.582-08
2016-12-12 18:06:39.582Z
都是非常相同的时刻,但通过两个不同值的镜头观察。到目前为止,一切都是合理和明智的

之后,您从Postgres数据库检索到
2016-12-12 18:06:39.582Z
值。显然,您通过JDBC将数据提取到
java.sql.Timestamp
对象中。这就是问题所在。这个类是与最早版本的Java捆绑在一起的旧日期时间类之一。虽然在约会时间处理方面是业界领先的尝试,但这些
Instant instant = Instant.now ();
ZoneId z = ZoneId.systemDefault ();  // Get current default time zone of this JVM.
String offset = z.getRules ().getOffset ( instant ).toString ();  // Extract the offset-from-UTC from our default time zone.
java.sql.Timestamp ts = new java.sql.Timestamp ( instant.toEpochMilli () );
System.out.println ( "instant.toString(): " + instant );
System.out.println ( "z.toString(): " + z );
System.out.println ( "offset.toString(): " + offset );
System.out.println ( "ts.toString(): " + ts );
myPreparedStatement.setObject( … , instant ) ;
Instant instant = myResultSet.getObject( … ) ;
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
Instant instant = myResultSet.getTimestamp( … ).toInstant() ;