Java 中日期的加减 & String Date Calendar 的相互转换 详细讲解

最近在 Java 中遇到这样一个需求:对字符串日期加减数天,比如

“20220815” 加 1 天变为 “20220816”

“20220901” 减 1 天变为 “20220831”

“20240228” 加 2 天变为 “20240301”

等等。不熟悉 API 的我愣了,于是经学习整理出本文,希望能帮到读者。本文会梳理 String Date Calendar 的基本用法及相互转换。复习的话直接看总结部分就可以了。

基本思路:

先说实现对字符串加减数日的需求,需要这样的方法:

String addDay(String str, int num)

但 Java 中没有方法能直接把字符串看作日期并对其加减若干天,所以先把格式五花八门的 String 解析成唯一的、能够代表一个时刻的 Date, 然后对 Date 一番操作,加数天也好减数月也好,再按希望的格式转回 String, 这就是基本思路。

而在这个过程中,涉及到 String Date Calendar 三种类型的对象的相互转换。如下:

String →simple.parse(str)→

Date →calendar.setTime(date)→

Calendar →calendar.getTime()→

Date →simple.format(date)→

String

接下来是面向 0 基础的逐步讲解:

String ← Date

Java 中一个 Date 类型对象可以表示一个瞬时时刻,最高精确到毫秒。

public static void main(String[] args) {
    Date date = new Date();
    System.out.println(date.toString());    // Mon Aug 15 18:19:55 CST 2022
}

new Date() 能够获得一个表示当前时刻的 Date 对象,直接输出之即可。(本文会在非 String 类型对象输出时加上 toString() 方法,便于理解)

如果不想输出默认字符串,可以借助:SimpleDateFormat 类自定义输出字符串的格式

public static void main(String[] args) {
    Date date = new Date();
    System.out.println(date.toString());    // Mon Aug 15 18:19:55 CST 2022
    
    SimpleDateFormat f = new SimpleDateFormat("yyyy/MM/dd");
    System.out.println(f.format(date));     // 2022/08/15
}

public final String format(Date date) 方法能按照调用对象指定的格式返回一个 date 存储的时刻对应的字符串。这里定义格式 “年/月/日” 并存储在 f 对象中。

指定格式的方法如下表所示:

格式解释举例
yyyy2022
MM08
dd15
EMon
HH小时18
mm分钟19
ss55

格式还有很多,比如说 M MM MMM 甚至分别表示 8 08 Aug, 所以很灵活。你甚至可以模仿默认格式……

public static void main(String[] args) {
    Date date = new Date();
    System.out.println(date.toString());    // Mon Aug 15 18:19:55 CST 2022

    SimpleDateFormat f2 = new SimpleDateFormat("E MMM d HH:mm:ss zz yyyy", Locale.ENGLISH);
    System.out.println(f2.format(date));    // Mon Aug 15 18:19:55 CST 2022
}

不过常用的就是上表的那些。

String → Date

若要解析 String → Date, 同样可以借助 SimpleDateFormat, 用另一个方法:parse().

public static void main(String[] args) {
    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
    System.out.println(f.parse("2022-08-15").toString());
}

public Date parse(String source) 方法按照调用者指定的格式返回一个与字符串参数对应时刻相同的 Date 类型的对象。

但要注意上面这样是会报错的,因为假设你将来没有按照 2022-08-15 输入,Java 应抛出异常,用 try/catch 包围代码吧。

public static void main(String[] args) {
    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
    try {
        System.out.println(f.parse("2022-08-15").toString());   // Mon Aug 15 00:00:00 CST 2022
    } catch (ParseException e) {
        throw new RuntimeException(e);
    }
}

因为没有指定时分秒,就按全为 0 输出了。这不重要,重要的是我们确实成功得到了一个 Date 类型对象。

如果格式不匹配会怎样呢?如图,抛出了 java.text.ParseException 异常。

public static void main(String[] args) {
    SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
    try {
        System.out.println(f.parse("20220815").toString());
    } catch (ParseException e) {
        throw new RuntimeException(e);
    }
}
图1 异常

到此为止就实现了任意格式字符串与 Date 的互转。

Date ↔ Calendar

