java中的时间操作

本文介绍了Joda-Time库的基本使用方法,包括初始化时间、格式化时间、日期加减操作及日期间隔计算等实用技巧。

一. Date 类

java.util.Date 是 Java 编程语言中最早提供的时间和日期处理类,用于表示一个特定的时间点,精确到毫秒。其时间表示方式是:自 1970 年 1 月 1 日 00:00:00 GMT 起的毫秒数。

1. Date 类的优点

1. 使用简单: 初学者可快速上手,例如 new Date() 获取当前时间。

2. 可与时间戳互转: 内部以毫秒时间戳表示,便于与系统时间、数据库等交互。

3. 向后兼容性强: 是 Java 平台核心类库的一部分,许多旧接口仍依赖 Date 类型。

4. 可与 JDBC 等框架配合使用: 可与 java.sql.Date、Timestamp 等数据库相关类直接转换。

2.Date 类的缺点

1. 线程不安全: 可变性导致线程不安全    Date 对象内部状态可变,在多线程环境中共享使用容易引发并发问题。

2. 设计不直观、语义混乱: 如 getYear() 返回的是“年份 - 1900”,getMonth() 从 0 开始;多数方法含糊不清。

3. 缺乏时区支持: Date 无法直接表示时区信息,只能与 TimeZone、Calendar 组合使用。

4. 功能有限: 不支持日期加减、时间截断、字段调整等常见操作,需借助其他类(如 Calendar)实现。

5. 大量方法已过时: 自 Java 1.1 开始,多数 Date 方法被标记为过时,不推荐继续使用。

6. 格式化依赖非线程安全的类: 格式化需配合 SimpleDateFormat,但该类本身也不是线程安全的,需小心使用。

3.Date 常用时间操作

3.1 获取当前格式化时间
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    /**
     * 获取当前时间
     * @return
     */
    public static String currentDatetime() {
        return dateFormat.format(new Date());
    }

注: yyyy-MM-dd HH:mm:ss HH大写时为24小时制,hh小写时为12小时制

或者:

String date = String.format("%1$tF %1$tT", new Date());
3.2 格式化指定时间
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 格式化日期
     * @param date
     * @return
     */
    public static String formatDate(Date date) {
        return dateFormat.format(date);
    }
3.3 时间戳转时间字符串
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    /**
     * 时间戳转为时间字符串
     *
     * @param timeStamp
     * @return
     */
    public static String formatTimeStamp(long timeStamp) {
        // 时间戳转换成时间
        return dateFormat.format(new Date(timeStamp));
    }
3.4 将字符串解析成时间
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 将字符串解析成时间对象
     *
     * @param date
     * @return
     */
    public static Date parseDatetime(String date) throws Exception{
        return dateFormat.parse(date);
    }
3.5 计算两个日期时间差
    /**
     * 计算两个日期时间差,单位 秒
     * @param start
     * @param end
     * @return
     */
    public static long interval(Date start, Date end) {
        // 获取结束时间的毫秒表示(自1970年1月1日00:00:00 UTC以来的毫秒数)
        long endTime = end.getTime();
        // 获取起始时间的毫秒表示
        long startTime = start.getTime();
        // 计算两个时间的差值(单位为毫秒),然后转换为秒(1秒 = 1000毫秒)
        return (endTime - startTime) / 1000;
    }

二. Calendar 类

java.util.Calendar 是 Java 在 1.1 版本中引入的时间处理类,旨在取代 java.util.Date 中已过时的方法Calendar 提供了更为全面的日期字段访问与运算功能,如获取年、月、日、小时等字段,以及对时间进行加减操作。

1. Calendar 类的优点

1. 提供字段访问: 支持通过常量(如 Calendar.YEAR、Calendar.MONTH)读取和设置时间字段。

2. 支持日期计算: 内置 add() 和 roll() 方法实现日期加减操作。

3. 支持时区操作: 可设置和处理 TimeZone,用于国际化场景。

