搞清日期、时间与时区

涉及到跨时区的业务,需要搞清楚几个时间相关的概念:

格林尼治标准时间:Greenwich Mean Time(简称 GMT)

指位于伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。
理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时的时间。
但由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能和实际的太阳时相差16分钟。
由于地球每天的自转是有些不规则的,而且正在缓慢减速。所以,格林尼治时间已经不再被作为标准时间使用。
现在的标准时间(UTC)由原子钟提供。(在计算机中GMT 和 UTC 仍是等同的)

协调世界时(UTC):

又称世界标准时间或世界协调时间,是当今最主要的世界时间标准,以原子时秒长为基础。
国际原子时的误差为每日数纳秒,世界时的误差为每日数毫秒,UTC 便是这两种时标的一种折中。
为确保 UTC 与世界时相差不会超过 0.9 秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别。位于巴黎的国际地球自转事务中央局负责决定何时加入闰秒,一般会在每年的 6 月 30 日、12 月 31 日的最后一秒进行调整。
UTC 的应用及其广泛,被应用在大多数的计算机以及网络标准中。

夏令时与冬令时 Daylight Saving Time(简称 DST)

又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。
一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。。

多时区处理

在实际开发中,当时间用于显示时,一般使用系统默认的时区时间作为显示时间。将时间做为数据存储或传递给其他系统时(特别是跨平台调用),则使用标准的UTC/GMT时间(后面统称UTC),或者保留时区的字符串(如:2017-02-24T23:57:06.000+0000)。

  1. Android中表示日期时间的类型,有Date、Calendar,均为系统默认时区的时间,都是与时区相关的。
  2. SimpleDateFormat对象本身也是跟时区相关。
  3. Calendar在手动修改时区后,不能使用calendar.getTime方法来直接获取Date日期,因为此时的日期与setTime时的值相同,想要正确获取修改时区后的时间,应该通过Calendar的get方法
  4. TimeZone,我们可以通过TimeZone对象获取关于系统默认时区及其相关的详细信息。
    // Date、Calendar SimpleDateFormat 默认时区
    Date date = new Date();
    System.out.println(date);

    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());

    SimpleDateFormat sdf = new SimpleDateFormat("E MMM dd hh:mm:ss z yyy", Locale.US);
    System.out.println(sdf.format(date));

    calendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    SimpleDateFormat calendarSdf = new SimpleDateFormat("M.dd", Locale.US);
    System.out.println(calendarSdf.format(calendar.getTime()));
    // 修改时区之后,通过calendar的get方法
    System.out.println((calendar.get(Calendar.MONTH) + 1) + "." + calendar.get(Calendar.DAY_OF_MONTH));

    // 输出
    Mon Feb 27 12:40:17 CST 2017
    Mon Feb 27 12:40:17 CST 2017
    2.27
    2.26
    Mon Feb 27 12:40:17 CST 2017

读取本地时区信息

    /**
     * Gets the default <code>TimeZone</code> for this host.
     * The source of the default <code>TimeZone</code>
     * may vary with implementation.
     * @return a default <code>TimeZone</code>.
     * @see #setDefault
     */
    public static TimeZone getDefault() {
        return (TimeZone) getDefaultRef().clone();
    }


    TimeZone timeZone = TimeZone.getDefault()
    String id = timeZone.getID(); //获取时区id
    String name = timeZone.getDisplayName(); //获取名字
    int rawOffset = timeZone.getRawOffset(); //获取时差,返回值毫秒
    System.out.println("TimeZone ID = " + id);
    System.out.println("TimeZone Name = " + name);
    System.out.println("TimeZone RawOffset = " + rawOffset + "ms");
    rawOffset /= 1000;// 转换成秒
    System.out.println("TimeZone RawOffset = " + rawOffset / 3600 + "h"
            + (rawOffset % 3600) / 60 + "m"
            + (rawOffset % 3600) % 60 + "s");

    // 输出
    TimeZone ID = Asia/Shanghai
    TimeZone Name = 中国标准时间
    TimeZone RawOffset = 28800000ms
    TimeZone RawOffset = 8h0m0s

日期时间格式(官方定义)

字符意义类型例子
GEra designatorTextAD
yYearYear1996; 96
MMonth in yearMonthJuly; Jul; 07
wWeek in yearNumber27
WWeek in monthNumber2
DDay in yearNumber189
dDay in monthNumber10
FDay of week in monthNumber2
EDay in weekTextTuesday; Tue
aAm/pm markerTextPM
HHour in day (0-23)Number0
kHour in day (1-24)Number24
KHour in am/pm (0-11)Number0
hHour in am/pm (1-12)Number12
mMinute in hourNumber30
sSecond in minuteNumber55
SMillisecondNumber978
zTime zoneGeneral time zoneCST; GMT-08:00
ZTime zoneRFC 822 time zone +0800

    Date date = new Date();

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z", Locale.US);
    // 默认 是本地时区
    System.out.println(sdf.format(date));
    // GMT 时间
    sdf.setTimeZone(TimeZone.getTimeZone("GMT+9"));
    System.out.println(sdf.format(date));

    // 美国东部标准时间
    sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    System.out.println(sdf.format(date));

    // 中国标准时间
    sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    System.out.println(sdf.format(date));

    // 输出
    2017-02-26T16:56:14 CST
    2017-02-26T17:56:14 GMT+09:00
    2017-02-26T03:56:14 EST
    2017-02-26T16:56:14 CST

z VS Z

小z(General time zone):如果有对应的时区名称,显示名称,否则以GMTOffsetTimeZone格式显示。

// 时区名称
2017-02-27T13:37:40 CST
// GMTOffsetTimeZone
GMT Sign Hours : Minutes
2017-02-27T14:37:40 GMT+09:00

大Z:RFC 822 4-digit 格式

// RFC822TimeZone:Sign TwoDigitHours Minutes
2017-02-27T13:37:40 +0800

不能使用 时区名称来获取 时区

因为CST可以同时表示美国,澳大利亚,中国,古巴四个国家的标准时间:

Central Standard Time (USA) UT-6:00
Central Standard Time (Australia) UT+9:30
China Standard Time UT+8:00
Cuba Standard Time UT-4:00

    // 不能使用 时区名称来获取 时区,发现EST是准的,但是CST不准。

    Date date = new Date();

    SimpleDateFormat sdfZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss Z", Locale.US);
    // 默认 是本地时区
    System.out.println(sdfZ.format(date));
    sdfZ.setTimeZone(TimeZone.getTimeZone("CST"));
    System.out.println(sdfZ.format(date));

    // 美国东部标准时间
    sdfZ.setTimeZone(TimeZone.getTimeZone("EST"));
    System.out.println(sdfZ.format(date));
    sdfZ.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    System.out.println(sdfZ.format(date));
    // 输出
    2017-02-27T13:52:44 +0800
    2017-02-26T23:52:44 -0600
    2017-02-27T00:52:44 -0500
    2017-02-27T00:52:44 -0500

其他

1. Date formats(SimpleDateFormat)不是线程安全的,推荐一个线程一个实例。

发表评论

电子邮件地址不会被公开。 必填项已用*标注