服務(wù)器時(shí)區(qū)和JAVA進(jìn)程時(shí)區(qū)不一致問(wèn)題解決

[TOC]

問(wèn)題說(shuō)明

JAVA進(jìn)程在運(yùn)行過(guò)程中發(fā)現(xiàn)和當(dāng)前時(shí)間相差8小時(shí),檢查服務(wù)器時(shí)間和互聯(lián)網(wǎng)的北京時(shí)間一致匹厘,由此推測(cè)操作系統(tǒng)時(shí)區(qū)不對(duì)嘀趟,經(jīng)過(guò)查看操作系統(tǒng)時(shí)區(qū),發(fā)現(xiàn)時(shí)區(qū)正確愈诚,通過(guò)jinfo命令查看Java進(jìn)程發(fā)現(xiàn)時(shí)區(qū)不是東八區(qū)她按,由此找到原因,在此把排查過(guò)程做簡(jiǎn)要記錄炕柔,便于后續(xù)遇到問(wèn)題快速解決酌泰。

中國(guó)跨越了東五區(qū)、東六區(qū)匕累、東七區(qū)陵刹、東八區(qū)、東九區(qū)五個(gè)時(shí)區(qū)欢嘿,一般都統(tǒng)一采用東八區(qū)計(jì)時(shí)時(shí)間衰琐。

查看操作系統(tǒng)當(dāng)前時(shí)間

[root@swk-204 ~]# date
Fri Jan 25 19:28:28 CST 2019
[root@swk-204 ~]# date "+%Y-%m-%d %H:%M:%S"
2019-01-25 19:28:36
[root@swk-204 ~]# 

查看操作系統(tǒng)當(dāng)前時(shí)區(qū)

方式一

[root@swk-204 ~]# date -R
Fri, 25 Jan 2019 19:04:13 +0800

-0800表示西八區(qū),是美國(guó)舊金山所在的時(shí)區(qū),+0800表示東八區(qū)炼蹦,是中國(guó)上海所在的時(shí)區(qū)

方式二

[root@swk-204 ~]# date "+%Z"
CST

方式三

[root@swk-204 ~]# date
Fri Jan 25 19:05:43 CST 2019

方式四

[root@engine ~]# cat /etc/localtime 
TZif2 
     °tǜ?'p??Z??p ~h!Iap"^J#)Cp$Gg%_??&??+(У??q侐LMTCDTCSTTZif2 
                                                           ????°t????ǜ?????'p??????????Z??p ~h!Iap"^J#)Cp$Gg%_??&??+(У??q侐LMTCDTCST
CST-8

時(shí)區(qū)為CST-8

查看JAVA組件進(jìn)程時(shí)區(qū)

方式一

通過(guò)jvisualvm即可得到

2019-01-25_193830.png

方式二

通過(guò)組件名稱得到進(jìn)程號(hào)

[root@engine bin]# jps -lm |grep zkui
1728 ./zkui-2.0-SNAPSHOT-jar-with-dependencies.jar

通過(guò)jinfo命令查看運(yùn)行中jvm的全部參數(shù)

[root@engine bin]# jinfo 1728
Attaching to process ID 1728, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.71-b15
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.71-b15
sun.boot.library.path = /usr/java/jdk1.8.0_71/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = US
user.dir = /iflytek/server/zkui2.0
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_71-b15
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/java/jdk1.8.0_71/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator = 

java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 2.6.32-431.el6.x86_64
user.home = /root
user.timezone = GMT
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = root
java.class.path = ./zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = ./zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
java.home = /usr/java/jdk1.8.0_71/jre
user.language = en
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_71
java.ext.dirs = /usr/java/jdk1.8.0_71/jre/lib/ext:/usr/java/packages/lib/ext
sun.boot.class.path = /usr/java/jdk1.8.0_71/jre/lib/resources.jar:/usr/java/jdk1.8.0_71/jre/lib/rt.jar:/usr/java/jdk1.8.0_71/jre/lib/sunrsasign.jar:/usr/java/jdk1.8.0_71/jre/lib/jsse.jar:/usr/java/jdk1.8.0_71/jre/lib/jce.jar:/usr/java/jdk1.8.0_71/jre/lib/charsets.jar:/usr/java/jdk1.8.0_71/jre/lib/jfr.jar:/usr/java/jdk1.8.0_71/jre/classes
java.vendor = Oracle Corporation
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist = 

