【JDK源码阅读】Date

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/midnight_time/article/details/89048823

【JDK源码阅读】Date

前言

都说James Gosling是Java语言之父,但是我前几天看的源码,比如Thread和File的作者都是 @author unascribed ,也就是说是团体整的,不归属于个人,而Runnable的作者是@author Arthur van Hoff,Collections的作者是@author Josh Bloch,都不是老高,所以今天来看一篇高司令的源码吧,当然,优秀的源码一般都不是 一个人完成的,这篇源码的作者不只高司令一个人。

知识点

  1. 地球自转在变慢!详情可见:闰秒·百度百科
  2. Date类中的getTime()和时区有关系,不要上来就莽答1970年1月1号0时0分0秒
  3. Date类中compareTo()方法返回值,7行代码压缩成1行
    return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
    

正文

Date

虽然这个类大多数方法已然被Calendar类和DateFormat类无缝代替,但是这里描述的闰秒还是能让人长见识的

/**
 * The class <code>Date</code> represents a specific instant
 * in time, with millisecond precision.
 	这个Date类代表了一个指定的时间点,精度为毫秒
 * <p>
 * Prior to JDK 1.1, the class Date had two additional
 * functions.  It allowed the interpretation of dates as year, month, day, hour,
 * minute, and second values.  It also allowed the formatting and parsing
 	在JDK 1.1版本之前,这个Date类还有两个额外的功能,
 	一、按年月日时分秒来解释日期
 	二、格式化或者解析日期字符串
 * of date strings.  Unfortunately, the API for these functions was not
 	非常不幸,这个API经不住国际化的考验
 * amenable to internationalization.  As of JDK 1.1, the
 * <code>Calendar</code> class should be used to convert between dates and time
 	自从JDK 1.1之后,两个日期之间的转换应该使用Calendar类
 * fields and the <code>DateFormat</code> class should be used to format and
 	格式化或者解析日期字符串应该使用DateFormat类
 * parse date strings.
 
 * The corresponding methods in <code>Date</code> are deprecated.
 	相应的方法在Date类中被废弃了
 * Although the <code>Date</code> class is intended to reflect
 * coordinated universal time (UTC), it may not do so exactly,
 * depending on the host environment of the Java Virtual Machine.
 	尽管Date类意图是反射出协调的同一时间,但是它并不完全精确,这个精确度主要依赖于JVM虚拟机的主机环境
 * Nearly all modern operating systems assume that 1 day = 24*60*60 = 86400 seconds
 	几乎所有的现代操作系统都认定一天等于86400秒
 * in all cases. In UTC, however, about once every year or two there
 	但是,按照UTC的所有情况来看,每一或两年就会产生额外的一秒,也就是通常说的“闰秒”
 * is an extra second, called a "leap second." The leap
 * second is always added as the last second of the day, and always
 * on December 31 or June 30. For example, the last minute of the
 	闰秒总是在 12月31 或者6月30被加上
 * year 1995 was 61 seconds long, thanks to an added leap second.
 	例如,由于多加了一个闰秒,1995年的最后一分钟会有61秒
 * Most computer clocks are not accurate enough to be able to reflect
 * the leap-second distinction.
 	大多数计算机都不能精确的反射出闰秒的特性
 * <p>
 * Some computer standards are defined in terms of Greenwich mean
 * time (GMT), which is equivalent to universal time (UT).  GMT is
 * the "civil" name for the standard; UT is the
 * "scientific" name for the same standard. 
 	一些计算机的标准是格林尼治时间。所谓的格林尼治时间和世界时间是相等的。
 	只不过格林尼治时间是世界时间的民间叫法罢了。
 * The distinction between UTC and UT is that UTC is based on an atomic
 	协调世界时间和世界时间的区别就在于,协调世界时间基于一个原子时钟,
 * clock and UT is based on astronomical observations, which for all
 	而世界时间基于天文观测
 * practical purposes is an invisibly fine hair to split. Because the
 * earth's rotation is not uniform (it slows down and speeds up
 	因为地球的自传是不均衡的(以某种复杂的方式,时而快,时而慢)
 * in complicated ways), UT does not always flow uniformly. Leap
 	世界时间并不总是均匀的流逝
 * seconds are introduced as needed into UTC so as to keep UTC within
 * 0.9 seconds of UT1, which is a version of UT with certain
 * corrections applied. There are other time and date systems as
 UTC中根据需要引入闰秒,使UTC保持在UT1的0.9秒以内,UT1是UT的一个版本,应用了一定的修正。
 * well; for example, the time scale used by the satellite-based
 	当然也有其他的时间日期系统,比如计入人造地球定位卫星的时间,是和UTC同步的时间,但是没有调整闰秒
 * global positioning system (GPS) is synchronized to UTC but is
 * <i>not</i> adjusted for leap seconds. An interesting source of
 * further information is the U.S. Naval Observatory, particularly
 	对于未来的一些有趣的资源信息是美国海军观测台,独特的指挥部时间,具体信息参考如下网站
 * the Directorate of Time at:
 * <blockquote><pre>
 *     <a href=http://tycho.usno.navy.mil>http://tycho.usno.navy.mil</a>
 * </pre></blockquote>
 * <p>
 * and their definitions of "Systems of Time" at:
 他们定义的系统实际具体信息参考如下网址
 * <blockquote><pre>
 *     <a href=http://tycho.usno.navy.mil/systime.html>http://tycho.usno.navy.mil/systime.html</a>
 * </pre></blockquote>
 * <p>
 * In all methods of class <code>Date</code> that accept or return
 * year, month, date, hours, minutes, and seconds values, the
 * following representations are used:
 	在Date类中的所有方法的参数和返回值遵从下面的描述
 * <ul>
 * <li>A year <i>y</i> is represented by the integer
 *     <i>y</i>&nbsp;<code>-&nbsp;1900</code>.
 	year 是一个整数
 * <li>A month is represented by an integer from 0 to 11; 0 is January,
 *     1 is February, and so forth; thus 11 is December.
 	month是一个从0-11的整数,0代表1月,11代表12月
 * <li>A date (day of month) is represented by an integer from 1 to 31
 *     in the usual manner.
 	date是一个从1-31的整数,代表一个月中的某一天
 * <li>An hour is represented by an integer from 0 to 23. Thus, the hour
 *     from midnight to 1 a.m. is hour 0, and the hour from noon to 1
 *     p.m. is hour 12.
 	hour是一个0-23的整数,midnight到凌晨一点这段时间为0点,noon到下午1点之间这段时间为12点
 * <li>A minute is represented by an integer from 0 to 59 in the usual manner.
 	minute是一个0-59的整数
 * <li>A second is represented by an integer from 0 to 61; the values 60 and
 *     61 occur only for leap seconds and even then only in Java
 *     implementations that actually track leap seconds correctly. Because
 *     of the manner in which leap seconds are currently introduced, it is
 *     extremely unlikely that two leap seconds will occur in the same
 *     minute, but this specification follows the date and time conventions
 *     for ISO C.
 	遵从ISO C的时间日期转换说明书,Java中的second是一个0-61的整数,包含闰秒
 * </ul>
 * <p>
 * In all cases, arguments given to methods for these purposes need
 * not fall within the indicated ranges; for example, a date may be
 * specified as January 32 and is interpreted as meaning February 1.
 *	考虑到所有可能,参数的传递没有约束,比如1月32会被解释为2月1
 * @author  James Gosling
 * @author  Arthur van Hoff
 * @author  Alan Liu
 * @see     java.text.DateFormat
 * @see     java.util.Calendar
 * @see     java.util.TimeZone
 * @since   JDK1.0
 */

Date类残存方法

getTime()

首先看一下Date类中常用的getTime()方法,这里有一个坑

/**
* Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
	返回 自从1970-1-1 00:00:00至今的毫秒数
* represented by this <tt>Date</tt> object.
*
* @return  the number of milliseconds since January 1, 1970, 00:00:00 GMT
*          represented by this date.
*/
public long getTime() {
    return getTimeImpl();
}

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

上面的源码说的明明白白的,返回 自从1970-1-1 00:00:00至今的毫秒数。我曾经也是这样理解的,不过这里有个坑,源码中还有三个字母 GMT 代表的是格林尼治时间,所谓格林尼治时间就是英国有条街,叫格林尼治,当年大英帝国一统全球,号称日不落,于是就有了制定时间起点的权利,然后挑了这一条街,将地球划分为东西半球,这条街也是时间的起点。

如果我们居住在英国,那么说getTime()方法返回的是自从1970-1-1 00:00:00至今的毫秒数,没有问题。关键是我们居住在天朝啊!大英帝国日不落的时候,天朝还在闭关锁国呢,于是沦落为了GMT+8,即东八区。也就是说,我们比人家早八个小时。所以,身在天朝的我们如果调用getTime()方法,返回的就是自从1970-1-1 08:00:00至今的毫秒数。

