最近在 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()
方法。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论