Java日期时间API系列12—–Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全

  通过Java日期时间API系列10—–Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决了SimpleDateFormat线程安全问题。下面是关于DateTimeFormatter的使用实例,包括常用日期格式大全,

以2020-01-01 00:00:00为例。

 

package com.xkzhangsan.time.test;

import java.time.LocalDateTime;
import java.util.Date;

import com.xkzhangsan.time.LunarDate;
import com.xkzhangsan.time.converter.DateTimeConverterUtil;
import com.xkzhangsan.time.formatter.DateTimeFormatterUtil;

public class NewYearFormatTest {

    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 1, 0, 0, 0);
        Date date = DateTimeConverterUtil.toDate(localDateTime);
        LunarDate lunarDate = LunarDate.from(localDateTime);
        System.out.println("=============================");
        System.out.println("Hello " + lunarDate.format());
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_CN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_EN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYYMM_FMT));
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_CN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_EN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_POINT_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_E_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYYMMDD_FMT));
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYYMMDDHHMM_FMT));
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_CN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_A_CN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYYMMDDHHMMSS_FMT));//22
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_SSS_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.YYYYMMDDHHMMSSSSS_FMT));        
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_HH_MM_SS_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_HH_MM_SS_CN_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_HH_MM_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_HH_MM_CN_FMT));
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.MM_DD_CN_FMT));
        
        System.out.println("=============================");
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.HH_MM_SS_FMT));
        System.out.println(DateTimeFormatterUtil.format(date, DateTimeFormatterUtil.HHMMSS_FMT));
        System.out.println("=============================");
    }
}

 

输出:

=============================
Hello 己亥猪年 二〇一九年腊月初七 星期三
=============================
2020
2020-01
2020年01月
2020/01
202001
=============================
2020-01-01
2020年01月01日
2020/01/01
2020.01.01
2020-01-01 星期三
20200101
=============================
2020-01-01 00:00
202001010000
=============================
2020-01-01 00:00:00
2020年01月01日 00:00:00
2020年01月01日 00:00:00 上午
20200101000000
=============================
2020-01-01 00:00:00.000
20200101000000000
=============================
01-01 00:00:00
01月01日 00:00:00
01-01 00:00
01月01日 00:00
=============================
01-01
01月01日
=============================
00:00:00
000000
=============================

 

注意:上面代码中的 DateTimeFormatterUtil.YYYY_FMT中的YYYY_FMT是定义的常量,不是真正的转换格式模板,原代码中是小写的y:yyyy。YYYY会出现bug。

源码为:

    private static final String YYYY = "yyyy";
    /**
     * such as 2019
     */
    public static final DateTimeFormatter YYYY_FMT = DateTimeFormatter.ofPattern(YYYY).withZone(ZONE);

 

    /**
     * 根据 formatter格式化 date
     * @param date
     * @param formatter
     * @return
     */
    public static String format(Date date, DateTimeFormatter formatter){
        Objects.requireNonNull(formatter, "formatter");
        return DateTimeConverterUtil.toLocalDateTime(date).format(formatter);
    }

 

源码地址:https://github.com/xkzhangsan/xk-time

Java日期时间API系列11—–Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate

  通过Java日期时间API系列7—–Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用java新的日期时间API重写农历LunarDate。

 1. version 0.1版本 农历原始算法来自网络,有2个问题:

(1)com.xkzhangsan.time.LunarDate.lunarInfo 数据有误,导致个别农历计算不准确。

(2)没有二十四节气计算。

2.version 0.2版本修改了上面问题:

package com.xkzhangsan.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.util.Date;

import com.xkzhangsan.time.calculator.DateTimeCalculatorUtil;
import com.xkzhangsan.time.converter.DateTimeConverterUtil;
import com.xkzhangsan.time.holiday.ChineseHolidayEnum;

/**
 * 农历日期
 * 仅支持公历1901-2050年的农历转换
* @ClassName: LunarDate 
* @Description: LunarDate
* @author xkzhangsan
* @date 2019年12月30日
* @version 0.2 试用
 */
public final class LunarDate implements Temporal{
    
    /**
     * 农历信息
     */
    private static final long[] lunarInfo = new long[] { 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554,
            0x056a0, 0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0,
            0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,
            0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550,
            0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0,
            0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263,
            0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0,
            0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5,
            0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960, 0x0d954, 0x0d4a0,
            0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,
            0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0,
            0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520,
            0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
            0x14b63 };

    /**
     * 农历月份列表
     */
    public static final String[] lunarMonth = new String[] { "", "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬",
            "腊" };
    
    /**
     * 天干列表
     */
    private static final String[] tianGan = new String[] { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };

    /**
     * 地支列表
     */
    private static final String[] diZhi = new String[] { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };

    /**
     * 生肖列表
     */
    private static final String[] animals = new String[] { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };

    /**
     * 中文数字0-9
     */
    public static final String[] numStr = new String[] { "〇", "一", "二", "三", "四", "五", "六", "七", "八", "九"};    

    
    /**
     * 二十四节气
     */
    public static final String[] solarTerms = new String[] {"小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"};
    
    
    /**
     * 二十四节气数据
     */
    private static final long[] solarTermInfo = new long[] {0,21208,42467,63836,85337,107014,128867,150921,173149,195551,218072,240693,263343,285989,308563,331033,353350,375494,397447,419210,440795,462224,483532,504758};
    
    /**
     * 标准日期
     */
    private final LocalDate localDate;

    /**
     * 农历日期,中文
     */
    private String lDateCn;

    /**
     * 岁次
     */
    private String suiCi;

    /**
     * 生肖
     */
    private String lAnimal;

    /**
     * 农历年
     */
    private int lYear;

    /**
     * 农历月
     */
    private int lMonth;

    /**
     * 农历日
     */
    private int lDay;

    /**
     * 农历年,中文
     */
    private String lYearCn;

    /**
     * 农历月,中文
     */
    private String lMonthCn;

    /**
     * 农历日,中文
     */
    private String lDayCn;

    /**
     * 星期,中文
     */
    private String weekCn;
    
    /**
     * 二十四节气 
     */
    private String solarTerm;
    
    /**
     * 当前日期月份是否为闰月,是 为"闰"
     */
    private String leapMonthCn;

    private LunarDate(LocalDate localDate) {
        super();
        this.localDate = localDate;
        initialize();
    }
    
    /**
     * 初始化农历日期
     */
    public void initialize() {
        int year = localDate.getYear();
        int month = localDate.getMonthValue();
        int day = localDate.getDayOfMonth();
        long[] l = calElement(year, month, day);
        
        this.lYear = (int) l[0];
        this.lMonth = (int) l[1];
        this.lDay = (int) l[2];
        
        this.suiCi = cyclical(this.lYear);
        this.lAnimal = animalsYear(this.lYear);


        this.lYearCn = getChinaYear(this.lYear);
        this.lMonthCn = lunarMonth[this.lMonth];
        this.lDayCn = getChinaDay(this.lDay);

        this.weekCn = getWeekCn(localDate.getDayOfWeek().getValue());
        if(l[7] != -1){
            this.solarTerm = solarTerms[(int)l[7]];
        }else{
            this.solarTerm = "";
        }
        if(l[6] == 1){
            this.leapMonthCn = "闰";
        }else{
            this.leapMonthCn = "";
        }
        this.lDateCn = this.lYearCn + "年" + this.leapMonthCn + this.lMonthCn + "月" + this.lDayCn;
    }
    
    /**
     * 通过LocalDateTime创建LunarDate
     * @param localDateTime
     * @return
     */
    public static LunarDate from(LocalDateTime localDateTime) {
        return new LunarDate(DateTimeConverterUtil.toLocalDate(localDateTime));
    }

    /**
     * 通过LocalDate创建LunarDate
     * @param localDate
     * @return
     */
    public static LunarDate from(LocalDate localDate) {
        return new LunarDate(localDate);
    }
    
    /**
     * 通过Instant创建LunarDate
     * @param instant
     * @return
     */
    public static LunarDate from(Instant instant) {
        return new LunarDate(DateTimeConverterUtil.toLocalDate(instant));
    }    

    /**
     * 通过Date创建LunarDate
     * @param date
     * @return
     */
    public static LunarDate from(Date date) {
        return new LunarDate(DateTimeConverterUtil.toLocalDate(date));
    }
    
    public static LunarDate from(Temporal temporal) {
        return new LunarDate(DateTimeConverterUtil.toLocalDate(temporal));
    }

    /**
     * 传回农历year年的总天数
     *
     * @param year
     * @return
     */
    private static final int lunarYearDays(int year) {
        int i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1) {
            if ((lunarInfo[year - 1900] & i) != 0)
                sum += 1;
        }
        return (sum + leapMonthDays(year));
    }

    /**
     * 传回农历 year年闰月的天数
     *
     * @param year
     * @return
     */
    private static final int leapMonthDays(int year) {
        if (leapMonth(year) != 0) {
            if ((lunarInfo[year - 1900] & 0x10000) != 0)
                return 30;
            else
                return 29;
        } else
            return 0;
    }

    /**
     * 传回农历 year年闰哪个月 1-12 , 没闰传回 0
     *
     * @param year
     * @return
     */
    private static final int leapMonth(int year) {
        return (int) (lunarInfo[year - 1900] & 0xf);
    }

    /**
     * 传回农历 year年month月的总天数
     *
     * @param year
     * @param month
     * @return
     */
    private static final int monthDays(int year, int month) {
        if ((lunarInfo[year - 1900] & (0x10000 >> month)) == 0)
            return 29;
        else
            return 30;
    }

    /**
     * 传回农历 year年的生肖
     *
     * @param year
     * @return
     */
    public static final String animalsYear(int year) {
        return animals[(year - 4) % 12];
    }

    /**
     * 传入 月日的offset 传回干支,0=甲子
     *
     * @param num
     * @return
     */
    private static final String cyclicalm(int num) {
        return (tianGan[num % 10] + diZhi[num % 12]);
    }

    /**
     * 传入 offset 传回干支, 0=甲子
     *
     * @param year
     * @return
     */
    public static final String cyclical(int year) {
        int num = year - 1900 + 36;
        return (cyclicalm(num));
    }
    
    /**
     * 计算某年第n个节气的天
     * @param year 公历年
     * @param n
     * @return
     */
    public static final int solarTerm(int year, int n){
        LocalDateTime startLocalDateTime = LocalDateTime.of(1900,1,6,2,5);
        long millis = (long) ((31556925974.7*(year-1900) + solarTermInfo[n]*60000));
        LocalDateTime tempLocalDateTime = DateTimeCalculatorUtil.plusMillis(startLocalDateTime, millis);
        return tempLocalDateTime.getDayOfMonth();
    }

    /**
     * 传出year年month月day日对应的农历.year0 .month1 .day2 .yearCyl3 .monCyl4 .dayCyl5
     * .isLeap6.solarTermIndex7
     *
     * @param year
     * @param month
     * @param day
     * @return
     */
    public static final long[] calElement(int year, int month, int day) {
        long[] nongDate = new long[8];
        int i = 0, temp = 0, leap = 0;
        LocalDateTime baseDate = LocalDate.of(1900, 1, 31).atStartOfDay();
        LocalDateTime objDate = LocalDate.of(year, month, day).atStartOfDay();
        long offset = DateTimeCalculatorUtil.betweenTotalDays(baseDate, objDate);        
        nongDate[5] = offset + 40;
        nongDate[4] = 14;
        for (i = 1900; i < 2050 && offset > 0; i++) {
            temp = lunarYearDays(i);
            offset -= temp;
            nongDate[4] += 12;
        }
        if (offset < 0) {
            offset += temp;
            i--;
            nongDate[4] -= 12;
        }
        nongDate[0] = i;
        nongDate[3] = i - 1864;
        leap = leapMonth(i); // 闰哪个月
        nongDate[6] = 0;
        for (i = 1; i < 13 && offset > 0; i++) {
            // 闰月
            if (leap > 0 && i == (leap + 1) && nongDate[6] == 0) {
                --i;
                nongDate[6] = 1;
                temp = leapMonthDays((int) nongDate[0]);
            } else {
                temp = monthDays((int) nongDate[0], i);
            }
            // 解除闰月
            if (nongDate[6] == 1 && i == (leap + 1))
                nongDate[6] = 0;
            offset -= temp;
            if (nongDate[6] == 0)
                nongDate[4]++;
        }
        if (offset == 0 && leap > 0 && i == leap + 1) {
            if (nongDate[6] == 1) {
                nongDate[6] = 0;
            } else {
                nongDate[6] = 1;
                --i;
                --nongDate[4];
            }
        }
        if (offset < 0) {
            offset += temp;
            --i;
            --nongDate[4];
        }
        nongDate[1] = i;
        nongDate[2] = offset + 1;
        
        //二十四节气
        int solarTermIndex = -1;
        int tempMonth = month - 1;
        int firstSolarTermOfMonth = solarTerm(year, tempMonth*2);
        int secondSolarTermOfMonth = solarTerm(year, tempMonth*2+1);
        if(day == firstSolarTermOfMonth){
            solarTermIndex = tempMonth*2;
        }else if(day == secondSolarTermOfMonth){
            solarTermIndex = tempMonth*2 + 1;
        }
        nongDate[7] = solarTermIndex;
        return nongDate;
    }

    /**
     * 获取农历中文年
     * @param year
     * @return
     */
    public final static String getChinaYear(int year) {
        String ge = numStr[year % 10];
        String shi = numStr[year / 10 % 10];
        String bai = numStr[year / 100 % 10];
        String qian = numStr[year / 1000 % 10];
        return qian + bai + shi + ge;
    }
    /**
     * 获取农历中文日期
     * @param day
     * @return
     */
    public final static String getChinaDay(int day) {
        String a = "";
        if (day == 10)
            return "初十";
        if (day == 20)
            return "二十";
        if (day == 30)
            return "三十";
        int two = (int) ((day) / 10);
        if (two == 0)
            a = "初";
        if (two == 1)
            a = "十";
        if (two == 2)
            a = "廿";
        if (two == 3)
            a = "三";
        int one = (int) (day % 10);
        switch (one) {
        case 1:
            a += "一";
            break;
        case 2:
            a += "二";
            break;
        case 3:
            a += "三";
            break;
        case 4:
            a += "四";
            break;
        case 5:
            a += "五";
            break;
        case 6:
            a += "六";
            break;
        case 7:
            a += "七";
            break;
        case 8:
            a += "八";
            break;
        case 9:
            a += "九";
            break;
        default:
            a += "";
            break;
        }
        return a;
    }
    
    /**
     * 获取中文星期
     * @param week
     * @return
     */
    public final static String getWeekCn(int week) {
        String weekCn = "";
        switch (week) {
        case 1:
            weekCn = "星期一";
            break;
        case 2:
            weekCn = "星期二";
            break;
        case 3:
            weekCn = "星期三";
            break;
        case 4:
            weekCn = "星期四";
            break;
        case 5:
            weekCn = "星期五";
            break;
        case 6:
            weekCn = "星期六";
            break;
        case 7:
            weekCn = "星期日";
            break;
        default:
            weekCn = "";
            break;
        }
        return weekCn;
    }

    /**
     * 以当前时间创建农历日期LunarDate
     * @return
     */
    public static LunarDate now() {
        LocalDate today = LocalDate.now();
        return new LunarDate(today);
    }

    public LocalDate getLocalDate() {
        return localDate;
    }

    public String getlDateCn() {
        return lDateCn;
    }

    public String getSuiCi() {
        return suiCi;
    }

    public String getlAnimal() {
        return lAnimal;
    }

    public int getlYear() {
        return lYear;
    }

    public int getlMonth() {
        return lMonth;
    }

    public int getlDay() {
        return lDay;
    }

    public String getlYearCn() {
        return lYearCn;
    }

    public String getlMonthCn() {
        return lMonthCn;
    }

    public String getlDayCn() {
        return lDayCn;
    }

    public String getWeekCn() {
        return weekCn;
    }
    
    public String getSolarTerm() {
        return solarTerm;
    }

    public String getLeapMonthCn() {
        return leapMonthCn;
    }
    
    @Override
    public String toString() {
        return "LunarDate [localDate=" + localDate + ",lDateCn=" + lDateCn + ", suiCi=" + suiCi + ", lAnimal=" + lAnimal + ", lYear=" + lYear
                + ", lMonth=" + lMonth + ", lDay=" + lDay + ", lYearCn=" + lYearCn + ", lMonthCn=" + lMonthCn
                + ", lDayCn=" + lDayCn + ", weekCn=" + weekCn + ", solarTerm=" + solarTerm + ", leapMonthCn=" + leapMonthCn + "]";
    }

    /**
     * 格式化输出,如:庚子鼠年 二〇二〇年正月初一 星期六 春节
     * @return
     */
    public String formatLongCnWithChineseHoliday(){
        return suiCi + lAnimal + "年 " + lDateCn + " " + weekCn + " " + ChineseHolidayEnum.getHoliday(localDate).getName();
    }    
    
    /**
     * 格式化输出,如: 己亥猪年 二〇一九年腊月初六 星期二
     * @return
     */
    public String formatLongCn(){
        return suiCi + lAnimal + "年 " + lDateCn + " " + weekCn;
    }
    
    /**
     * 格式化输出,如: 0101
     * @return
     */
    public String formatShort(){
        return String.format("%02d", lMonth) + String.format("%02d", lDay);
    }

    @Override
    public boolean isSupported(TemporalField field) {
        return localDate.isSupported(field);
    }

    @Override
    public long getLong(TemporalField field) {
        return localDate.getLong(field);
    }

    @Override
    public boolean isSupported(TemporalUnit unit) {
        return localDate.isSupported(unit);
    }

    @Override
    public Temporal with(TemporalField field, long newValue) {
        return localDate.with(field, newValue);
    }

    @Override
    public Temporal plus(long amountToAdd, TemporalUnit unit) {
        return localDate.plus(amountToAdd, unit);
    }

    @Override
    public long until(Temporal endExclusive, TemporalUnit unit) {
        return localDate.until(endExclusive, unit);
    }
    
    public static void main(String[] args) {
        String str =LunarDate.now().toString();
        System.out.println(str);
    }
    
}

 

 输出:

 

LunarDate [localDate=2020-03-22,lDateCn=二〇二〇年二月廿九, suiCi=庚子, lAnimal=鼠, lYear=2020, lMonth=2, lDay=29, lYearCn=二〇二〇, lMonthCn=二, lDayCn=廿九, weekCn=星期日, solarTerm=, leapMonthCn=]

 

源码地址:https://github.com/xkzhangsan/xk-time

Java日期时间API系列10—–Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter

1.DateTimeFormatter

final修饰,线程安全,用于打印和解析日期-时间对象的格式化程序。

创建DateTimeFormatter:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());

 

2.格式化日期

例如:LocalDateTime

     LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(dateTimeFormatter.format(localDateTime));

输出:2019-12-28 23:06:12

 

3.解析日期

(1)使用 LocalDateTime.parse

例如:解析 “2019-12-28 23:06:12”

     LocalDateTime localDateTime2 = LocalDateTime.parse("2019-12-28 23:06:12", dateTimeFormatter);
        System.out.println("localDateTime2:"+dateTimeFormatter.format(localDateTime2));

输出:localDateTime2:2019-12-28 23:06:12

(2)DateTimeFormatter.parse

例如:解析 “2019-12-28 23:06:12”

        LocalDateTime localDateTime3 = LocalDateTime.from(dateTimeFormatter.parse("2019-12-28 23:06:12"));
        System.out.println("localDateTime2:"+dateTimeFormatter.format(localDateTime3));

输出:localDateTime3:2019-12-28 23:06:12

 

4.常用符号含义

 更多说明见 Patterns for Formatting and Parsing部分:

 https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html   

.

5.YYYY格式问题

格式化时,年部分的y是小写的y,如:yyyy-MM-dd。写成YYYY的话,会出现bug。

 

6.Jdk8 解析 yyyyMMddHHmmssSSS 问题

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8031085

这个问题在Jdk9中修复。

Java8中推荐创建DateTimeFormatter方式:

DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmss").appendValue(ChronoField.MILLI_OF_SECOND, 3).toFormatter();

 

Java日期时间API系列9—–Jdk8中java.time包中的新的日期时间API类的Period和Duration的区别

1.Period

final修饰,线程安全,ISO-8601日历系统中基于日期的时间量,例如2年3个月4天。

主要属性:年数,月数,天数。

    /**
     * The number of years.
     */
    private final int years;
    /**
     * The number of months.
     */
    private final int months;
    /**
     * The number of days.
     */
    private final int days;

用于时间量,比较2个日期。

例如:

     LocalDate localDate1 = LocalDate.of(2019, 11, 15);
        LocalDate localDate2 = LocalDate.of(2020, 1, 1);
        Period p = Period.between(localDate1, localDate2);
        System.out.println("years:"+p.getYears()+" months:"+p.getMonths()+" days:"+p.getDays());

输出:

years:0 months:1 days:17

2.Duration

final修饰,线程安全,基于时间的时间量,如“34.5秒”。

主要属性:秒,纳秒

    /**
     * The number of seconds in the duration.
     */
    private final long seconds;
    /**
     * The number of nanoseconds in the duration, expressed as a fraction of the
     * number of seconds. This is always positive, and never exceeds 999,999,999.
     */
    private final int nanos;

用于时间量,比较2个时间。

例如:

        LocalDateTime localDateTime1 = LocalDateTime.of(2019, 11, 15, 0, 0);
        LocalDateTime localDateTime2 = LocalDateTime.of(2019, 11, 15, 10, 30);
        Duration d = Duration.between(localDateTime1, localDateTime2);
        System.out.println("days:"+d.toDays());
        System.out.println("hours:"+d.toHours());
        System.out.println("minutes:"+d.toMinutes());
        System.out.println("millis:"+d.toMillis());

输出:

days:0
hours:10
minutes:630
millis:37800000

3.Period和Duration的区别

(1)包含属性不同

Period包含年数,月数,天数,而Duration只包含秒,纳秒。

Period只能返回年数,月数,天数;Duration可以返回天数,小时数,分钟数,毫秒数等。

(2)between方法可以使用的类型不同

Period只能使用LocalDate,Duration可以使用所有包含了time部分且实现了Temporal接口的类,比如LocalDateTime,LocalTime和Instant等。

Period:

public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive)

 

Duration:

public static Duration between(Temporal startInclusive, Temporal endExclusive)

 

(3)between获取天数差的区别

通过上面的实例可以看出:

Period  p.getDays()  获取天数时,只会获取days属性值,而不会将年月部分都计算成天数,不会有2020.1.1和2019.1.1比较后获取天数为365天的情况。

    public int getDays() {
        return days;
    }

 

Duration d.toDays()  获取天数时,会将秒属性转换成天数。

    public long toDays() {
        return seconds / SECONDS_PER_DAY;
    }

 

所以,想要获取2个时间的相差总天数,只能用Duration。

(4)Period有获取总月数的方法,为什么没有获取总天数方法?

Period有获取总月数的方法:

    public long toTotalMonths() {
        return years * 12L + months;  // no overflow
    }

为什么没有获取总天数方法?

因为between后获取到的Period,不会记录2个日期中间的闰年信息,有闰年的存在,每年的天数不一定是365天,所以计算不准确。

Java日期时间API系列8—–Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析

目录

0.前言

1.TemporalAccessor源码

2.Temporal源码

3.TemporalAdjuster源码

4.ChronoLocalDate源码

5.LocalDate源码

6.总结

 

0.前言

  通过前面Java日期时间API系列6—–Jdk8中java.time包中的新的日期时间API类中主要的类关系简图如下:

 

可以看出主要的LocalDate, LocalTime, LocalDateTime, Instant都是实现相同的接口,这里以LocalDate为例分析java8时间api源码,其他的类与LocalDate类似。

  LocalDate的相关类图如下:完整类图

可以看出LocalDate同时实现了Temporal, TemporalAdjuster, ChronoLocalDate三个接口。

java.time包是在jdk8中上添加进来的,jdk8接口有了一些新的特性:接口的默认方法、静态方法和函数式接口

接口的默认方法:使用default 关键字给接口增加非抽象的方法实现,子类可以选择性实现。

静态方法:接口里可以声明静态方法,并且可以实现。

函数式接口:增加@FunctionalInterface 注解,只要这个接口只包含一个抽象方法。

更多描述可以参考Java 8 指南:https://www.cnblogs.com/xkzhangsanx/p/10847284.html

 

1.TemporalAccessor源码

TemporalAccessor是框架级接口,定义对时态对象(如日期、时间、偏移量或它们的某些组合)的只读访问。

这是日期、时间和偏移量对象的基本接口类型。它是由那些可以提供信息的类实现的,比如{@linkplain TemporalField字段}或{@linkplain TemporalQuery查询}。

(1)boolean isSupported(TemporalField field)

检查是否支持指定的字段

(2)default ValueRange range(TemporalField field)

默认方法,获取指定字段的有效值范围

(3)default int get(TemporalField field)

这个方法为默认方法,以int的形式获取指定字段的值

(4)long getLong(TemporalField field)

以long的形式获取指定字段的值

(5)default R query(TemporalQuery query)

默认方法,这个日期-时间查询。它使用指定的查询策略对象查询此日期-时间。

 

2.Temporal源码

 Temporal继承TemporalAccessor接口。

Temporal也是框架级接口,定义对时态对象(如日期、时间、偏移量或它们的某些组合)的读写访问。

这是日期、时间和偏移量对象的基本接口类型,这些对象足够完整,可以使用加减操作。

(1)boolean isSupported(TemporalUnit unit)

检查是否支持指定的单元

(2)default Temporal with(TemporalAdjuster adjuster)

默认方法,返回调整后的对象

例如:

date = date.with(lastDayOfMonth());

返回当前月的最后一天

(3)Temporal with(TemporalField field, long newValue)

根据指定的字段更改

(4)default Temporal plus(TemporalAmount amount)

默认方法,增加指定时间

(5)Temporal plus(long amountToAdd, TemporalUnit unit)

根据指定的单位增加时间

(6)default Temporal minus(TemporalAmount amount)

默认方法,减少指定时间

(7)default Temporal minus(long amountToSubtract, TemporalUnit unit)

默认方法,根据指定的单元减少时间

(8)long until(Temporal endExclusive, TemporalUnit unit)

根据指定的单元计算到另一个时间的相差值

例如:

        LocalDate localDate = LocalDate.of(2019, 1, 1);
        LocalDate endDate = LocalDate.of(2019, 1, 16);
        long days = localDate.until(endDate, ChronoUnit.DAYS);
        System.out.println(days);

输出:15

3.TemporalAdjuster源码

 

TemporalAdjuster接口加了函数式接口@FunctionalInterface注解,用于调整时间对象的策略。

(1)Temporal adjustInto(Temporal temporal)

调整指定的时间对象。

java.time.temporal.TemporalAdjusters 为常用的时间调节器,包含当月第一天,最后一天等等。

方法说明:

dayOfWeekInMonth   同一个月中每一周的第几天
firstDayOfMonth     当月的第一天
firstDayOfNextMonth   下月的第一天
firstDayOfNextYear   明年的第一天
firstDayOfYear     当年的第一天
firstInMonth          同一个月中,第一个符合星期几要求的值
lastDayOfMonth     当月的最后一天
lastDayOfNextMonth   下月的最后一天
lastDayOfNextYear   明年的最后一天
lastDayOfYear   今年的最后一天
lastInMonth   同一个月中,最后一个符合星期几要求的值
next/previous  将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期
nextOrSame/previousOrSame  将其值设定为日期调整后或者调整前,第一个符合指定星
期几要求的日期,如果该日期已经符合要求,直接返回该对象

 例如:当月最后一天

temporal = temporal.with(lastDayOfMonth());

 

4.ChronoLocalDate源码

ChronoLocalDate 接口 继承了Temporal, TemporalAdjuster, Comparable接口。

在任意年表中不包含(时间或时区)的日期,用于高级全球化用例。

从图中圈中部分可以看出 ChronoLocalDate实现了实现或重写了Temporal, TemporalAdjuster, Comparable接口。

其他方法:

(1)default boolean isLeapYear()

默认方法,计算闰年

(2)int lengthOfMonth()

根据日历系统的定义,返回由这个日期表示的月份的长度。

(3)default int lengthOfYear()

默认方法,返回由日历系统定义的日期表示的年份长度。

(4)default long toEpochDay()

默认方法,返回1970-01-01开始的天数

(5)default int compareTo(ChronoLocalDate other)

默认方法,实现接口Comparable,比较大小

(6)default boolean isAfter(ChronoLocalDate other)

是否在other后面

(7)default boolean isBefore(ChronoLocalDate other)

是否在other前面

(8)default boolean isEqual(ChronoLocalDate other)

是否与other相等

(9)object接口相关的方法:

boolean equals(Object obj)

int hashCode()

String toString()

(10)static ChronoLocalDate from(TemporalAccessor temporal)

从时态对象获取ChronoLocalDate的实例。

(11)default ChronoLocalDateTime atTime(LocalTime localTime)

将这个日期和时间组合起来,创建一个ChronoLocalDateTime。

(12)default String format(DateTimeFormatter formatter)

ChronoLocalDate 格式化处理

(13)Chronology getChronology()

获取此日期的年表

(14)default Era getEra()

获取由年表定义的年代

 

5.LocalDate源码

LocalDate同时实现了Temporal, TemporalAdjuster, ChronoLocalDate三个接口,是ChronoLocalDate接口国际化的ISO-8601标准实现,final修饰,线程安全,方法特别多,但很有规律。

类图如下:

主要属性为:

/**
*最小日期:-999999999-01-01
*/
public static final LocalDate MIN = LocalDate.of(Year.MIN_VALUE, 1, 1); 

/**
*最d大日期:+999999999-12-31
*/
public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31);

/**
*400年周期中的天数
*/
private static final int DAYS_PER_CYCLE = 146097;

/**
*从0年到1970年的天数
*/
static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);

/**
*年
*/
private final int year;

/**
*月
*/
private final short month;

/**
*日
*/
private final short day;

主要方法,除了实现Temporal, TemporalAdjuster, ChronoLocalDate三个接口还添加了一些特有的方法。所有可以分为6类方法:

(1)创建LocalDate对象方法

 例如:创建当前日期:

LocalDate now = LocalDate.now();

源码:可以看出是使用系统默认的Clock创建当前日期的。

    public static LocalDate now() {
        return now(Clock.systemDefaultZone());
    }

 

 (2)获取属性方法

 例如:获取年

int year = now.getYear();

源码:直接获取属性中year

    public int getYear() {
        return year;
    }

 

 (3)修改属性的方法

 例如:修改年,输出:2020-12-26

        LocalDate localDate1 = now.withYear(2020);
        System.out.println(localDate1);

源码:可以看到底层,return new LocalDate(year, month, day); 创建了一个新对象。

    public LocalDate withYear(int year) {
        if (this.year == year) {
            return this;
        }
        YEAR.checkValidValue(year);
        return resolvePreviousValid(year, month, day);
    }


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);
    }

 

 (4)增加或减少日期方法

 例如:减少1年,输出:2018-12-26

        LocalDate localDate2 = now.minusYears(1);
        System.out.println(localDate2);

源码:可以看出底层使用了:plusYears(-yearsToSubtract),相当于+(-1)

    public LocalDate minusYears(long yearsToSubtract) {
        return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
    }

 

(5)转换为各类DateTime(LocalDateTime、ZonedDateTime和OffsetDateTime)方法

 例如:转为LocalDateTime 输出为:2019-12-26T00:00

        LocalDateTime localDateTime = now.atStartOfDay();
        System.out.println(localDateTime);

源码:this为当前的日期,LocalTime.MIDNIGHT为零点时间,组合为为LocalDateTime

    public LocalDateTime atStartOfDay() {
        return LocalDateTime.of(this, LocalTime.MIDNIGHT);
    }

 

 (6)其他方法主要为实现ChronoLocalDate定义的方法

例如:ChronoLocalDate中定义的判断和比较方法

   

6.总结

  TemporalAccessor主要定义了只读的获取属性方法,Temporal主要定义了修改日期属性和日期加减运算方法,ChronoLocalDate定义了国际化lDate的通用方法,LocalDate是ChronoLocalDate的ISO标准实现,实现了上述所有接口,final修饰,线程安全,方法定义明确,丰富多样,适用广。

Java日期时间API系列7—–Jdk8中java.time包中的新的日期时间API类的优点

1.不变性

新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处。

比如:LocalDateTime

 

 

 

2.关注点分离

新的API将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。

不同时间分解成了各个类,比如:LocalDate, LocalTime, LocalDateTime, Instant,Year,Month,YearMonth,MonthDay,DayOfWeek等,满足各种不同场景使用需求。

 

3.清晰

在所有的类中,方法都被明确定义用以完成相同的行为。

举个例子,要拿到当前实例我们可以使用now()方法,在所有的类中都定义了format()和parse()方法,而不是像以前那样专门有一个独立的类。方法明确,清晰,统一,方便好记。

 

4.实用操作

(相当于很多工具方法,不再需要我们自己封装了):所有新的日期/时间API类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。

比如:LocalDateTime,包含非常丰富的实用操作(转换,获取各个字段,修改字段,增加和减少等)。

 

 

5.TemporalAdjuster 让你能够用更精细的方式操纵日期

不再局限于一次只能改变它的 一个值,并且你还可按照需求定义自己的日期转换器。比如:将日期调整到下个周日、下个工作日,或者是本月的最后一天。

lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天。

 

6.对比Jdk7及以前的日期时间类的缺点改进

6.1 Date和Calendar的不方便使用问题

(1)new Date(2019,01,01)实际是3919年2月。因为Date的构造函数 的年份表示的始于1900年的差值。

  LocalDate创建实例:

LocalDate localDate = LocalDate.of(2019, 1, 1);

(2)month是从0开始的。

LocalDate month是从1开始的:

        LocalDate localDate = LocalDate.of(2019, 1, 1);
        System.out.println(localDate.getMonthValue());

输出是1

(3)DAY_OF_WEEK 的取值,是从周日(1)开始的。

LocalDate week是从周一(1)开始的:

        LocalDate localDate = LocalDate.of(2019, 1, 1);
        System.out.println(localDate.getDayOfWeek());
        System.out.println(localDate.getDayOfWeek().getValue());

输出

TUESDAY
2

 

(4)Date如果不格式化,打印出的日期可读性差。

LocalDate的输出,清晰。

        LocalDate localDate = LocalDate.of(2019, 1, 1);
        System.out.println(localDate.getMonthValue());
        System.out.println(localDate.toString());

输出:

2019-01-01

(5)日期类并不提供国际化,没有时区支持

java8的时间类都支持了时区操作。

例如:LocalDateTime

         //中国时间,输出时不包含时区
         LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
         System.out.println(ldt);
        //意大利罗马时间,输出时包含时区
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Rome"));
        System.out.println(zdt);

输出

2019-12-20T23:17:07.914
2019-12-20T16:17:07.915+01:00[Europe/Rome]

6.2 线程安全问题

(1)Date、Calendar可变性,线程安全问题

java8中所有类都是final修饰的,每次修改都会生成新的副本。

(2)DateFormat和SimpleDateFormat线程安全问题

java8中的DateTimeFormatter也是不可变的,源码:

 

 6.3 java8对日期api进行系统的设计,增加了许多实用方便的操作,几乎不用再使用dateutil额外工具类。

比如,增加一天。

        LocalDate localDate = LocalDate.of(2019, 1, 1);
        LocalDate localDate2 = localDate.plusDays(1);
        System.out.println("localDate:"+localDate);
        System.out.println("localDate2:"+localDate2);

输出:

localDate:2019-01-01
localDate2:2019-01-02

 

 

参考:https://blog.csdn.net/wangsun300/article/details/103403490

Java日期时间API系列6—–Jdk8中java.time包中的新的日期时间API类

  因为Jdk7及以前的日期时间类的不方便使用问题线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API。Stephen向JCP提交了一个规范,他本人作为规范的领导人,该规范就是JSR 310,在Java8中实现并发布。

1.Java8日期、时间API包介绍

  • java.time包:这是新的Java日期/时间API的基础包,所有的主要基础类都是这个包的一部分,如:LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration等等。所有这些类都是不可变的和线程安全的,在绝大多数情况下,这些类能够有效地处理一些公共的需求。
  • java.time.chrono包:这个包为非ISO的日历系统定义了一些泛化的API,我们可以扩展AbstractChronology类来创建自己的日历系统。
  • java.time.format包:这个包包含能够格式化和解析日期时间对象的类,在绝大多数情况下,我们不应该直接使用它们,因为java.time包中相应的类已经提供了格式化和解析的方法。
  • java.time.temporal包:这个包包含一些时态对象,我们可以用其找出关于日期/时间对象的某个特定日期或时间,比如说,可以找到某月的第一天或最后一天。你可以非常容易地认出这些方法,因为它们都具有“withXXX”的格式。
  • java.time.zone包:这个包包含支持不同时区以及相关规则的类

 

2.Java8日期时间API主要类有:

LocalDate:表示不带时间的日期
LocalTime:表示不带日期的时间
LocalDateTime:日期和时间类
ZoneId:时区
ZonedDateTime:一个带时区的完整时间
Instant:Unix 时间,它代表的是时间戳,比如 2018-01-14T02:20:13.592Z
Clock:获取某个时区下当前的瞬时时间,日期或者时间
Duration:表示一个绝对的精确跨度,使用毫秒为单位
Period:这个类表示与 Duration 相同的概念,但是以人们比较熟悉的单位表示,比如年、月、周
DateTimeFormatter:格式化输出
TemporalAdjusters:获得指定日期时间等,如当月的第一天、今年的最后一天等

ChronoUnit:时间单位枚举,用于加减操作

ChronoField:字段枚举,用于设置字段值。

主要类图:

 

 时间范围示意图:

 

 

参考:https://www.iteye.com/news/26064

  https://segmentfault.com/a/1190000020179839

  《Java8实战》

  https://blog.csdn.net/wangsun300/article/details/103403490

Java日期时间API系列5—–Jdk7及以前的日期时间类TimeUnit在并发编程中的应用

TimeUnit是一个时间单位枚举类,主要用于多线程并发编程,时间单元表示给定粒度单元的时间持续时间,并提供实用程序方法来跨单元转换,以及在这些单元中执行计时和延迟操作。

1.时间单位换算

(1)支持的单位

TimeUnit.DAYS          //
TimeUnit.HOURS         //小时
TimeUnit.MINUTES       //分钟
TimeUnit.SECONDS       //
TimeUnit.MILLISECONDS  //毫秒
TimeUnit.MICROSECONDS  //微秒
TimeUnit.NANOSECONDS  //纳秒

(2)转换方法,例如:TimeUnit.HOURS 的转换源码

    HOURS {
        public long toNanos(long d)   { return x(d, C5/C0, MAX/(C5/C0)); }
        public long toMicros(long d)  { return x(d, C5/C1, MAX/(C5/C1)); }
        public long toMillis(long d)  { return x(d, C5/C2, MAX/(C5/C2)); }
        public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); }
        public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); }
        public long toHours(long d)   { return d; }
        public long toDays(long d)    { return d/(C6/C5); }
        public long convert(long d, TimeUnit u) { return u.toHours(d); }
        int excessNanos(long d, long m) { return 0; }
    }

(3)使用举例

//小时转换为秒
long sec = TimeUnit.HOURS.toSeconds(1);
System.out.println(“sec:” + sec);

// 另一种形式
long sec2 = TimeUnit.SECONDS.convert(1, TimeUnit.HOURS);
System.out.println(“sec2:” + sec2);

输出结果:

sec:3600
sec2:3600

 

2.计时操作

计时操作需要2个参数:数值和单位TimeUnit。

(1)Lock,tryLock,尝试获取锁50毫秒。

  Lock lock = ...;
   if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...

 

(2)线程池构造方法参数:keepAliveTime和unit

Java线程池ThreadPoolExecutor原理和用法,ThreadPoolExecutor构造方法:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止;
unit:参数keepAliveTime的时间单位(TimeUnit);
 
