時間戳 時區 java mysql


  • 當一個時間 比如2016年5月6日,生成時間戳。這個運算是與時區有關的。首先得確認這個時間是哪個時區的,然后轉換成utc時區的時間。再減去1970,得到的秒數,就是時間戳。
  • 時間戳是個一定的值,他與時區沒關。
  • 當想把時間戳還原成時間,必須指定時區,才能確認什么時間。
  • 總結:時間與時區有關。時間戳與時區無關,它是utc,也就是gmt時區的時間與1970年的差。在時間軸的某一點時刻,不管位於哪個時區(如北京 +8小時,或者格林威治 +0小時),它轉換成的時間戳是相等的。
  • 首先澄清一個概念:

 

所有的linux系統文件系統底層存儲的都是UTC時間,也就是說都是自1970年0時0分0秒以來的UTC標准時間的秒數。

無論系統配置是什么時區,顯示如何不同,底層存儲都是一致的。

 

  • 如何得到UTC系統時間?

 

在shell環境下 >date '+%s' 即可得到

在mysql環境下>select unix_timestamp();即可得到

 

  • timestamp是什么?
timestamp是mysql數據庫中的一種字段類型,
不用說太多,
  • 內部存儲是4個字節
  • 精度達到微妙
  • 回轉換成UTC時間存儲
  • 讀取的時候再根據時區轉換回來

mysql> select now()+0;
+-----------------------+
| now()+0               |
+-----------------------+
| 20130722184134.000000 |
+-----------------------+
1 row in set (0.00 sec)


mysql> select now();  
+---------------------+
| now()               |
+---------------------+
| 2013-07-22 18:41:37 |
+---------------------+
1 row in set (0.00 sec)
可以看到,我們看到的timestamp實際是已經根據當前時區轉換過格式的字符形式
 
  • 如何在mysql中根據timestamp得到UTC系統時間戳呢呢?
很簡單,mysql> select unix_timestamp( 20130722183356.000000);
+----------------------------------------+
| unix_timestamp( 20130722183356.000000) |
+----------------------------------------+
|                             1374489236 |
+----------------------------------------+
             
  • 如何在mysql中根據UTC 系統時間戳得到當前時區的timestamp呢?
mysql> select from_unixtime(1374489236);
+---------------------------+
| from_unixtime(1374489236) |
+---------------------------+
| 2013-07-22 18:33:56       |
+---------------------------+
1 row in set (0.00 sec)
 
  •  如何在mysql中根據當前時區的時區得到其他時區的timestamp呢?
mysql> SELECT CONVERT_TZ('2013-07-22 18:41:37','+08:00','+00:00') as UTC;         
+---------------------+
| UTC                 |
+---------------------+
| 2013-07-22 10:41:37 |
+---------------------+
1 row in set (0.00 sec)

 

 

  • 一個罕見的問題

 

數據庫使用Amazon RDS 是無法修改時區的,統一使用UTC時區

應用程序使用Amazon 服務器 是UTC -7 美西時間

數據庫部分時間字段使用了current_timestamp,使用了UTC時區

部分字段使用了unix_time,由應用程序插入,理論上也是UTC時間

部分字段使用了unix_time並根據app服務器的時區轉成了時間的string格式,導致出現UTC-7的時間

導致了同一IDC的數據有了兩種時間

===========================================

結論:

我們不需要被timestamp和時區弄糊塗,其實很簡單,timestamp存儲的時間是帶時區偏移的。

所有的數據庫的時間應該統一操作,要么使用DB的current_timestamp,要么使用應用程序插入。

在有多IDC且不同時區的情況下,

如果需要標准UNIX時間戳:我建議只需要每次取出UTC的時間戳進行處理

如果需要標准的timestamp,我建議全部統一convert到一個時區去處理

 
0

 

(摘自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)

 

二. 從數據庫取數據的過程

?
   mysql> select  auction_id,starts from  auctions  where  auction_id=88888;
+ -------------+---------------------+
| auction_id  | starts |
+ -------------+---------------------+
| 88888       | 2011-10-24 20:32:58 |
+ -------------+---------------------+
1 row in  set  (0.00 sec)
 
mysql> show columns from  auctions;
+ --------------------------------+---------------+------+-----+---------+-------+
| Field | Type | Null  Key Default  | Extra |
+ --------------------------------+---------------+------+-----+---------+-------+
|starts | datetime | YES | MUL | NULL  | |

  可見:數據庫的時間字段starts存的是datetime類型,它是一個和時區相關的string(顯然:string都是和時區相關的)

  而且數據庫是按照CST時區存的時間

     程序中從數據庫取數據用的sql語句:

