1. 程式人生 > >Java 時區問題解析

Java 時區問題解析

這裡寫圖片描述
—-Java每個時間區域都有一個時間區域ID識別符號,這個ID是個字串,是由位於J2SE 安裝程式的jre/lib子目錄中的tzmappings檔案儲存這些ID列表。 J2SE 1.3 僅僅只包含tzmappings檔案,但是 J2SE 1.4包含世界不同地區的時間區域資料檔案。jre/lib/zi存放著這些檔案。
—-TimeZone取預設值,先取該物件已經設定好的預設值,如果沒有則取system.property中的”user.timezone”,再沒有的話才根據“java.home”和“user.country”來獲取。。。。(從TimeZone.getDefault()原始碼可知);
–System.property中user.timezone會在TimeZone執行由“java.home”和“user.country”獲取timezone的方法後獲得初值;

SimpleDateFormat使用

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            // 前臺傳進來的時間,預設是本地時區的時間,所以日期轉換的時候,不會有變化
            System.out.println("set default:" + TimeZone.getDefault());
            Date dat = sdf.parse("2017-11-15");
            System.out.println(dat);
//修改SimpleDateFormat的時區,這種方法修改時區不會更改系統的預設 --------------------時區,只是修改了轉換類的使用時區 //前臺傳進來的時間,經轉換類轉換成指定時區的時間 sdf.setTimeZone(TimeZone.getTimeZone("GMT")); System.out.println("default:" + TimeZone.getDefault()); System.out.println("user set default:"
+ TimeZone.getTimeZone("GMT")); Date dat1 = sdf.parse("2017-11-15"); System.out.println(dat1); }

結果:

set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
②Wed Nov 15 00:00:00 CST 2017default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
④user set default:sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
⑤Wed Nov 15 08:00:00 CST 2017

TimeZone/Calendar/Date/DateFormat

①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"兩個時區的偏移量分別為-1800000028800000。
②Calendar
    Calendar 類是一個抽象類,不能new,Calendar的getInstance()獲取當前日曆物件。
    Calendar calendar = Calendar.getInstance();//獲取當前日曆物件
    long unixTime = calendar.getTimeInMillis();//獲取當前時區下日期時間對應的時間戳,和date.getTime()一樣;
    cal.getTime() 就是當前日期即相當於new date();
    Calendar的getInstance()方法有引數為TimeZone和Locale的過載,可以使用指定時區和語言環境獲得一個日曆。無參則使用預設時區和語言環境獲得日曆。
③Date
   計算機內部記錄的時間(Date date = new Date()),  即  java.util.Date代表一個時間點,其值為距公元19701100:00:00的毫秒數。所以可以認為是沒有時區和Locale概念的。但是通常我們把date的時區一定是當前作業系統的時區,String型別的時間可以通過轉換類指定時區,但是最終轉為date的時候,都需要將指定時區的時間轉為系統所在時區的時間。
   date.getTimeInMillis();//獲取當前時區下日期時間對應的時間戳

     Date date11 = new Date(1391174450000L); // 2014-1-31 21:20:50    
    String dateStr11 = "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(dateStr11);    
        System.out.println(dateTmp);    
     } catch (ParseException e) {    
        e.printStackTrace();    
    }    
    String dateStrTmp = dateFormat.format(date11);    
    System.out.println(dateStrTmp);
    結果:
    Sat Feb 01 05:20:50 CST 2014
    2014-01-31 13:20:50  

④DateFormat:
    日期格式化類DateFormat, 對於不同地區的配置一般有兩個點, 一個是Locale , 一個是TimeZone。
    前者(Locale)使DateFormat按所配置的地區特性來輸出文字(例如中國,美國,法國不同地區對日期的表示格式不一樣,中國可能是2001105日)
    後者(TimeZone)讓DateFormat知道怎麼去轉換,去調整時間偏移度,從而得到符合配置的時區的時間.
    GMT與UTC的時區是一樣的,都是以倫敦時間為基準. 而GMT+8時區就是北京時間所在時區.同一時刻的時間比GMT快8小時。
注意事項:
    Date.toString()和Calendar以及TimeZone依賴於TimeZone中defaultTimezone,即Calendar取得是TimeZone中設定的時區.
    TimeZone中設定的時區由jre這個jar下的TimeZone的原始碼可知,先取使用者自己設定的,沒有的話再取system中的。

程式碼分析

package com.yu;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
 * 關於時區的問題:
 *  TimeZone中設定的時區由jre這個jar下的TimeZone的原始碼可知,先取使用者自己設定的,沒有的話再取system中的。
 * 最好不要用TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))等設定timezone的時區,因為這會改變Jvm的時區,影響其他業務
 * SimpleDateFormat 預設取得本地系統的時區,可以修改轉換時候使用的時區
 * @author yu<br>
 * @version 1.0<br>
 * @taskId <br>
 * @CreateDate Nov 13, 2017 <br>
 * @since V80<br>
 * @see com.yu<br>
 */
