计算Android中的重复事件时剩余-1469913天

时间:2012-06-12 12:42:36

标签: android date calendar gregorian-calendar

对于重复发生的事件,我想在Android日历应用程序中显示下一次出现的剩余天数。

示例:

Today: 2012-06-12
Reoccurring event: 19th June
=> 13 days left

为了实现这一点,我将第一次出现保存在数据类型为Calendar的对象中:

private Calendar cal;
...
cal = new GregorianCalendar();
cal.set(Calendar.YEAR, USER_INPUT_YEAR);
cal.set(Calendar.MONTH, USER_INPUT_MONTH);
...

要计算剩余的天数,我使用此功能:

public int getDaysLeft() {
    Date next = this.getNextOccurrence();
    if (next == null) {
        return -1;
    }
    else {
        long differenceInMilliseconds = next.getTime()-System.currentTimeMillis();
        double differenceInDays = (double) differenceInMilliseconds/DateUtils.DAY_IN_MILLIS;
        return (int) Math.ceil(differenceInDays);
    }
}

使用此功能:

public Date getNextOccurrence() {
    if (this.cal == null) {
        return null;
    }
    else {
        Calendar today = new GregorianCalendar();
        Calendar next = new GregorianCalendar();
        next.setTime(this.cal.getTime());
        next.set(Calendar.YEAR, today.get(Calendar.YEAR));
        if ((today.get(Calendar.MONTH) > this.cal.get(Calendar.MONTH)) || ((today.get(Calendar.MONTH) == this.cal.get(Calendar.MONTH)) && (today.get(Calendar.DAY_OF_MONTH) > this.cal.get(Calendar.DAY_OF_MONTH)))) {
            next.add(Calendar.YEAR, 1);
        }
        return next.getTime();
    }
}

顺便说一下,为了获得初始日期,我希望找到一个YYYY-MM-DD值,然后解析它:

(new SimpleDateFormat("yyyy-MM-dd")).parse(INPUT_DATE_STRING)

在大多数情况下,此功能正常,但有些用户报告他们将-1469913等数字视为“剩余天数”。怎么会发生这种情况?

我认为日期(cal)可能没有设置或无效,但是它会显示-1或类似的东西,因为所有部分都有空检查,对吧?

-1469913意味着-4027年之前的事情!由于这是一个重复发生的事件,我认为“剩余的日子”信息应始终在0到366之间。什么可能导致此代码产生这样的数字?这是否意味着getNextOccurrence()返回过去4027年的数据?我无法解释这种行为。

我希望你能帮助我。非常感谢你!

修改:因为它可能有所帮助:使用1时,错误的日期年份始终输出为DateFormat.getDateInstance().format(),例如Jan 3, 1。然而,getDaysLeft()的结果类似于4k年。

编辑#2:我发现像1--22199-1这样的日期产生了“4k年剩余”的输出。然而,它由(new SimpleDateFormat("yyyy-MM-dd")).parse()成功解析。同样,-1-1-1-91-被正确解析为Jan 1, 2

编辑#3:事实证明,像“0000-01-03”这样简单的日期造成了所有麻烦。当我以毫秒为单位输出时间时,它显示-62167222800000。当我将其输出为GMT字符串时,它会显示0001-01-03 - 奇怪,不是吗?当我将年份设置为1900时,毫秒时间突然变为-122095040400000。为什么呢?

2 个答案:

答案 0 :(得分:1)

使用日期在将它们发生在野外用户之前很难找出这些模糊的错误。在许多情况下,值得花些时间进行一次小单元测试,在机器中投入几千万个日期,看看是否有任何极端的答案弹出。

同样this可能值得一读。在尝试更好的方法之前,你不会意识到java日期类有多糟糕。 :)

编辑:如果用户提供非常高的输入值,那么当您将结果抛出getDaysLeft()中的整数时,可能会出现数字溢出。保持它很长。甚至更好:只接受合理的输入值,如果他们输入20120年或类似的东西警告用户:)

EDIT2:我上次编辑时错了,.ceil()可以防止号码溢出。说实话,我已经不知道这个错误会如何发生。

EDIT3:回复您的第三次修改:请注意,DateCalendar使用Unix time。这意味着零所代表的时间是1970年。1970年之前的所有东西都将用负值表示。

EDIT4:请记住,javas日历类很糟糕。此代码段演示了错误实际上是在Calendar-class中:

Calendar next = new GregorianCalendar();
long date1 = -62167222800000L;
long date2 = -62135600400000L;

next.setTimeInMillis(date1);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());

next.setTimeInMillis(date2);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());

输出:

-125629491600000
1325545200000

然而,很难找到导致这种情况的确切错误。所有这些错误仍然存​​在的原因是因为修复它们可能会破坏全世界的遗留系统。我的猜测是,这个错误源于无法给出负面年份。例如,这将给出输出“2013”​​:

Calendar next = new GregorianCalendar();
next.set(Calendar.YEAR, -2012);
System.out.println(next.get(Calendar.YEAR));

我只是建议你不要在输入中允许这样的极端值。确定可接受的跨度,如果值超出这些边界,则给出错误消息。如果您想在某个更进一步的应用程序中处理所有可能的日期,只需使用joda time。你不后悔:))

答案 1 :(得分:0)

您可能会在几天内得到负值,因为用户已输入下一次出现的日期,即之前的任何日期。

我认为你应该像这样计算你的daysLeft,

            String inputDateString = "19/06/2012";
            Calendar calCurr = Calendar.getInstance();//current date
            Calendar calNext = Calendar.getInstance();// for next date
            calNext.setTime(new Date(inputDateString)); // or do set Day,Month,Year like in your Question

            if(calNext.after(calCurr)) // if the next date is after current
            {
                long timeDiff = calNext.getTimeInMillis() - calCurr.getTimeInMillis(); // time of next year if today is 15 june and some one enter 16 june for next occurance
                int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
            }
            else
            {
                long timeDiff = calCurr.getTimeInMillis() - calNext.getTimeInMillis();
                timeDiff = DateUtils.YEAR_IN_MILLIS - timeDiff; // time of next year if today is 15 june and some one enter 14 june for next occurance
                int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
            }