得到一个 Date 对象后,如何对其进行加减?这需要抽象类 Calendar 的帮助。其实 Calendar 能在许多场景替换 Date, 不过似乎却不能不借助 Date 和 SimpleDateFormat 来与 String 转换,所以先梳理了上文。

总之,Calendar 也可以表示一个瞬时时刻,不过不能像 Date 那样直接输出,需要先借助 getTime() 转换为 Date.

public static void main(String[] args) {
    Date date = new Date();
    System.out.println(date.toString());                // Mon Aug 15 18:19:55 CST 2022

    Calendar badExample = Calendar.getInstance();
    System.out.println(badExample.toString());          // java.util.GregorianCalendar[time=....非常长,后面省略

    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime().toString());  // Mon Aug 15 18:19:55 CST 2022
}

因为是抽象类,所以借助 public static Calendar getInstance() 方法得到一个表示当前时刻的 Calendar 对象。

public final Date getTime() 方法可以直接返回一个表示此调用者存储的时间值的 Date 对象。相似的,public final void setTime(Date date) 同理。没错,Date 和 Calendar 类互转非常简单。

并且 Calendar 类是比较强大的,借助 public int get(int field) 方法可以返回指定时间字段的值,public void set(int field, int value) 方法可以设置指定时间字段的值。时间字段代表是年,还是月,还是日,使用 Calendar 的静态成员变量即可。举例:

public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();

    int year = calendar.get(Calendar.YEAR);         // 2022
    int month = calendar.get(Calendar.MONTH);       // 7
    int day = calendar.get(Calendar.DAY_OF_MONTH);  // 15
    calendar.set(Calendar.YEAR, 2333);              // 年份从 2022 变为 2333
}

注意:month ∈ [0, 11], 比人类习惯的 [1, 12] 少 1.

加减日期

截止目前已经完成了 String Date Calendar 互转了。(Calendar 到 String 恐怕只能通过 Date 中转一下,我没有发现任何无需借助 Date 的方法)

接下来该实现按格式输入字符串日期,任意加减天数的需求了,用到 Calendar 的成员方法:public abstract void add(int field, int amount).

它能在 field 代表的时间字段上加 amount 个单位(可以为负数)。字段同样使用 Calendar 的静态成员变量。结合例子很容易理解:

public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime().toString());  // Mon Aug 15 18:19:55 CST 2022

    calendar.add(Calendar.YEAR, 2);                     // Mon Aug 15 18:19:55 CST 2024
    calendar.add(Calendar.MONTH, -6);                   // Thu Feb 15 18:19:55 CST 2024
    calendar.add(Calendar.DAY_OF_MONTH, 14);            // Thu Feb 29 18:19:55 CST 2024

    System.out.println(calendar.getTime().toString());  // Thu Feb 29 18:19:55 CST 2024
}

最后,把上面的内容连起来,笔者终于完成了:按指定格式 “yyyyMMdd” 传入字符串与加减天数,返回同样格式字符串的方法:String addDay(String str, int num). 如下:

public class Main {

    /**
     * 对日期加减数天
     *
     * @param yyyyMMdd 形如 "20240228"
     * @param day      形如 2(支持负数)
     * @return 形如 "20240301"
     */
    public static String addDay(String yyyyMMdd, int day) {

        // String to Date
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
        Date date;
        try {
            date = format.parse(yyyyMMdd);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        // Change Date
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, day);
        date = calendar.getTime();

        // Date to String
        yyyyMMdd = format.format(date);

        return yyyyMMdd;
    }

    public static void main(String[] args) {
        System.out.println(addDay("20240228", 2));  // 20240301
    }
}

即使天数很多也没问题:

System.out.println(addDay("20240228", 2333));  // 20300719

也可以完全同理实现加减年/月/时/分/秒,等等,只需更改 fieldCalendar.XXX 即可,不一而足。

总结

1、三者互转:

图2 总结

(SimpleDateFormat 对象和 Calendar 对象用 s c 简写)

2、实现日期字符串的加减:String → Date → Calendar → Date → String.

核心是对 Calendar 对象使用 add() 方法。


那么本文就结束了,作者非职业自学 Java,有写得不好的地方欢迎斧正。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值