public class TimeZoneUtil {
    public TimeZoneUtil() {
    }
    public static void main (String[] args) {
        notSetDefaultTimeZone();
        setDefaultTimeZone();
        testTime();
    }
    /**
     * 
     * Description: 使用者不設定TimeZone<br> 
     *  
     * @author yu.xiaoyan1<br>
     * @taskId <br> <br>
     */
    private static void notSetDefaultTimeZone() {
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            // 前臺傳進來的時間,先根據轉換類的設定的時區轉換日期(東八區),再轉換為系統所在時區的日期。
            System.out.println("set default:" + TimeZone.getDefault());
            Date dat = sdf.parse("2017-11-15");
            System.out.println(dat);

            //修改SimpleDateFormat的時區,這種方法修改時區不會更改系統的預設時區,只是修改了轉換類的使用時區
            //前臺傳進來的時間,先根據轉換類的設定的時區轉換日期(GMT標準時區),再轉換為系統所在時區的日期
            sdf.setTimeZone(TimeZone.getTimeZone("GMT")); 
            System.out.println("default:" + TimeZone.getDefault());
            System.out.println("user set default:" + TimeZone.getTimeZone("GMT"));
            Date dat1 = sdf.parse("2017-11-15");
            System.out.println(dat1);
        }
        catch (ParseException e) {
            e.printStackTrace();
        }
        Date date = new Date();
        System.out.println("date.toString():" + date.toString());
        System.out.println("date.getTime()" + date.getTime());

        Calendar cal = Calendar.getInstance();
        System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
        System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
        System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        Date date1 = new Date();
        String dateStr = format.format(date1);
        System.out.println(dateStr);

        System.out.println("user set default:" + TimeZone.getDefault());

        System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

        System.out.println("system user.country:" + System.getProperty("user.country"));
        System.out.println("system java.home:" + System.getProperty("java.home"));

        System.out.println("預設時區:" + TimeZone.getDefault().getID());
        System.out.println("--------------------------------------");
    }
    /**
     * 
     * Description: 取使用者設定的time zone<br> 
     *  
     * @author yu.xiaoyan1<br>
     * @taskId <br> <br>
     */
    private static void setDefaultTimeZone() {
        System.out.println("After setTimeZone0:");
        //設定TimeZone 預設的時區
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+2"));

        Date date = new Date();
        System.out.println("date.toString():" + date);
        System.out.println("date.getTime()" + date.getTime());

        Calendar cal = Calendar.getInstance();
        System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
        System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
        System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        String dateStr = format.format(cal.getTime());
        System.out.println(dateStr);

        System.out.println("user set default:" + TimeZone.getDefault());

        System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

        System.out.println("system user.country:" + System.getProperty("user.country"));
        System.out.println("system java.home:" + System.getProperty("java.home"));

        System.out.println("預設時區:" + TimeZone.getDefault().getID());
        System.out.println("--------------------------------------");
    }
    /**
     * 
     * Description:TimeZone和系統user.timezone不一致的時候 ,取TimeZone設定的值<br> 
     *  
     * @author yu.xiaoyan1<br>
     * @taskId <br> <br>
     */
    private static void testTime() {

        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        System.setProperty("user.timezone", "GMT+2");
        Date date = new Date();
        System.out.println("date.toString():" + date);
        System.out.println("date.getTime()" + date.getTime());
        Calendar cal = Calendar.getInstance();
        System.out.println("Calendar.getInstance().getTime():" + cal.getTime());
        System.out.println("Calendar.getInstance().getTimeInMillis():" + cal.getTimeInMillis());
        System.out.println("Calendar.getInstance().getTimeZone():" + cal.getTimeZone());

        System.out.println("user set default:" + TimeZone.getDefault());

        System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

        System.out.println("system user.country:" + System.getProperty("user.country"));
        System.out.println("system java.home:" + System.getProperty("java.home"));

        System.out.println("預設時區:" + TimeZone.getDefault().getID());
        //.SimpleDateFormat.setTimeZone()並不會改變TimeZone的值
        //既SimpleDateFormat.setTimeZone()又TimeZone.setDefault()時,SimpleDateFormat取SimpleDateFormat.setTimeZone()設定的值
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
        date = new Date();
        format.setTimeZone(TimeZone.getTimeZone("GMT+6"));
        String dateStr = format.format(date);
        System.out.println(dateStr);

        System.out.println("user set default:" + TimeZone.getDefault());

        System.out.println("system user.timezone:" + System.getProperty("user.timezone"));

        System.out.println("system user.country:" + System.getProperty("user.country"));
        System.out.println("system java.home:" + System.getProperty("java.home"));

        System.out.println("預設時區:" + TimeZone.getDefault().getID());
        System.out.println("--------------------------------------");
    }


}

