到底该用GMT+8还是GMT
最近在使用date命令时,发现表示东8区(中国时区)要使用GMT-8,但在Java中却需要使用GMT+8,如下:
而在Java中,如下:
这就让人有点迷糊了,经过一段时间搜索,发现在时区表达形式上还有不少知识点呢!
众所周知,为了方便各地区本地时间之间的转换,人们将全球划分为了24个时区,以格林尼治天文台(GMT)为零时区,往东西两个方向分别有12个时区,所以自然有了以GMT为前缀的时区表示法,如下:
GMT+8表示东8区,中国就是使用这个时区,而GMT-8表示西8区,如果格林尼治天文台的本地时间是2022-03-19的0点,那么GMT+8地区的本地时间就是2022-03-19的8点,而GMT-8的本地时间就是往前8小时,即2022-03-18的16点。
注意,上面的各地区本地时间的表述虽然不同,但它们实际是同一个时刻(绝对时间),要理解本地时间与绝对时间的区别。
GMT+8正是Java中支持的时区表示法,那为啥Linux中却是GMT-8呢?实际上Linux中的GMT-8也可以写成Etc/GMT-8,这才是它的标准名称,如下:
可以发现用Etc/GMT-8的话,Linux与Java的输出都是一样的了,是的,Etc/GMT-8也是一种类似GMT+8的时区表示机制,只不过它的+-号是反的。
Ok,虽然上面的差异弄清楚了,但时区的表示形式还没有介绍完,接着往下看…
除了GMT+8表示方式外,我们还经常会看到UTC+8这样的表示方式,这是UTC时区表示法。
即生GMT何生UTC?这是由于GMT是以格林尼治天文台为时间基准,但地球不是完美球体且自转速度在变慢,所以地球自转速度并不均匀,这导致以格林尼治天文台为时间基准是不准的。
为了更准确度量时间,科学家们发明了UTC时间,以铯原子跃迁次数来度量时间,比GMT时间更准确,为了保证GMT的准确性,每隔几年GMT时间会做一次调整,以与UTC时间对齐。
因此,既然有了更准确的UTC,那么就有了以UTC为前缀的时区表示法,如中国时区可使用UTC+8。
各时区偏移量表示法一览表,如下:
除了用偏移量来表示时区,为了方便,人们还按区域/城市的方式来定义时区,如Asia/Shanghai,Asia/Hong_Kong都表示东8区,具体有哪些城市命名的时区,可以在时区数据库中查看。
另外,为了简化区域时区表示法,又定义了一套时区缩写,如CST是中国时区China Standard Time的缩写,可以在时区缩写中查看各种缩写定义。
注意,一般都不建议使用时区缩写,因为时区缩写的命名经常会重复,比如CST是Central Standard Time(北美中部标准时间UTC -6)、China Standard Time(中国标准时间UTC +8)、Cuba Standard Time(古巴标准时间UTC -5)。
由于不同软件对CST的解释可能不同,导致会出现时间相差13或14个小时的情况,这在Java搭配MySQL时经常出现,我还专门写了一篇文章mysql的timestamp会存在时区问题?,对于一定要使用时区缩写的场景,可以使用香港时区缩写HKT,它不重复且和上海处于同一个时区。
在Java中和时区相关的类有TimeZone、ZoneId,其中TimeZone是老的时区类,而ZoneId是新的时区类,它有ZoneOffset和ZoneRegion两个子类,分别代表偏移量表示法和区域表示法。
那它们都支持上述的哪些时区写法呢?写个Demo验证一下,如下:
输出如下:
虽然偏移量表示法与区域表示法都可以表示时区,但由于夏令时的存在,它们并不完全等同。
夏令时(Daylight Saving Time: DST),也叫 夏时制,是指为了节约能源,在天亮的早的夏季,人为将时间调快一小时,以充分利用光照资源,节约照明用电。
而中国在 1986 年至 1991 年也实行过夏令时,在1986~1991的每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即将表针由2时拨至3时,夏令时开始;到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即将表针由2时拨至1时,夏令时结束。从1986年到1991年的六个年度,除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。
故会有下面看起来有点奇怪的现象:
为什么Asia/Shanghai输出为3点,而GMT+8输出为2点呢?原因是1986-05-04 02:00:00这个时间点中国正开始实行夏令时,时钟拨快了1小时。
而GMT+8为什么输出为2点呢?因为中国、马来西亚、菲律宾、新加坡的时区都是GMT+8,只有中国在实行夏令时,而在GMT+8中没法感知到区域信息,那java只能以没有实行夏令时的方法来计算本地时间了。
正是由于夏令时的存在,导致程序可能出现诡异的现象甚至bug,如下:
输出如下:
为啥会这样呢?原因是本地时间虽然看起来没变,但Asia/Shanghai这个代表的时区却发生了变化。
我们可以将上面printZonedDateTime中时间格式由yyyy-MM-dd HH:mm:ss VV修改为yyyy-MM-dd HH:mm:ss VV xxx再执行,发现输出如下:
如上,夏令时导致Asia/Shanghai这个时区不一定是东8区了,也可能是东9区,故Java中,想将ZoneRegion转换为ZoneOffset,需要传递一个instant时刻参数,如下:
夏令时真是一种自欺欺人的做法,还好中国从1991年后就没再实行了!
作者:打码日记
链接:
如何在linux下 使用java代码正确获取夏令时的时间
一:环境搭建
OpenOffice 下载地址
下载地址
解压后将目录下的所有jar包放在工程的lib下面或者采用引用的方式调用这些jar包。
下载后安装,我安装的路径为D:/openOffice/install/
二:启动服务
可以通过cmd调用服务, ” cd D:/openOffice/install/program”
执行
soffice -headless -accept=”socket,host=127.0.0.1,port=8100;urp;” -nofirststartwizard
查看是否安装成功,查看端口对应的pid
netstat -ano|findstr 8100
查看pid对应的服务程序名
tasklist|findstr pid值
也可以把这一步省略,放到java程序中调用服务,因为启动服务占用内存比较大,在java中可以在使用
的时候调用,然后马上销毁。
三:程序代码
1:将word转换为pdf方法
1 // 将word格式的文件转换为pdf格式
2 public void Word2Pdf(String srcPath, String desPath) throws IOException {
3 // 源文件目录
4 File inputFile = new File(srcPath);
5 if (!inputFile.exists()) {
6 System.out.println(“源文件不存在!”);
7 return;
8 }
9 // 输出文件目录
10 File outputFile = new File(desPath);
11 if (!outputFile.getParentFile().exists()) {
12 outputFile.getParentFile().exists();
13 }
14 // 调用openoffice服务线程
15 String command = “D:/openOffice/install/program/soffice.exe -headless -accept=\”socket,host=127.0.0.1,port=8100;urp;\””;
16 Process p = Runtime.getRuntime().exec(command);
17
18 // 连接openoffice服务
19 OpenOfficeConnection connection = new SocketOpenOfficeConnection(
20 “127.0.0.1”, 8100);
21 connection.connect();
22
23 // 转换word到pdf
24 DocumentConverter converter = new OpenOfficeDocumentConverter(
25 connection);
26 converter.convert(inputFile, outputFile);
27
28 // 关闭连接
29 connection.disconnect();
30
31 // 关闭进程
32 p.destroy();
33 System.out.println(“转换完成!”);
34 }
2:调用方法
1 @Test
2 public void testWord2Pdf() throws IOException {
3 String srcPath = “E:/test.docx”;
4 String desPath = “E:/test.pdf”;
5 Word2Pdf(srcPath, desPath);
6 }
以上代码经过验证,可以正常运行。
四:遇到问题
错误信息:
java.net.ConnectException: connection failed: socket,host=10.101.50.71,port=8100,tcpNoDelay=1: java.net.ConnectException: Connection refused: connect
at com.artofsolving.jodconverter.openoffice.connection.AbstractOpenOfficeConnection.connect(AbstractOpenOfficeConnection.java:79)
原因以及解决方法:第一次调用,soffice需要注册,所以到soffice.exe的安装路径下双击soffice.exe,注册即可。
java中utc时间怎么转换为本地时间?
java utc转本地时间的方法:
1、创建一个格式化时间对象simpleDateFormat,并初始化格式yyyy-MM-dd HH:mm:ss:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
2、创建时区对象utcZone,获取utc所在的时区
TimeZone utcZone = TimeZone.getTimeZone(“UTC”);
3、设置utc时区,为转换做准备
simpleDateFormat.setTimeZone(utcZone);
4、获取本地时间,并转换
Date myDate = simpleDateFormat.parse(rawQuestion.getString(“AskDateTime”));
5,按照上面的流程就转换本地时间了。
java 日历bug 时间增加1小时?
这个其实是夏令时问题,Calendar是有默认时区的,默认时区为欧美那边的:
1991-4-14这天由于夏令时,时钟需要拨快1小时,你查下什么是夏令时即可。查询夏令时表后,可知类似会出现时钟+1小时的日期还有比如1989-4-16、1990-4-15等日期。
为解决夏令时问题,在中国大陆,可以将时区定义为中国所在时区GMT+8,将代码修改为以下,可解决夏令时问题: