Java开发篇 - 还在为计算2个日期间隔的天数纠结?是时候换掉java.util.Date

简介: 即然Date及Calendar在计算时间间隔或者其他场景下都比较麻烦,那么有没有更好的API使用呢?当然是有的,JDK1.8中,就更新了新的日期/时间处理工具类。具体的包在java.time目录下,有兴趣的小伙伴可以打开进行查看。

最近打开了尘封较久的项目,发现原来旧的项目中,在处理日期/时间的方法中,都是使用java.util.Date,而且发现在计算2个日期相差的天数,相差的月数的代码是相当的纠结,需要将Date转换成long类型的时间戳,然后将2个时间戳相减的结果,在除以1000 x 60 x 60 x 24,(别问我为什么要这样),因为long类型的时间戳是毫秒级的,对应到前面的公式的说明是(毫秒 x 秒 x 分 x 小时),这样应该就好理解,一天有24小时,一小时有60分钟,一分钟有60秒,一秒等1000毫秒

/**
 * 返回自1970年1月1日00:00:00 GMT以来由该【日期】对象显示的毫秒数
 *
 * @return  自1970年1月1日00:00:00 GMT以来与该日期相对应的毫秒数
 */
public long getTime() {
   
   
    return getTimeImpl();
}

Date类型算是JDK中比较久远的一种类型,从JDK1.0开始就提供了,对于大部分的Java开发人员来说,并不陌生,是开发中经常会使用到的(还有一个叫SimpleDateFormat),比如获取当前的日期,格式化日期为字符串形式(如yyyy-MM-dd),将字符串的日期格式变成Date的类型,还有就是上面说的计算2个日期间隔的天/月数,得取月的第一天,最后一天等。

先来看下旧代码(比较过时的写法):

/**
 * 日期工具类
 */
public final class DateUtils {
   
   

    private DateUtils() {
   
   
    }

    /**
     * 获得当前日期
     */
    public static Date now() {
   
   
        return new Date();
    }

    /**
     * 返回以【formatString 】格式的当前日期
     * @param formatString
     *    时间格式(如yyyy-MM-dd)
     */
    public static String getNowStrFormat(String formatString) {
   
   
        return format(now(), formatString);
    }

    /**
     * 返回以【formatString 】格式的【日期】
     * @param date
     *    输入的日期
     * @param formatString
     *    时间格式(如yyyy-MM-dd)
     */
    public static String format(Date date, String formatString) {
   
   
        return format(date, formatString, Locale.PRC);
    }

    /**
     * 返回以【formatString 】格式的【日期】
     * @param date
     *    输入的日期
     * @param formatString
     *    时间格式(如yyyy-MM-dd)
     * @param locale
     *   指定的地区
     */
    private static String format(Date date, String formatString, Locale locale) {
   
   
        if (date == null) {
   
   
            return null;
        } else {
   
   
            SimpleDateFormat dateFormat = new SimpleDateFormat(formatString, locale);
            return dateFormat.format(date);
        }
    }

    /**
     * 将日期字符串【dateString 】按【format】的格式转换成 Date类型
     * @param dateString
     *    输入的日期字符串
     * @param format
     *    时间格式(如yyyy-MM-dd)
     */
    public static Date parse(String dateString, String format) {
   
   
        Date date = null;
        try {
   
   
            SimpleDateFormat df = new SimpleDateFormat(format);
            date = df.parse(dateString);
        } catch (ParseException e) {
   
   
            LOG.error("日期转换错误,日期格式不正确", e);
            throw new IllegallArgumentException("输入的字符串【" + dateString + "】"与格式【" + format + "】不匹配);
        }

        return date;
    }

    /**
     * 获取指定year(年),month(月)的第一天
     * @param year
     *    输入年份
     * @param month
     *    输入的月份
     */
    public static String getFirstDayOfMonth(Integer year, Integer month){
   
   
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, (month - 1));
       //c.add(Calendar.MONTH, 0);
        c.set(Calendar.DAY_OF_MONTH,1);//设置为1号,当前日期既为本月第一天
        return format(c.getTime(), "yyyy-MM-dd");
    }

    /**
     * 获取指定year(年),month(月)的最后一天
     * @param year
     *    输入年份
     * @param month
     *    输入的月份
     */
    public static String getLastDayOfMonth(Integer year, Integer month){
   
   
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, (month - 1));
        //c.add(Calendar.MONTH, 0);
        c.set(Calendar.DAY_OF_MONTH,c.getActualMaximum(Calendar.DAY_OF_MONTH));//设置为1号,当前日期既为本月第一天    
        return format(c.getTime(), "yyyy-MM-dd");
    }

    /**
     * 得取某个日期(date)在月中是第几天
     * @param date
     *    输入的日期
     */
    public static int dayOfMonth(Date date){
   
   
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * 得取某个日期(date)在月中是周几
     * @param date
     *    输入的日期
     */
    public static int dayOfWeek(Date date){
   
   
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * 得取某个日期(date)是一年中的第几个月(注意结果要 + 1)
     * @param date
     *    输入的日期
     */
    public static int month(Date date){
   
   
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.MONTH);
    }

    /**
     * 得取某个日期(date)的年份
     * @param date
     *    输入的日期
     */
    public static int year(Date date){
   
   
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c.get(Calendar.YEAR);
    }

    /**
     * 得到指定时间,在添加/减少的分钟后的时间(减用 负数)
     * @param date
     *    输入的日期
     * @param minute
     *    输入的分钟数(可以用负数)
     */
    public static Date dayByAddMin(Date date, int minute) {
   
   
        return addDay(date, Calendar.MINUTE, minute);
    }

    /**
     * 得到指定时间,在添加/减少的小时后的时间(减用 负数)
     * @param date
     *    输入的日期
     * @param hour
     *    输入的小时数(可以用负数)
     */
    public static Date dayByAddHour(Date date, int hour) {
   
   
        return addDay(date, Calendar.HOUR_OF_DAY, hour);
    }

    /**
     * 得到指定时间,在添加/减少的天后的时间(减用 负数)
     * @param date
     *    输入的日期
     * @param day
     *    输入的天数(可以用负数)
     */
    public static Date dayByAddDay(Date date, int day) {
   
   
        return addDay(date, Calendar.DAY_OF_MONTH, day);
    }

    /**
     * 得到指定时间,在添加/减少的月后的时间(减用 负数)
     * @param date
     *    输入的日期
     * @param month
     *    输入的月数(可以用负数)
     */
    public static Date dayByAddMonth(Date date, int month) {
   
   
        return addDay(date, Calendar.MONTH, month);
    }

    /**
     * 得到指定时间,在添加/减少对应类型的间隔数后的时间(减用 负数)
     * @param date
     *    输入的日期
     * @param type
     *    输入的时间类型(详细参考Calendar中的常量)
     * @param interval
     *    输入的间隔数(可以用负数)
     */
    private static Date addDay(Date date, int type, int interval) {
   
   
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(type, interval);
        return c.getTime();
    }

    /**
     * 计算2个日期之间相差的天数
     * @param date
     *    输入的日期1
     * @param date2
     *    输入的日期2
     * @return int
     *    2个日期间隔的天数
     */
    public static int diff2days(Date date, Date date2) {
   
   
        if (date2.compareTo(date) <= 0) {
   
   
            return 0;
        } else {
   
   
            long interval = date2.getTime() - date.getTime();
            return (int)(interval /1000 * 60 * 60 * 24));
        }
    }
  /**
   * 得到2个日期之间的天数的数组
   * @param start
   *  开始时间
   * @param end 
   *  结束时间
   * @return
   */
  public static List<Date> dayBetween(Date start, Date end){
   
   
      int interval = diff2days(start, end);
      List<Date> result = Utils.newList();
      result.add(start);
      if(interval == 0){
   
   
          //同一天
          return result;
      }

      for(int i = 1; i < interval; i ++){
   
   
          result.add(dayByAddDay(start, i));
      }

      return result;
  }

  /**
   * 得到2个日期之间的天数的数组
   * @param startStr
   *  开始时间
   * @param endStr
   *  结束时间
   * @return
   */
  public static List<Date> dayBetween(String startStr, String endStr){
   
   
      Date start = parse(startStr, "yyyy-MM-dd");
      Date end = parse(endStr, "yyyy-MM-dd");
      return dayBetween(start, end);
  }

  /**
   * 计算2个日期之间相隔的月数
   * @param date
   *      开始时间
   * @param target
   *      结束时间
   * @return
   *     返回2个日期之间相差的月数
   */
  public static Integer intervalMonths(Date date, Date target){
   
   
      Calendar c1 = Calendar.getInstance();
      c1.setTime(date);
      int fromMonth = c1.get(Calendar.MONTH);
      Calendar c2 = Calendar.getInstance();
      c2.setTime(target);
      int targetMonth = c2.get(Calendar.MONTH);
      return targetMonth - fromMonth;
  }

    //省略其他....
}

