当前位置: 首页 > 知识库问答 >
问题:

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

终洛华
2023-03-14

我从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””;

  rs1 = conn.createStatement().executeQuery(sql1);

  if (rs1.next()) 
        {
  Timestamp attempt_datetime = rs1.getTimestamp("date_time");
        }

我正在得到的结果

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

共有3个答案

韩弘壮
2023-03-14

https://www.postgresql.org/docs/current/static/datatype-datetime.html

对于带时区的时间戳,内部存储的值总是以UTC(通用协调时间,传统上称为格林尼治标准时间,GMT)为单位。指定了明确时区的输入值将使用该时区的适当偏移量转换为UTC。如果输入字符串中没有说明时区,则假定它位于系统的TimeZone参数指示的时区,并使用时区的偏移量转换为UTC。

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

检查客户的时区:

select setting from pg_settings where name = 'TimeZone';

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

屠和洽
2023-03-14

你没有给我们看你所有的代码,所以我们不能肯定地回答。但是经过一些推断,我有一个理论,三个偏移的理论。

以比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

笨拙的报告→ <代码>2016-12-12 23:36:39.582

感谢Laurenz Albe解决核心问题的答案;我只是在这里添加了一段冗长的叙述。

几个术语:

  • 从-UTC偏移是UTC的小时数、分钟数和秒数,例如05:00
  • 时区是一个偏移量,加上一组处理异常的规则,如夏令时(DST)。正确地命名为大陆/地区,如美国/蒙特利尔

您输入的2016-12-12 10:06:39.582-08代表了比UTC晚八小时的时刻,在北美西海岸的大部分地区都有使用。

您将该值提交给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-082016-12-12 18:06:39.582Z都是非常相同的时刻,但通过两个不同的墙上时钟时间值的镜头看到。到目前为止,一切都是合理和明智的。

后来,您从Postgres数据库中检索了2016-12-12 18:06:39.582Z值。显然,您通过JDBC将数据提取到一个java.sql.Timestamp对象中。这就是问题所在。这个类是与Java最早版本捆绑在一起的旧日期-时间类之一。虽然作为日期-时间处理的行业领先尝试值得称赞,但这些类被证明是令人困惑、麻烦和有缺陷的。尽可能避免它们。它们现在是遗产,被java.time类取代。

这些遗留的日期-时间类的许多问题之一是它们的toString方法的实现。在隐式应用JVM的当前时区时,这些toString方法过于渴望取悦。这些给这么多Java程序员带来了无尽的困惑。添加这个问题作为这种困惑的另一个例子。

首先,让我们演示时区的隐式应用。首先在UTC中获取当前时刻,作为Instant

Instant instant = Instant.now ();

查看JVM当前的默认时区及其偏移量。

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查看其toString方法的行为。

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 );

瞬间toString():2016-12-13T23:06:22.635Z
z.toString():美国/洛杉矶
偏移。toString():-08:00
t.toString():2016-12-13 15:06:22.635

从这个输出中,我们可以看到,时间戳确实将我自己当前默认的-08:00偏移量应用于UTC的内部值。更糟糕的是,该方法的输出忽略了其偏移量的任何指示,这让我们假设它是UTC,而实际上不是。更令人沮丧的是:这个“功能”完全没有文档记录!

现在我们已经看到了这个反功能的行为,我们可以回到问题中的示例数据。显然,结果数据是2016-12-12 23:36:39.582,没有任何偏移指示,正如我们刚才提到的。但是看看一天中的时间,23:36:39.582与我们可能预期的UTC时间,18:06:39.582。意外值比预期UTC值提前五个半小时,或05:30。印度和斯里兰卡在Asia/Kolkata等地区碰巧使用了05:30的偏移量。因此我们可以推断,这个问题的作者在JVM上运行她的代码,当前默认时区为Asia/columboAsia/Kolkata偏移量为05:30

避免遗留的日期时间类。

使用java。改为上时间课。爪哇。时间类都有合理的toString方法,可以明智地使用标准ISO 8601文本格式,而不会出现任何意外的额外行为。

您的JDBC 4.2兼容驱动程序可以通过调用准备声明::setObjectResultSet::getObject来直接处理java.time类型。

myPreparedStatement.setObject( … , instant ) ;

…还有…

Instant instant = myResultSet.getObject( … ) ;

如果没有,回到使用java.sql类型,但尽可能简短。使用添加到旧类中的新转换方法。

myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;

…还有…

Instant instant = myResultSet.getTimestamp( … ).toInstant() ;

java.time框架构建在Java8和更高版本中。这些类取代了麻烦的旧的遗留日期-时间类,如java.util.Date日历

Joda Time项目现在处于维护模式,建议迁移到java。时间课。

要了解更多信息,请参阅Oracle教程。并在Stack Overflow中搜索许多示例和解释。规范是JSR 310。

在哪里可以获得java。时间课?

  • Java SE 8和SE 9及更高版本

Three10-Extra项目通过附加类扩展java.time。该项目是未来可能添加到java.time.的试验场。您可以在这里找到一些有用的类,如IntervalYearZacksYearQuter等。

郎仰岳
2023-03-14

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

您可以在这样的Java更改您的时区:

java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("Europe/Vienna"));
 类似资料:
  • 问题内容: 全部, 我有一个带有称为时间戳记的列的MYSQL表。它的数据类型为“ 10/1/2009 3:25:08 PM”,“ 10/1/2009 3:30:05 PM”,“ 10/4/2009 3:40:01 PM”,等等.. 我想编写一个SQL查询来选择在两个日期之间出现的时间戳字段中的所有值。 userInput日期将没有时间部分。您能为此建议正确的MySQL查询语法吗?谢谢 问题答案:

  • 返回我女巫是错误的。 但接下来的查询如下: 我看对日期

  • 我需要将查询中的日期值转换为带有时区的时间戳,但目前我得到的时区区域(“欧洲/巴黎”)是EF使用的无效区域。 例如,在执行此操作时:

  • 我正在尝试将日期插入我的PostgreSQL数据库。以下是我在java应用程序中解析日期的方式: 在我的PostgreSQL数据库中。我将其作为带有时区的。处理和插入它的理想方式是什么? 数据库期望的示例。

  • 我将Datagrip用于PostgreSQL。我有一个带有时间戳格式的日期字段的表。我希望能够: 应用数学运算符减去1天 根据当前-130天的时间窗口对其进行筛选 不显示邮票的HH/MM/SS部分(2016-10-31) 当前开始查询: 第1行的子句导致: [42883]错误:运算符不存在:没有时区的时间戳-整数提示:没有与给定名称和参数类型匹配的运算符。您可能需要添加显式类型转换。职位:69 子