例如:java.util.concurrent.Executors.newCachedThreadPool(ThreadFactory)
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue(),
                                      threadFactory);
    }

 

(3)ArrayBlockingQueue的poll方法,long和TimeUnit
java.util.concurrent.ArrayBlockingQueue.poll(long, TimeUnit)
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(100);
        for (long i = 0; i < 100; i++) {
            arrayBlockingQueue.add(i);
        }
        
        for (long i = 0; i < 100; i++) {
            try {
                System.out.println(arrayBlockingQueue.poll(50, TimeUnit.MILLISECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

 

3.延迟操作

(1)比如当前线程延迟5s

TimeUnit.SECONDS.sleep(5);

 

4.TimeUnit 与 Thread sleep的区别

(1)TimeUnit sleep的原理

    public void sleep(long timeout) throws InterruptedException {
        if (timeout > 0) {
            long ms = toMillis(timeout);
            int ns = excessNanos(timeout, ms);
            Thread.sleep(ms, ns);
        }
    }

TimeUnit sleep的底层调用了Thread.sleep。

(2)区别:TimeUnit sleep使用起来更方便,更易懂

比如:比如当前线程延迟5s:

使用Thread.sleep

Thread.sleep(5000);
// 或者
Thread.sleep(5*1000);

使用TimeUnit

TimeUnit.SECONDS.sleep(5);

Java日期时间API系列4—–Jdk7及以前的日期时间类的线程安全问题

1.Date类为可变的,在多线程并发环境中会有线程安全问题。

(1)可以使用锁来处理并发问题。

(2)使用JDK8  Instant 或 LocalDateTime替代。

2.Calendar的子类为可变的,在多线程并发环境中会有线程安全问题。

(1)可以使用锁来处理并发问题。

(2)使用JDK8  LocalDateTime 替代。

3.DateFormat和SimpleDateFormat不是线程安全的原因

(1)DateFormat中calendar是共享变量,其子类SimpleDateFormat中也是共享变量。

DateFormat源码:

public abstract class DateFormat extends Format {

/**
* The {@link Calendar} instance used for calculating the date-time fields
* and the instant of time. This field is used for both formatting and
* parsing.
*
*

Subclasses should initialize this field to a {@link Calendar}
* appropriate for the {@link Locale} associated with this
* DateFormat.
* @serial
*/
protected Calendar calendar;

(2)SimpleDateFormat format方法源码:

    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }

  当多个线程同时使用相同的 SimpleDateFormat 对象【如用static修饰的 SimpleDateFormat 】调用format方法时,多个线程会同时调用 calendar.setTime 方法,可能一个线程刚设置好 time 值另外的一个线程马上把设置的 time 值给修改了导致返回的格式化时间可能是错误的。

4.SimpleDateFormat线程安全使用。

(1)使用ThreadLocal处理static方法

    public static final ThreadLocal df = new ThreadLocal() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
System.out.println(df.get().format(new Date()));
2019-12-14

(2)使用JDK8  DateTimeFormatter 替代。

 

参考:https://www.cnblogs.com/wupeixuan/p/11511915.html?utm_source=gold_browser_extension

  《阿里巴巴Java开发手册》

Java日期时间API系列3—–Jdk7及以前的日期时间类的不方便使用问题

使用Java日期时间类,每个人都很熟悉每个项目中必不可少的工具类就是dateutil,包含各种日期计算,格式化等处理,而且常常会遇到找不到可用的处理方法,需要自己新增方法,处理过程很复杂。

1.Date中的过时方法等

Date中的方法一般都过时了,不建议使用,有一些歧义。比如:

(1)new Date(2019,01,01)实际是3919年2月。因为Date的构造函数 的年份表示的始于1900年的差值。

(2)month是从0开始的。

(3)Date如果不格式化,打印出的日期可读性差。

Fri Dec 13 23:08:12 CST 2019

(4)Date和java.sql.Date命名完全一样,不易区分。

2 Calendar操作繁琐、不支持复杂计算等

Calendar虽然能够处理大部分的Date计算,但设计不是很成功,一些简单操作都要多次调用。对一些复杂的计算比如两个日期之间有多少个月,生日计算年龄等都不支持。比如:

(1)DAY_OF_WEEK 的取值,是从周日(1)开始的。

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        System.out.println(calendar.get(Calendar.DAY_OF_WEEK));

(2)MONTH 的取值,是从0开始的。

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        System.out.println(calendar.get(Calendar.MONTH));

(3)set()方法延迟修改

通过set()方法设置某一个字段的值得时候,该字段的值不会立马修改,直到下次调用get()、getTime()等时才会重新计算日历的时间。延迟修改的优势是多次调用set()方法不会触发多次不必要的计算。下面程序演示了set()方法延迟修改的效果:

Calendar cal = Calendar.getInstance();
cal.set(2003,7,31);//2003-8-31
//将月份设为9,但9月31不存在
//如果立即修改,系统会把cal自动调整到10月1日
cal.set(Calendar.MONTH,8);
//下面代码输出了10月1日
System.out.println(cal.getTime());//(1)
//设置DATE字段为5
cal.set(Calendar.DATE, 5);//(2)
System.out.println(cal.getTime());//(3)

打印结果为:

Wed Oct 01 22:25:41 CST 2003
Sun Oct 05 22:25:41 CST 2003

如果将(1)处的代码注释掉,打印结果为:

Fri Sep 05 22:28:06 CST 2003

你看明白了吗?如果将(1)处的代码注释掉,因为set()方法具有延迟性,它内部只是先记录下MONTH字段的值为8,接着程序将DATE字段设置为5,程序内部再次记录DATE字段的值为5——就是9月5日。

3.日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

 

参考:https://www.jianshu.com/p/1478af429a1e

      https://www.cnblogs.com/bingyimeiling/p/10420752.html