JDK1.8源码解读之 Date

1,453 阅读7分钟

前言

  • Date类表示特定的时间瞬间,精度为毫秒。
  • 在JDK 1.1之前,Date类具有两个附加功能。
  • 它允许将日期解释为年,月,日,小时,分钟和秒值。
  • 它还允许格式化和解析日期字符串。
  • 不幸的是,这些功能的API不适合国际化。
  • 从JDK 1.1开始,应该使用Calendar类在日期和时间字段之间进行转换,而DateFormat类应该用于格式化和解析日期字符串。
  • 不建议使用Date中的相应方法。
  • 尽管Date类旨在反映协调世界时(UTC),但它可能并非完全如此,具体取决于Java虚拟机的宿主环境* 在所有情况下,几乎所有现代操作系统都假定1天= 24×60×60 = 86400秒。
  • 但是,在UTC中,大约每年一到两年一次,称为“leep second”。
  • leep second 秒始终被添加为一天的最后一秒,并且总是在12月31日或6月30日。
  • 例如,由于增加了leap秒,1995年的最后一分钟长61秒。大多数计算机时钟不够精确,无法反映leep second 的区别。
  • 某些计算机标准是按照格林威治标准时间(GMT)定义的,格林威治标准时间等于世界标准时间(UT)。
  • GMT是标准的“民事”名称; UT是同一标准的“科学”名称。
  • UTC与UT之间的区别在于,UTC基于原子钟,而UT基于天文观测,从所有实际目的来看,这都是很难区分的。
  • 由于地球的自转不均匀(它以复杂的方式减速并加速),所以UT并不总是均匀地流动。
  • 可以根据需要将秒引入UTC,以使UTC保持在UT1的0.9秒以内,UT1是UT的版本,并进行了某些校正。
  • 还有其他时间和日期系统;例如,基于卫星的全球定位系统(GPS)使用的时间标度已与UTC同步,但并未针对leap秒进行调整。
  • 有趣的进一步信息来源是美国海军天文台,特别是时间局,其网址为:tycho.usno.navy.mil
  • 及其在以下时间对“时间系统”的定义: tycho.usno.navy.mil/systime.htm…
  • 在Date类的所有接受或返回年,月,日,时,分和秒值的方法中,使用以下表示形式:
  • 年y由整数y-1900表示。
    
  • 一个月由0到11之间的整数表示; 0是一月,1是二月,依此类推;因此11月是12月。
    
  • 日期(一个月中的一天)通常以1到31之间的整数表示。
    
  • 一个小时用0到23的整数表示。因此,从午夜到凌晨1点是0,而从正午到下午1点则是12点。
    
  • 分钟通常以0到59之间的整数表示。
    
  • 秒由0到61之间的整数表示;
    
  • 值60和61仅在leap秒出现,甚至仅在实际上正确跟踪leap秒的Java实现中才出现。
    
  • 由于当前采用的是seconds秒,因此极不可能在同一分钟内出现两个leep second,但是此规范遵循ISO C的日期和时间约定。
    
  • 在所有情况下,为此目的而给方法提供的参数都不必在指定的范围内;例如,日期可以指定为1月32日,并解释为2月1日。

源码

package java.util;

