之前在我的知识星球的Java面对面版块问过粉丝这样一个问题:在Java中,如何获取不同时区的当前时间?你知道如何正确回答这个问题吗?原理是什么?然后,紧接着,我提出了以下问题:为什么以下代码无法获取美国时间。(在东八区的电脑上)System.out.println(Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles")).getTime());接下来,本文将围绕这两个问题,带领读者一起学习Java中与时间相关的概念。时区前面提到了时区。很多读者可能不知道什么是时区。我先简单介绍一下。时区是地球上使用相同时间定义的区域。过去,人们通过观察太阳的位置(时角)来确定时间,这使得不同经度的地方的时间(当地时间)不同。1863年,时区的概念首次被使用。时区通过为一个地区设置标准时间来部分解决这个问题。世界各国在地球上所处的位置不同,所以不同国家,尤其是东西跨度大的国家,日出日落时间必然不同。这些偏差被称为时差。为了照顾到各地使用的方便,其他地方的人也很容易将本地时间换算成其他地方的时间。有关国际会议决定将地球表面按经度从东向西划分为区域,并规定相邻区域之间的时差为1小时。同一地区东西两端的人们看到太阳升起的时间间隔不会超过一个小时。人们穿越一个区域时,将时钟校正1小时(向西减1小时,向东加1小时),穿越多个区域时加减几个小时。这使用起来非常方便。当今世界分为24个时区。在实践中,一个国家或一个省份往往同时跨越两个或多个时区。为了行政上的方便,往往将一个国家或一个省归为一组。因此,时区并不是严格按照南北直线划分的,而是按照自然条件划分的。例如,中国幅员辽阔,跨越近5个时区。但为了方便和简单使用,实际上只以东八时区的标准时间,即北京时间为准。格林威治标准时间前面提到,时区通过为一个地区建立一个标准时间,部分解决了因不同地方看到的太阳位置不同而无法确定时间的问题。那么这个标准时间是多少?前面也提到过。中国位于东八区,一般用GMT+8来表示东八区的时区。好吧,看起来GMT就是所谓的标准时间。什么是格林威治标准时间?为什么要加+8代表东八区?GMT是GreenwichMeanTime的缩写,GreenwichMeanTime指的是位于英国伦敦郊区的皇家格林威治天文台。地方平均太阳时,因为本初子午线被定义为经过那里的经度线。格林威治子午线始于1924年2月5日,格林威治天文台负责每小时向全世界发布时间调整信息。国际天文学联合会于1928年决定采用从格林威治午夜开始的平均太阳时作为世界时,即通常所说的格林威治标准时间。一般用GMT+8来表示中国时间,因为中国地处东八区,时间比格林威治标准时间早8小时。北京时间也可以用CST表示,即ChinaStandardTime,又称ChinaStandardTime,是中国的标准时间。当GMT为0:00AM时,中国标准时间恰好为8:00AM。因此,有一个等式:CST=GMT+8小时时间戳。前面说了,全球各个时区的时间可能不一样,那么有没有什么办法可以不受时区的限制,准确的表示时间呢?呢绒布。其实还有,这个方法就是时间戳。时间戳是一种完整的、可验证的数据,可以表明一段数据存在于某个时间之前,通常是一个字符序列,唯一标识某个时刻。时间戳是指从格林威治标准时间1970年1月1日00:00:00开始到现在的总秒数。有了时间戳,无论我们处于哪个时区,从格林威治标准时间1970年1月1日00:00:00到现在的总秒数应该是相同的。因此,时间戳是一个完整的、可验证的数据,可以表明一条数据在特定的时间点已经存在。1970-01-01不知道大家有没有注意到一个特殊的时间,1970-01-01,这个时间相信每个开发者都不陌生。一般如果软件系统出现这个时间,说明有网络故障,在线BUG等。在计算机上,通常默认为0。当Timestamp为0时,表示1970年1月1日0:00:00:00:00:00的时间(GMT)。中国使用北京时间,在8区东部,对应于早上8:00。所以在中国这边,如果时间不对,往往会显示为1970年1月1日08:00。System.out.println(newDate(0));//ThuJan0108:00:00CST1970当我们在Java代码中使用newDate(0)创建时间时,结果为ThuJan0108:00:00CST1970,即,1970年1月1日上午8:00。Date前面提到了java.util.Java中的Date类,这个类通常用来表示时间。您可以通过getTime()方法访问java.util.Date实例的日期和时间,例如:Datedate=newDate();longtime=date.getTime();上面的代码其实是获取了时间戳,在源码中也有明确的表述:?所以,我们可以认为java.util.Java其实代表的是格林威治1970年1月1日0:00到0:00之间的总秒数当下。从Date的源码也可以看出,Date是不包含时区信息的,因为时间戳与时区无关。那么,我想把一个时间戳转换成不同时区的时间输出怎么办呢?显示不同时区的时间如果要将一个时间戳转换成对应时区的时间,就必须有一个地方可以获取时区。事实上,我们的电脑有时区相关的信息。不管我们使用的是什么操作系统的电脑,我们都可以查看时间,一般情况下,我们拿到的电脑都会显示中国时间,因为操作系统已经设置了一个默认的时区。其实Java中的时区信息也是从操作系统获取的,默认会使用操作系统的时区。当我们用System.out.println输出一个时间的时候,他会调用Date类的toString方法,这个方法会读取操作系统默认的时区来转换时间。publicStringtoString(){//"EEEMMMddHH:mm:sszzzyyyy";BaseCalendar.Datedate=normalize();...}privatefinalBaseCalendar.Datenormalize(){...TimeZonetz=TimeZone.getDefaultRef();if(tz!=cdate.getZone()){cdate.setZone(tz);CalendarSystemcal=getCalendarSystem(cdate);cal.getCalendarDate(fastTime,cdate);}returncdate;}staticTimeZonegetDefaultRef(){TimeZonedefaultZone=defaultTimeZone;if(defaultZone==null){//Needtoinitializethedefaulttimezone.defaultZone=setDefaultZone();assertdefaultZone!=null;}//这里不要clone.returndefaultZone;}主要代码如上。也就是说,如果我们要通过System.out.println输出一个Date类,输出美国的洛杉矶时间,就需要想办法把defaultTimeZone改成America/Los_Angeles。这个方法是:TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));所以,当我们要输出美国的洛杉矶时间时,可以这样选择:TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));Datedate=newDate();System.out.println(日期);还有一种方式是通过SimpleDateFormat来处理,我们在为什么阿里巴巴禁止将SimpleDateFormat定义为静态类型中也有介绍。这里就不展开了。接下来我们回到文章开头的问题:为什么下面的代码获取不到美国时间。(在东八区的电脑上)System.out.println(Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles")).getTime());其实答案前面已经说过了,我们查一下Date。在toString的源码中发现该方法在输出过程中只会获取系统默认的时区,只有修改默认时区才会显示该时区的时间。但是通过阅读Calendar的源码我们可以发现,getInstance方法虽然有一个参数可以传入时区,但是并没有将默认时区设置为传入的时区。但是Calendar.getInstance.getTime之后得到的时间只是一个时间戳,并没有保留任何与时区相关的信息。所以在输出的时候,还是显示当前系统默认时区的时间。Java8和时区了解Java8的朋友可能知道,Java8提供了一套新的时间处理API,比之前的时间处理API友好很多。Java8增加了对时区的支持,有时区的时间有:ZonedDate、ZonedTime、ZonedDateTime。每个时区对应一个ID,区域ID格式为“{region}/{city}”,如Asia/Shanghai、America/Los_Angeles等。Java8中可以直接使用如下代码输出美国洛杉矶时间:综上所述,世界上有很多时区,不同时区的时间是不一样的。中国以东八区时间为标准时间。美国从东海岸到西海岸,横跨第五区到第十区,共六个时区。所谓东八区一般表示为GMT+8,其中GMT指的是格林威治标准时间。计算机中经常使用时间戳来表示时间。时间戳是指当前时间的总秒数,例如格林威治的1970-01-0100:00:00。Java中的Date类不包含时区信息。在使用System.out.println打印Date时,会回调Date.toString方法,会获取系统默认的时区来转换时间。在Java8中,可以使用ZonedTime、ZonedDate和ZonedDateTime来表示带有时区信息的时间。LocalDateTimenow=LocalDateTime.now(ZoneId.of("美国/洛杉矶"));System.out.println(现在);拓展知识什么是冬令时?什么是夏令时?夏令时和冬令时的出现是为了充分利用夏天的阳光,所以时钟要向前拨一小时,冬天再拨回一小时。夏令时从三月的第二个星期日持续到十一月的第一个星期日。冬令时:北京与洛杉矶时差16小时,北京与纽约时差13小时。夏令时:北京与洛杉矶时差15小时,北京与纽约时差15小时。CET、UTC、GMT和CST的含义和关系是什么?CET,欧洲中部时间(英语:CentralEuropeanTime,CET)是比世界标准时间(UTC)早一小时的时区名称之一。UTC,CoordinatedUniversalTime,又称世界标准时间或世界协调时间,简称UTC。GMT,GreenwichMeanTime,是指位于英国伦敦郊外的皇家格林威治天文台的标准时间,因为本初子午线被定义为经过那里的经度。CST,北京时间,ChinaStandardTime,又称中国标准时间,是中国的标准时间。CET=UTC/GMT+1小时,CST=UTC/GMT+8小时,CST=CET+9