VM Flags:
Non-default VM flags: -XX:CICompilerCount=15 -XX:InitialHeapSize=1056964608 -XX:MaxHeapSize=16888365056 -XX:MaxNewSize=5629280256 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=352321536 -XX:OldSize=704643072 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC 
Command line:  

通過(guò)grep關(guān)鍵字進(jìn)行過(guò)濾

[root@engine bin]# jinfo 1728 |grep user.timezone
user.timezone = GMT

時(shí)區(qū)為GMT

問(wèn)題分析

知識(shí)儲(chǔ)備

通過(guò)查詢相關(guān)資料:

  1. GMT(Greenwich Mean Time羡宙,格林威治標(biāo)準(zhǔn)時(shí)間): 是指位于英國(guó)倫敦郊區(qū)的皇家格林尼治天文臺(tái)的標(biāo)準(zhǔn)時(shí)間,因?yàn)楸境踝游缇€被定義在通過(guò)那里的經(jīng)線框弛。
  2. UTC(Universal Time/Temps Cordonné 世界標(biāo)準(zhǔn)時(shí)間)
  3. CST可以為如下4個(gè)不同的時(shí)區(qū)的縮寫:美國(guó)辛辨、澳大利亞、古巴或中國(guó)的標(biāo)準(zhǔn)時(shí)間
    • 美國(guó)中部時(shí)間:Central Standard Time (USA) UT-6:00
    • 澳大利亞中部時(shí)間:Central Standard Time (Australia) UT+9:30
    • 中國(guó)標(biāo)準(zhǔn)時(shí)間:China Standard Time UT+8:00
    • 古巴標(biāo)準(zhǔn)時(shí)間:Cuba Standard Time UT-4:00

以上換算關(guān)系:GMT + 8 = UTC + 8 = CST-8

UTC 是指當(dāng)前使用的時(shí)間系統(tǒng)為世界標(biāo)準(zhǔn)時(shí)間,也稱世界協(xié)調(diào)時(shí)間斗搞。英文名稱為 Coordinated Universal Time指攒,法文名稱為 Temps Universel Coordonné。作為英文縮寫 CUT 和法文縮寫 TUC 的妥協(xié)方案僻焚,簡(jiǎn)稱 UTC允悦。中國(guó)所處時(shí)區(qū)為 UTC+8
CST:中國(guó)標(biāo)準(zhǔn)時(shí)間(China Standard Time),這個(gè)解釋針對(duì)RedHat Linux和CentOS
CET(Central European Time歐洲中部時(shí)間)=UTC/GMT + 1

時(shí)區(qū)對(duì)比

通過(guò)前兩個(gè)章節(jié)的對(duì)比可以發(fā)現(xiàn)

操作系統(tǒng)時(shí)區(qū)為CST-8 <=> JAVA組件進(jìn)程時(shí)區(qū)為GMT

故相差8小時(shí)屬于正常虑啤,符合預(yù)期

問(wèn)題解決

重新設(shè)置操作系統(tǒng)時(shí)區(qū)

[root@engine ~]# tzselect
Please identify a location so that time zone rules can be set correctly.
Please select a continent or ocean.
 1) Africa
 2) Americas
 3) Antarctica
 4) Arctic Ocean
 5) Asia
 6) Atlantic Ocean
 7) Australia
 8) Europe
 9) Indian Ocean
10) Pacific Ocean
11) none - I want to specify the time zone using the Posix TZ format.
#? 5
Please select a country.
 1) Afghanistan       18) Israel            35) Palestine
 2) Armenia       19) Japan         36) Philippines
 3) Azerbaijan        20) Jordan            37) Qatar
 4) Bahrain       21) Kazakhstan        38) Russia
 5) Bangladesh        22) Korea (North)     39) Saudi Arabia
 6) Bhutan        23) Korea (South)     40) Singapore
 7) Brunei        24) Kuwait            41) Sri Lanka
 8) Cambodia          25) Kyrgyzstan        42) Syria
 9) China         26) Laos          43) Taiwan