執行結果:

date.toString():Tue Nov 14 10:46:32 CST 2017
date.getTime()1510627592734
Calendar.getInstance().getTime():Tue Nov 14 10:46:32 CST 2017
Calendar.getInstance().getTimeInMillis():1510627592767
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
2017-11-14 10:46:32 +0800
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:Asia/Shanghai
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
預設時區:Asia/Shanghai
--------------------------------------
After setTimeZone0:
date.toString():Tue Nov 14 04:46:32 GMT+02:00 2017
date.getTime()1510627592776
Calendar.getInstance().getTime():Tue Nov 14 04:46:32 GMT+02:00 2017
Calendar.getInstance().getTimeInMillis():1510627592777
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="GMT+02:00",offset=7200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
2017-11-14 04:46:32 +0200
user set default:sun.util.calendar.ZoneInfo[id="GMT+02:00",offset=7200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
system user.timezone:Asia/Shanghai
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
預設時區:GMT+02:00
--------------------------------------
date.toString():Tue Nov 14 10:46:32 CST 2017
date.getTime()1510627592778
Calendar.getInstance().getTime():Tue Nov 14 10:46:32 CST 2017
Calendar.getInstance().getTimeInMillis():1510627592778
Calendar.getInstance().getTimeZone():sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:GMT+2
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
預設時區:Asia/Shanghai
2017-11-14 08:46:32 +0600
user set default:sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
system user.timezone:GMT+2
system user.country:CN
system java.home:D:\path\1work_software\jdk1.7
預設時區:Asia/Shanghai
--------------------------------------

參考文獻:

jvm 時區

時區

時區

時區

SImpleDateFormat的構造方法
這裡寫圖片描述

夏令時:

 將一個以字串形式輸入的北京時間轉換成美國東部時間
String inputDate = "2011-05-14 23:30:00";
TimeZone timeZoneSH = TimeZone.getTimeZone("Asia/Shanghai");
TimeZone timeZoneNY = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
inputFormat.setTimeZone(timeZoneSH);
Date date = null;
try 
{
    date = inputFormat.parse(inputDate);
} 
catch (ParseException e) 
{
}

SimpleDateFormat outputFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss Z yyyy", Locale.US);
outputFormat.setTimeZone(timeZoneSH);
System.out.println("Asia/Shanghai:" + outputFormat.format(date));
outputFormat.setTimeZone(timeZoneNY);
System.out.println("America/New_York:" + outputFormat.format(date));

那麼,夏令時(DST)的問題怎麼解決呢?令人高興的是,JDK(or JRE)已自動為我們進行了夏令時處理。可以做個試驗,來驗證以上第2段程式碼能適用於夏令時轉換。美國在2011年開始和結束夏令時的時間是:3.13 2AM和11.6 2AM。

1. 將輸入時間inputDate設定為"2011-03-13 14:59:59",輸出: 
Asia/Shanghai:Sun Mar 13 14:59:59 +0800 2011 
America/New_York:Sun Mar 13 01:59:59 -0500 2011 
此時,美國東部時間還差1秒進入夏令時,與北京時間相差13小時。

2. 將輸入時間inputDate設定為"2011-03-13 15:00:00",輸出: 
Asia/Shanghai:Sun Mar 13 15:00:00 +0800 2011 
America/New_York:Sun Mar 13 03:00:00 -0400 2011 
此時,美國東部時間剛好進入夏令時,與北京時間相差12小時,同時,所使用的時區也發生了變化。

結束夏令時的試驗就不再贅述了。

JDK(or JRE)之所以能自動的進行DST處理,是因為其已內建了各個國家的夏令時政策,並提供Timezone Updater Tool來保持低版本JDK(or JRE)的TimeZone更新,但SUN官方推薦使用JDK(or JRE)的更新來更新TimeZone資訊。

綜上,我們應儘量在系統中使用如上描述的long型別變數來記錄時間,藉助相應的方法,可方便的格式化為不同時區的時間進行顯示。

相關推薦

Java 時區問題解析

—-Java每個時間區域都有一個時間區域ID識別符號,這個ID是個字串,是由位於J2SE 安裝程式的jre/lib子目錄中的tzmappings檔案儲存這些ID列表。 J2SE 1.3 僅僅只包含tzmappings檔案,但是 J2SE 1.4包含世界不同地

Java - 異常解析基礎

這樣的 習慣 希望 tof 指針 array 經歷 構造器 body java提高篇(十六)-----異常(一) 一、為什麽要使用異常 首先我們可以明確一點就是異常的處理機制可以確保我們程序的健壯性,提高系統可用率。雖然我們不是特別喜歡看到它,但是我們不能不

Java解析xml

tle void code public cnblogs () clas 河南 asn xml: <?xml version="1.0" encoding="GB2312"?> <RESULT> <VALUE>  

Java:JSON解析利器JackSon

ice ota 註解 repr eval bit version write cti Java:JSON解析利器JackSon JackSon基礎 1.Maven項目引入 <!-- https://mvnrepository.com/artifact/org.cod

Java如何解析二維碼

implement pre lin sage buffere com dede nts pack package com.ust.map;import java.awt.image.BufferedImage;import java.io.File;import java.

Java Base64解析

測試 測試環境 decode 對數 sign new 進行 第三方 bytes 最近在業務場景中,需要對第三方傳遞進來的字符進行base64解密,根據第三方文檔提供的解析工具,對數據進行了解析,關於Base64的解析方式如下: String s

JAVA解析JSON物件裡包含的JSON陣列

例如現在有這樣一個Json String Value={"data":[{"school_name":"西北農林科技大學","school_id":"8"},{"school_name":"西北大學","school_id":"6"},{"school_name":"西北工業大學",

Java進階(一)Java記憶體解析

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

java 後臺解析前臺傳json串雙引號轉義問題

jsp 頁面傳的String :  "[{"prodIndex":1,"catOne":"311","catOneName":"輪式拖拉機","brandId":"3","brand":"東方紅","model":"LX600","njName":"輪式拖拉機 LX600 東方紅"

java簡單解析json

一、什麼是JSON? JSON是一種取代XML的資料結構,和xml相比,它更小巧但描述能力卻不差,由於它的小巧所以網路傳輸資料將減少更多流量從而加快速度。 JSON就是一串字串 只不過元素會使用特定的符號標註。 {} 雙括號表示物件 [] 中括號表示陣列 "" 雙引號內是屬性或值

java後臺解析XML檔案

解析XML檔案分為兩種: 1、DOM(Document Object Model) 2、SAX(Simple API for XML) DOM是基於XML文件樹結構的解析,SAX是基於事件流的解析。 我用到的是SAX解析>> 一、XML檔案 <?xml

java深入淺出解析異常機制

  版權宣告:本文為博主原創文章,轉載請註明原地址,謝謝 https://blog.csdn.net/QuinnNorris/article/details/57428399 java中的異常處理的目的在於通過使用少量的程式碼,使得程式有著強大的魯棒性,並且這種異常處理機制會讓你變

java w3c解析xml檔案,獲取指定節點內容,讀取外部配置檔案。

原始碼: package com.ys.adage.utils; import com.ys.adage.message.CodeObjectResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.

JAVA-JSON解析-JSONObject

總結歸納一些常用的 JSONObject 使用,僅供參考。 1 .  net.sf.json-lib 解析JSON     pom.xml  :          

Java Sax解析xml(轉載)

出處: http://www.iteye.com/topic/763895 1.   Java Sax解析是按照xml檔案的順序一步一步的來解析,在解析xml檔案之前,我們要先了解xml檔案的節點的種類,一種是ElementNode,一種是TextNod

java xml解析工具

wxml工具 ## 現在為大家介紹個自己製作的xml解析工具:wxml。這塊工具可以自動的將xml和java類進行相互的轉換, 整個過程可能只需要1-2行程式碼。當然jaxb也是可以完成這樣的功能的,不過我個人認為我的工具更簡單些。不說這麼多,看程式碼。 使用方式:1.非註

【原創】JAVA面試解析(有贊一面)

本文的題目出自部落格 http://www.54tianzhisheng.cn/2018/07/12/youzan/ 但是作者沒有給出答案,博主斗膽來製作答案版。 引言 說在前面的話: 本文適合人群:急等著換工作的人 我承認刷面試題很有用的,縱觀幾年來的JAVA面試題,你會發現每家都差不多。比如,你仔細觀

小川學習筆記--BP神經網路JAVA程式碼解析

小川學習筆記–BP神經網路JAVA程式碼解析 闊別有些時日了,今天我就寫一篇最近學習BP神經網路JAVA程式碼的一個筆記,我們大家都知道BP神經網路是在上個世紀進行了兩次熱潮,由於反向傳播的發現從而促進了神經網路的發展。由於筆者在本科期間還未學習過JAVA,因此還在學習階段,對於一些程式

11 java原始碼解析-Thread(草稿)

1類的宣告 public class Thread implements Runnable 實現了Runnable介面 在程式開發中只要是多執行緒肯定永遠以實現Runnable介面為主。 1.1Runnable 說明 public interface Run

Java原始碼解析系列(二)ArrayList原始碼解析

備註:以下都是基於JDK8 原始碼分析 ArrayList簡介        ArrayList 是一個數組佇列,相當於 動態陣列。與Java中的陣列相比,它的容量能動態增長。它繼承於AbstractList,實現了List, RandomAccess, Clonea