验证方法,构造一个日期,1970-01-02 00:00:00,看看返回值是16h还是24h,如果不人为更改时区,那么在天朝的答案是16h的毫秒数

Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1970-01-02 00:00:00");
System.out.println(date.getTime()); // 57,600,000 = 16*60*60*100
	    						 // 86,400,000 = 24*60*60*100 相差8h

手动设置时区为0区的方式如下

TimeZone zone = TimeZone.getTimeZone("GMT+00:00:00");
TimeZone.setDefault(zone);

Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("1970-01-02 00:00:00");
System.out.println(date.getTime());// 86,400,000 = 24*60*60*100

如果Date对象在构造的时候,使用的是空构造方法,那么,Date.getTime()方法和System.currentTimeMillis()方法等价,如下

Date date = new Date();
System.out.println(date.getTime());

System.out.println(System.currentTimeMillis());

有了上面的经验,这里就可以知道,这两个方法得到的都是自从1970-1-1 00:00:00至今的毫秒数加减所在时区的毫秒差

下次再有人问你System.currentTimeMillis()的作用是什么,一定记得提一下时区啊!

setTime()

既然有getTime(),对应就有setTime(),它的用法需要我们先计算好,某个日期到1970-1-1 08:00:00的毫秒数。

这里要注意的是setTime()方法接收的参数是一个Long类型的

 public void setTime(long time)

为此,我又复习了一下Java的八种基本数据类型及其表示方法

int i = 2147483647;   // int 类型: -2,147,483,648 至 2,147,483,647 (-2^31 至 2^31 -1)
long t = 9223372036854775807L; // long 类型:-9,223,372,036,854,775,808至9,223,372,036,854,775,807(-2^63至2^63-1),注意要以L/l结尾,不能省略,如果省略,不能通过编译

float f = 0.123F; // F/f 不能省略,如果省略,不能通过编译
double d = 0.345D; // D/d 可以省略

char c = 'c';  // char ==> ' '
String s = "s";  // String ==> " "

// 获取对象的类型,基本类型只能获取包装类
System.out.println(getType(date));


// 在main方法之外,自己实现getType()方法
public static String getType(Object o){
    return o.getClass().toString();
}

toString()

如果不是前辈们已经写好了这些方法,真要让我来重写一下Date类的toString()方法,还真写不出来。

/**
     * Converts this <code>Date</code> object to a <code>String</code>
     按照如下格式,将一个date对象转为字符串
     * of the form:
     * dow mon  dd  hh:mm:ss  zzz  yyyy 
      周几 几月 几号 时: 分:秒 时区 年份
     * where:<ul>
     * <li><tt>dow</tt> is the day of the week (<tt>Sun, Mon, Tue, Wed,
     *     Thu, Fri, Sat</tt>).
     		day of  week 简写为 dow?
     * <li><tt>mon</tt> is the month (<tt>Jan, Feb, Mar, Apr, May, Jun,
     *     Jul, Aug, Sep, Oct, Nov, Dec</tt>).
     		month of number 简写为 mon?
     * <li><tt>dd</tt> is the day of the month (<tt>01</tt> through
     *     <tt>31</tt>), as two decimal digits.
     * <li><tt>hh</tt> is the hour of the day (<tt>00</tt> through
     *     <tt>23</tt>), as two decimal digits.
     * <li><tt>mm</tt> is the minute within the hour (<tt>00</tt> through
     *     <tt>59</tt>), as two decimal digits.
     * <li><tt>ss</tt> is the second within the minute (<tt>00</tt> through
     *     <tt>61</tt>, as two decimal digits.
     * <li><tt>zzz</tt> is the time zone (and may reflect daylight saving
     *     time). Standard time zone abbreviations include those
     *     recognized by the method <tt>parse</tt>. If time zone
     *     information is not available, then <tt>zzz</tt> is empty -
     *     that is, it consists of no characters at all.
     * <li><tt>yyyy</tt> is the year, as four decimal digits.
     * </ul>
     *
     * @return  a string representation of this date.
     * @see     java.util.Date#toLocaleString()
     * @see     java.util.Date#toGMTString()
     */
    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();
    }