10) Cyprus        27) Lebanon           44) Tajikistan
11) East Timor        28) Macau         45) Thailand
12) Georgia       29) Malaysia          46) Turkmenistan
13) Hong Kong         30) Mongolia          47) United Arab Emirates
14) India         31) Myanmar (Burma)       48) Uzbekistan
15) Indonesia         32) Nepal         49) Vietnam
16) Iran          33) Oman          50) Yemen
17) Iraq          34) Pakistan
#? 9
Please select one of the following time zone regions.
1) east China - Beijing, Guangdong, Shanghai, etc.
2) Heilongjiang (except Mohe), Jilin
3) central China - Sichuan, Yunnan, Guangxi, Shaanxi, Guizhou, etc.
4) most of Tibet & Xinjiang
5) west Tibet & Xinjiang
#? 1

The following information has been given:

    China
    east China - Beijing, Guangdong, Shanghai, etc.

Therefore TZ='Asia/Shanghai' will be used.
Local time is now:  Fri Jan 25 15:10:18 CST 2019.
Universal Time is now:  Fri Jan 25 07:10:18 UTC 2019.
Is the above information OK?
1) Yes
2) No
#? 1

You can make this change permanent for yourself by appending the line
    TZ='Asia/Shanghai'; export TZ
to the file '.profile' in your home directory; then log out and log in again.

Here is that TZ value again, this time on standard output so that you
can use the /usr/bin/tzselect command in shell scripts:
Asia/Shanghai
[root@engine ~]# 

調(diào)整時(shí)區(qū)文件到對(duì)應(yīng)目錄

備份當(dāng)前的時(shí)區(qū)配置

[root@engine ~]# mv /etc/localtime /etc/localtime-old

替換系統(tǒng)時(shí)區(qū)文件

[root@engine ~]# cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

或者創(chuàng)建鏈接文件

[root@engine ~]# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

修改clock系統(tǒng)配置文件/etc/sysconfig/clock為如下內(nèi)容

[root@swk-204 ~]# cat /etc/sysconfig/clock
ZONE="Asia/Shanghai"
UTC=false                          #設(shè)置為false隙弛,硬件時(shí)鐘不于utc時(shí)間一致
ARC=false
[root@swk-204 ~]# 

設(shè)置操作系統(tǒng)環(huán)境變量TZ

/etc/profile~/.bashrc文件中設(shè)置環(huán)境變量TZ

export TZ='Asia/Shanghai'

或者

TZ='Asia/Shanghai'; export TZ

通過(guò)source命令即可完成設(shè)置

JAVA進(jìn)程調(diào)整時(shí)區(qū)

修改Java虛擬機(jī)時(shí)間

JAVA啟動(dòng)參數(shù)

-Duser.timezone=GMT+8

每個(gè)java程序啟動(dòng)的時(shí)候加參數(shù)

JAVA硬編碼

import java.util.TimeZone;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class TimeZoneStartup {  
    /**
     * 設(shè)置時(shí)區(qū)
     */
    @PostConstruct
    public void init(){
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
    }
    
}

每個(gè)java程序都需要編碼

JRE時(shí)區(qū)加載

JVM的時(shí)區(qū)默認(rèn)的是操作系統(tǒng)的時(shí)區(qū)JVM是從/etc/sysconfig/clock這個(gè)文件中 獲取時(shí)區(qū)信息的

獲取當(dāng)前操作系統(tǒng)時(shí)區(qū)

編輯JAVA文件MainClass.java

import java.util.TimeZone;

public class MainClass {
     public static void main(String[] args) {
        System.out.println(TimeZone.getDefault());
    }
}

執(zhí)行程序

[root@swk-204 ~]# javac MainClass.java 
[root@swk-204 ~]# java MainClass
sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null]
[root@swk-204 ~]# 

