转自: http://www.cnblogs.com/flying5/archive/2011/12/05/2276578.html
我的程序是一个在hadoop上运行的分布式程序,从mysql数据库中取数据,经过处理之后输出
一. 基本概念
时区 :time zone 1884年国际经线会议规定,全球按经度分为24个时区,每区各占经度15°。
以本初子午线为中央经线的时区为零时区,由零时区向东、西各分12区,东、西12区都是半时区,共同使用180°经线的地方时。
CST :China Standard Time UTC+8:00 中国标准时间(北京时间),在东八区
UTC :Universal Time Coordinated,世界协调时间,又称世界标准时间、世界统一时间。UTC 提供了一种与时区无关(或非特定于时区)的时间。
世界上的所有时区都可以表示为 UTC 加上或减去一个偏移量。
因此,UTC是0时区的时间,如北京为早上八点(东八区),UTC时间就为零点,时间比北京时晚八小时
GMT :Greenwich Mean Time格林威治标准时间,指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。
Unix timestamp :Unix时间戳,或称Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,
定义为从格林威治时间(UTC/GMT的午夜)1970年01月01日00时00分00秒起至现在的总秒数。
可以这么说:
UTC和GMT几乎是同一概念,两者的区别是GMT是一个天文上的概念,UTC是基于原子钟。
GMT=UTC
GMT + 8 = UTC + 8 = CST
UTC+时间差=本地时间 (时间差东为正,西为负,东八区记为 +0800)
二. 从数据库取数据的过程
|
可见:数据库的时间字段starts存的是datetime类型,它是一个和时区相关的string(显然:string都是和时区相关的)
而且数据库是按照CST时区存的时间
程序中从数据库取数据用的sql语句:
|
这里只是简单的用DATE_FORMAT函数把datetime类型的starts字段转换为我们需要的格式 %Y%m%d%H%i%S 而已
三、java代码
看这样一段转换时间的java代码:
|
计算结果:
getUnixTimestamp(
"20111204212224"
) =
1323004944
|
说明:java.util.Date中的getTime函数定义如下:
java.util.Date代表一个时间点,其值为距公元1970年1月1日 00:00:00的毫秒数。所以它是没有时区和Locale概念的。
public long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数
java中通过如下形式取得当前时间点:
Date now =
new
Date();
//这个时间点与本地系统的时区无关
|
而正因为其与时区的无关性,才使得我们的存储数据(时间)是一致的(时区一致性)。
一般的我们将now存储于数据库中,当我们需要展现数据时,将now格式化成想要的格式,如:2011-12-04 21:22:24
而这个功能一般交由java.text.DateFormat来实现。例如:
SimpleDateFormat sdf =
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"
);
String snow = sdf.format(now);
|
我们发现snow是带时间(如2011-12-04 21:22:24)的字符串,那么 2011-12-04 21:22:24 这个时间是哪个时区的时间呢?
默认情况下,SimpleDateFormat 取得本地系统的时区(我的时区为GMT+8北京),然后按照 pattern("yyyy-MM-dd HH:mm:ss")格式化now,
此时输出的就是 GMT+8 区的时间了。如果想支持国际化时间,则先指定时区,然后再格式化date数据。例如:
|
另外,你可以通过如下代码修改本地时区信息:
TimeZone.setDefault(TimeZone.getTimeZone(
"GMT+8"
));
|
在windows操作系统中,是通过桌面右下角,也可以指定操作系统的时区。
在linux系统中,通过如下命令可以得到当前时区
[admin
@localhost
]$ date -R
Sun,
04
Dec
2011
22
:
49
:
00
+
0800
|
四、结论:
getTime()返回的已经是一个UTC的unix timestamp秒数了,与时区无关;而转换为字符串后,就和时区相关了
对于这个秒数,不同时区的人,按照自己所在的时区去解析,就可以得到正确的时间了
[admin
@localhost
]$ date -d
@1323004944
2011
年
12
月
04
日 星期日
21
:
22
:
24
CST
[admin
@localhost
]$ date -d
@1323004944
-u
2011
年
12
月
04
日 星期日
13
:
22
:
24
UTC
|
对于涉及到时间转换的程序来说,如果代码里面没有强行指定时区,那就会依赖于操作系统的时区。
特别是对于分布式程序,如果不同机器上系统时区不一样,那就会出现不一致的数据了!
五、对unix timestamp和时区概念的曲解和误用
由于历史原因,发现程序中有这样一段代码:
|
计算结果:
getUnixTimestamp(
"20111204212224"
) =
1323033744
|
显然,这个时间比上面通过 getUnixTimestamp("20111204212224") = 1323004944 得到的时间多了8个小时
1323033744 - 1323004944 = 28800 = 8 * 3600 = 8h
如果用户将得到的 1323033744 按照自己所在的时区解析后得到的结果是:
[admin
@localhost
]$ date -d
@1323033744
2011
年
12
月
05
日 星期一
05
:
22
:
24
CST
|
得到了一个完全错误的结果!
但如果用户将这个 1323033744 按照UTC时区来解析后得到的结果是:
[admin
@localhost
]$ date -d
@1323033744
-u
2011
年
12
月
04
日 星期日
21
:
22
:
24
UTC
|
为了方便对比,把 1323004944 的解析结果也拿来对比
[admin
@localhost
]$ date -d
@1323004944
2011
年
12
月
04
日 星期日
21
:
22
:
24
CST
[admin
@localhost
]$ date -d
@1323004944
-u
2011
年
12
月
04
日 星期日
13
:
22
:
24
UTC
|
可以看到,这个代码中得到的秒数时间是比UTC的unix timestamp秒数多了八个小时
这个时间 1323033744 可以理解为北京时区得到的秒数,但是不是unix timstamp时间!
unix timestamp秒数是与时区无关的,不管是在哪个时区得到的unix timestamp都是一样的
我们可以验证一下,用北京时间“20111204212224”减去“19700000000000”得到的秒数,就是 1323033744
|
或者用shell命令来求时间差
|
1323004944 + 28800 = 1323033744
对于东八区的人来说,1323033744 这个时间按照UTC时间可以解析正确。不能按照自己所在的时区去解析,不然就是错的
但是如果是东七区的人呢?需要按照UTC时间解析后,自己去减1个小时的时差,so ugly!
所以,用户在解析1323033744 这个数据的时候:
(1) 按照UTC时间来解析得到北京时间,然后根据时间差换算成自己所在时区的时间
(当然,一般都是在北京时区了,所以不用换算,按UTC时间来解析就能得到正确的时间)
(2) 将这个时间减去8小时得到unix timestamp,然后按照自己所在的时区去解析就可以了
总结:这段代码是对unix timestamp和时区的曲解和误用。
六、从数据库获取unix timestamp时间
其实从数据库是可以直接获取到unix timestamp时间的
|
七、参考
java.util.Date http://www.jar114.com/jdk6/zh_CN/api/java/util/Date.html
关于java Date和时区的问题 http://blog.163.com/haizai219@126/blog/static/444125552009101924912981/