对于上面的代码,相信很多开发的小伙伴应该都比较熟悉,无论从教程或者网上的例子来说,都基本上是使用DateSimpleDateFormatCalendar 这三个API工具类来处理。

  • Date : 表示特定的瞬间,精确到毫秒。Date类用于表示日期和时间(注意这里是java.util.Date,不是java.sql.Date),以1970年1⽉1⽇ 8点0分0秒为起始点(为什么选择1970年?小伙伴可以去查下)
  • SimpleDateFormat : 日期格式化工具类(在java.text包下),比如yyyy-MM-dd这样的格式,(注意:这是线程不安全的)
  • Calendar : 日历工具类(在java.util包下),常用于计算日历上的某一天/月中,比如下个月的今天是几,或者还有多久。

至于具体API方法,可以点开源码进行查看,不是太难。

回归正题,即然Date及Calendar在计算时间间隔或者其他场景下都比较麻烦,那么有没有更好的API使用呢?当然是有的,JDK1.8中,就更新了新的日期/时间处理工具类。具体的包在java.time目录下,有兴趣的小伙伴可以打开进行查看。

image.png

java.time包下提供了日期、时间、周期、时区等工具类及API,它是基于ISO日历系统,遵循 Gregorian规则的,并且类都是不可变的,线程安全的,比较常用的类主要是:

  • Instant :本质上是一个时间戳。
  • LocalDate :存储了日期,如:2010-12-03。可以用来存储生日。
  • LocalTime :存储了时间,如:11:30。
  • LocalDateTime :存储了日期和时间,如:2010-12-03T11:30。
  • ZonedDateTime :存储一个带时区的日期和时间。
  • DateTimeFormatter:在日期对象与字符串之间进行转换。
  • ChronoUnit:计算出两个时间点之间的时间距离,可按多种时间单位计算。
  • TemporalAdjuster:各种日期计算功能。
  • Period:用于测量以年,月和日为单位的时间。
  • Duration:用于以秒和纳秒为单位测量时间。

这个包下包括了很多API,而且都有比较好的命名规则:

  • of:静态工厂方法。
  • parse:静态工厂方法,关注于解析。
  • get:获取值。
  • is:用于比较。
  • with:不可变的setter等价物。
  • plus:加一些量到某个对象。
  • minus:从某个对象减去一些量。
  • to:转换到另一个类型。
  • at:把这个对象与另一个对象组合起来,例如:date.atTime(time)。

学习了上面的这种工具类及api方法,来看一下例子:

@Slf4j
public class DateTest {
   
   

    @Test
    public void testNewDate(){
   
   
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        log.info("当前时间为:{}", now);

        //格式化当前时间
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        log.info("当前时间的yyyy-MM-dd HH:mm:ss为:{}", now.format(dateTimeFormatter));

        //得到某年的某一天
        Year year = Year.of(2022);
        LocalDate localDate = year.atDay(52);
        log.info("2022年的第52天的日期是:{}", localDate);
        log.info("是否是闰年:{}", localDate.isLeapYear());

        // 解析字符串形式的日期时间
        DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        TemporalAccessor temporalAccessor = dateTimeFormatter2.parse("2022-05-06");
        log.info("字符串转为日期:{}", LocalDate.from(temporalAccessor));

        LocalDateTime with = now.with(TemporalAdjusters.firstDayOfMonth());
        log.info("得到当前月份:{}的第一天:{}", localDate.getMonthValue(), with.toLocalDate());

        // 计算某月的第一个星期一的日期
        TemporalAdjuster temporalAdjuster = TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY);
        LocalDate firstDayOfWeek = now.toLocalDate().with(temporalAdjuster);
        log.info("当前月的第一个星期一的日期:{}", firstDayOfWeek);