public class Date
    implements java.io.Serializable, Cloneable, Comparable<Date>
{
    private static final BaseCalendar gcal =
                                CalendarSystem.getGregorianCalendar();
    private static BaseCalendar jcal;

    private transient long fastTime;

    private transient BaseCalendar.Date cdate;

    private static int defaultCenturyStart;

    private static final long serialVersionUID = 7523967970034938905L;

    /**
     * 分配一个Date对象并对其进行初始化,以便它表示分配该对象的时间(以最近的毫秒为单位)。
     *
     * @see     java.lang.System#currentTimeMillis()
     */
    public Date() {
        this(System.currentTimeMillis());
    }

    /**
     * 分配一个Date对象,并将其初始化为表示自标准基准时间(即epoch)以来的指定毫秒数,即标准时间1970年1月1日,格林尼治标准时间00:00:00。
     */
    public Date(long date) {
        fastTime = date;
    }


    /**
     * Return a copy of this object.
     * 返回一个Date对象的复制。
     */
    public Object clone() {
        Date d = null;
        try {
            d = (Date)super.clone();
            if (cdate != null) {
                d.cdate = (BaseCalendar.Date) cdate.clone();
            }
        } catch (CloneNotSupportedException e) {}
        return d;
    }


    private final static String wtb[] = {
        "am", "pm",
        "monday", "tuesday", "wednesday", "thursday", "friday",
        "saturday", "sunday",
        "january", "february", "march", "april", "may", "june",
        "july", "august", "september", "october", "november", "december",
        "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
        "mst", "mdt", "pst", "pdt"
    };
    
    private final static int ttb[] = {
        14, 1, 0, 0, 0, 0, 0, 0, 0,
        2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
        10000 + 0, 10000 + 0, 10000 + 0,    // GMT/UT/UTC
        10000 + 5 * 60, 10000 + 4 * 60,     // EST/EDT
        10000 + 6 * 60, 10000 + 5 * 60,     // CST/CDT
        10000 + 7 * 60, 10000 + 6 * 60,     // MST/MDT
        10000 + 8 * 60, 10000 + 7 * 60      // PST/PDT
    };

    /**
     * 返回毫秒时间
     */
    public long getTime() {
        return getTimeImpl();
    }

    private final long getTimeImpl() {
        if (cdate != null && !cdate.isNormalized()) {
            normalize();
        }
        return fastTime;
    }

    /**
     * 设置时间,参数为1970年开始的毫秒时间
     */
    public void setTime(long time) {
        fastTime = time;
        cdate = null;
    }

    /**
     * 判断当前时间是否比指定的时间早。
     */
    public boolean before(Date when) {
        return getMillisOf(this) < getMillisOf(when);
    }

    /**
     * 判断当前的时间是否比指定的时间晚。
     */
    public boolean after(Date when) {
        return getMillisOf(this) > getMillisOf(when);
    }

    /**
     * 比较两个日期是否相等。当且仅当参数不为null且是一个Date对象,该Date对象表示与该对象相同的时间点(以毫秒为单位)时,结果为true。
     * 因此,当且仅当getTime方法为两个对象返回相同的long值时,两个Date对象才相等。
     */
    public boolean equals(Object obj) {
        return obj instanceof Date && getTime() == ((Date) obj).getTime();
    }

    static final long getMillisOf(Date date) {
        if (date.cdate == null || date.cdate.isNormalized()) {
            return date.fastTime;
        }
        BaseCalendar.Date d = (BaseCalendar.Date) date.cdate.clone();
        return gcal.getTime(d);
    }

    /**
     * 比较两个Date对象的顺序。
     */
    public int compareTo(Date anotherDate) {
        long thisTime = getMillisOf(this);
        long anotherTime = getMillisOf(anotherDate);
        return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
    }

    public int hashCode() {
        long ht = this.getTime();
        return (int) ht ^ (int) (ht >> 32);
    }

    /**
     * Date对象转化为字符串
     */
    public String toString() {
        // "EEE MMM dd HH:mm:ss zzz yyyy";
        BaseCalendar.Date date = normalize();
        StringBuilder sb = new StringBuilder(28);
        int index = date.getDayOfWeek();
        if (index == BaseCalendar.SUNDAY) {
            index = 8;
        }
        convertToAbbr(sb, wtb[index]).append(' ');                        // EEE
        convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' ');  // MMM
        CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd

        CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':');   // HH
        CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
        CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
        TimeZone zi = date.getZone();
        if (zi != null) {
            sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
        } else {
            sb.append("GMT");
        }
        sb.append(' ').append(date.getYear());  // yyyy
        return sb.toString();
    }

    private static final StringBuilder convertToAbbr(StringBuilder sb, String name) {
        sb.append(Character.toUpperCase(name.charAt(0)));
        sb.append(name.charAt(1)).append(name.charAt(2));
        return sb;
    }

    private final BaseCalendar.Date getCalendarDate() {
        if (cdate == null) {
            BaseCalendar cal = getCalendarSystem(fastTime);
            cdate = (BaseCalendar.Date) cal.getCalendarDate(fastTime,
                                                            TimeZone.getDefaultRef());
        }
        return cdate;
    }

    private final BaseCalendar.Date normalize() {
        if (cdate == null) {
            BaseCalendar cal = getCalendarSystem(fastTime);
            cdate = (BaseCalendar.Date) cal.getCalendarDate(fastTime,
                                                            TimeZone.getDefaultRef());
            return cdate;
        }

        // Normalize cdate with the TimeZone in cdate first. This is
        // required for the compatible behavior.
        if (!cdate.isNormalized()) {
            cdate = normalize(cdate);
        }

        // If the default TimeZone has changed, then recalculate the
        // fields with the new TimeZone.
        TimeZone tz = TimeZone.getDefaultRef();
        if (tz != cdate.getZone()) {
            cdate.setZone(tz);
            CalendarSystem cal = getCalendarSystem(cdate);
            cal.getCalendarDate(fastTime, cdate);
        }
        return cdate;
    }

    // fastTime and the returned data are in sync upon return.
    private final BaseCalendar.Date normalize(BaseCalendar.Date date) {
        int y = date.getNormalizedYear();
        int m = date.getMonth();
        int d = date.getDayOfMonth();
        int hh = date.getHours();
        int mm = date.getMinutes();
        int ss = date.getSeconds();
        int ms = date.getMillis();
        TimeZone tz = date.getZone();

        if (y == 1582 || y > 280000000 || y < -280000000) {
            if (tz == null) {
                tz = TimeZone.getTimeZone("GMT");
            }
            GregorianCalendar gc = new GregorianCalendar(tz);
            gc.clear();
            gc.set(GregorianCalendar.MILLISECOND, ms);
            gc.set(y, m-1, d, hh, mm, ss);
            fastTime = gc.getTimeInMillis();
            BaseCalendar cal = getCalendarSystem(fastTime);
            date = (BaseCalendar.Date) cal.getCalendarDate(fastTime, tz);
            return date;
        }

        BaseCalendar cal = getCalendarSystem(y);
        if (cal != getCalendarSystem(date)) {
            date = (BaseCalendar.Date) cal.newCalendarDate(tz);
            date.setNormalizedDate(y, m, d).setTimeOfDay(hh, mm, ss, ms);
        }
        // Perform the GregorianCalendar-style normalization.
        fastTime = cal.getTime(date);

        // In case the normalized date requires the other calendar
        // system, we need to recalculate it using the other one.
        BaseCalendar ncal = getCalendarSystem(fastTime);
        if (ncal != cal) {
            date = (BaseCalendar.Date) ncal.newCalendarDate(tz);
            date.setNormalizedDate(y, m, d).setTimeOfDay(hh, mm, ss, ms);
            fastTime = ncal.getTime(date);
        }
        return date;
    }

    /**
     * Returns the Gregorian or Julian calendar system to use with the
     * given date. Use Gregorian from October 15, 1582.
     *
     * @param year normalized calendar year (not -1900)
     * @return the CalendarSystem to use for the specified date
     */
    private static final BaseCalendar getCalendarSystem(int year) {
        if (year >= 1582) {
            return gcal;
        }
        return getJulianCalendar();
    }

    private static final BaseCalendar getCalendarSystem(long utc) {
        // Quickly check if the time stamp given by `utc' is the Epoch
        // or later. If it's before 1970, we convert the cutover to
        // local time to compare.
        if (utc >= 0
            || utc >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER
                        - TimeZone.getDefaultRef().getOffset(utc)) {
            return gcal;
        }
        return getJulianCalendar();
    }

    private static final BaseCalendar getCalendarSystem(BaseCalendar.Date cdate) {
        if (jcal == null) {
            return gcal;
        }
        if (cdate.getEra() != null) {
            return jcal;
        }
        return gcal;
    }

    synchronized private static final BaseCalendar getJulianCalendar() {
        if (jcal == null) {
            jcal = (BaseCalendar) CalendarSystem.forName("julian");
        }
        return jcal;
    }

    /**
     * Save the state of this object to a stream (i.e., serialize it).
     *
     * @serialData The value returned by <code>getTime()</code>
     *             is emitted (long).  This represents the offset from
     *             January 1, 1970, 00:00:00 GMT in milliseconds.
     */
    private void writeObject(ObjectOutputStream s)
         throws IOException
    {
        s.writeLong(getTimeImpl());
    }

    /**
     * Reconstitute this object from a stream (i.e., deserialize it).
     */
    private void readObject(ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
        fastTime = s.readLong();
    }

    /**
     * 从{@code Instant}对象获取{@code Date}的实例。
     * {@code Instant}使用纳秒级的精度,而{@code Date}使用毫秒级的精度。
     * 转换将转换任何多余的精度信息,就好像纳秒的数量要被整数除以一百万。
     * {@code Instant}可以在时间轴上比{@code Date}更远和过去存储点。在这种情况下,此方法将引发异常。
     */
    public static Date from(Instant instant) {
        try {
            return new Date(instant.toEpochMilli());
        } catch (ArithmeticException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    /**
     * 当前Date对象转化为Instant对象。
     */
    public Instant toInstant() {
        return Instant.ofEpochMilli(getTime());
    }
}

问题记录

  • Date Calendar Instant 区别
  • 各时区的意思区别。