您有多个问题交织在一起。
您不应该从数据库中读取日期时间值的字符串,而应该读取日期时间对象。 StackOverflow 上有很多关于从/向数据库读取/写入日期时间值的问题,因此无需在此重复。
如果您确实有一个字符串,例如“2015-03-08 02:00:00”,请注意缺少任何时区或偏移量指示符。如果您想假设该字符串表示特定于美国中部时间的时间,那么您必须接受这样一个事实,即没有这样的日期时间,因为夏令时 (DST) 将其定义为凌晨 3 点。在凌晨 2 点的行程中,时间标记跳到凌晨 2 点。因此,尝试获取这样一个不存在的日期时间是没有意义的。
使用正确的时区名称
日期时间工作的重要提示:避免将时区视为“中部时间”和“CST”等 3-4 个字母代码。这些都不是标准化的,也不是唯一的(许多重复),并且进一步混淆了夏令时的混乱。使用proper time zone,以“continent/majorCityOrRegion”的模式。
本地日期时间
也许您的意思是我们所说的“本地时间”,其中日期时间并不特定于任何一个时区。例如,“圣诞节从 2015 年 12 月 25 日午夜开始”。这意味着每个特定时区的不同时刻。例如,巴黎的圣诞节比蒙特利尔早。
乔达时间
让我们在 Joda-Time 中将该字符串解释为 LocalDateTime。首先,为方便起见,我们将 SPACE 替换为“T”,以利用 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
…
似乎没有好的通用解决方法,只有 hacks。基本上,如果您期望某个时区,您可以自己进行调整。请参阅 this、this、this 和 the Joda-Time FAQ 等讨论。
java.time
在 Java 8 及更高版本中,我们在 java.time package (Tutorial) 中有新的内置日期时间框架。该框架受到 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,您可以直接将 LocalDateTime 或 ZonedDateTime 提供给您的 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 );