        //计算localDate的下一个星期一的日期
        LocalDate nextMonday = now.toLocalDate().with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        log.info("当前月的下一个星期一的日期:{}", nextMonday);

        //计算2个日期相差的天数,月数
        LocalDateTime localDate1 = LocalDateTime.of(2022, 1, 1, 16, 12, 0);
        LocalDateTime localDate2 = LocalDateTime.of(2022, 4, 22, 16, 12, 0);
        long dayBetween = ChronoUnit.DAYS.between(localDate1, localDate2);
        long monthBetween = ChronoUnit.MONTHS.between(localDate1, localDate2);
        log.info("间隔的天数为:{}", dayBetween);
        log.info("间隔的月数为:{}", monthBetween);

        //除了用ChronoUnit来计算外,还可以用Duration, Period 
        Duration duration = Duration.between(localDate1, localDate2);
        log.info("间隔的天数为:{}", duration.toDays());

        Period period = Period.between(localDate1.toLocalDate(), localDate2.toLocalDate());
        log.info("间隔的月数为:{}", period.toTotalMonths());
     }
}

输出结果为:

17:02:44.344 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 当前时间为:2022-05-06T17:02:44.341
17:02:44.373 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 当前时间的yyyy-MM-dd HH:mm:ss为:2022-05-06 17:02:44
17:02:44.374 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 2022年的第52天的日期是:2022-02-21
17:02:44.375 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 是否是闰年:false
17:02:44.380 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 字符串转为日期:2022-05-06
17:02:44.382 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 得到当前月份:2的第一天:2022-05-01
17:02:44.388 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 当前月的第一个星期一的日期:2022-05-02
17:02:44.393 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 当前月的下一个星期一的日期:2022-05-09
17:02:44.393 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 间隔的天数为:111
17:02:44.393 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 间隔的月数为:3
17:02:44.393 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 间隔的天数为:111
17:02:44.393 [main] INFO net.jhelp.easyql.demo.tests.DateTest - 间隔的月数为:3

通过上面的测试用例,新的API是不是简单很多,尤其在计算时间差方面,测试例子保是列举了一些常用的API的使用,具体的例子,可以打开源码进行查阅。

好了,学习了这么多,旧项目的日期工具类,可以进行一翻新的重构(又可以吹了),重写后的内容如下,觉得用的小伙伴可以拿去项目中使用。

/**
 * 获得当前日期
 */
public static LocalDateTime now() {
   
   
    return LocalDateTime.now();
}

/**
 * 将java.util.Date转换成 LocalDateTime
 * @param date
 *      输入的时间
 * @return
 */
public static LocalDateTime from(Date date){
   
   
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
}

/**
 * 将localDateTime转换成Date
 * @param localDateTime
 *      输入的时间
 * @return
 */
public static Date to(LocalDateTime localDateTime){
   
   
    return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

/**
 * 返回以【formatString 】格式的当前日期
 * @param formatString
 *    时间格式(如yyyy-MM-dd) 
 */
public static String getNowStrFormat(String formatString) {
   
   
    return format(now(), formatString);
}


public static String formatByNumber(Long date, String formatString) {
   
   
    Date date1 = new Date(date);
    LocalDateTime localDateTime = date1.toInstant().atZone(
            ZoneId.systemDefault()).toLocalDateTime();
    return format(localDateTime, formatString);
}

/**
 * 返回以【formatString 】格式的【日期】
 * @param date
 *    输入的日期
 * @param formatString
 *    时间格式(如yyyy-MM-dd)
 */
public static String format(LocalDateTime date, String formatString) {
   
   
    if (date == null) {
   
   
        return "";
    } else {
   
   
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(formatString);
        return date.format(dateTimeFormatter);
    }
}

/**
 * 将日期字符串【dateString 】按【format】的格式转换成 Date类型
 * @param datetime
 *    输入的日期字符串
 * @param format
 *    时间格式(如yyyy-MM-dd)
 */
public static LocalDateTime parse(String datetime, String format) {
   
   
    SimpleDateFormat df = new SimpleDateFormat(format);
    try {
   
   
        Date date = df.parse(datetime);
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    } catch (ParseException e1) {
   
   
        log.error("日期转换错误,日期格式不正确", e1);
    }
    return null;
}


public static Long dateToLong(String dateString, String format){
   
   
    try {
   
   
        SimpleDateFormat df = new SimpleDateFormat(format);
        Date date = df.parse(dateString);
        return date.getTime();
    } catch (ParseException var4) {
   
   
        log.error("日期转换错误,日期格式不正确", var4);
    }
    return null;
}

/**
 * 获取指定year(年),month(月)的第一天
 * @param year
 *    输入年份
 * @param month
 *    输入的月份
 */
public static String getFirstDayOfMonth(Integer year, Integer month){
   
   
    //设置为1号,当前日期既为本月第一天
    LocalDate localDate = LocalDate.of(year, month, 1);
    return localDate.format(YYYY_MM_DD);
}

/**
 * 获取指定year(年),month(月)的最后一天
 * @param year
 *    输入年份
 * @param month
 *    输入的月份
 */
public static String getLastDayOfMonth(Integer year, Integer month){
   
   
    LocalDate localDate = LocalDate.of(year, month, 1);
    LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());
    return lastDayOfMonth.format(YYYY_MM_DD);
}

/**
 * 得取某个日期(date)在月中是第几天
 * @param date
 *    输入的日期
 */
public static int dayOfMonth(LocalDate date){
   
   
    return date.getDayOfMonth();
}

/**
 * 得取某个日期(date)在月中是周几
 * @param date
 *    输入的日期
 */
public static int dayOfWeek(LocalDate date){
   
   
    return date.getDayOfWeek().getValue();
}

/**
 * 得取某个日期(date)是一年中的第几个月(注意结果要 + 1)
 * @param date
 *    输入的日期
 */
public static int month(LocalDate date){
   
   
    return date.getMonthValue();
}

/**
 * 得取某个日期(date)的年份
 * @param date
 *    输入的日期
 */
public static int year(LocalDate date){
   
   
    return date.getYear();
}

/**
 * 得到指定时间,在添加/减少的分钟后的时间(减用 负数)
 * @param date
 *    输入的日期
 * @param minute
 *    输入的分钟数(可以用负数)
 */
public static LocalDateTime dayByAddMin(LocalDateTime date, int minute) {
   
   
    return addDay(date, ChronoUnit.MINUTES, minute);
}

/**
 * 得到指定时间,在添加/减少的小时后的时间(减用 负数)
 * @param date
 *    输入的日期
 * @param hour
 *    输入的小时数(可以用负数)
 */
public static LocalDateTime dayByAddHour(LocalDateTime date, int hour) {
   
   
    return addDay(date, ChronoUnit.HOURS, hour);
}

/**
 * 得到指定时间,在添加/减少的天后的时间(减用 负数)
 * @param date
 *    输入的日期
 * @param day
 *    输入的天数(可以用负数)
 */
public static LocalDateTime dayByAddDay(LocalDateTime date, int day) {
   
   
    return addDay(date, ChronoUnit.DAYS, day);
}
/**
 * 得到指定时间,在添加/减少的月后的时间(减用 负数)
 * @param date
 *    输入的日期
 * @param month
 *    输入的月数(可以用负数)
 */
public static LocalDateTime dayByAddMonth(LocalDateTime date, int month) {
   
   
    return addDay(date, ChronoUnit.MONTHS, month);
}

public static LocalDateTime dayByAddYear(LocalDateTime cur, int n) {
   
   
    return addDay(cur, ChronoUnit.YEARS, n);
}

/**
 * 得到指定时间,在添加/减少对应类型的间隔数后的时间(减用 负数)
 * @param date
 *    输入的日期
 * @param type
 *    输入的时间类型(详细参考Calendar中的常量)
 * @param interval
 *    输入的间隔数(可以用负数)
 */
private static LocalDateTime addDay(LocalDateTime date, ChronoUnit type, int interval) {
   
   
    return date.plus(interval, type);
}

/**
 * 得到当前时间的24小时格式的小时数
 *
 * @return
 */
public static int getCurHour24() {
   
   
    return LocalDateTime.now().getHour();
}

/**
 * 得到2个日期之间的天数的数组
 * @param start
 *  开始时间
 * @param end
 *  结束时间
 * @return
 */
public static List<LocalDate> dayBetween(LocalDate start, LocalDate end){
   
   
    Long interval = intervalDays(start, end);
    List<LocalDate> result = Utils.newList();

    for(int i = 0; i <= interval; i ++){
   
   
        result.add(start.plusDays(i));
    }

    return result;
}

/**
 * 得到2个日期之间的天数的数组
 * @param startStr
 *  开始时间
 * @param endStr
 *  结束时间
 * @return
 */
public static List<LocalDate> dayBetween(String startStr, String endStr){
   
   
    LocalDate start = LocalDate.parse(startStr, YYYY_MM_DD);
    LocalDate end = LocalDate.parse(endStr, YYYY_MM_DD);
    return dayBetween(start, end);
}

/**
 * 得到2个日期之间的天数的数组
 * @param start
 *  开始时间
 * @param end
 *  结束时间
 * @return
 */
public static List<String> dayStrBetween(LocalDate start, LocalDate end){
   
   
    Long interval = intervalDays(start, end);
    List<String> result = Utils.newList();

    for(int i = 0; i <= interval; i ++){
   
   
        LocalDate tmp = start.plusDays(i);
        result.add(tmp.format(YYYY_MM_DD));
    }

    return result;
}
/**
 * 得到2个日期之间的天数的数组
 * @param startStr
 *  开始时间
 * @param endStr
 *  结束时间
 * @return
 */
public static List<String> dayStrBetween(String startStr, String endStr){
   
   
    LocalDate localDate1 = LocalDate.parse(startStr, YYYY_MM_DD);
    LocalDate localDate2 = LocalDate.parse(endStr, YYYY_MM_DD);
    return dayStrBetween(localDate1, localDate2);
}

/**
 * 得到2个日期之间相差的月份,并用数组返回
 * @param startStr
 * @param endStr
 * @return
 */
public static List<LocalDateTime> monthBetween(String startStr, String endStr){
   
   
    List<LocalDateTime> result = Utils.newList();
    LocalDateTime start = LocalDateTime.parse(startStr, YYYY_MM_DD);
    Temporal temporal1 = LocalDate.parse(startStr);
    Temporal temporal2 = LocalDate.parse(endStr);
    Long interval = ChronoUnit.MONTHS.between(temporal1, temporal2);
    for(int i = 0; i <= interval; i ++){
   
   
        LocalDateTime tmp = start.plusMonths(i);
        result.add(tmp);
    }
    return result;
}

/**
 * 得到2个日期之间相差的月份,并用数组返回
 * @param startStr
 * @param endStr
 * @return
 */
public static List<String> monthStrBetween(String startStr, String endStr){
   
   
    List<String> result = Utils.newList();
    LocalDateTime start = LocalDateTime.parse(startStr, YYYY_MM_DD);
    Temporal temporal1 = LocalDate.parse(startStr);
    Temporal temporal2 = LocalDate.parse(endStr);
    Long interval = ChronoUnit.MONTHS.between(temporal1, temporal2);
    for(int i = 0; i <= interval; i ++){
   
   
        LocalDateTime tmp = start.plusMonths(i);
        result.add(tmp.format(DateTimeFormatter.ofPattern("yyyy-MM-01")));
    }
    return result;
}

/**
 *  2个日期之间相关的天数(输入的参数要求:yyyy-MM-dd)
 * @param target
 *    结束日期
 * @param source
 *      开始日期
 * @return
 */
public static Long intervalDays(String source, String target) {
   
   
    LocalDate localDate1 = LocalDate.parse(source, YYYY_MM_DD); 
    LocalDate localDate2 = LocalDate.parse(target, YYYY_MM_DD);
    return intervalDays(localDate1, localDate2);
}

/**
 *  2个日期之间相关的天数
 * @param target
 *    结束日期
 * @param source
 *      开始日期
 * @return
 */
public static Long intervalDays(LocalDate source, LocalDate target) {
   
   
    return ChronoUnit.DAYS.between(source, target);
}

/**
 * 计算2个日期之间相隔的月数
 * @param date
 *      开始时间
 * @param target
 *      结束时间
 * @return
 */
public static Long intervalMonths(LocalDateTime date, LocalDateTime target){
   
   
    Period period = Period.between(date.toLocalDate(),target.toLocalDate());
    return period.toTotalMonths();
}

/**
 * 判断输入的日期是否大于当前日期
 * @param targetTime
 *      输入的日期
 * @return
 *      true : 输入日期大于等于当前日期
 *      false : 输入日期小于当前日期
 */
public static Boolean afterNow(String targetTime){
   
   
    Temporal now = LocalDate.now();
    Temporal target = LocalDate.parse(targetTime);
    Long interval = ChronoUnit.DAYS.between(now, target);
    return interval >= 0 ;
}

是不是看上去简单清晰了很多,JDK其实已经想到了Date存在的不足,多看看JDK版本更新带来的改动,会有意想不到的收获,比如JDK1.8中的Stream、Lamda等。优化后的程序有没有问题,还跑一下测试例子看看。

long timeMillis = System.currentTimeMillis();

log.info("当前时间:{}", now());
log.info("当前时间yyyy-MM-dd: {}", getNowStrFormat("yyyy-MM-dd"));
log.info("当前时间yyyy-MM-dd HH:mm:ss: {}", getNowStrFormat("yyyy-MM-dd HH:mm:ss"));
log.info("formatByNumber : {}", formatByNumber(timeMillis, "yyyy-MM-dd"));
log.info("format(xx, xx): {}", format(LocalDateTime.now(), "yyyy-MM-dd"));
log.info("字符串转日期: {}", parse("2022-03-11 14:13:31", "yyyy-MM-dd"));
log.info("字符串日期输出数字:{}", dateToLong("2022-02-11 11:11:11", "yyyy-MM-dd HH:mm:ss"));
log.info("2022/4月的第一天:{}", getFirstDayOfMonth(2022,4));
log.info("2022/2月的最后一天:{}", getLastDayOfMonth(2022, 2));
log.info("dayOfMonth : {}", dayOfMonth(LocalDate.now()));
log.info("dayOfWeek : {}", dayOfWeek(LocalDate.now()));
log.info("month : {}", month(LocalDate.now()));
log.info("year : {}", year(LocalDate.now()));
log.info("dayByAddMin : {}", dayByAddMin(LocalDateTime.now(), 30));
log.info("dayByAddHour : {}", dayByAddHour(LocalDateTime.now(), 1));
log.info("dayByAddDay : {}", dayByAddDay(LocalDateTime.now(), 1));
log.info("dayByAddMonth : {}", dayByAddMonth(LocalDateTime.now(), 1));
log.info("dayByAddYear : {}", dayByAddYear(LocalDateTime.now(), 1));
log.info("getCurHour24 : {}", getCurHour24());

String startStr = "2022-03-01";
String endStr = "2022-03-22";
List<String> tmp = dayStrBetween(startStr, endStr);
for(String d : tmp){
   
   
    log.info("dayStrBetween : {}", d);
}

log.info("afterNow: {}", afterNow("2022-04-30"));
log.info("intervalDays: {}", intervalDays("2021-11-01", "2022-03-22"));

输出结果:

09:56:58.976 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 当前时间:2022-05-07T09:56:58.970
09:56:58.996 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 当前时间yyyy-MM-dd: 2022-05-07
09:56:58.997 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 当前时间yyyy-MM-dd HH:mm:ss: 2022-05-07 09:56:58
09:56:58.999 [main] INFO net.jhelp.easyql.kits.time.DateUtils - formatByNumber : 2022-05-07
09:56:58.999 [main] INFO net.jhelp.easyql.kits.time.DateUtils - format(xx, xx): 2022-05-07
09:56:59.000 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 字符串转日期: 2022-03-11T00:00
09:56:59.001 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 字符串日期输出数字:1644549071000
09:56:59.001 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 2022/4月的第一天:2022-04-01
09:56:59.003 [main] INFO net.jhelp.easyql.kits.time.DateUtils - 2022/2月的最后一天:2022-02-28
09:56:59.004 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayOfMonth : 7
09:56:59.005 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayOfWeek : 6
09:56:59.005 [main] INFO net.jhelp.easyql.kits.time.DateUtils - month : 5
09:56:59.005 [main] INFO net.jhelp.easyql.kits.time.DateUtils - year : 2022
09:56:59.006 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayByAddMin : 2022-05-07T10:26:59.005
09:56:59.006 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayByAddHour : 2022-05-07T10:56:59.006
09:56:59.007 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayByAddDay : 2022-05-08T09:56:59.006
09:56:59.007 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayByAddMonth : 2022-06-07T09:56:59.007
09:56:59.007 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayByAddYear : 2023-05-07T09:56:59.007
09:56:59.007 [main] INFO net.jhelp.easyql.kits.time.DateUtils - getCurHour24 : 9
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-01
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-02
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-03
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-04
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-05
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-06
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-07
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-08
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-09
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-10
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-11
09:56:59.022 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-12
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-13
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-14
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-15
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-16
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-17
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-18
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-19
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-20
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-21
09:56:59.023 [main] INFO net.jhelp.easyql.kits.time.DateUtils - dayStrBetween : 2022-03-22
09:56:59.024 [main] INFO net.jhelp.easyql.kits.time.DateUtils - afterNow: false
09:56:59.024 [main] INFO net.jhelp.easyql.kits.time.DateUtils - intervalDays: 141

总结

这个文章没有说java.util.Date不好,这是Java技术发展的必经之路,没办法一始就考虑全面,技术的发展是一步一步前进,只有不断地去思考、去实践、去重构、去考虑效率才能有更好的提升,这也是技术人员的成长之路,从初级、中级、高级、架构师这样的路发展,而如果不去思考与研究,可能就十年如一日,原地踏步,技术水平得不到提升。

建议在新的项目中多使用java.time中新的日期和时间处理的工具类及api,慢慢减少java.util.Date、Calendar、SimpleDateFormat的使用。

在下一篇中,会整理java中有关日期和时间的工具类及API的说明,及使用的例子(目前还未整理好),方便更多的称发小伙伴进行参考与使用,会提供PDF文档出来(可以点击查看:https://juejin.cn/post/7095672865785118750)。

想得到最新的文章内容,可以关注我的公众号:技术老男孩。

目录
相关文章
|
1月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
68 4
|
8天前
|
Java API Maven
如何使用Java开发抖音API接口?
在数字化时代,社交媒体平台如抖音成为生活的重要部分。本文详细介绍了如何用Java开发抖音API接口,从创建开发者账号、申请API权限、准备开发环境,到编写代码、测试运行及注意事项,全面覆盖了整个开发流程。
51 10
|
1月前
|
监控 Java 测试技术
Java开发现在比较缺少什么工具?
【10月更文挑战第15天】Java开发现在比较缺少什么工具?
36 1
|
14天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
32 4
|
16天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
43 4
|
17天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
39 2
|
26天前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
83 8
|
21天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
21天前
|
SQL Java 程序员
倍增 Java 程序员的开发效率
应用计算困境:Java 作为主流开发语言,在数据处理方面存在复杂度高的问题,而 SQL 虽然简洁但受限于数据库架构。SPL(Structured Process Language)是一种纯 Java 开发的数据处理语言,结合了 Java 的架构灵活性和 SQL 的简洁性。SPL 提供简洁的语法、完善的计算能力、高效的 IDE、大数据支持、与 Java 应用无缝集成以及开放性和热切换特性,能够大幅提升开发效率和性能。
|
22天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
40 2