最近有很多读者朋友和朋友交朋友cst哪个时区有疑问。有网友整理了相关内容,希望能回答你的疑惑。关于,这个网站已经为你找到了问题的答案,希望对你有所帮助。
原创:编码日记(微信微信官方账号)ID:codelogs),欢迎分享,请保留转载来源。
简介
最近在使用date在命令中,发现东8区(中国时区)需要使用GMT-8,但在Java但需要在中间使用GMT 8,如下:
$ TZ='GMT-8' date -d@1647658144 '%F %T %:z'2022-03-19 10:49:04 08:00# 如果用GMT 8,反而慢了16个小时$ TZ='GMT 8' date -d@1647658144 '%F %T %:z'2022-03-18 18:49:04 -08:00
而在Java中,如下:
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XXX");String dateStr=dtf.format(Instant.ofEpochSecond(1647658144).atZone(ZoneId.of("GMT 8&);System.out.println(dateStr);//输出2022-03-19 10:49:04 08:00
这让人有点困惑,经过一段时间的搜索,发现时区表达形式上有很多知识点!
时区偏移表示法
众所周知,为了方便各地区当地时间的转换,人们将世界划分为24个时区,格林尼治天文台(GMT)零时区,东西方向有12个时区,所以自然有GMT前缀时区表示法如下:
GMT 8表示东8区,中国使用这个时区,而GMT-如果格林尼治天文台的本地时间是2022-03-190点,那么8表示西8区,GMT 8个地区的当地时间是2022-03-19点,GMT-本地时间为前8小时,即2022-03-18的16点。
请注意,虽然上述地区的当地时间表达不同,但它们实际上是同一时刻(绝对时间),以了解当地时间和绝对时间的区别。
请注意,虽然上述地区的当地时间表达不同,但它们实际上是同一时刻(绝对时间),以了解当地时间和绝对时间的区别。
GMT 8正是Java支持时区表示法,那为什么呢?Linux中却是GMT-8呢?实际上Linux中的GMT-8也可以写成Etc/GMT-8.这是它的标准名称,如下:
$ TZ='Etc/GMT-8' date -d@1647658144 -Is2022-03-19T10:49:04 08:00
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XXX");String dateStr=dtf.format(Instant.ofEpochSecond(1647658144).atZone(ZoneId.of("Etc/GMT-8&);System.out.println(dateStr);//输出2022-03-19 10:49:04 08:00
可以发现用Etc/GMT-8的话,Linux与Java输出是一样的,是的,Etc/GMT-8也是类似的GMT 8时区表示机制,但它 -号是反的。
Ok,虽然上述差异已经明确,但时区的表达形式尚未介绍,然后向下看…
除了GMT 除了表达方式,我们还经常看到UTC 8这种表达方式,这是UTC时区表示法。
Ok,虽然上述差异已经明确,但时区的表达形式尚未介绍,然后向下看…
除了GMT 除了表达方式,我们还经常看到UTC 8这种表达方式,这是UTC时区表示法。
即生GMT何生UTC?这是由于GMT以格林尼治天文台为时间基准,但地球不是完美的球体,自转速度较慢,因此地球自转速度不均匀,导致以格林尼治天文台为时间基准不准确。
|
偏移表示法 |
描述 | |
相对GMT多8个小时 |
|
同GMT 8, -号相反 |
|
同GMT 8 |
|
精确到分钟级 |
|
精确到秒级 |
|
精确到分钟级,省略冒号 |
|
准确到秒级,省略冒号 |
|
精确到分钟级,省略前缀 |
|
精确到秒级,省略前缀 |
|
精确到分钟级,省略前缀和冒号 |
|
准确到秒级,省略前缀和冒号 |
表示零时区,等同于GMT、UTC、GMT 0、UTC 0
时区区域表示法
人们除了用偏移来表示时区外,还根据区域/城市的方式来定义时区,如Asia/Shanghai,Asia/Hong_Kong都说东8区,具体有哪些城市命名的时区,可在时区数据库中查看。
|
区域表示法 |
描述 | |
时区缩写,慎用
Java中表示时区
在Java与中和时区相关的类别有TimeZone、ZoneId,其中TimeZone是老时区类,而且ZoneId是新时区类,它有ZoneOffset和ZoneRegion两个子类分别代表偏移表示和区域表示。
那么他们都支持上述时区写作呢?写个Demo验证一下,如下:
public static void main(String[]args){ printZoneId(" 08:00"); printZoneId(" 0800"); printZoneId("GMT 8"); printZoneId("Etc/GMT-8"); printZoneId("UTC 8"); printZoneId("Asia/Shanghai"); printZoneId("CST"); printZoneId("Z");}public static void printZoneId(String zone){ ZoneId zoneId; if(!ZoneId.SHORT_IDS.containsKey(zone)){ zoneId=ZoneId.of(zone); }else{ zoneId=ZoneId.of(ZoneId.SHORT_IDS.get(zone)); }TimeZone timeZone=TimeZone.getTimeZone(zone); ZoneOffset zoneOffset=zoneId.getRules().getOffset(Instant.now()); DateTimeFormatter dtf=DateTimeFormatter.ofPattern("xxx ZZZ O OOOO"); System.out.printf("%-14s -> %-28s -> class:%s -> TimeZone.offset:%d \
", zone, dtf.format(zoneOffset), zoneId.getClass().getSimpleName(), timeZone.getRawOffset());}
|
时区写法 |
ZoneId |
TimeZone | +08:00 |
|
+0800 |
||
GMT+8 |
||
Etc/GMT-8 |
||
UTC+8 |
||
Asia/Shanghai |
||
CST |
||
Z |
支持
偏移量表示法与区域表示法区别
虽然偏移量表示法与区域表示法都可以表示时区,但由于夏令时的存在,它们并不完全等同。夏令时(Daylight Saving Time: DST),也叫 夏时制,是指为了节约能源,在天亮的早的夏季,人为将时间调快一小时,以充分利用光照资源,节约照明用电。
而中国在 1986 年至 1991 年也实行过夏令时,在1986~1991的每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即将表针由2时拨至3时,夏令时开始;到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即将表针由2时拨至1时,夏令时结束。从1986年到1991年的六个年度,除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。
故会有下面看起来有点奇怪的现象:
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV");Instant instant=Instant.ofEpochSecond(515527200);System.out.println(dtf.format(instant.atZone(ZoneId.of("Asia/Shanghai"))));//输出1986-05-04 03:00:00 Asia/ShanghaiSystem.out.println(dtf.format(instant.atZone(ZoneId.of("GMT+8"))));//输出1986-05-04 02:00:00 GMT+08:00
- 为什么Asia/Shanghai输出为3点,而GMT+8输出为2点呢?原因是1986-05-04 02:00:00这个时间点中国正开始实行夏令时,时钟拨快了1小时。
而GMT+8为什么输出为2点呢?因为中国、马来西亚、菲律宾、新加坡的时区都是GMT+8,只有中国在实行夏令时,而在GMT+8中没法感知到区域信息,那java只能以没有实行夏令时的方法来计算本地时间了。
夏令时导致的奇怪现象正是由于夏令时的存在,导致程序可能出现诡异的现象甚至bug,如下:
- 由于夏令时会将2点改成3点,导致2点没了,所以date命令报错了
$ TZ='Asia/Shanghai' date -d 1986-05-04T02:00:00 +%sdate: invalid date ‘1986-05-04T02:00:00’$ TZ='Asia/Shanghai' date -d 1986-05-04T03:00:00 +%s515527200
- 时间解析后再格式化输出,发现不一样了
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV");ZonedDateTime time1=ZonedDateTime.parse("1986-05-04 02:00:00 Asia/Shanghai", dtf);System.out.println(time1.format(dtf));//输出1986-05-04 03:00:00 Asia/Shanghai
时间加1小时,发现加了2小时或根本没变
public static void main(String[] args) { DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV"); //加1小时刚好夏令时开始 ZonedDateTime time1=ZonedDateTime.parse("1986-05-04 01:00:00 Asia/Shanghai", dtf); printZonedDateTime(time1); printZonedDateTime(time1.plusHours(1)); //加1小时刚好夏令时结束 ZonedDateTime time2=ZonedDateTime.parse("1986-09-14 01:00:00 Asia/Shanghai", dtf); printZonedDateTime(time2); printZonedDateTime(time2.plusHours(1)); }private static void printZonedDateTime(ZonedDateTime time){ DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss VV"); System.out.println(time.format(dtf));}
输出如下:
1986-05-04 01:00:00 Asia/Shanghai1986-05-04 03:00:00 Asia/Shanghai //加1小时,结果看起来加了2个小时1986-09-14 01:00:00 Asia/Shanghai1986-09-14 01:00:00 Asia/Shanghai //加1小时,结果时间看起来没变
为啥会这样呢?原因是本地时间虽然看起来没变,但Asia/Shanghai这个代表的时区却发生了变化。
我们可以将上面printZonedDateTime中时间格式由yyyy-MM-dd HH:mm:ss VV修改为yyyy-MM-dd HH:mm:ss VV xxx再执行,发现输出如下:
1986-05-04 01:00:00 Asia/Shanghai +08:001986-05-04 03:00:00 Asia/Shanghai +09:001986-09-14 01:00:00 Asia/Shanghai +09:001986-09-14 01:00:00 Asia/Shanghai +08:00
如上,夏令时导致Asia/Shanghai这个时区不一定是东8区了,也可能是东9区,故Java中,想将ZoneRegion转换为ZoneOffset,需要传递一个instant时刻参数,如下://输出+08:00Instant instant=Instant.now();System.out.println(ZoneId.of("Asia/Shanghai").getRules().getOffset(instant));//输出+09:00,在1986-05-04 02:00:00 +08:00处于夏令时,增加了1小时Instant instant=Instant.ofEpochSecond(515527200);System.out.println(ZoneId.of("Asia/Shanghai").getRules().getOffset(instant));夏令时真是一种自欺欺人的做法,还好中国从1991年后就没再实行了!
主题测试文章,只做测试使用。发布者:艾迪号,转转请注明出处:https://www.cqaedi.cn/baike/39121.html