你可能不会注意的Timestamp-创新互联

提起java里面的时间戳,相信很多人都用过。
不就是java.sql.Timestamp类,两个构造器,13个方法,这也许属于java中最简单的基础类了。

我们提供的服务有:成都网站制作、成都做网站、微信公众号开发、网站优化、网站认证、乐至ssl等。为近千家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的乐至网站制作公司

俗话说淹死的都是会游泳的,同样在开发中让我们栽跟头的往往都是耳熟能详的一些类库。

引子

首先让我们看看以下几行代码的输出结果吧


Timestamp t1 = new Timestamp(0);
System.out.println(t1);
Timestamp t2 = Timestamp.valueOf("2037-12-31 23:59:59");
System.out.println(t2.getTime());
System.out.println(t2.getTime() == new Long(Integer.MAX_VALUE) * 1000);
Timestamp t3 = new Timestamp(new Long(Integer.MAX_VALUE) * 1000);
System.out.println(t3);Timestamp t21 = Timestamp.valueOf("9999-12-31 23:59:59");
System.out.println(t21.getTime());
Timestamp t22 = new Timestamp(t21.getTime());
System.out.println(t22);Date d1 = new Date(0);
System.out.println(d1.equals(t1));
System.out.println(t1.equals(d1));

如果你能很明确的说出每一个输出结果是什么以及为什么,那么你已经不需要继续往下看了。
如果你对一个或者更多的结果感到迷茫,就让我们来一起踩踩坑吧。


时间范围

要搞清楚Timestamp类的范围方法有很多,可以看文档、看源代码,写个代码测试一下或者搜索引擎看看别人怎么说。
当我们实践了上面的一种或者多种方法后我们再回过头来看看我们引子里的第一段和第二段代码的输出结果。


Timestamp t1 = new Timestamp(0);
System.out.println(t1);//1970-01-01 08:00:00.0

有没有看着很熟悉?跟格林尼治标准时(计算机用的比较多的说法是UTC)就差8个小时,那也很好理解我们是东八区嘛。
换句话说Timestamp类的开始时间可以认为是格林尼治标准时,在不同时区使用会加上时区的偏移量。


Timestamp t2 = Timestamp.valueOf("2037-12-31 23:59:59");
System.out.println(t2.getTime());//2145887999000
System.out.println(t2.getTime() == new Long(Integer.MAX_VALUE) * 1000);//false
Timestamp t3 = new Timestamp(new Long(Integer.MAX_VALUE) * 1000);
System.out.println(t3);//2038-01-19 11:14:07.0
Timestamp t21 = Timestamp.valueOf("9999-12-31 23:59:59");
System.out.println(t21.getTime());//253402271999000
Timestamp t22 = new Timestamp(t21.getTime());
System.out.println(t22);//9999-12-31 23:59:59.0

上面几段代码其实都是根据网上关于Timestamp的大值做出的验证

第一种 2037-12-31 23:59:59(t2)

可以发现获取出来的毫秒数和Integer.MAX_VALUE差不多就是1000倍的差距,
那不妨推断一下这种说法的依据是在32位系统中存储毫秒数,无溢出情况下的大月末或者年末。
接下来通过Integer.MAX_VALUE构造出来的时间(t3)是2038-01-19 11:14:07.0 也验证了我们的推断。

第二种 9999-12-31 23:59:59

这个时间是我们在目前时间格式下的大时间了,通过t21,t22的验证发现通过,Timestamp类是可以存储这种时间的。
再回过头去看源代码,发现用于存储毫秒数的是Long而不是Integer,64位的Long完全可以到9999年嘛。

通过上面的验证我们可以确认Timestamp类的大时间可以是9999-12-31 23:59:59

Tips:
实际使用的时候我们的数据需要存储到数据库。
以mysql为例,如果你想当然的将java的Timestamp直接就对应到数据库的Timestamp,你会发现两个数据类型范围并不一样。
摘抄一段mysql官方文档的说明:

The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in ‘YYYY-MM-DD’ format. The supported range is ‘1000-01-01’ to ‘9999-12-31’. The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in ‘YYYY-MM-DD’ format. The supported range is ‘1000-01-01’ to ‘9999-12-31’.
The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in ‘YYYY-MM-DD HH:MM:SS’ format. The supported range is ‘1000-01-01 00:00:00’ to ‘9999-12-31 23:59:59’.
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC.

在真实使用的时候将业务,java类库,数据库数据类型进行对照,尽可能的找出符合要求的组合。

Equals并不相等

用到了时间,当然不能不比较了,看看下面这段代码的输出吧


Date d1 = new Date(0);
System.out.println(d1.equals(t1));//true
System.out.println(t1.equals(d1));//false

很有意思的结果,我们先来看看他是怎么做到的吧,看源码>>
Date.equals:


public boolean equals(Object obj) {	return obj instanceof Date && getTime() == ((Date) obj).getTime();}

由于Timestamp是继承自Date,并且在强转后高精度部分丢失导致getTime完全一致,所以第一个比较返回了true

Timestamp.equals:


public boolean equals(java.lang.Object ts) {
	if (ts instanceof Timestamp) {
		return this.equals((Timestamp)ts);	
		} else {
			return false;	}
			}

判断Date并不是Timestamp类型,直接返回false;

代码没毛病,可是回到现实世界不就是说a = b 而b != a吗,作为一个严谨的程序员是不是总感觉哪里不舒服呢?

相信作者也是这样,先是在类上出现了这段说明

The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

然后方法上还要补上

Note: This method is not symmetric with respect to the equals(Object) method in the base class.

大意就是:我写这个继承关系只是实现继承并不是类型继承(为了少写点代码,其实没毛关系),equals方法和父类的equals方法不对等(你们用错了我不负责啊)

总结

最后总结以下几点
1, java.sql.Timestamp可以接受的时间范围是1970-01-01 00:00:00 - 9999-12-31 23:59:59,具体使用的时候需要考虑时区带来的偏移量
2, 和数据库结合使用需要结合实际需求、数据库数据类型的具体范围选择合适的范围,不能仅仅通过名字来选择数据类型
3, Timestamp类的equals方法和父类的equals方法并不是对等的,只有两个比较的对象都是Timestamp的时候才能返回一致的结果
4, 我们在实际设计开发的时候尽量避免这种不舒服的设计,结合继承,封装,多态等多种手段确保我们设计的鲁棒。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


文章名称:你可能不会注意的Timestamp-创新互联
链接地址:http://scyanting.com/article/didhss.html