存储"假的"时间戳到数据库中

时间:2015-07-07 20:53:56

标签: java date calendar timezone jodatime

以下是我要解决的问题:从数据库A读取字符串,将字符串转换为Date对象,将Date对象存储到数据库B中。

EX)数据库A:读入日期字符串" 2015-03-08 02:00:00"从数据库A转换为Date对象,存储回数据库B.

此处出现的问题是因为凌晨2点是美国中部时间夏令时的开始,因此数据对象将凌晨2点直接转换为凌晨3点,这意味着凌晨3点将存储到数据库B

有没有办法纠正这个?如果有必要,我不反对使用Joda Time。

我正在努力关注上述日期,2015-03-08 02:00:00

这是我正在使用的代码:

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    String date = "2015-03-08 02:00:00.0";



    try 
    {
        d = sdf.parse(date);
        sdf.format(d);

        //Insert into database here
        // ---
        //
    } 
    catch (ParseException e) 
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

2 个答案:

答案 0 :(得分:1)

你有多个问题交织在一起。

您不应该从数据库中读取日期时间值的字符串,您应该阅读日期时间对象。 StackOverflow上有关于从/向数据库读取/写入日期时间值的许多问题,因此无需在此重复。

如果您有字符串,例如“2015-03-08 02:00:00”,请注意缺少时区或偏移的任何指示。如果您想假设该字符串表示美国中部时间特定的时间,那么您必须接受没有这样的日期时间这一事实,因为夏令时(DST)将其定义为凌晨3点。在凌晨2点,时间标记跳到凌晨2点。因此,试图获得这样一个不存在的日期时间是没有意义的。

使用适当的时区名称

日期工作的重要提示:避免将时区视为“中央时间”和3-4个字母代码,如“CST”。这些不是标准化的,也不是唯一的(许多重复的),并且进一步混淆了夏令时的混乱。以“continent / majorCityOrRegion”模式使用proper time zone

本地日期时间

也许你的意思是我们称之为“当地时间”,其中日期时间并非特定于任何一个时区。例如,“圣诞节从2015年12月25日午夜开始”。这意味着每个特定时区的不同时刻。例如,圣诞节早在巴黎就比蒙特利尔晚了。

约达时间

让我们在Joda-Time中将该字符串解释为LocalDateTime。首先,为方便起见,我们用“T”替换SPACE,以利用Joda-Time的ISO 8601格式的内置解析器。

String input = "2015-03-08 02:00:00";
String inputStandardized = input.replace( " ", "T" );  // For convenience, convert input text to comply with ISO 8601 standard’s canonical format. Replace SPACE between date & time portions with "T".

接下来我们解析该标准化字符串。

LocalDateTime localDateTime = LocalDateTime.parse( inputStandardized );

转储到控制台。

System.out.println( "inputStandardized: " + inputStandardized );
System.out.println( "localDateTime: " + localDateTime );

跑步时。

inputStandardized: 2015-03-08T02:00:00
localDateTime: 2015-03-08T02:00:00.000

可以使用SQL type TIMESTAMP WITHOUT TIME ZONE将本地日期时间存储在SQL数据库中。此类型表示不会在获取(SELECT)或放入(INSERT / UPDATE)数据库值中对UTC时区进行调整。有关这些SQL类型的详细信息,请参阅Postgres doc

分区日期时间

如果您想表示特定时区(例如America/Chicago)中的特定时刻,则需要指定该时区。对于此类特定于时区的值,您可以在数据库中使用数据类型TIMESTAMP WITH TIME ZONE。该类型名称具有误导性 - 它意味着尊重时区,因为它输入数据调整为UTC。然后,数据的原始时区将丢失。

不幸的是,这是Joda-Time让我们失望的少数情况之一。而不是做一个调整,Joda-Time拒绝,抛出异常。 ☹

亲自看看......让我们将以下代码添加到上面的示例代码中。

DateTimeZone zone = DateTimeZone.forID( "America/Chicago" );
DateTime dateTimeChicago = localDateTime.toDateTime( zone ); // If the input lacks an offset, then Joda-Time *assigns* the value the specified time zone. If the input has an offset, Joda-Time *adjusts* the value to the specified zone.

转储到控制台。

System.out.println( "zone: " + zone );
System.out.println( "dateTime: " + dateTimeChicago );

跑步时。

Exception in thread "main" org.joda.time.IllegalInstantException: Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-08T02:00:00.000 (America/Chicago
…

似乎没有好的通用解决方法,只是黑客攻击。基本上,如果您期望某个时区,您可以自己进行调整。查看thisthisthisthe Joda-Time FAQ等讨论。

java.time

在Java 8及更高版本中,我们在java.time packageTutorial)中有了新的内置日期时间框架。这个框架的灵感来自于Joda-Time,并且比Joda-Time有一些优势。其中一个优点是处理这个DST不存在的价值问题。

String input = "2015-03-08 02:00:00";
String inputStandardized = input.replace( " ", "T" );  

LocalDateTime localDateTime = LocalDateTime.parse( inputStandardized );

让我们调整本地日期时间以指定特定时区。 java.time框架检测不存在的日期时间并自动滑动时间向前以尊重DST转换。

ZoneId zone = ZoneId.of( "America/Chicago" );
ZonedDateTime zdt = ZonedDateTime.of( localDateTime, zone );

转储到控制台。

System.out.println("inputStandardized: " + inputStandardized );
System.out.println("localDateTime: " + localDateTime );
System.out.println("zone: " + zone );
System.out.println("zdt: " + zdt );

跑步时。

inputStandardized: 2015-03-08T02:00:00
localDateTime: 2015-03-08T02:00
zone: America/Chicago
zdt: 2015-03-08T03:00-05:00[America/Chicago]

SQL

如上所述,您可以搜索StackOveflow以获取有关获取和退出数据库的日期时间的大量信息。

理想情况下,使用java.time,您可以直接将LocalDateTimeZonedDateTime提供给JDBC驱动程序。但是大多数驱动程序尚未更新以处理java.time类型。在您的驱动程序更新之前,请回到java.sql.* classes。可以在与Java捆绑的新旧类中找到方便的转换方法。

java.sql.Timestamp ts = java.sql.Timestamp.valueOf( localDateTime );

...或...

Instant instant = zdt.toInstant();
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );

答案 1 :(得分:0)

通常最好将日期存储为自纪元以来的毫秒数。这样,您可以使用long来存储数据库中的数字,当您需要格式化日期时,您可以使用Joda Time的DateTime(long)构造函数,也可以只使用内置的Date(long)构造函数。