关于toString()的用法

Date date = new Date();

// 打印当前日期
System.out.println("toString() :"+date.toString());
// 单纯的打印对象,会自动调用toString()方法
System.out.println("Object:     "+date);

//toString():Sun Apr 07 15:09:24 GMT+08:00 2019
//Object:    Sun Apr 07 15:09:24 GMT+08:00 2019

比较

除了上面的那几个方法,Date类中还有4个用于日期之间比较的方法,3个boolean类型,1个int类型

 /**
     * Tests if this date is before the specified date.
     *
     * @param   when   a date.
     * @return  <code>true</code> if and only if the instant of time
     *            represented by this <tt>Date</tt> object is strictly
     *            earlier than the instant represented by <tt>when</tt>;
     *          <code>false</code> otherwise.
     * @exception NullPointerException if <code>when</code> is null.
     */
    public boolean before(Date when) {
        return getMillisOf(this) < getMillisOf(when);
    }

    /**
     * Tests if this date is after the specified date.
     *
     * @param   when   a date.
     * @return  <code>true</code> if and only if the instant represented
     *          by this <tt>Date</tt> object is strictly later than the
     *          instant represented by <tt>when</tt>;
     *          <code>false</code> otherwise.
     * @exception NullPointerException if <code>when</code> is null.
     */
    public boolean after(Date when) {
        return getMillisOf(this) > getMillisOf(when);
    }

    /**
     * Compares two dates for equality.
     * The result is <code>true</code> if and only if the argument is
     * not <code>null</code> and is a <code>Date</code> object that
     * represents the same point in time, to the millisecond, as this object.
     * <p>
     * Thus, two <code>Date</code> objects are equal if and only if the
     * <code>getTime</code> method returns the same <code>long</code>
     * value for both.
     *
     * @param   obj   the object to compare with.
     * @return  <code>true</code> if the objects are the same;
     *          <code>false</code> otherwise.
     * @see     java.util.Date#getTime()
     */
    public boolean equals(Object obj) {
        return obj instanceof Date && getTime() == ((Date) obj).getTime();
    }

    /**
     * Compares two Dates for ordering.
     *
     * @param   anotherDate   the <code>Date</code> to be compared.
     * @return  the value <code>0</code> if the argument Date is equal to
     *          this Date; a value less than <code>0</code> if this Date
     *          is before the Date argument; and a value greater than
     *      <code>0</code> if this Date is after the Date argument.
     * @since   1.2
     * @exception NullPointerException if <code>anotherDate</code> is null.
     */
    public int compareTo(Date anotherDate) {
        long thisTime = getMillisOf(this);
        long anotherTime = getMillisOf(anotherDate);
        return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
    }

值得一提的就是这个返回值为int类型的**compareTo()**方法,大家看下面这句代码:

return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));

我第一次见这样的代码是在一个晚上,当时感动的我是热泪盈眶。因为,如果换做是我写的话,肯定就是if-else就上去了。

if (thisTime<anotherTime) {
    return -1;
} else if (thisTime == anotherTime) {
    return 0;
} else {
    return 1;
}

JDK的作者真的是深谙代码简洁之道,同时又很会装X!哈哈。

展开阅读全文

没有更多推荐了,返回首页