Java時區處理之Date,Calendar,TimeZone,SimpleDateFormat
一、概述
1、問題描述
使用Java處理時間時,我們可能會經常發現時間不對,比如相差8個小時等等,其真實原因便是TimeZone。只有正確合理的運用TimeZone,才能保證系統時間無論何時都是準確的。由於我在外企工作,伺服器在美國,美國也有很多時區,經常會碰到向處於不同時區的伺服器發請求時需要考慮時區轉換的問題。譬如,伺服器位於西八區(GMT-8:00),而身處東八區的使用者想要查詢當天的銷售記錄。則需把東八區的“今天”這個時間範圍轉換為伺服器所在時區的時間範圍。
2、時區認識
GMT時間:即格林威治平時(Greenwich Mean Time)。平太陽時是與視太陽時對應的,由於地球軌道非圓形,執行速度隨地球與太陽距離改變而出現變化,因此視太陽時欠缺均勻性。為了糾正這種不均勻 性,天文學家就計算地球非圓形軌跡與極軸傾斜對視太陽時的效應,而平太陽時就是指經修訂之後的視太陽時。在格林威治子午線上的平太陽時稱為世界時(UTC), 又叫格林威治平時(GMT)。
3、Java 時間和時區API
3.1、Date
類Date表示特定的瞬間,精確到毫秒。獲得一個表示當前時間的Date物件有兩種方式:
1. Date date = new Date();
2. Date date = Calendar.getInstance().getTime();
Date物件本身所儲存的毫秒數可以通過date.getTime()方法得到;該函式返回自1970年1月1日 00:00:00 GMT以來此物件表示的毫秒數。它與時區和地域沒有關係(其實可以認為是GMT時間),而且還會告訴我們這個時區是否使用夏令時。有個這個資訊,我們就能夠繼續將時區物件和日期格式化器結合在一起在其它的時區和其它的語言顯示時間了。
3.2、 Calendar
Calendar的getInstance()方法有引數為TimeZone和Locale的過載,可以使用指定時區和語言環境獲得一個日曆。無參則使用預設時區和語言環境獲得日曆。
3.2、TimeZone
TimeZone物件給我們的是原始的偏移量,也就是與GMT相差的微秒數,即TimeZone表示時區偏移量,本質上以毫秒數儲存與GMT的差值。
獲取TimeZone可以通過時區ID,如"America/New_York",也可以通過GMT+/-hh:mm來設定。例如北京時間可以表示為GMT+8:00。
TimeZone.getRawOffset()方法可以用來得到當前時區的標準時間到GMT的偏移量。上段提到的"America/New_York"和"GMT+8:00"兩個時區的偏移量分別為-18000000和28800000。
4、影響TimeZone的因素
1. 作業系統的時區設定。
2. 資料傳輸時時區設定。
第一個原因其實是根本原因,當資料在不同作業系統間流轉時,就有可能因為作業系統的差異造成時間偏差,而JVM預設情況下獲取的就是作業系統的時區設定。因此在專案中最好事先設定好時區,例如:
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
5、解決的方法:
從以上的分析可以看出,解決時區問題就簡單了,在時區間轉換時間時,首先用原時間減掉原時間所在時區相對於GMT的偏移量,得到原時間相對於GMT的值,然後再加上目標時區相對GMT的偏移量即可。需要注意的是這樣得到的結果依然是毫秒數,所以我們要按照指定日期格式重新轉換成Date物件即可。
6、例項:
在例項之前,假設當前的時區為中國的東八區。即GMT+8:00
package com.wsheng.aggregator.timezone;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
/**
* @author Josh Wang(Sheng)
*
* @email [email protected]
*
*/
public class TimeZone1 {
public static void main(String[] args) {
Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
String dateStr = "2014-1-31 21:20:50 ";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
Date dateTmp = dateFormat.parse(dateStr);
System.out.println(dateTmp);
} catch (ParseException e) {
e.printStackTrace();
}
String dateStrTmp = dateFormat.format(date);
System.out.println(dateStrTmp);
}
}
執行結果:
Sat Feb 01 05:20:50 CST 2014 2014-01-31 13:20:50
我們發現同一時間,字串和日期執行出來的結果並不相同,那麼我們應該怎麼理解呢?
一切都要以根本原因, 即當前作業系統的時間為基準。
我的作業系統 是"Asia/Shanghai",即GMT+8的北京時間,那麼執行日期轉字串的format方法時,由於日期生成時預設是作業系統時區,因此 2014-1-31 21:20:50是北京時間,那麼推算到GMT時區,自然是要減8個小時的,即結果(2014-01-31 13:20:50);而執行字串轉日期的parse方法時,由於字串本身沒有時區的概念,因此 2013-1-31 22:17:14就是指GMT(UTC)時間【ps:所有字串都看做是GMT時間】,那麼當轉化為日期時要加上預設時區, 即"Asia/Shanghai",因此要加上8個小時。
用Calendar的話,如下:
package com.wsheng.aggregator.timezone;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* @author Josh Wang(Sheng)
*
* @email [email protected]
*
*/
public class TimeZone2 {
public static void main(String[] args) {
Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE));
}
}
執行結果:
Fri Jan 31 21:20:50 CST 2014 13:20
Calendar不涉及到日期與字串的轉化,因此不像SimpleDateFormat那麼複雜,與日期轉字串的思路類似。但是需要注意的是,設定完時區後,我們不能用calendar.getTime()來直接獲取Date日期,因為此時的日期與一開始setTime時是相同值,要想獲取某時區的時間,正確的做法是用calendar.get()方法,那麼我們怎麼獲得Date型別的日期呢?
正確的做法如下:
package com.wsheng.aggregator.timezone;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
/**
* @author Josh Wang(Sheng)
*
* @email [email protected]
*
*/
public class TimeZone3 {
public static void main(String[] args) {
Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
System.out.println(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
// 或者可以 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.setTime(date);
Calendar calendar2 = Calendar.getInstance();
calendar2.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
System.out.println(calendar2.getTime());
}
}
執行結果:
Fri Jan 31 21:20:50 CST 2014 Fri Jan 31 13:20:50 CST 2014
完美通用轉換方法
其實上面兩個轉換方法都要受到作業系統的時區設定影響,如果軟體在不同作業系統執行,仍然會有時間誤差,那麼怎麼才能統一呢?
/**
*
*/
package com.wsheng.aggregator.timezone;
import java.util.Date;
import java.util.TimeZone;
/**
* @author Josh Wang(Sheng)
*
* @email [email protected]
*
*/
public class TimeZone4 {
public static void main(String[] args) {
Date date = new Date(1391174450000L); // 2014-1-31 21:20:50
System.out.println(date);
date = changeTimeZone(date, TimeZone.getTimeZone("Asia/Shanghai"), TimeZone.getTimeZone("GMT"));
System.out.println(date);
}
/**
* 獲取更改時區後的日期
* @param date 日期
* @param oldZone 舊時區物件
* @param newZone 新時區物件
* @return 日期
*/
public static Date changeTimeZone(Date date, TimeZone oldZone, TimeZone newZone) {
Date dateTmp = null;
if (date != null) {
int timeOffset = oldZone.getRawOffset() - newZone.getRawOffset();
dateTmp = new Date(date.getTime() - timeOffset);
}
return dateTmp;
}
}
執行結果:
Fri Jan 31 21:20:50 CST 2014 Fri Jan 31 13:20:50 CST 2014
更通用的,我們可以寫一個支援型別轉換的類:
package com.wsheng.aggregator.timezone;
import java.text.*;
import java.util.*;
/**
*
* @author Josh Wang(Sheng)
*
* @email [email protected]
*
*/
public class DateTransformer {
public static final String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss";
public static String dateTransformBetweenTimeZone(Date sourceDate, DateFormat formatter,
TimeZone sourceTimeZone, TimeZone targetTimeZone) {
Long targetTime = sourceDate.getTime() - sourceTimeZone.getRawOffset() + targetTimeZone.getRawOffset();
return DateTransformer.getTime(new Date(targetTime), formatter);
}
public static String getTime(Date date, DateFormat formatter){
return formatter.format(date);
}
public static void main(String[] args){
DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
Date date = Calendar.getInstance().getTime();
System.out.println(" date: " + date);
TimeZone srcTimeZone = TimeZone.getTimeZone("EST");
TimeZone destTimeZone = TimeZone.getTimeZone("GMT+8");
System.out.println(DateTransformer.dateTransformBetweenTimeZone(date, formatter, srcTimeZone, destTimeZone));
}
}
DateFormat是日期/時間格式化子類的抽象類,它以與語言無關的方式格式化並解析日期或時間。日期/時間格式化子類(如 SimpleDateFormat)允許進行格式化(也就是日期 -> 文字)、解析(文字-> 日期)和標準化。將日期表示為 Date 物件,或者表示為從 GMT(格林尼治標準時間)1970 年 1 月 1 日 00:00:00 這一刻開始的毫秒數。SimpleDateFormat則是一個以與語言環境有關的方式來格式化和解析日期的具體類,可以以“日期和時間模式”字串指定日期和時間格式。我們函式中所用模式字串為"MM/dd/yyyy HH:mm:ss",則輸出日期:"07/16/2013 04:00:00"
其他常見的模式字母定義如下:
字母 日期或時間元素 表示 示例G |
Era 標誌符 | Text | AD |
y |
年 | Year |
1996 ; 96
|
M |
年中的月份 | Month |
July ; Jul ; 07
|
D |
年中的天數 | 189 |
|
d |
月份中的天數 | 10 |
|
E |
星期中的天數 | Text |
Tuesday ; Tue
|
a |
Am/pm 標記 | Text | PM |
H |
一天中的小時數(0-23) | 0 |
|
k |
一天中的小時數(1-24) | 24 |
|
K |
am/pm 中的小時數(0-11) | 0 |
|
h |
am/pm 中的小時數(1-12) | 12 |
|
m |
小時中的分鐘數 | 30 |
|
s |
分鐘中的秒數 | 55 |
|
z |
時區 |
Pacific Standard Time ; PST ; GMT-08:00
|
由上面的分析和事例說明可知:
1. 計算機內部記錄的時間(Date date = new Date()), 為格林威治標準時(GMT). 即java.util.Date代表一個時間點,其值為距公元1970年1月1日 00:00:00的毫秒數。所以它可以認為是沒有時區和Locale概念的。
2. 日期格式化類DateFormat, 對於不同地區的配置一般有兩個點, 一個是Locale , 一個是TimeZone
前者(Locale)使DateFormat按所配置的地區特性來輸出文字(例如中國,美國,法國不同地區對日期的表示格式不一樣,中國可能是2001年10月5日)
後者(TimeZone)讓DateFormat知道怎麼去轉換,去調整時間偏移度,從而得到符合配置的時區的時間.
(即假設取得當前時間(假設當前時區為GMT+0,即與new Date()最後轉換的時間毫秒數一致)為2:00, 那麼如果你配置DateFormat.setTimeZome("GMT+8"), 即北京時間的時區, 那麼這時候格式化輸出的就是10:00了, 因為系統對原始毫秒數進行了時間偏移調整(調到你設定的時區),即加多8小時,之後再格式化輸出日期的字串形式)
3. GMT與UTC的時區是一樣的,都是以倫敦時間為基準. 而GMT+8時區就是北京時間所在時區.同一時刻的時間比GMT快8小時。
相關推薦
Java時區處理之Date,Calendar,TimeZone,SimpleDateFormat
一、概述 1、問題描述 使用Java處理時間時,我們可能會經常發現時間不對,比如相差8個小時等等,其真實原因便是TimeZone。只有正確合理的運用TimeZone,才能保證系統時間無論何時都是準確的。由於我在外企工作,伺服器在美國,美國也有很多時區,經常會碰到向處於不同時區的伺服器發請求時需
java 異常處理之宣告丟擲異常
java 異常處理宣告丟擲異常 2.宣告丟擲異常是Java中處理異常的第二種方式 2.宣告丟擲異常是Java中處理異常的第二種方式 如果一個方法(中的語句執行時)可能生成某種異常,但是並不能確定如何處理這種異常,則此方
Java 異常處理 之 例項
package com.exception2; /* 題目: 老師正在給學生上課, 突然某個學生說網路斷了, 老師你來幫助處理一下 沒辦法,我只能去找網管 */ public class TestException3//網管類 {
Java 異常處理 之 捕獲處理流程
package com.exception2; /* * 注意: 1. 當異常發生的時候,程式會終止,其後面的程式碼不會執行. 2. 異常分為編譯時異常和執行期異常 編譯期異常必須用 try{}catch{}捕獲,執行期異常可以不捕獲 編譯期異常從 Except
java異常處理之自定義異常類
在實際開發中,開發人員往往需要定義一些異常類用於描述自身程式中的異常資訊,以區分其他程式的異常資訊。就需要自定義異常類。 實現自定義異常類的方法如下: 1、類java.lang.Throwable是所有異常類的基類,它包括兩個子類:Exceptio
python時間處理之date
#!/usr/bin/python # -*- coding:utf-8 -*- """ date的用法 (test_datetime.py) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Created by bix
【面試加分項】java異常處理之try_catch_finally
try_catch_finally大家應該用的很多,不少人也瞭解一些他們的執行順序,可是它真的有這麼簡單麼,下面我們先來看一段程式碼,大家想想他的執行結果是什麼? public class TestException { int goabl=1
Java Exception處理之最佳實踐
本文是Exception處理的一篇不錯的文章,從Java Exception的概念介紹起,依次講解了Exception的型別(Checked/Unchecked),Exception處理的最佳實現: 1. 選擇Checked還是Unchecked的幾個經典依據 2. Exception的封裝
Java Exception 處理之最佳實踐
本文是Exception處理的一篇不錯的文章,從Java Exception的概念介紹起,依次講解了Exception的型別(Checked/Unchecked),Exception處理的最佳實現: 1. 選擇Checked還是Unchecked的幾個經典依據 2. Exc
java異常處理之throw, throws,try和catch
程式執行過程中可能會出現異常情況,比如被0除、對負數計算平方根等,還有可能會出現致命的錯誤,比如記憶體不足,磁碟損壞無法讀取檔案等,對於異常和錯誤情況的處理,統稱為異常處理。 Java異常處理主要通過5個關鍵字控制:try、catch、throw、throws和
Calendar TimeZone SimpleDateFormat
關於Calendar類的使用可參考:Java Calendar類的使用總結 獲取日曆(Calendar):java.util.C
TimeZone以及時區有關的操作Date/Calendar/SimpleDateFormat
TimeZone 1. System.getProperty("user.timezone") System.setProperty("user.timezone","GMT+8") 2. TimeZone.getDefault().getID()
Java之Date類,DateFormat類,SimpleDateFormat詳解
java.util.Date Date 日期類 不過很多方法都已經過時了 1.構造方法有兩種 有參(Long) 無參 2.方法主要有兩種 setTime 和 getTime兩種 import java.util.Date; public class Demo
Java之Date Time API (Java 8 新特性)
今天 utc eating mes interval etime api int isa Java 8 – Date Time APIJava 8 comes with a much improved and much required change in the way
Java Date Calendar DateFormat Details
leg present tar efi which extract .mm operate HERE From https://www.ntu.edu.sg/home/ehchua/programming/java/DateTimeCalendar.html Date
java.sql.SQLException: Zero date value prohibited 異常處理
原因 datetime java SQ mysql數據庫 rod 沒有 str round 最近使用MyBatis時發現了java.sql.SQLException: Zero date value prohibited 的異常,網上查了一下原因。其實是Mysql數據庫在面
一篇文章吃透iOS、JS的時間日期(Date, Calendar, Locale, TimeZone)
內容 dig fun sta target 格式 特定 ID lin iOS 時間相關類 NSDate - 表示一個絕對的時間點。 NSCalendar - 代表一個特定的日歷,例如公歷或者希伯來日歷。它提供了一系列基於日期的計算,並且可以讓你在"NSDate"和"NSD
Java 常用類庫 之 Date 日期類
ID ret ati 轉換成 知識 HR text exceptio imp http://www.verejava.com/?id=16993074079095 /** 知識點: Date 日期類 */ import java.util.Date; import
java的專題之異常處理——將異常處理運用在用戶管理系統中(很重要)
ava 例子 AS 異常處理 構造方法 str run 用戶 per 如何讓我們的異常處理變得足夠靈活—— 我們可以自己創建一個異常類(所有異常的通用操作) 在用戶管理系統中具體操作如下: 1、創建一個UserException的類 2、找到Superclass,點擊Bro
Java學習筆記之異常處理
span 怎麽辦 空指針 處理 異常處理 空指針異常 一個 算數 輸入 一.異常的分類 1.由Java虛擬機拋出的異常(Error):程序無法處理的問題,用戶不用去進行處理(虛擬機錯誤丶內存溢出錯誤丶線程死鎖) 2.Exception異常:程序本身可以進行處理的異