刨根問(wèn)底

Sun上面有和我這種情況相關(guān)的bug - Default timezone is incorrectly set occasionally on Linux(http://bugs.sun.com/view_bug.do?bug_id=6456628), 里面描述了java vm取的默認(rèn)timezone的算法

By examining the available JVM source code, I noticed that the logic that works out the default timezone on Linux is incorrect.
The way JVM works out the default timezone is as follows:

1) Looks to environment variable TZ
This is not set in our linux box

2) JVM looks for the file /etc/sysconfig/clock and tries to find the "ZONE" entry.
However, on these host the ZONE entry does not have a double quote around the actual variable, and the JVM code is unable to recongise the entry.

3) If the ZONE entry is not found, the JVM will compare contents fo /etc/localtime with the contents of every file in /usr/share/zoneinfo recursively. When the contents matches, it returns the path and filename, referenced from /usr/share/zoneinfo
On our machine, there are three files in /usr/share/zoneinfo that matches /etc/localtime (these files are standard on RHEL4 machines):
/usr/share/zoneinfo/America/New_York
/usr/share/zoneinfo/posixrules
/usr/share/zoneinfo/EST5EDT

What happens is that depends on the way OS transverse the filesystem, the name that JVM get can be different. On the box that fail the test, the jvm actually found posixrules file first, which means that the JVM thinks the timezone is "posixrules". The JVM will then attempt to look up timezone mapping in <java.home>/jre/lib/zi directory, and it will fail the find the timezone. So it will revert to the default GMT+offset timezone id as a fail safe, which does not take into the account for daylight saving.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
We have three identical Linux server and ran the test case included below, the time reported by the machine is different.

1)如有環(huán)境變量 TZ設(shè)置,則用TZ中設(shè)置的時(shí)區(qū)

2)在 /etc/sysconfig/clock文件中找 "ZONE"的值

3)如何2)都沒(méi)狞山,就用/etc/localtime 和 /usr/share/zoneinfo 下的時(shí)區(qū)文件進(jìn)行匹配全闷,如找到匹配的,就返回對(duì)應(yīng)的路徑和文件名萍启。

Java TimeZone 和 Linux TimeZone問(wèn)題參考文檔:

echo $TZ

附錄

jinfo命令參考文檔:http://www.reibang.com/p/ece32dacce64

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末总珠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子勘纯,更是在濱河造成了極大的恐慌局服,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驳遵,死亡現(xiàn)場(chǎng)離奇詭異淫奔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)堤结,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門唆迁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人竞穷,你說(shuō)我怎么就攤上這事媒惕。” “怎么了来庭?”我有些...
    開(kāi)封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)穿挨。 經(jīng)常有香客問(wèn)我月弛,道長(zhǎng),這世上最難降的妖魔是什么科盛? 我笑而不...
    開(kāi)封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任帽衙,我火速辦了婚禮,結(jié)果婚禮上贞绵,老公的妹妹穿的比我還像新娘厉萝。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布谴垫。 她就那樣靜靜地躺著章母,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翩剪。 梳的紋絲不亂的頭發(fā)上乳怎,一...
    開(kāi)封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音前弯,去河邊找鬼蚪缀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恕出,可吹牛的內(nèi)容都是我干的询枚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浙巫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼金蜀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起狈醉,我...
    開(kāi)封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤廉油,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后苗傅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抒线,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年渣慕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘶炭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逊桦,死狀恐怖锅移,靈堂內(nèi)的尸體忽然破棺而出晦雨,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布恨锚,位于F島的核電站,受9級(jí)特大地震影響扭倾,放射性物質(zhì)發(fā)生泄漏薪缆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一炬称、第九天 我趴在偏房一處隱蔽的房頂上張望汁果。 院中可真熱鬧,春花似錦玲躯、人聲如沸据德。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棘利。三九已至橱野,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赡译,已是汗流浹背仲吏。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝌焚,地道東北人裹唆。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像只洒,于是被迫代替她去往敵國(guó)和親许帐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容