4. 与 Date 无缝转换: 提供 getTime() / setTime() 方法,可与 Date 相互转换。

2. Calendar 类的缺点

1.API 使用复杂: 方法设计繁琐、不直观,字段操作依赖常量,降低代码可读性。

2.月份从 0 开始: 与人类习惯不符,易导致逻辑错误。

3.线程不安全: 可变对象,多线程环境中必须手动同步。

4.不能链式调用: 设置多个字段需多次调用 set(),代码不简洁。

5.设计臃肿: 实现过于重量级,缺乏现代时间处理类的灵活性与表现力。

3.Calendar 常用时间操作

3.1 获取日期
3.1.1 获取当天开始时间
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    /**
     * 获取当天的开始时间
     * @return
     */
    public static String getDayBegin() {
        // 创建一个基于当前系统时间的 GregorianCalendar 实例(默认时区、地区)
        Calendar calendar = new GregorianCalendar();
        // 将小时字段设置为 0(24 小时制中的午夜)
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        // 将分钟字段设置为 0
        calendar.set(Calendar.MINUTE, 0);
        // 将秒字段设置为 0
        calendar.set(Calendar.SECOND, 0);
        // 将毫秒字段设置为 0,确保时间精确到 00:00:00.000
        calendar.set(Calendar.MILLISECOND, 0);
        // 将 Calendar 对象转换为 Date 类型,并返回
        return dateFormat.format(calendar.getTime());
    }
3.1.2 获取昨天此刻的时间
    private static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 获取昨天时间
     * @return
     */
    public static String getBeginDayOfYesterday() {
        // 获取当前时间的 Calendar 实例
        Calendar calendar = Calendar.getInstance();
        // 日期减 1 天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        // 获取 Date 对象(昨天的时间)
        Date yesterday = calendar.getTime();
        return dateFormat.format(yesterday.getTime());
    }
3.1.3 获取上个月的今天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取上个月当前日期
     * @return
     */
    public static String getLastMonthToday() {

        // 获取当前时间的 Calendar 实例
        Calendar cl = Calendar.getInstance();

        // 将月份减一,切换到上个月同一天
        cl.add(Calendar.MONTH, -1);

        // 获取上个月同一天的 Date 对象
        Date dateFrom = cl.getTime();

        // 将 Date 格式化成字符串返回
        return format.format(dateFrom);
    }
 3.1.4 获取此刻前多少分钟的时间
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    /**
     * 获取前多少分钟的时间
     * @param beforeMinutes
     * @return
     */
    public static String checkPointMinutes(int beforeMinutes){
        // 获取当前时间
        Calendar calendar = Calendar.getInstance();
        // 向前推指定分钟数
        calendar.add(Calendar.MINUTE, -beforeMinutes);
        // 获取 Date 对象
        Date daysAgo = calendar.getTime();
        return dateFormat.format(daysAgo);
    }
 3.1.5 获取指定日期前多少天的日期
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取前多少天的日期
     * @param beforeDay
     * @return
     */
    public static String checkPointDay(int beforeDay){
        // 获取当前时间
        Calendar calendar = Calendar.getInstance();
        // 向前推指定天
        calendar.add(Calendar.DAY_OF_MONTH, 1 - beforeDay);
        // 获取 Date 对象
        Date daysAgo = calendar.getTime();
        return format.format(daysAgo);
    }
3.1.6 获取指定日期的下一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    
    /**
     * 获取指定日期的下一天
     * @param date
     * @return
     * @throws Exception
     */
    public static String getNextDate(String date) throws Exception{

        // 用于保存解析后的 Date 对象,初始为 null
        Date dateDay = format.parse(date);
        // 创建 Calendar 实例,准备操作日期
        Calendar rightNow = Calendar.getInstance();
        // 把 Calendar 时间设置为解析后的日期
        rightNow.setTime(dateDay);

        // 日期加 1 天
        rightNow.add(Calendar.DATE, 1);
        // 获取加一天后的 Date 对象
        Date nestDay = rightNow.getTime();
        // 格式化加一天后的日期为字符串
        String nestDayStr = format.format(nestDay);

        // 返回结果字符串
        return nestDayStr;
    }
3.1.7 获取指定日期后多少天的日期
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取指定日期加上天数后的日期
     * @param num
     * @param date
     * @return
     */
    public static String plusDay(int num, String date) throws Exception{
        // 初始化 Date 对象为 null,用于后续接收解析结果
        Date thisDay = format.parse(date);

        // 创建 Calendar 实例,用于进行日期的加减运算
        Calendar rightNow = Calendar.getInstance();
        // 将 Calendar 设置为目标日期
        rightNow.setTime(thisDay);
        // 在目标日期的基础上加(或减)指定的天数
        // 正数为向后推,负数为向前推
        rightNow.add(Calendar.DAY_OF_YEAR, num);

        // 获取加减后的日期对象
        Date plusDay = rightNow.getTime();
        // 将新日期格式化为字符串
        String plusDayStr = format.format(plusDay);

        // 返回结果日期字符串
        return plusDayStr;
    }


    public static void main(String[] args) {
        try {
            System.out.println(plusDay(2, "2025-07-17"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
3.1.8 获取指定日期后几个月的日期
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 指定日期加上月数后的日期
     * @param num
     * @param date
     * @return
     */
    public static String plusMonth(int num, String date) throws Exception{
        // 初始化 Date 类型变量,用于存放解析后的日期
        Date dt = format.parse(date);

        // 获取一个 Calendar 实例,用于进行日期计算
        Calendar rightNow = Calendar.getInstance();
        // 将 Calendar 的时间设置为解析后的日期
        rightNow.setTime(dt);
        // 在当前日期基础上增加(或减少)指定的月份数
        // num 为正数表示向后推移,为负数表示向前推移
        rightNow.add(Calendar.MONTH, num);  // 例如 num=3 表示加3个月

        // 获取加减操作后的日期对象
        Date plusDay = rightNow.getTime();
        // 将计算后的日期格式化为字符串
        String plusDayStr = format.format(plusDay);

        // 返回最终的日期字符串
        return plusDayStr;
    }


    public static void main(String[] args) {
        try {
            System.out.println(plusMonth(2, "2025-07-17"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
3.1.9 获取某日去年的时间

如果是闰年二月29号,前一年自动变成28号

    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取传入日期在去年的同一天对应的日期
     * @param date
     * @return
     */
    public static String getDateDayLastYear(String date) throws Exception{

        // 初始化 Date 类型变量用于接收解析结果
        Date rightDate = format.parse(date);

        // 获取 Calendar 实例,用于日期计算
        Calendar cal = Calendar.getInstance();
        // 将 Calendar 的时间设置为解析后的日期
        cal.setTime(rightDate);
        // 将年份减去 1,即得到去年的同一天
        cal.add(Calendar.YEAR, -1);
        // 将计算结果格式化为字符串
        String lastDay = format.format(cal.getTime());

        // 返回最终字符串结果
        return lastDay;
    }


    public static void main(String[] args) {
        try {
            System.out.println(getDateDayLastYear("2024-02-28"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
3.2 获取日期范围
3.2.1.获取指定日期的某周的第一天和最后一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取某个时间的当周的第一天和最后一天
     * @param date
     * @return
     */
    public static String[] getWeekDayScope(String date) {
        // 用于存储解析后的 Date 对象
        Date time = null;

        try {
            // 将传入的日期字符串解析为 Date 类型
            time = format.parse(date);
        } catch (ParseException e) {
            // 如果解析失败,打印异常信息
            e.printStackTrace();
        }

        // 创建一个 Calendar 实例,并设置为解析后的时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);

        // 获取当前是星期几(1 表示周日,2 表示周一,依此类推)
        int dayWeek = calendar.get(Calendar.DAY_OF_WEEK);

        // 如果是周日(1),手动减一天,归到上一周,便于统一处理
        if (1 == dayWeek) {
            calendar.add(Calendar.DAY_OF_MONTH, -1);
        }

        // 设置一周的第一天为“星期一”(默认是周日)
        calendar.setFirstDayOfWeek(Calendar.MONDAY);

        // 获取当前是本周的第几天
        int day = calendar.get(Calendar.DAY_OF_WEEK);

        // 调整日期:从当前日期向前回退,回到“本周一”
        calendar.add(Calendar.DATE, calendar.getFirstDayOfWeek() - day);

        // 格式化为字符串,表示本周的周一
        String timeBegin = format.format(calendar.getTime());

        // 在当前基础上加 6 天,即得到本周周日
        calendar.add(Calendar.DATE, 6);

        // 格式化为字符串,表示本周的周日
        String timeEnd = format.format(calendar.getTime());

        return new String[]{timeBegin, timeEnd};
    }

    
    public static void main(String[] args) {
        System.out.println(Arrays.toString(getWeekDayScope("2025-07-21")));
    }
3.2.2 获取当月的第一天和最后一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取当前月份的第一天和最后一天的字符串表示
     * @return
     */
    public static String[] getMonthScope() {
        // 获取当前时间的 Calendar 实例
        Calendar calendar = Calendar.getInstance();

        // 当前月份加 0,等价于不变,保留结构用于未来可能支持月份偏移
        calendar.add(Calendar.MONTH, 0);

        // 将日期设置为当前月份的第一天
        calendar.set(Calendar.DAY_OF_MONTH, 1);

        // 格式化第一天为字符串
        String first = format.format(calendar.getTime());

        // 再获取一个新的 Calendar 实例,依然是当前时间
        Calendar ca = Calendar.getInstance();

        // 将日期设置为当前月份的最后一天
        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));

        // 格式化最后一天为字符串
        String last = format.format(ca.getTime());

        return new String[]{first, last};
    }


    public static void main(String[] args) {
        System.out.println(Arrays.toString(getMonthScope()));
    }
3.2.3 获取上个月的第一天和最后一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取上个月的第一天和最后一天
     * @return
     */
    public static String[] getLastMonthScope() {
        // 获取当前时间对应的 Calendar 实例
        Calendar calendar = Calendar.getInstance();

        // 将当前月份减去 1,移动到上个月
        calendar.add(Calendar.MONTH, -1);

        // 设置为上个月的第一天
        calendar.set(Calendar.DAY_OF_MONTH, 1);

        // 此时 calendar 已指向上个月的第一天

        // 创建另一个 Calendar 实例,用于表示上个月的最后一天
        Calendar calendar1 = Calendar.getInstance();

        // 获取当前月份(0~11),并减去 1,定位到上个月
        int month = calendar1.get(Calendar.MONTH);
        calendar1.set(Calendar.MONTH, month - 1);

        // 设置为上个月的最后一天
        calendar1.set(Calendar.DAY_OF_MONTH,
                calendar1.getActualMaximum(Calendar.DAY_OF_MONTH));

        // 格式化上个月的第一天为字符串
        String firstDay = format.format(calendar.getTime());

        // 格式化上个月的最后一天为字符串
        String lastDay = format.format(calendar1.getTime());

        // 返回格式为 "yyyy-MM-dd and yyyy-MM-dd" 的结果
        return new String[]{firstDay, lastDay};
    }
    
    public static void main(String[] args) {
        System.out.println(Arrays.toString(getLastMonthScope()));
    }
3.2.4 获取去年当月的第一天和最后一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    
    /**
     * 获取“去年本月”的第一天与最后一天
     * @return
     */
    public static String[] getMonthLastYearScope() {

        // 创建 Calendar 实例,代表“去年本月第一天”
        Calendar lastYearStart = Calendar.getInstance();

        // 年份减一,即跳转到去年
        lastYearStart.add(Calendar.YEAR, -1);

        // 月份保持不变(add 0 实际无效,但结构清晰)
        lastYearStart.add(Calendar.MONTH, 0);

        // 设置为当月的第一天(1号)
        lastYearStart.set(Calendar.DAY_OF_MONTH, 1);

        // 将“去年本月的第一天”格式化为字符串
        String firstDayLastYear = format.format(lastYearStart.getTime());

        // 创建另一个 Calendar 实例,代表“去年本月最后一天”
        Calendar lastYearEnd = Calendar.getInstance();

        // 同样年份减一,回到去年
        lastYearEnd.add(Calendar.YEAR, -1);

        // 月份保持不变
        lastYearEnd.add(Calendar.MONTH, 0);

        // 设置为该月的最后一天(如:30 或 31)
        lastYearEnd.set(Calendar.DAY_OF_MONTH,
                lastYearEnd.getActualMaximum(Calendar.DAY_OF_MONTH));

        // 将“去年本月的最后一天”格式化为字符串
        String endDayLastYear = format.format(lastYearEnd.getTime());

        // 返回格式为 "yyyyMMdd and yyyyMMdd" 的字符串结果
        return new String[]{firstDayLastYear, endDayLastYear};
    }
3.2.5 获取指定年月的第一天和最后一天
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取指定月份所在的整月起止日期
     * @param date
     * @return
     */
    public static String[] getMonthScope(String date) {

        // 创建用于解析输入月份的格式器(只含年和月)
        SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM");

        // 用于接收转换后的 Date 对象
        Date monthDate = null;

        // 将传入的字符串(如 "2024-07")转换为 Date 对象
        try {
            monthDate = monthFormat.parse(date);
        } catch (ParseException e) {
            // 如果输入格式不符合预期,打印异常信息
            e.printStackTrace();
        }

        // 创建一个 Calendar 实例并设为指定月份
        Calendar lastYearStart = Calendar.getInstance();
        lastYearStart.setTime(monthDate);

        // 设置为该月的第一天(如 1 号)
        lastYearStart.set(Calendar.DAY_OF_MONTH, 1);

        // 格式化该月的第一天为字符串(如 "2024-07-01")
        String firstDayLastYear = format.format(lastYearStart.getTime());

        // 创建另一个 Calendar 实例,并设为同一月份
        Calendar lastYearEnd = Calendar.getInstance();
        lastYearEnd.setTime(monthDate);

        // 设置为该月的最后一天(如 "2024-07-31")
        lastYearEnd.set(Calendar.DAY_OF_MONTH,
                lastYearEnd.getActualMaximum(Calendar.DAY_OF_MONTH));

        // 格式化该月的最后一天为字符串
        String endDayLastYear = format.format(lastYearEnd.getTime());

        // 返回格式为 "yyyy-MM-dd and yyyy-MM-dd" 的字符串结果
        return new String[]{firstDayLastYear, endDayLastYear};
    }


    public static void main(String[] args) {
        System.out.println(Arrays.toString(getMonthScope("2025-07")));
    }
3.2.6 获取两个日期范围的所有日期
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取两个日期之间的所有日期(含起止)
     * @param startDateStr 起始日期字符串
     * @param endDateStr 结束日期字符串
     * @return 日期字符串列表
     */
    public static List<String> getDateRange(String startDateStr, String endDateStr) throws Exception{
        List<String> dateList = new ArrayList<>();

        // 将字符串解析为 Date 对象
        Date startDate = format.parse(startDateStr);
        Date endDate = format.parse(endDateStr);

        // 使用 Calendar 设置开始和结束日期
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);

        // 遍历日期范围,逐天添加
        while (!startCal.after(endCal)) {
            String result = format.format(startCal.getTime());
            dateList.add(result);

            // 日期加 1 天
            startCal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return dateList;
    }

    public static void main(String[] args) throws Exception{
        List<String> result = getDateRange("2027-06-15", "2027-07-18");
        System.out.println(result);  // 输出:[2027-07-15, 2027-07-16, 2027-07-17, 2027-07-18]
    }
3.2.7 获取两个日期范围的所有月份
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM");

    /**
     * 获取两个日期之间的所有月份(包含起止月份
     * @param startDateStr 起始日期字符串
     * @param endDateStr 结束日期字符串
     * @return 所有月份组成的字符串列表
     */
    public static List<String> getMonthRange(String startDateStr, String endDateStr) throws Exception{
        List<String> monthList = new ArrayList<>();

        // 将输入字符串解析为 Date 对象
        Date startDate = format.parse(startDateStr);
        Date endDate = format.parse(endDateStr);

        // 设置起始日期
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        // 确保是月份的第一天
        startCal.set(Calendar.DAY_OF_MONTH, 1);

        // 设置结束日期
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate);
        // 统一成月份起始日
        endCal.set(Calendar.DAY_OF_MONTH, 1);

        // 遍历月份范围
        while (!startCal.after(endCal)) {
            String result = format.format(startCal.getTime());
            monthList.add(result);
            // 月份加 1
            startCal.add(Calendar.MONTH, 1);
        }
        return monthList;
    }

    public static void main(String[] args) throws Exception{
        List<String> months = getMonthRange("2026-11", "2027-03");
        System.out.println(months);
    }
 3.2.8 打印 00-23 的小时
    /**
     * 打印 00-23 的小时
     * @return
     */
    public static List<String> hours(){
        return IntStream.range(0, 24)
                .mapToObj(i -> String.format("%02d", i))
                .collect(Collectors.toList());
    }
3.3 时间计算
3.3.1 计算两个日期之间所包含的天数
    private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 计算两个日期字符串之间相差的天数(包含当天,结果至少为 1)
     * @param startDate
     * @param endDate
     * @return
     */
    public static int differentDays(String startDate, String endDate) {
        Date date2 = null;
        Date date1 = null;
        try {
            // 将字符串 startDate 解析为 Date 类型
            date1 = format.parse(startDate);

            // 将字符串 endDate 解析为 Date 类型
            date2 = format.parse(endDate);
        } catch (ParseException e) {
            // 解析失败时输出异常堆栈信息
            e.printStackTrace();
        }

        // 创建第一个 Calendar 实例,并设置为 date1 时间
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        // 创建第二个 Calendar 实例,并设置为 date2 时间
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);

        // 获取两个日期在各自年份中的“第几天”(即从年初算起的天数)
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        // 获取两个日期的年份
        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);

        // 如果不在同一年,需要考虑跨年累加天数
        if (year1 != year2) {
            int timeDistance = 0;

            // 遍历中间年份,累加天数
            for (int i = year1; i < year2; i++) {
                // 判断是否为闰年
                if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) {
                    timeDistance += 366;
                } else {
                    timeDistance += 365;
                }
            }

            // 返回跨年的总天数 + 年内天数差 + 1(包含当天)
            return timeDistance + (day2 - day1) + 1;

        } else {
            // 同一年,直接用 day2 - day1 即可(再 +1 表示包含起始日)
            return day2 - day1 + 1;
        }
    }

    public static void main(String[] args) {
        System.out.println(differentDays("2019-2-26","2019-2-28"));
    }
3.3.2 计算时间是否过期
    private static SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 校验开始时间是否早于或等于结束时间(是否过期)
     * @param startTime 开始时间 可以为null
     * @param endTime 结束时间 不能为null
     * @return
     * @throws Exception
     */
    public static boolean checkExpired(String startTime, String endTime) throws Exception{
        // 当开始或者结束时间为空时,不进行比较
        if(StringUtils.isEmpty(endTime)){
            return true;
        }
        // 转换为日期格式
        Date start = format.parse(startTime);
        Date end = format.parse(endTime);
        // 进行过期判断
        return checkExpired(start,end);
    }


    /**
     * 校验开始时间是否早于或等于结束时间(是否过期)
     * @param startTime 开始时间(允许为 null,若为 null 则默认为当前系统时间)
     * @param endTime 结束时间(必须不为 null)
     * @return 如果开始时间 ≤ 结束时间,则返回 true;否则返回 false
     */
    public static boolean checkExpired(Date startTime, Date endTime) {
        boolean flag = true;  // 默认标志为 true(即认为时间顺序合法)

        // 如果开始时间为空,则使用当前时间作为默认开始时间
        if (null != startTime) {
            // 使用系统当前时间
            startTime = new Date();
        }

        // 获取开始时间的时间戳(毫秒表示)
        long startTimestamp = startTime.getTime();
        // 获取结束时间的时间戳(毫秒表示)
        long endTimestamp = endTime.getTime();

        // 判断开始时间是否晚于结束时间
        if (startTimestamp > endTimestamp) {
            // 如果开始时间在结束时间之后,则时间顺序非法
            flag = false;
        }

        // 返回判断结果
        return flag;
    }

    public static void main(String[] args) throws Exception{
        boolean ifExpired = checkExpired("2025-07-18 12:13:14", "2025-07-17 12:13:14");
        System.out.println(ifExpired);
    }
3.3.3 获取当前时间到次日凌晨的剩余毫秒数
    /**
     * 获取当前时间到次日凌晨(00:00:00)的剩余毫秒数
     * @return 距离第二天 00:00:00 的时间间隔(以毫秒为单位)
     */
    public static long getExecuteDuration() {

        // 获取当前系统时间
        Calendar now = Calendar.getInstance();

        // 克隆当前时间,用于设置第二天的凌晨时间点
        Calendar midnight = (Calendar) now.clone();

        // 将克隆的时间增加一天,表示“明天”的时间
        midnight.add(Calendar.DAY_OF_YEAR, 1);

        // 设置“明天”的时间为凌晨 00:00:00.000
        midnight.set(Calendar.HOUR_OF_DAY, 0);     // 设置小时为 0
        midnight.set(Calendar.MINUTE, 0);          // 设置分钟为 0
        midnight.set(Calendar.SECOND, 0);          // 设置秒为 0
        midnight.set(Calendar.MILLISECOND, 0);     // 设置毫秒为 0
        // 计算从“当前时间”到“明天凌晨”的时间差(单位:毫秒)
        long delay = midnight.getTimeInMillis() - now.getTimeInMillis();

        // 返回时间差
        return delay;
    }

三.DateTime 类

在早期的 Java 开发中,时间和日期的处理主要依赖于 java.util.Date 和 java.util.Calendar 类。然而他们存在设计上的缺陷、操作繁琐以及线程不安全的缺点。

org.joda.time.DateTime 是 Joda-Time 中最核心的类之一,用于表示一个不变的时间点(包括日期和时间)。它具有丰富的 API 接口,用于时间的创建、解析、比较、偏移、格式化等操作。

1. DateTime 类的优点

1.设计清晰: 遵循不可变对象设计,更符合函数式风格。

2.API 丰富: 支持多种日期操作,如加减年月日、判断闰年、周数等。

3.线程安全: 所有核心类(如 DateTime、LocalDate 等)默认是线程安全的。

4.格式化/解析能力强: 提供 DateTimeFormatter 类,支持灵活的时间格式转换。

5.国际化支持: 支持时区、地区设置等国际化需求。

2. DateTime 类的缺点

1.非 Java 标准库: 需要额外引入第三方依赖,增加构建与兼容复杂度。

2.与 java.util.Date 不兼容: 使用时常需要手动进行类型转换。

3.被 Java 8 替代 : Java 8 引入的 java.time.*(JSR-310) 包吸收了 Joda-Time 的精华设计。

3. DateTime 常用时间操作

引入org.joda.time.DateTime

<dependency>
   <groupId>joda-time</groupId>
   <artifactId>joda-time</artifactId>
   <version>2.9.8</version>
</dependency>
3.1 初始化时间
DateTime time = new DateTime(2018,4,23,23, 7,18,888);
// 2018年4月23日23点7分18秒888毫秒  
3.2 按格式输出时间(将DateTime格式转换为字符串)
 String time = dateTime.toString("yyyy-MM-dd hh:mm:ss.SSSa");

小写hh是12小时制,大写HH是24小时制

3.3 将字符串转换为DateTime格式
 DateTimeFormatter format = DateTimeFormat .forPattern("yyyy-MM-dd HH:mm:ss");  
                   
 DateTime dateTime = DateTime.parse("2018-4-23 23:12:16", format); 
3.4 取得当前时间
DateTime time= new DateTime();
3.5 计算两个日期间隔的天数
 LocalDate start=new LocalDate(2018,4,23);    
 LocalDate end=new LocalDate(2019, 06, 16);    
 int days = Days.daysBetween(start, end).getDays(); 
3.6 增加日期
      DateTime dateTime = DateTime.parse("2018-04-23");  
                dateTime = dateTime1.plusDays(1);  
                dateTime = dateTime1.plusHours(2);  
                dateTime = dateTime1.plusMinutes(3);  
                dateTime = dateTime1.plusMonths(4);  
                dateTime = dateTime1.plusSeconds(5);  
                dateTime = dateTime1.plusWeeks(6);  
                dateTime = dateTime1.plusYears(7);  
3.7 减少日期
  DateTime dateTime = DateTime.parse("2018-04-23");  
                dateTime = dateTime1.minusMillis(1);  
                dateTime = dateTime1.minusHours(1);  
                dateTime = dateTime1.minusSeconds(1);;  
3.8 判断是否闰月
DateTime time = new DateTime();    
 org.joda.time.DateTime.Property month = time.monthOfYear();    
  System.out.println("是否闰月:" + month.isLeap());  
3.9 DateTime与Date转换
DateTime time = new DateTime(new Date());    
 Date date = time.toDate();    
 DateTime time2 = new DateTime(System.currentTimeMillis());    
 time2.getMillis();  
3.10.DateTime与Calendar转换
Calendar calendar = Calendar.getInstance();    
 dateTime = new DateTime(calendar);

 四. 设置时区

1. Calendar 设置时区

较为复杂, 如果要使用 SimpleDateFormat 格式化时间, SimpleDateFormat 要同时设置时区

    /**
     * 时区测试
     */
    public static void testTimeZone() {
        // UTC 时间格式化器
        SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        utcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));  // 设置为 UTC

        Calendar utcDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
        System.out.println("UTC时间: " + utcFormat.format(utcDate.getTime()));

        // 北京时间格式化器
        SimpleDateFormat beijingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        beijingFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));  // 设置为北京时间

        Calendar beijingDate = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));
        System.out.println("北京时间: " + beijingFormat.format(beijingDate.getTime()));
    }

2. java.time 设置时区

    public static void main(String[] args) {
        // 获取当前北京时间(默认 JVM 时区如果是 UTC+8)
        LocalDateTime nowInBeijing = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println("北京时间: " + nowInBeijing);
        
        // 设置当前时区为 UTC
        LocalDateTime nowInUtc = LocalDateTime.now(ZoneId.of("UTC"));
        System.out.println("UTC时间: " + nowInUtc);
    }

3. springboot 设置全局 UTC 时区

可以在springboot 启动类中加入该方法:

    @PostConstruct
    public void init() {
        // 全局设置为 UTC 时区
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
        logger.info("当前默认时区已设置为: {}", TimeZone.getDefault().getID());
    }

4. @Scheduled定时注解指定时区

@Scheduled(cron = "0 0 0 * * ?", zone = "Asia/Shanghai")

 该方法触发时按照指定的时区, 方法内部的时间操作, 也是按照指定时区

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纯洁的小魔鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值