0%

Calendar的时间比对问题

发现

Calendar的比较时基于毫秒的,但是calendar使用set方法只能精确到秒,如果用Calendar进行同一秒的比较,可能会有…

会有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MainActivity extends AppCompatActivity{
private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Calendar calStart = Calendar.getInstance();
calStart.set(2008,0,1,0,0,0);//2008.01.01 00:00:00

setOnClickListener(v->{
Calendar calEnd = Calendar.getInstance();
calEnd.set(2008,0,1,0,0,0);//2008.01.01 00:00:00
Log.i(TAG,"is startTime before endTime?"+isNotBefore(calStart,calEnd));
},R.id.tv_main_comparative)
}

private boolean isNotBefore(Calendar calStart,Calendar calEnd){
return !calStart.before(calEnd);
}

private void setOnClickListener(OnClickListener listener,@ResId int ... ids){
for(int id:ids){
findViewById(id).setOnClickListener(listener)
}
}
}
//日志结果
//首次点击:MainActivity:is startTime before endTime?false
//再次点击:MainActivity:is startTime before endTime?true
//多次点击:...或false或true

每次点击时的结果都是不尽相同的。为什么?

探索

我们尝试新增如下日志

1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
...
setOnClickListener(v->{
...
Log.i(TAG,"startTime.getTimeInMillis():"+calStart.getTimeInMillis()+"--->endTime.getTimeInMillis():"+calEnd.getTimeInMillis());
},R.id.tv_main_comparative);
}

可以看到打出的日志(这里就不贴日志了,可以自行尝试)有毫秒级的差别,也就是:

  1. calStart的真实时间是2008.01.01 00:00:00.xxx
    (注:xxx是Calendar calStart = Calendar.getInstance();时的毫秒数)
  2. calEnd的真实时间的毫秒数则是点击瞬间的毫秒数
  3. 由于毫秒数的不确定性,导致时而calStart大于、时而小于、时而等于calEnd

解决方案

解决方案有两种:

  1. 在使用到Calendar的类中定义一个基础Calendar

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Calendar calBase = Calendar.getInstance();

    Calendar calStart = Calendar.getInstance();
    calStart.setTime(calBase.getTime());
    calStart.set(2008,0,1,0,0,0);

    Calendar calEnd = Calendar.getInstance();
    calEnd.setTime(calBase.getTime());
    calEnd.set(2008,0,1,0,0,0);

    千万别写成如下:

    1
    2
    3
    4
    5
    6
    //那我还得和你解释数据是怎么存储在堆和栈中... pass
    Calendar calStart = calBase;
    calStart.set(2008,0,1,0,0,0);

    Calendar calEnd = calBase;
    calStart.set(2008,0,1,0,0,0);

    这种写法其实都是在操作calBase,都不能算是另外操作calStart和calEnd.可尝试如下代码看看效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void initTry() {
    Calendar calBase = Calendar.getInstance();
    Calendar calStart = calBase;
    calBase.set(2008,1,1,0,0,0);
    Calendar calEnd = calBase;
    calEnd.set(2009,1,1,0,0,0);
    Log.i(TAG,"log\nCalendar0-->"+calStart.getTime().toString()+"\nCalendar1-->"+calEnd.getTime().toString());
    }

    //日志
    //log
    // Calendar0-->Sun Feb 01 00:00:00 GMT+08:00 2009
    // Calendar1-->Sun Feb 01 00:00:00 GMT+08:00 2009
  2. 比较时根据精确度进行比较:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    public class CalendarUtils{
    public boolean isNotBefore(Calendar calStart, Calendar calEnd, @NotNull CalendarType type) {
    // int startYear = calStart.get(Calendar.YEAR);
    // int endYear = calEnd.get(Calendar.YEAR);
    // if (startYear <= endYear) return false;
    if (ifBefore(calStart,calEnd,Calendar.YEAR)) return false;
    if (CalendarType.YEAR == type) return true;

    // int startMonth = calStart.get(Calendar.MONTH);
    // int endMonth = calEnd.get(Calendar.MONTH);
    // if (startMonth <= endMonth) return false;
    if (ifBefore(calStart,calEnd,Calendar.MONTH)) return false;
    if (CalendarType.MONTH == type) return true;

    // int startDay = calStart.get(Calendar.DAY_OF_MONTH);
    // int endDay = calEnd.get(Calendar.DAY_OF_MONTH);
    // if (startDay <= endDay) return false;
    if (ifBefore(calStart,calEnd,Calendar.DAY_OF_MONTH)) return false;
    if (CalendarType.DAY == type) return true;

    // int startHour = calStart.get(Calendar.HOUR_OF_DAY);
    // int endHour = calEnd.get(Calendar.HOUR_OF_DAY);
    // if (startHour <= endHour) return false;
    if (ifBefore(calStart,calEnd,Calendar.HOUR_OF_DAY)) return false;
    if (CalendarType.HOUR == type) return true;

    // int startMinute = calStart.get(Calendar.MINUTE);
    // int endMinute = calEnd.get(Calendar.MINUTE);
    // if (startMinute <= endMinute) return false;
    if (ifBefore(calStart,calEnd,Calendar.MINUTE)) return false;
    if (CalendarType.MINUTE == type) return true;

    // int startSecond = calStart.get(Calendar.SECOND);
    // int endSecond = calEnd.get(Calendar.SECOND);
    // if (startSecond <= endSecond) return false;
    if (ifBefore(calStart,calEnd,Calendar.SECOND)) return false;
    if (CalendarType.SECOND == type) return true;

    return calStart.getTimeInMillis() >= calEnd.getTimeInMillis();
    }

    //后续做了变更,封装此方法,方便调整代码
    private boolean isBefore(Calendar calStart,Calendar calEnd,int calendarField){
    return calStart.get(calendarField)<=calEnd.get(calendarField);
    }

    public enum CalendarType {
    YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLIS_SECOND
    }
    }

结语

写代码时务必要注重细节~

另:(免责声明)以上代码未在编辑器上校验过,纯手打~

如有问题,请根据实际情况修改代码

如有疑问,评论区见~

------------本文结束感谢您的阅读------------

Thank you for your accept!