Glo*_*del 1 java datetime date jdbc language-lawyer
我有一个程序,我将两个日期相互比较; 即使date1以前date2,也date1.after(date2)回来了true.时区没有效果; 这两个日期都是UTC.
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Test {
public static void main(String[] args) throws Exception {
TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date date1 = dateFormat.parse("2018-07-27 01:22:14.077");
Date date2 = new Timestamp(1532654534390l);
System.out.println(dateFormat.format(date1));
System.out.println(dateFormat.format(date2));
System.out.println(date1.getTime() > date2.getTime());
System.out.println(date1.after(date2));
}
}
Run Code Online (Sandbox Code Playgroud)
这输出:
2018-07-27 01:22:14.077
2018-07-27 01:22:14.390
false
true
这里发生了什么?
在我的真实程序中,date1从日志文件中解析date2并由Hibernate从数据库中检索,这会导致不同的数据类型.即使我找到了根本原因并知道如何避免这个问题,我仍然对防止这个陷阱的解决方案非常感兴趣.
这里潜在的"问题"是java.sql.Timestamp,在扩展时java.util.Date,不会在指定字段中存储毫秒(fastTime相当于Unix时间),而是在单独的字段中nanos.该after方法仅考虑该fastTime字段(这是有意义的,因为它可以在所有Date对象上使用).
会发生什么事在这种情况下是,fastTime的Timestamp是四舍五入由1532654534 390至1532654534 000,这是比1532654534下077的其他日期(以及更低意味着一个较早的日期).因此,after()而before()不是在这种情况下,可靠; 解决方案是在两个日期使用getTime()(重载Timestamp以提供正确的值)并比较它们.
使用现代java.time类,从来没有可怕的遗留日期时间类.
Instant
.ofEpochMilli( 1_532_654_534_390L )
.isAfter(
LocalDateTime
.parse(
"2018-07-27 01:22:14.077"
.replace( " " , "T" )
)
.atOffset(
ZoneOffset.UTC
)
.toInstant()
)
Run Code Online (Sandbox Code Playgroud)
Timestamp对象Date你的代码:
Date date2 = new Timestamp… // Violates class documentation.
Run Code Online (Sandbox Code Playgroud)
......违反了课程文件中规定的合同.
由于时间戳类和上述java.util.Date类之间的差异,建议代码一般不要时间戳值作为java.util.Date的实例.Timestamp和java.util.Date之间的继承关系实际上表示实现继承,而不是类型继承.
该文档指出,虽然java.sql.Timestamp从技术上继承java.util.Date,但您被指示忽略继承的事实.你不要将Timestamp对象用作Date.您的代码完全按照文档告诉您不要执行的操作.
当然,这种假装 - 它不是一个子类策略是一个非常糟糕的类设计.这个hack是从不使用这些类的众多原因之一.
您看到的行为,关于毫秒分秒和纳秒的不匹配,记录在案:
注意:此类型是java.util.Date和单独的纳秒值的组合.只有整数秒存储在java.util.Date组件中.分数秒 - 纳米 - 是分开的.传递一个不是java.sql.Timestamp实例的对象时,Timestamp.equals(Object)方法永远不会返回true,因为日期的nanos组件是未知的.因此,Timestamp.equals(Object)方法与java.util.Date.equals(Object)方法不对称.此外,hashCode方法使用底层的java.util.Date实现,因此在其计算中不包括nanos.
你正在使用臭名昭着的糟糕课程.你发现的问题是由于他们糟糕的黑客攻击设计糟糕.不要费心去理解这些课程; 完全避开它们.
几年前java.time类取代了这些遗留类.
解析输入字符串.
LocalDateTime ldt = LocalDateTime.parse( "2018-07-27 01:22:14.077".replace( " " , "T" ) ; // Without a time zone or offset, this value has no specific meaning, is *not* a point on the timeline.
Run Code Online (Sandbox Code Playgroud)
显然,您知道输入字符串隐式表示UTC中的时刻.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ; // Assign an offset-from-UTC to give the date and time a meaning as an actual point on the timeline.
Run Code Online (Sandbox Code Playgroud)
解析其他输入,显然是自UTC 1970年第一个时刻以来的毫秒数.该Instant班是不是更基本的OffsetDateTime,在UTC片刻,总是定义UTC.
Instant instant = Instant.ofEpochMilli( 1_532_654_534_390L ) ; // Translate a count of milliseconds from 1970-01-01T00:00:00Z into a moment on the timeline in UTC.
Run Code Online (Sandbox Code Playgroud)
相比.
Boolean stringIsAfterLong = odt.toInstant().isAfter( instant ) ;
Run Code Online (Sandbox Code Playgroud)
该java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.
现在处于维护模式的Joda-Time项目建议迁移到java.time类.
要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.
您可以直接与数据库交换java.time对象.使用符合JDBC 4.2或更高版本的JDBC驱动程序.不需要字符串,不需要课程.java.sql.*
从哪里获取java.time类?
该ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.
| 归档时间: |
|
| 查看次数: |
140 次 |
| 最近记录: |