使用夏令时分配java.sql.Timestamp

时间:2013-02-18 13:57:21

标签: java sql timestamp dst

我无法理解java.sql.timestamp

如果我运行java弃用的构造函数:

java.sql.Timestamp(106,2,26,1,0,0,0)
java.sql.Timestamp(106,2,26,2,0,0,0)
java.sql.Timestamp(106,2,26,3,0,0,0)   //<-- Separated by one hour

我明白了:

2006-03-26 01:00:00.0
2006-03-26 03:00:00.0     
2006-03-26 03:00:00.0   //<--These last two are the same

在这些时间周围(至少在我的国家)发生夏令时。但是时间之前和之后的日期都没有移动。为什么两个单独的小时同时返回?

我想把时间戳作为我的输入,我该怎么强迫这个呢?

3 个答案:

答案 0 :(得分:1)

请勿使用此不推荐使用的构造函数,该构造函数已被弃用,因为它使用默认时区。

使用具有适当时区(CET)的日历(或DateFormat),设置日历的字段(或解析包含要插入的日期的字符串),从日历/日期获取毫秒,以及从毫秒开始构造一个时间戳。

答案 1 :(得分:0)

使用System.currentTimeMillis();可以获得格林威治标准时间,该时间不受夏令时,闰秒和其他对日期的意外调整的影响。

long now = System.currentTimeMillis();

或手动指定时区:

long ms = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTimeInMillis();

来源:Get GMT Time in Java

答案 2 :(得分:0)

tl; dr

夏令时(DST)转换意味着凌晨2点不存在。因此,您输入的内容无效。

ZonedDateTime类尝试通过调整您的时间输入来帮助您,该时间跳到凌晨3点,就像凌晨02:00时时钟比早晨跳一个小时。

ZonedDateTime
.of( 2006 , 3 , 26 , 2 , 0 , 0 , 0 , ZoneId.of( "Africa/Tunis" ) )
.toString()
  

2006-03-26T03:00 + 02:00 [非洲/突尼斯]

避免使用旧的日期时间类

java.sql.Timestamp是一个可怕的类,以及其同级类,例如java.util.DateCalendar / GregorianCalendar。在其许多设计问题中,是时区的混乱处理。

相反,仅使用现代的 java.time 类。

java.time

对于时刻(日期,日期,时区或UTC偏移量),请使用以下类别之一:

  • Instant —时刻在UTC。
  • OffsetDateTime-与UTC偏移(小时-分钟-秒的数量)但时区未知的时刻
  • ZonedDateTime-具有指定时区的时刻。

听起来您打算在自己的区域中使用日期时间。

ZoneId

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;  

如果要使用JVM的当前默认时区,请提出要求并作为参数传递。如果省略,代码将变得难以理解,因为我们不确定您是否打算使用默认值,还是像许多程序员一样不知道该问题。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

ZonedDateTime

指定一个时间和日期,如特定地区(时区)的人们使用的挂钟时间所示。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt_1 = ZonedDateTime.of( 2006, 3 , 26 , 1 , 0 , 0 , 0 , z ) ;
ZonedDateTime zdt_2 = ZonedDateTime.of( 2006, 3 , 26 , 2 , 0 , 0 , 0 , z ) ;
ZonedDateTime zdt_3 = ZonedDateTime.of( 2006, 3 , 26 , 3 , 0 , 0 , 0 , z ) ;

System.out.println("zdt_1.toString(): " + zdt_1);
System.out.println("zdt_2.toString(): " + zdt_2);
System.out.println("zdt_3.toString(): " + zdt_3);
  

zdt_1.toString():2006-03-26T01:00 + 01:00 [非洲/突尼斯]

     

zdt_2.toString():2006-03-26T03:00 + 02:00 [非洲/突尼斯]

     

zdt_3.toString():2006-03-26T03:00 + 02:00 [非洲/突尼斯]

夏令时(DST)

我们在第三项上看到了一个惊喜,我们要求2 AM,但要3 AM。这是可以理解的,因为有Daylight Saving Time (DST)转换或“春天来临”。从2005年开始,Tunisia adopted DST。在2009年下半年,他们恢复了意识,只回到标准时间。

凌晨2点不存在

因此,突尼斯当天没有凌晨2点。当时钟敲到凌晨2点时,它跳到了凌晨3点。 凌晨2点钟根本不存在。该天只有23小时,而不是通常的24小时。因此,我们上面的代码要求输入无效的日期时间。 ZonedDateTime类而不是抛出Exception,而是尝试通过调整为有效的时间来帮助我们。 JavaDoc for ZonedDateTime.of对此进行了说明:

  

从年,月,日,小时,分钟,秒,纳秒和时区获取ZonedDateTime的实例。

     

这将创建一个与七个指定字段的本地日期时间尽可能匹配的分区日期时间。夏令时等时区规则意味着,并非每个本地日期时间都对指定时区有效,因此可以调整本地日期时间。

     

本地日期时间在时间​​轴上解析为单个时刻。这是通过根据区域ID规则定义的本地日期时间找到UTC /格林威治标准时间的有效偏移量来实现的。

     

在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当时钟调回时,有两个有效的偏移量。此方法使用通常对应于“夏季”的更早偏移量。

     

在间隙的情况下,当时钟向前跳时,没有有效的偏移量。取而代之的是,将本地日期时间调整为晚一些间隔时间。对于典型的一小时夏令时更改,本地日期时间将在一小时后移入通常对应于“夏季”的偏移量中。

所以行为是功能,而不是错误。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore