LocalDate的源码解析

1,012 阅读5分钟

首先:

有了LocalDate和LocalDateTime之后,就不要在使用Date了。
DateTimeFormatter也可以代替SimpleDateFormat了。

理解:

时间类的本质就是一个Instant对象。
里面存储了准确的秒数和毫秒数。(从1970-01-01开始)
就可以根据秒数推算出 年月日,时分秒。

LocalDate有3个属性。了解这3个参数就能理解这个类了

// 年
private final int year;
// 月
private final short month;
// 日
private final short day;
    

LocalTime有4个属性*

/**
 * The hour.
 */
private final byte hour;
/**
 * The minute.
 */
private final byte minute;
/**
 * The second.
 */
private final byte second;
/**
 * The nanosecond.
 */
private final int nano;

LocalDateTime就是有两个属性, LocalDate和LocalTime*

涉及到的其他接口或类

// 这两个感觉和Locald差不多 不管是属性还是方法。
Period implements TemporalAmount  
Duration implements TemporalAmount

// xx_of_xx之类的枚举  比如 今年的第几天 DAY_OF_YEAR
public enum ChronoField implements TemporalField 
// 年月日时分秒之类的枚举 比如 年 Years 
public enum ChronoUnit implements TemporalUnit

// 获取某个值的时间范围  比如DAY_OF_YEAR的时间范围。 就是1-365or366
ValueRange

// get的几个方法
public interface TemporalAccessor
// with,plus,minus的几个方法
public interface Temporal extends TemporalAccessor
// 时区对应的id
public abstract class ZoneId
// 系统时区
static final class SystemClock extends Clock
// 时间戳
Instant
// 时区偏移量 比如当前时区距离标准时区 偏移8小时
ZoneOffset

LocalDate的创建:

获取系统默认的时区。抽象类ZoneId中的值,默认map.put("CTT", "Asia/Shanghai");

    public static LocalDate now() {
        // 获取默认的时区去创建LocalDate
        return now(Clock.systemDefaultZone());
    }

秒数。天数。针对的是指从1970-01-01开始计时

    public static LocalDate now(Clock clock) {
        Objects.requireNonNull(clock, "clock");
        // 获取开头说的时间的本质。 Instant.
        final Instant now = clock.instant();  
        // 计算出时区偏移量。 比如当前时区(东八区)的偏移量 +8
        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
        // 计算出符合当前时区的 秒数
        long epochSec = now.getEpochSecond() + offset.getTotalSeconds();  
        // 通过 秒数 计算出具体天数
        long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
        return LocalDate.ofEpochDay(epochDay);
    }

通过天数。计算出准确的年月日,并返回一个LocalDate。 这就是创建LocalDate的原理

    public static LocalDate ofEpochDay(long epochDay) {
        long zeroDay = epochDay + DAYS_0000_TO_1970;
        // find the march-based year
        zeroDay -= 60;  // adjust to 0000-03-01 so leap day is at end of four year cycle
        long adjust = 0;
        if (zeroDay < 0) {
            // adjust negative years to positive for calculation
            long adjustCycles = (zeroDay + 1) / DAYS_PER_CYCLE - 1;
            adjust = adjustCycles * 400;
            zeroDay += -adjustCycles * DAYS_PER_CYCLE;
        }
        long yearEst = (400 * zeroDay + 591) / DAYS_PER_CYCLE;
        long doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
        if (doyEst < 0) {
            // fix estimate
            yearEst--;
            doyEst = zeroDay - (365 * yearEst + yearEst / 4 - yearEst / 100 + yearEst / 400);
        }
        yearEst += adjust;  // reset any negative year
        int marchDoy0 = (int) doyEst;

        // convert march-based values back to january-based
        int marchMonth0 = (marchDoy0 * 5 + 2) / 153;
        int month = (marchMonth0 + 2) % 12 + 1;
        int dom = marchDoy0 - (marchMonth0 * 306 + 5) / 10 + 1;
        yearEst += marchMonth0 / 10;

        // check year now we are certain it is correct
        int year = YEAR.checkValidIntValue(yearEst);
        return new LocalDate(year, month, dom);
    }

LocalDate的原理就这么简单

    private LocalDate(int year, int month, int dayOfMonth) {
        this.year = year;
        this.month = (short) month;
        this.day = (short) dayOfMonth;
    }

其他的创建方式

    // 比如直接指定时区
    public static LocalDate now(ZoneId zone)
    // 比如直接指定时钟
    public static LocalDate now(Clock clock) 
    // 比如直接给定年月日 Month是英文的1-12月,也可以直接给1-12 
    public static LocalDate of(int year, Month month, int dayOfMonth)
    public static LocalDate of(int year, int month, int dayOfMonth)
    // 比如某一年的第几天
    LocalDate ofYearDay(int year, int dayOfYear)
    // 比如直接根据天数(从2970-1-1)
    public static LocalDate ofEpochDay(long epochDay)

LocalDate的日期变化

plusXXX对指定的字段(年月周日)进行添加 比如2019年变成2020年。直接用plusYears(1)就可以了,当然负数就是倒退
    // 重新设置年  原来年+yearsToAdd
    public LocalDate plusYears(long yearsToAdd)
    // 重新设置月
    public LocalDate plusMonths(long monthsToAdd)
    // 获取天数(weeksToAdd*7) 然后根据天数重新生成年月日
    public LocalDate plusWeeks(long weeksToAdd)
    // 根据天数重新生成年月日
    public LocalDate plusDays(long daysToAdd)
    // 给指定字段unit(年Years月Months日Days时分秒)做加减, 比如改变“年”就用ChronoUnit.Years作为参数。
    // 本质就是根据不同的参数调用不同的plusXXX
    public LocalDate plus(long amountToAdd, TemporalUnit unit)

    

minusXXX的原理就是把值改成相反数。然后在调用plus的方法。

plusXXX本质就是改变 年月日属性, 然后重新创建一个LocalDate。

    public LocalDate plusYears(long yearsToAdd) {
        if (yearsToAdd == 0) {
            return this;
        }
        // 计算出新的年份
        int newYear = YEAR.checkValidIntValue(year + yearsToAdd); 
        return resolvePreviousValid(newYear, month, day);
    }
    // 根据月份判断应该显示那一天。 比如1月31号。加了一个月。如果直接加的话就是2月31号了。所以要计算出2月28还是29
    private static LocalDate resolvePreviousValid(int year, int month, int day) {
        switch (month) {
            case 2:
                day = Math.min(day, IsoChronology.INSTANCE.isLeapYear(year) ? 29 : 28);
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                day = Math.min(day, 30);
                break;
        }
        return new LocalDate(year, month, day);
    }

各种数据的获取

getXXX系列 就是获取特定属性

    // 直接获取某个字段的值。 比如 ChronoField.DAY_OF_YEAR 今年的第几天
    public int get(TemporalField field)
    public long getLong(TemporalField field)
    private int get0(TemporalField field)
    
    // 获取对应的字段的值
    public int getYear()
    public int getMonthValue()
    public Month getMonth()
    // 获取在所在的天数。 这星期的第几天,这个月的第几天,今年的第几天
    public DayOfWeek getDayOfWeek()
    public int getDayOfMonth()
    public int getDayOfYear()
    

替换各个字段的值,新建并返回一个LocalDate

withXXX系列。 就是替换指定字段的值,并返回新值

// 原理就是其他字段不变。 改变指定字段的值。重新生成。
LocalDate with(TemporalAdjuster adjuster)
LocalDate with(TemporalField field, long newValue)
public LocalDate withYear(int year)
public LocalDate withMonth(int month)
public LocalDate withDayOfMonth(int dayOfMonth)
public LocalDate withDayOfYear(int dayOfYear)
    // 比如年。相当于日月没变。 年变成指定值。
    public LocalDate withYear(int year) {
        if (this.year == year) {
            return this;
        }
        YEAR.checkValidValue(year);
        return resolvePreviousValid(year, month, day);
    }

字符串转化为LocalDate

// 把字符串解析出LocalDate 默认格式 yyyy-MM-dd
public static LocalDate parse(CharSequence text){
   return parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
}
// 把字符串按指定的规则去解析。   DateTimeFormatter.of("yyyy-MM-dd") 值必须与text的值对应上。
public static LocalDate parse(CharSequence text, DateTimeFormatter formatter) {
   Objects.requireNonNull(formatter, "formatter");
   return formatter.parse(text, LocalDate::from);
}

LocalDate转化为LocalDateTime

atTime 就是把LocalDate转化为LocalDateTime。 通过时分秒创建LocalTime 然后把两个参数设置为LocalDateTime的属性

public LocalDateTime atTime(LocalTime time)
public LocalDateTime atTime(int hour, int minute)
public LocalDateTime atTime(int hour, int minute, int second)
LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond) 
OffsetDateTime atTime(OffsetTime time) 
// 获取一天的开始 00:00
public ZonedDateTime atStartOfDay(ZoneId zone)

也可以通过LocalDateTime里面的toLocalDate方法。 把LocalDateTime转化为LocalDate

LocalDate差不多就是这样。