?
mysql> select  auction_id,DATE_FORMAT(starts, '%Y%m%d%H%i%S' ) from  auctions  where  auction_id=88888;
+ -------------+------------------------------------+
| auction_id  | DATE_FORMAT(starts, '%Y%m%d%H%i%S' ) |
+ -------------+------------------------------------+
| 88888       | 20111024203258 |
+ -------------+------------------------------------+
1 row in  set  (0.00 sec)

  這里只是簡單的用DATE_FORMAT函數把datetime類型的starts字段轉換為我們需要的格式 %Y%m%d%H%i%S 而已

 

三、Java代碼

  看這樣一段轉換時間的java代碼:

?
// 將字符串時間轉化為秒數(yyyyMMddHHmmss)
static public  long  getUnixTimestamp(String srcTime)
{        
           SimpleDateFormat sdf = new  SimpleDateFormat( "yyyyMMddHHmmss" );
           Date result_date;
           long result_time =  0 ;
           try
                    result_date = sdf.parse(srcTime);
                    //返回的是毫秒數故除以1000
                    result_time = result_date.getTime()/ 1000 ;
           } catch  (Exception e) { 
                    //出現異常時間賦值為20000101000000
                    result_time = 946684800 ;
           }
           return result_time;
  }

  計算結果:

?
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數據。例如:

?
SimpleDateFormat sdf = new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
sdf.setTimeZone(TimeZone.getTimeZone( "GMT+8" ));
String snow = sdf.format(now); // snow = 2011-12-04 21:22:24
sdf.setTimeZone(TimeZone.getTimeZone( "GMT+7" ));
String snow2 = sdf.format(now); // snow2 = 2011-12-04 20:22:24 (可見:東八區比東七區早一個小時)

  另外,你可以通過如下代碼修改本地時區信息:

?
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和時區概念的曲解和誤用

  由於歷史原因,發現程序中有這樣一段代碼:

?
// 將字符串時間轉化為秒數(yyyyMMddHHmmss),有8個小時的時差
   static public  long  getLongTime(String srcTime)
   {        
             SimpleDateFormat sdf = new  SimpleDateFormat( "yyyyMMddHHmmss" );
             Date result_date;
             long result_time =  0 ;
             try
                      result_date = sdf.parse(srcTime);
                      //返回的是毫秒數故除以1000
                      result_time = result_date.getTime()/ 1000 8  3600 ;   // 這里加了八個小時
             } catch  (Exception e) { 
                      //出現異常時間賦值為20000101000000
                      result_time = 946684800 ;
             }
             
             return result_time;
    }

  計算結果:

?
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

?
SimpleDateFormat df = new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
java.util.Date end = df.parse( "2011-12-04 21:22:24" );
java.util.Date start = df.parse( "1970-01-01 00:00:00" );
long delta = (end.getTime() - start.getTime())/ 1000 ;
System.out.println( "delta=" + delta);  // delta=1323033744

  或者用shell命令來求時間差

?
[admin @localhost ]$ date -d "2011-12-04 21:22:24"  +%s
1323004944
[admin @localhost ]$ date -d "1970-01-01 0:0:0"  +%s
- 28800
[admin @localhost ]$ date -d "2011-12-04 21:22:24"  +%s -u
1323033744
[admin @localhost ]$ date -d "1970-01-01 0:0:0"  +%s -u
0

    1323004944 + 28800 = 1323033744

  對於東八區的人來說,1323033744 這個時間按照UTC時間可以解析正確。不能按照自己所在的時區去解析,不然就是錯的

  但是如果是東七區的人呢?需要按照UTC時間解析后,自己去減1個小時的時差,so ugly!

  所以,用戶在解析1323033744 這個數據的時候:

    (1) 按照UTC時間來解析得到北京時間,然后根據時間差換算成自己所在時區的時間

        (當然,一般都是在北京時區了,所以不用換算,按UTC時間來解析就能得到正確的時間)

    (2) 將這個時間減去8小時得到unix timestamp,然后按照自己所在的時區去解析就可以了

  總結:這段代碼是對unix timestamp和時區的曲解和誤用。

 

六、從數據庫獲取unix timestamp時間

    其實從數據庫是可以直接獲取到unix timestamp時間的

?
mysql> select auction_id,unix_timestamp(starts)  from auctions where auction_id= 88888 ;                                                   
  +-------------+------------------------+
  | auction_id  | unix_timestamp(starts) |
  +-------------+------------------------+
  | 88888        |             1319459578  |
  +-------------+------------------------+
  1 row in set ( 0.02  sec)

 

七、參考

  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/

八、unix時間戳轉換工具

http://code.aosoo.com/unixtime

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM