Java8的LocalDate LocalTime LocalDateTime家族

3,201 阅读6分钟

前言

最近做了很多统计方面的功能,基本是要和日期这个东西打交道,各种,前移多少天,前移几个小时,前移几分钟的操作,虽然Java的Calendar这个日历操作类能搞定,但是还是感觉写起来有点费劲.果不其然,让我发现了LocalDate LocalTime LocalDateTime这个JAVA8新增的时间类

介绍

java.util.Date和java.sql.Date是jdk1就开始提供了,java.sql.Date继承了java.util.Date,我们和数据库交互,一般是采用java.util.Date这个类,因为java.sql.Date的getHours,getMinutes,gitSeconds这三个方法都是这么写的:

    @Deprecated
    public int getHours() {
        throw new java.lang.IllegalArgumentException();
    }
    
    @Deprecated
    public int getMinutes() {
        throw new java.lang.IllegalArgumentException();
    }
    
     @Deprecated
    public int getSeconds() {
        throw new java.lang.IllegalArgumentException();
    }
    
    对应的setXXXX方法也是如出一辙的抛出IllegalArgumentException异常
    例如:
    @Deprecated
    public void setHours(int i) {
        throw new java.lang.IllegalArgumentException();
    }
    并且还带有@Deprecated(废弃)这个注解,看来,java.sql.Date只能显示到:年月日这个级别了,也就是:2019-11-22这种形式的数据

LocalDate,LocalTime,LocalDateTime的介绍

LocalDate,LocalTime,LocalDateTIme这三个类是java8新提供的时间操作类,在java.time包下面.这三个类是从org.joda.time吸收过来的(地方武装的工具类被提成标准部队了).
1.LocalDate是主管:yyyy-MM-dd(2019-11-22)这种形式,也就是只管:年-月-日
2.LocalTime是主管:HH:mm:ss (14:56:23)这种形式,也就是只管:时:分:秒
3.LocalDateTime是主管:yyyy-MM-dd HH:mm:ss 这种形式,也就是只管:年-月-日 时:分:秒(LocalDateTime是由LocalDate和LocalTime组成的)

1.构造对象方法

LocalDate,LocaTime,LocalDateTime提供了基本上一致,有以下集中:
now()系列:
    //默认时区
    public static LocalDate now() {
        return now(Clock.systemDefaultZone());
    }
    //自定义时区,例如你可以通过ZoneId.of("America/New_York")使用纽约的时区
    public static LocalDate now(ZoneId zone) {
        return now(Clock.system(zone));
    }
    //自定义时钟,这个有点高深,还没使用过
    public static LocalDate now(Clock clock) {
        Objects.requireNonNull(clock, "clock");
        // inline to avoid creating object and Instant checks
        final Instant now = clock.instant();  // called once
        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
        long epochSec = now.getEpochSecond() + offset.getTotalSeconds();  // overflow caught later
        long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
        return LocalDate.ofEpochDay(epochDay);
    }

of系列:(这里只列举LocalDate的of)
    //根据年月日构造LocalDate对象,这个地方的dayOfMonth是1-31的取值
    public static LocalDate of(int year, Month month, int dayOfMonth) {
        YEAR.checkValidValue(year);
        Objects.requireNonNull(month, "month");
        DAY_OF_MONTH.checkValidValue(dayOfMonth);
        return create(year, month.getValue(), dayOfMonth);
    }
    //同上,只是都是使用int类型来传递参数了
    public static LocalDate of(int year, int month, int dayOfMonth) {
        YEAR.checkValidValue(year);
        MONTH_OF_YEAR.checkValidValue(month);
        DAY_OF_MONTH.checkValidValue(dayOfMonth);
        return create(year, month, dayOfMonth);
    }
    //这个是定位到一年当中的第几天
    public static LocalDate ofYearDay(int year, int dayOfYear) {
        YEAR.checkValidValue(year);
        DAY_OF_YEAR.checkValidValue(dayOfYear);
        boolean leap = IsoChronology.INSTANCE.isLeapYear(year);
        if (dayOfYear == 366 && leap == false) {
            throw new DateTimeException("Invalid date 'DayOfYear 366' as '" + year + "' is not a leap year");
        }
        Month moy = Month.of((dayOfYear - 1) / 31 + 1);
        int monthEnd = moy.firstDayOfYear(leap) + moy.length(leap) - 1;
        if (dayOfYear > monthEnd) {
            moy = moy.plus(1);
        }
        int dom = dayOfYear - moy.firstDayOfYear(leap) + 1;
        return new LocalDate(year, moy.getValue(), dom);
    }

2.具体的使用例子(这个才是上手的好东西)

获取现在的时间对象:

//时间是:2019-11-22
LocaDate now = LocalDate.now();

//时间是:14:43:56
LocalTime now = LocalTime.now();

//时间是:2019-11-22 14:43:56
LocalDateTime now = LocalDateTime.now();

使用of来具体定制时间

//时间是:2015-12-20
LocalDate localDate = LocalDate.of(2015,12,20);

//时间是:15:36:48
LocalTime localTime = LocalTime.of(15,36,48);

//时间是:2013-09-21 05:56:24
LocalDateTime localDateTime = LocalDateTime.of(2013,9,21,5,58,24);

各种条件的定位时间

老铁们一定要注意:凡是LocalDate各种操作之后,他都返回一个新的LocalDate对象,所以,你要接着这个新对象啊,你要是不用对象接着,那么就会看到不起作用,这样子好链式编程

我们在开发中可能遇到各种条件的定位时间:本月的第一天,本月的最后一天,本月每周的周一,本月每周的周三,本年度的第9月的第二天,或者哪一年的上述各种情况:

//我们随便拿个时间做实验,以求做到一般化处理,就拿2019-10-22日来处理吧
LocalDate localDate = LocalDate.of(2019,10,22);
//**老铁们一定要注意:凡是LocalDate各种操作之后,他都返回一个新的LocalDate对象,所以,你要接着这个新对象啊,你要是不用对象接着,那么就会看到不起作用,这样子好链式编程**

//1.获取本月的第一天,输出:2019-10-01
localDate = localDate.with(TemporalAdjusters.firstDayOfMonth())

//2.获取本月的最后一天,输出:2019-10-31
localDate = localDate.with(TemporalAdjusters.lastDayOfMonth());

//3.本年第一天,输出:2019-01-01
localDate = localDate.with(TemporalAdjusters.firstDayOfYear());

//4.本年最后一天,输出:2019-12-31
localDate = localDate.with(TemporalAdjusters.lastDayOfYear());

//5.下一个周几的操作,输出:2019-10-24
//注意,2019-10-22是周二,所以下一个周四就是2019-10-24,如果是下一个周一,那就是:2019-10-28了
//当然也有对应的previes方法,就是上一个周几
localDate = localDate.with(TemporalAdjusters.next(DayOfWeek.THURSDAY));
localDate = localDate.with(TemporalAdjusters.previes(DayOfWeek.THURSDAY));

//6.本月第2周的周五,输出是:2019-10-11
localDate = localDate.with(TemporalAdjusters.dayOfWeekInMonth(2,DayOfWeek.FRIDAY));

//7.还有下一个月的第一天,输出是:2019-11-01,当然还有下一年
localDate = localDate.with(TemporalAdjusters.firstDayOfNextMonth());

//8.加一天,输出是:2019-10-23
localDate = localDate.plusDays(1L);

//9.加一周,输出是:2019-10-29(即使是跨年和跨月,也会正确加的,不用担心)
localDate = localDate.plusWeeks(1L);

//10.加一个月,输出是:2019-11-22(下一月没有31号这种情况,他们会帮我们处理的),例如:2019-10-31加一个月,返回:2019-11-30(放心,JDK都是进过各种验证的,不会出现人不能理解的错误的)
localDate = localDate.plusMonths(1L);

同理,LocalTime和LocalDateTime也可以按照上面的方法来加减小时,分钟,秒等操作

3.时间的比较

这个之前比较时间都是getTime然后比较一下就行,现在LocalDate,LocalTime,LocalDateTime提供了isBefore,isAfter方法

LocalDate first = LocalDate.now();
LocalDate second = LocalDate.of(2015,12,25);
first.isBefore(second);//返回:false
first.isAfter(second);//返回:true

4.日期的字符串格式化

我们避免不了经常需要格式化日期字符串,这方面Local系列做的也很好

//输出为:2019-10-22
localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
不用像之前还要一个SimpleDateFormatter了

5.扯到最后,有点不适应,就是java.util.Date和LocalDate,LocalTime,LocalDateTime没有直接互转的方法,没办法,自己补充一个了

    //java.util.Date类型转LocalDateTime
    public static LocalDateTime dateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }
    
    //java.time.LocalDateTime转java.util.Date
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }
    
    有了LocalDateTime,你就可以操作获取LocalDate,LocalTime了,当然也有转Date<-->LocalDate  Date<-->LocalTime的操作,
    在这个地方就不一一列举了!,其实就是instant.atZone(zoneId).toLocalDateTime()修改这句话就行

6.贴出来我操作的一个例子

        String sqlFormat = "drop table gsms_phone_detection_0101";

        LocalDate startDate = LocalDate.of(2012, 1, 1);
        LocalDate endDate = LocalDate.of(2012, 12, 31);
        List<String> result = Stream.iterate(startDate, localDate -> localDate.plusDays(1L))
                .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1)
                .map(localDate -> localDate.format(DateTimeFormatter.ofPattern("MMdd")))
                .map(tableSuffix -> sqlFormat.replaceAll("0101",tableSuffix))
                .collect(Collectors.toList());
        Files.write(Paths.get("/home/liuxu/Pictures/gsms_ded_red_det_verify_xxxx.sql"),result);
        
        这是我生成删除按照日分表的sql语句,(不会写存储过程或者函数,也没有功夫探索了)