最近在 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 对象中。
指定格式的方法如下表所示:
| 格式 | 解释 | 举例 |
|---|---|---|
| yyyy | 年 | 2022 |
| mm | 月 | 08 |
| dd | 日 | 15 |
| e | 周 | mon |
| hh | 小时 | 18 |
| mm | 分钟 | 19 |
| ss | 秒 | 55 |
格式还有很多,比如说 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);
}
}
到此为止就实现了任意格式字符串与 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也可以完全同理实现加减年/月/时/分/秒,等等,只需更改 field 为 calendar.xxx 即可,不一而足。
总结
1、三者互转:

(simpledateformat 对象和 calendar 对象用 s c 简写)
2、实现日期字符串的加减:
string → date → calendar → date → string.
核心是对 calendar 对象使用 add() 方法。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论