【Java】Java8.0全新的时间API使用示例

为什么引入?

Java8.0以前:
Date对象:
时间基准以1900年元旦开始,JDK1.0时出现,大部分方法已过时(@Deprecated),格式化时需要借助text包,结构混乱;

Calendar对象:
每周第一天从周日开始,月份从0开始,日期运算默认正向加法

TimeZone对象:
时区换算不方便

以上缺点如果都还能补救,接下来的情况就不容乐观了

多线程下的时间转换

使用线程池,实现带有返回值的多线程程序

    @Test
    public void time0() {
        //使用多线程,批量转换时间
        Callable task = new Callable() {
            //volatile不能解决,底层缓存还是多线程公用,发生冲突
            /*volatile*/ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

            @Override//synchronized彻底解决同步,效率降低
            public /*synchronized*/ Date call() throws Exception {
                return sdf.parse("2018-01-01 01:01:01");
            }
        };
        ExecutorService pool = Executors.newFixedThreadPool(10);
        List> results = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            results.add(pool.submit(task));
        }
        //输出
        results.forEach(f -> {
            try {
                System.out.println(f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
    }

执行结果,发现有些线程转换成功了,但是有些线程直接出异常了,更有的线程竟然转换出错误的结果
【Java】Java8.0全新的时间API使用示例_第1张图片

为了解决这个问题,经过尝试volatile关键字和synchronized关键字,最后只有synchronized成功了,但是牺牲了效率

新引入的API解决方法

    @Test
    public void time0() {
        //可以多线程公用
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        Callable task = () -> LocalDate.parse("2018-01-01", dtf);
        ExecutorService pool = Executors.newFixedThreadPool(10);
        List> results = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            results.add(pool.submit(task));
        }
        //输出,正常输出
        results.forEach(f -> {
            try {
                System.out.println(f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
    }

彻底解决:
【Java】Java8.0全新的时间API使用示例_第2张图片

新引进的API——java.time

java.time:处理时间的功能类包
java.time.chrono:时间特殊的格式包,依据不同国家和地区进行格式化,包括年号纪年、其他历法、特殊公元纪年
java.time.temporal:时间运算包
java.time.zone:时区转换包

ISO-8601

国际标准化组织制定的现代公民的日期和时间的表示法规范

使用示例

现在时间

@Test
public void time1(){
    //13:55:31.613
    LocalTime localTime = LocalTime.now();
    //2018-06-07
    LocalDate localDate = LocalDate.now();
    //2018-06-07T13:55:31.613
    LocalDateTime localDateTime = LocalDateTime.now();        
}

创建时间

@Test
public void time2() {
    //2018年1月1日,ofEpochDay以1970年元旦为准的天数,ofYearDay以指定年的元旦加指定的天数
    LocalDate localDate = LocalDate.of(2018, 1, 1);
    //10时10分10秒123毫秒456微秒789纳秒,ofSecondOfDay当日秒,ofNanoOfDay当日纳秒
    LocalTime localTime = LocalTime.of(10, 10, 10, 123_456_789);

    //时间合并
    LocalDateTime localDateTime1 = localTime.atDate(localDate);
    LocalDateTime localDateTime2 = localDate.atTime(localTime);

    //重写toString()方法实现格式化输出
    System.out.println(localDate);//2018-01-01
    System.out.println(localTime);//10:10:10.123456789
    System.out.println(localDateTime1);//2018-01-01T10:10:10.123456789
    System.out.println(localDateTime2);//2018-01-01T10:10:10.123456789
}

获取日期和时间的参数

@Test
public void time3() {
    LocalDate localDate = LocalDate.of(-1018,Month.JANUARY,1);
    LocalTime localTime = LocalTime.of(10,10,10,123456789);

    int num;
    //时
    num = localTime.getHour();//10

    //分
    num = localTime.getMinute();//10

    //秒
    num = localTime.getSecond();//10

    //纳秒
    num = localTime.getNano();//123456789


    //年
    num = localDate.getYear();//2018

    //月
    num = localDate.getMonthValue();//1

    //日
    num = localDate.getDayOfMonth();//1

    //一年中的第几日
    num = localDate.getDayOfYear();//1

    //去掉getValue()直接输出英文单词
    //星期,1:周一(ordinal  1为周日)
    DayOfWeek dayOfWeek = localDate.getDayOfWeek();//MONDAY
    int value = dayOfWeek.getValue();//1

    //月,1:一月
    Month month = localDateTime.getMonth();//JUNE
    value = month.getValue();//6

    //BCE:公元前,0;CE:公元后,1
    Era era = localDate.getEra();//CE
    value = era.getValue();//1
}

时区

@Test
public void time4() {
    //时区
    Set set = ZoneId.getAvailableZoneIds();
    //遍历所有支持的时区ID
    //set.forEach(System.out::println);

    //时区,可以负数,输出东8区时间:2018-06-07T14:14:03.436+08:00
    OffsetDateTime offsetDateTime = LocalDateTime.now().atOffset(ZoneOffset.ofHours(8));

    //获取指定时区当前时间,输出东9区时间:2018-06-07T15:14:03.437
    LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Japan"));//等价于"Etc/GMT-9"

    //获取指定时间差的时区,不能为负数,时间以0时区为准,输出东8区30分时间:2018-06-07T14:44:03.437
    LocalDateTime ldt2 = LocalDateTime.now(ZoneOffset.ofHoursMinutes(8, 30));

    //给时间指定时区,输出:2018-06-07T14:14:03.433-08:00[Etc/GMT+8]
    ZonedDateTime zdt = localDateTime.atZone(ZoneId.of("Etc/GMT+8"));

    //指定时间的时区,输出:01:01:01
    LocalTime lt = LocalTime.of(1, 1, 1);
    //输出:01:01:01+08:00
    OffsetTime odt = lt.atOffset(ZoneOffset.ofHoursMinutesSeconds(8, 0, 0));
}

时间戳

@Test
public void time5() {
    //时间戳(以1970年元旦为准,Unix元年),输出:2018-06-07T06:21:51.082Z
    Instant instant = Instant.now();//UTC时区(欧洲,格林尼治时间)
    //从1970年元旦至今的毫秒数,UTC时区时间
    long l = instant.toEpochMilli();//1528352511082
    //创建时间戳
    instant = Instant.ofEpochMilli(l);//2018-06-07T06:33:44.298Z

    //时钟戳
    Clock clock = Clock.systemDefaultZone();//SystemClock[Asia/Shanghai]

    //获取时区ID
    ZoneId zone = clock.getZone();//Asia/Shanghai

    //从1970年元旦至今的毫秒数
    long millis = clock.millis();//1528352511099

    //获取当前时间的时间戳,输出:2018-06-07T14:31:45.147Z
    instant = LocalDateTime.now().toInstant(ZoneOffset.UTC);
}

时间运算

@Test
public void time6() {
    //时间差
    Duration duration = Duration.between(Instant.ofEpochSecond(1_0000_0000), Instant.ofEpochSecond(2_0000_0000));//PT27777H46M40S
    long hours = duration.toHours();//27777
    long minutes = duration.toMinutes();//1666666
    long seconds = duration.getSeconds();//100000000

    //日期差
    Period period = Period.between(LocalDate.of(2000, 1, 1), LocalDate.of(3000, 12, 31));//P1000Y11M30D
    int years = period.getYears();//1000
    int months = period.getMonths();//11
    int days = period.getDays();//30

    //日期运算,plus为加法,minus为减法,可以出现负数
    LocalDate localDate = LocalDate.of(2018, 1, 1);
    //100天后
    LocalDate ld = localDate.plusDays(100);//2018-04-11
    //30月前
    ld = localDate.minusMonths(30);//2015-07-01
    //13周后
    ld = localDate.plusWeeks(30);//2018-07-30
    //10年10月10周10日后
    ld = localDate.plusYears(10).plusYears(10).plusWeeks(10).plusDays(10);//2038-03-22

    LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 1, 1, 1, 1, 1);
    //纳秒增加运算
    LocalDateTime ldt = localDateTime.plusNanos(123_1234_1234_1234_1234L);//2057-01-06T10:29:44.412341235

    /*
     使用指定时间单位
     */
    localDateTime = LocalDateTime.of(2018, 1, 1, 1, 1, 1, 1);

    //1纳秒后
    ldt = localDateTime.plus(1, ChronoUnit.NANOS);//2018-01-01T01:01:01.000000002
    //1微秒后
    ldt = localDateTime.plus(1, ChronoUnit.MICROS);//2018-01-01T01:01:01.000001001
    //1毫秒后
    ldt = localDateTime.plus(1, ChronoUnit.MILLIS);//2018-01-01T01:01:01.001000001
    //1秒后
    ldt = localDateTime.plus(1, ChronoUnit.SECONDS);//2018-01-01T01:01:02.000000001
    //1分钟后
    ldt = localDateTime.plus(1, ChronoUnit.MINUTES);//2018-01-01T01:02:01.000000001
    //1小时后
    ldt = localDateTime.plus(1, ChronoUnit.HOURS);//2018-01-01T02:01:01.000000001
    //半天后
    ldt = localDateTime.plus(1, ChronoUnit.HALF_DAYS);//2018-01-01T13:01:01.000000001
    //1天后
    ldt = localDateTime.plus(1, ChronoUnit.DAYS);//2018-01-02T01:01:01.000000001
    //1周后
    ldt = localDateTime.plus(1, ChronoUnit.WEEKS);//2018-01-08T01:01:01.000000001
    //1个月后
    ldt = localDateTime.plus(1, ChronoUnit.MONTHS);//2018-02-01T01:01:01.000000001
    //1年后
    ldt = localDateTime.plus(1, ChronoUnit.YEARS);//2019-01-01T01:01:01.000000001
    //1个年代后
    ldt = localDateTime.plus(1, ChronoUnit.DECADES);//2028-01-01T01:01:01.000000001
    //1个世纪后
    ldt = localDateTime.plus(1, ChronoUnit.CENTURIES);//2118-01-01T01:01:01.000000001
    //1000年
    ldt = localDateTime.plus(1, ChronoUnit.MILLENNIA);//3018-01-01T01:01:01.000000001
    //跨公元
    ldt = LocalDateTime.of(-2017, 1, 1, 1, 1, 1, 1)
            .plus(1, ChronoUnit.ERAS);//2018-01-01T01:01:01.000000001
}

with(时间校正)

@Test
public void time7() {
    //时间校正器
    LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 1, 1, 1, 1);
    LocalDateTime result;
    //设置为10号
    result = localDateTime.withDayOfMonth(10);//2018-01-10T01:01:01

    //设置为下一个周日
    result = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));//2018-01-07T01:01:01

    //下一个工作日,输出:2018-01-02T01:01:01
    result = localDateTime.with(in -> {
        LocalDateTime out = (LocalDateTime) in;
        DayOfWeek dow = out.getDayOfWeek();
        if (dow.equals(DayOfWeek.FRIDAY)) {
            out = out.plusDays(3);
        } else if (dow.equals(DayOfWeek.SATURDAY)) {
            out = out.plusDays(2);
        } else {
            out = out.plusDays(1);
        }
        return out;
    });

    //预产期,输出:2018-10-08T01:01:01
    result = localDateTime.with((in) -> {
        LocalDateTime out = (LocalDateTime) in;
        return out.plusDays(280);
    });
}

时间格式化

@Test
public void time8() {
    //时间格式化
    DateTimeFormatter dtfISO = DateTimeFormatter.ISO_DATE_TIME;
    LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 1, 1, 1, 1, 123_456_789);
    String strDate = localDateTime.format(dtfISO);//2018-01-01T01:01:01.123456789

    //自定义格式化时间
    /* https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html */
    DateTimeFormatter dtfDIY = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒n纳秒");
    strDate = localDateTime.format(dtfDIY);//2018年01月01日01时01分01秒123456789纳秒

    //自定义解析时间
    TemporalAccessor ta = dtfDIY.parse("1234年05月06日07时08分09秒987654321纳秒");
    LocalDateTime ldt = LocalDateTime.from(ta);//1234-05-06T07:08:09.987654321

    //国际化
    String dateIN = "2010-01-01 01:23:45";
    DateTimeFormatter dtfIN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    LocalDateTime ldtIN = LocalDateTime.parse(dateIN, dtfIN);

    DateTimeFormatter dtfOUT = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM).withLocale(Locale.KOREA);
    String dateOUT = ldtIN.format(dtfOUT);
    System.out.println(dateOUT);//2010년 1월 1일 금요일 오전 1:23:45

}

地方历法

@Test
public void time9() {
    /*
    特殊的四种历法,以公元2018年1月1日为准
     */
    //佛历,以公元543年为元年
    ThaiBuddhistDate thaiBuddhistDate = ThaiBuddhistChronology.INSTANCE.date(2561, 1, 1);//ThaiBuddhist BE 2561-01-01

    //台湾,以公元1911年为元年
    MinguoDate minguoDate = MinguoChronology.INSTANCE.date(107, 1, 1);//Minguo ROC 107-01-01

    //日本
    /*
    明治(1868-01-01 - 1912-07-29)
    大正(1912-07-30 - 1926-12-24)
    昭和(1926-12-25 - 1989-01-07)
    平成(1989-01-08 - 2019-04-30)(日本历法改年号时,Java需要打补丁补充年号)
     */
    //JapaneseDate japaneseDate = JapaneseChronology.INSTANCE.date(2018, 1, 1);
    JapaneseDate japaneseDate = JapaneseChronology.INSTANCE.date(JapaneseEra.HEISEI, 30, 1, 1);//Japanese Heisei 30-01-01

    //伊斯兰历(回历),公元639年为元年,伊历=公历-622+(公历-622)/32,公历=伊历+622-伊历/33,日落天黑为一天的开始
    HijrahDate hijrahDate = HijrahChronology.INSTANCE.date(1439, 4, 14);//Hijrah-umalqura AH 1439-04-14

    /*
    借助Unix元年(1970年元旦)进行转换
     */
    //公历转伊历
    HijrahDate hd = HijrahChronology.INSTANCE.dateEpochDay(LocalDate.of(2018, 1, 1).toEpochDay());//Hijrah-umalqura AH 1439-04-14
    //伊历转公历
    LocalDate ld = LocalDate.ofEpochDay(HijrahDate.of(1439, 4, 14).toEpochDay());//2018-01-01

    //台湾历转日本历
    JapaneseDate jd = JapaneseChronology.INSTANCE.dateEpochDay(MinguoChronology.INSTANCE.date(107, 1, 1).toEpochDay());//Japanese Heisei 30-01-01
}

你可能感兴趣的:(Java)