写这篇博客来纪念一下leetcode177周赛翻车的第一题。第一部分是该题的分析,第二部分是Java Calendar使用以及注意点。
1. 题目分析
1.1 题目
请你编写一个程序来计算两个日期之间隔了多少天。
日期以字符串形式给出,格式为 YYYY-MM-DD
1.2 思路
将日期的每一项转化为int后设置Calendar, 然后使用getTimeInMillis方法获取从1970.01.01到该时间的毫秒值,然后相减再换算成天数
1.3 代码
public int daysBetweenDates(String date1, String date2) {
String[] s1=date1.split("-");
String[] s2=date2.split("-");
java.util.Calendar c1=java.util.Calendar.getInstance();
java.util.Calendar c2=java.util.Calendar.getInstance();
//1 之前提交的错误版本没有下面这2句
c1.clear();
c2.clear();
//2 之前提交的错误版本c1.set后面的第二项没有-1
c1.set(Integer.parseInt(s1[0]),Integer.parseInt(s1[1])-1,Integer.parseInt(s1[2]));
c2.set(Integer.parseInt(s2[0]),Integer.parseInt(s2[1])-1,Integer.parseInt(s2[2]));
long temp=Math.abs(c1.getTimeInMillis()-c2.getTimeInMillis());
long ans=temp/(24*60*60*1000);
return ((int)ans);
}
1.4 分析
- 也是最重要的一点,没有在对Calendar使用set方法之前clear,导致系统时间会影响calendar的值(比如当前时分秒)。测试成功然后提交,奇幻的情况发生了!相同的样例,在测试和提交的时候跑出不同的结果。造成这个情况的原因是:1. 统时间会影响calendar的值 2.long的除法将计算的小数部分舍去 3.计算出来的结果在不同的运行时间是随机的。
PS:中途曾经试过c1.set(Integer.parseInt(s1[0]),Integer.parseInt(s1[1])-1,Integer.parseInt(s1[2]),0,0,0),仍然提交错误,推测是因为这种方法只能设置到秒,毫秒也会影响结果。 - Calendar的月份是0-11,存入的时候要减去1以获得正确结果
2. Calendar使用总结
2.0 简介
Calendar是一个抽象类,GregorianCalendar 是它的一个具体实现。Calendar.getInstance()获得一个以当前时间为初始化的calendar对象。(GregorianCalendar的实例)
2.1 获取时间
注意Calendar.DATE和Calendar.DAY_OF_MONTH相同,而Calendar.HOUR(12小时制)和Calendar.HOUR_OF_DAY不相同
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
//比当前月份少1
int month = cal.get(Calendar.MONTH);
//date与day_of_month相同
int date = cal.get(Calendar.DATE);
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
//表示本周的第几天,从周日开始计算
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
//12小时制
int hour = cal.get(Calendar.HOUR);
//24小时制
int hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
int minute = cal.get(Calendar.MINUTE);
int second = cal.get(Calendar.SECOND);
int millisecond = cal.get(Calendar.MILLISECOND);
//获取本月的最后一天,getMaximum是获取日的最大值,一定是31
int maxDate = cal.getActualMaximum(Calendar.DATE);
2.2设置时间
set
set(int field, int value)
field 可以用来设置年/月/日/小时/分钟/秒/微秒等值,例如取值Calendar.MONTH
set(int year, int month, int day, int hour, int minute, int second)
注意:没有set(int year, int month, int day, int hour, int minute, int second,int millsecond)函数,并且上面设置到秒的这个函数,并不会把millsecond清零
Calendar 为了性能原因对 set() 方法采取延缓计算的方法,即多次set不多次计算,在调用get(), getTime(), getTimeInMillis(), add(), or roll() 这些函数时才计算。java document上的栗子:
Calendar c = Calendar.getInstance();
c.set(2000, 7, 31, 0, 0 , 0); //2000-8-31
c.set(Calendar.MONTH, Calendar.SEPTEMBER); //应该是 2000-9-31,也就是 2000-10-1
c.set(Calendar.DAY_OF_MONTH, 30); //如果 Calendar 转化到 2000-10-1,那么现在的结果就该是 2000-10-30
cal1.getTime(); //2000-9-30,说明调用get这类函数后才计算
add
- 当被修改的字段超出它可以的范围时,那么比它大的字段会自动修正(比如下面例子的第2,3行,修改月份年份修正)
- 当被修改的字段导致比它小的字段的范围改变时,比它小的字段调整为尽可能接近其期望值(如下面例子的3,4行)
Calendar cal1 = Calendar.getInstance();
cal1.set(1999, 7, 31, 0, 0 , 0); //1999-8-31
cal1.add(Calendar.MONTH, 13); //2000-9-31
System.out.println(cal1.getTime()); //结果是2000-9-31
roll
当被修改的字段超出它可以的范围时,那么比它大的字段不会被修正
2.3 容错性设置
当一个用户给出错误的日期时,自动计算,但如果Lenient==false,Calendar 报错
Calendar cal1 = Calendar.getInstance();
cal1.set(2000, 1, 32, 0, 0, 0);
System.out.println(cal1.getTime());//显示为Tue Feb 01 00:00:00 PST 2000
cal1.setLenient(false);
cal1.set(2000, 1, 32, 0, 0, 0);
System.out.println(cal1.getTime());//报错