Table of Contents
一切缘起于学习Spark的日期时间函数。其中的to_utc_timestamp和from_utc_timestamp函数会涉及到TimeZone的参数。在Spark的源码中追溯,会发现TimeZone的参数会传到源文件:spark/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala,从而看到Spark实际上用了很多的Java有关时间的库。
...
import java.sql.{Date, Timestamp}
import java.time._
import java.time.Year.isLeap
import java.time.temporal.IsoFields
import java.util.{Locale, TimeZone}
import java.util.concurrent.TimeUnit._
...
于是需要学习这个库:java.util.TimeZone。参考其说明文档:https://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html。我们使用ID的字符串,通过函数getTimeZone得到相应的TimeZone:
TimeZone tz = TimeZone.getTimeZone(String ID);
而这里的String ID可以有两种形式
- 一种是有效的ID,例如"America/Los_Angeles",能看出其意义。而这些字符串可以通过函数getAvailableIDs得到;
- 一种则是自定义的ID(CustomID),就是以GMT开头的一定格式的时区表示。比较简单,参考上面的网页。在实际生成TimeZone的时候,自定义的ID会先转成正规化的自定义ID(NormalizedCustomID)。
现在来看getAvailableIDs得到的有效ID。注意到有效的ID字符串中会包含“/”字符,于是考虑以“/”划分的单词的个数来考察这些ID。而这个库与Spakr没有什么关系,我们可以在Scala的REPL中做一些探索。
import java.util.TimeZone
val timezoneIds = TimeZone.getAvailableIDs
仅包含一个单词的ID
timezoneIds.filter(_.split('/').length<2)
我们可以把结果分成这几个小类:
- GMT, GMT0, UCT, UTC,
- GB, GB-Eire, NZ, NZ-CHAT, PRC, ROK, W-SU,
- Cuba, Egypt, Eire, Greenwich, Hongkong, Iceland, Iran, Israel, Jamaica, Japan, Kwajalein, Libya, Navajo, Poland, Portugal, Singapore, Turkey, Universal, Zulu,
- CST6CDT, EST5EDT, MST7MDT, PST8PDT,
- CET, EET, MET, WET,
- EST, HST, MST, ACT, AET, AGT, ART, AST, BET, BST, CAT, CNT, CST, CTT, EAT, ECT, IET, IST, JST, MIT, NET, NST, PLT, PNT, PRT, PST, SST, VST
第一类是标准时间。第二类是一些地名的缩写,搜索一下就能知道。第三类则是全的地名。第四类是美国那几个有夏时制的时区。第五类则是欧洲时区,可参考网页:
第六类是那些已经废弃的时区缩写,具体内容可以参考网页:https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html。这里看看具体的解释:
EST - -05:00
HST - -10:00
MST - -07:00
ACT - Australia/Darwin
AET - Australia/Sydney
AGT - America/Argentina/Buenos_Aires
ART - Africa/Cairo
AST - America/Anchorage
BET - America/Sao_Paulo
BST - Asia/Dhaka
CAT - Africa/Harare
CNT - America/St_Johns
CST - America/Chicago
CTT - Asia/Shanghai
EAT - Africa/Addis_Ababa
ECT - Europe/Paris
IET - America/Indiana/Indianapolis
IST - Asia/Kolkata
JST - Asia/Tokyo
MIT - Pacific/Apia
NET - Asia/Yerevan
NST - Pacific/Auckland
PLT - Asia/Karachi
PNT - America/Phoenix
PRT - America/Puerto_Rico
PST - America/Los_Angeles
SST - Pacific/Guadalcanal
VST - Asia/Ho_Chi_Minh
包含多于两个单词的ID
timezoneIds.filter(_.split('/').length>2)
结果不多,而且第一个单词都是America,总单词的个数都是三:
- America/Argentina/Buenos_Aires, America/Argentina/Catamarca, America/Argentina/ComodRivadavia, America/Argentina/Cordoba, America/Argentina/Jujuy, America/Argentina/La_Rioja, America/Argentina/Mendoza, America/Argentina/Rio_Gallegos, America/Argentina/Salta, America/Argentina/San_Juan, America/Argentina/San_Luis, America/Argentina/Tucuman, America/Argentina/Ushuaia,
- America/Indiana/Indianapolis, America/Indiana/Knox, America/Indiana/Marengo, America/Indiana/Petersburg, America/Indiana/Tell_City, America/Indiana/Vevay, America/Indiana/Vincennes, America/Indiana/Winamac,
- America/Kentucky/Louisville, America/Kentucky/Monticello,
- America/North_Dakota/Beulah, America/North_Dakota/Center, America/North_Dakota/New_Salem
timezoneIds.filter(_.split('/').length>2).map(_.split('/')(1)).toSet
这个命令可以得到有哪些不同的第二个单词:
- Argentina, Indiana, Kentucky, North_Dakota
正好包含两个单词的ID
这一类ID比较多,因此我们先查看一下有哪些不同的第一个单词:
timezoneIds.filter(_.split('/').length==2).map(_.split('/')(0)).toSet
结果是:
- US, Mexico, Atlantic, Europe, Arctic, Canada, SystemV, Brazil, Australia, Etc, Asia, Indian, Chile, America, Pacific, Africa, Antarctica
也可以直接做统计:
timezoneIds.filter(_.split('/').length==2).groupBy(_.split('/')(0)).mapValues(_.length)
得到结果:
- US -> 13, Mexico -> 3, Atlantic -> 12, Europe -> 63, Arctic -> 1, Canada -> 8, SystemV -> 13, Brazil -> 4, Australia -> 23, Etc -> 35, Asia -> 98, Indian -> 11, Chile -> 2, America -> 140, Pacific -> 43, Africa -> 54, Antarctica -> 12
而用下面的命令就可以对不同的第二个单词做分别的列举了:
timezoneId.filter(_.split('/').length==2).filter(_.split('/')(0)=="?")
可以按第二个单词来分作几个小类。第一小类是国家名US, Mexico, Canada, Brazil和Chile
- US/Alaska, US/Aleutian, US/Arizona, US/Central, US/East-Indiana, US/Eastern, US/Hawaii, US/Indiana-Starke, US/Michigan, US/Mountain, US/Pacific, US/Pacific-New, US/Samoa
- Mexico/BajaNorte, Mexico/BajaSur, Mexico/General
- Canada/Atlantic, Canada/Central, Canada/Eastern, Canada/Mountain, Canada/Newfoundland, Canada/Pacific, Canada/Saskatchewan, Canada/Yukon
- Brazil/Acre, Brazil/DeNoronha, Brazil/East, Brazil/West
- Chile/Continental, Chile/EasterIsland
第二小类是特殊的两个体系SystemV和Etc
- SystemV/AST4, SystemV/AST4ADT, SystemV/CST6, SystemV/CST6CDT, SystemV/EST5, SystemV/EST5EDT, SystemV/HST10, SystemV/MST7, SystemV/MST7MDT, SystemV/PST8, SystemV/PST8PDT, SystemV/YST9, SystemV/YST9YDT
- Etc/GMT, Etc/GMT+0, Etc/GMT+1, Etc/GMT+10, Etc/GMT+11, Etc/GMT+12, Etc/GMT+2, Etc/GMT+3, Etc/GMT+4, Etc/GMT+5, Etc/GMT+6, Etc/GMT+7, Etc/GMT+8, Etc/GMT+9, Etc/GMT-0, Etc/GMT-1, Etc/GMT-10, Etc/GMT-11, Etc/GMT-12, Etc/GMT-13, Etc/GMT-14, Etc/GMT-2, Etc/GMT-3, Etc/GMT-4, Etc/GMT-5, Etc/GMT-6, Etc/GMT-7, Etc/GMT-8, Etc/GMT-9, Etc/GMT0, Etc/Greenwich, Etc/UCT, Etc/UTC, Etc/Universal, Etc/Zulu
剩下的第三小类就是所有ID的主力了,包含了六大洲和四大洋
- Atlantic -> 12,
- Europe -> 63,
- Arctic -> 1,
- Australia -> 23,
- Asia -> 98,
- Indian -> 11,
- America -> 140,
- Pacific -> 43,
- Africa -> 54,
- Antarctica -> 12