微秒
您的问题文本引用输入字符串 as2016-05-14 12:54:01.363
但您的错误消息另有说明,2016-05-14 12:54:01.363000
. 那些额外的三位数字是微秒。
诸如 java.util.Date 之类的旧日期时间类仅解析为毫秒(小数点后 3 位表示秒的分数)。所以SimpleDateFormat
不能处理这样的输入。截断输入字符串,或者更好地使用现代日期时间类。
java.time
更大的问题是您使用的是 Java 最早版本中过时的旧日期时间类。这些类已被Java 8 及更高版本中内置的java.time框架所取代。ThreeTen- Backport项目已将大部分 java.time 功能向后移植到 Java 6 和 7,并由ThreeTenABP进一步适应 Android 。因此,没有理由与那些陈旧且臭名昭著的麻烦课程搏斗。
java.time 类具有更精细的粒度,可解析为纳秒(秒的 9 位小数)。
在 java.time 中,Instant
该类是 UTC 时间线上的一个时刻,分辨率为纳秒。AnInstant
等效于java.util.Date
具有更精细分辨率的除外。
有关在旧类型和 java.time 类型之间进行转换的更多信息,请参阅我对另一个问题的带图表的回答。
检索日期时间值作为日期时间类型
您应该从数据库中检索日期时间值作为日期时间类型,而不是字符串。如果您的 JDBC 驱动程序符合 JDBC 4.2(对JSR 221的更新,并在此处描述),您可以直接检索到OffsetDateTime
. 您可以将 anOffsetDateTime
视为与 UTCInstant
的偏移(数字小时、分钟和秒)。
java.time.OffsetDateTime odt = resultSet.getObject( 1 );
如果没有,请回退到使用旧的java.sql类型。对于日期时间,这意味着java.sql.Timestamp
.
java.sql.Timestamp ts = resultSet.getTimestamp( 1 );
尽量减少对旧 java.sql 类型的使用,因为它们是 java.util.Date/.Calendar 等混乱的一部分。立即转换为 java.time。要进行转换,请查找添加到旧类的新方法,例如toInstant
.
Instant instant = ts.toInstant();
解析
如果您必须解析该输入字符串,最简单的方法是使其符合ISO 8601标准格式。java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。
为了遵守,将中间的空格替换为T
. 并且,假设这个字符串确实代表 UTC 中的一个时刻,附加一个Z
. The是UTC的Z
缩写。Zulu
String input = "2016-05-14 12:54:01.363000";
String inputModified = input.replace( " " , "T" );
Instant instant = Instant.parse( inputModified );
时区
该问题忽略了时区的关键问题。
最佳实践是以 UTC 格式存储日期时间值。更好的数据库会将传入的数据调整为 UTC。当作为 a 检索时,java.sql.Timestamp
您的值是 UTC,转换为Instant
.
日期和时间取决于时区。将时区 ( ZoneId
) 应用于您Instant
的ZonedDateTime
.
ZoneId zoneId = zoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
然后询问以获得您需要的月份日期和时间。
int dayOfMonth = zdt.getDayOfMonth();
如果您希望将时间作为自己的对象,请使用LocalTime
该类。
LocalTime localTime = zdt.toLocalTime();
如果您真的想要时间字符串,请使用java.time.format包。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "hh:mm a" );
String output = zdt.format( formatter );
更好的是,让 java.time 完成自动格式化的工作。请注意语言环境的使用,它决定了 (a) 人类语言的月份名称等,以及 (b) 文化规范,例如年-月-日等元素的顺序。显式指定区域设置比隐式依赖 JVM 的当前默认区域设置更好,后者可以在运行时更改。
DateTimeFormatter timeOfDayFormatter = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT );
timeOfDayFormatter = timeOfDayFormatter.withLocale( Locale.CANADA_FRENCH );
String output = zdt.toLocalTime().format( timeOfDayFormatter );