2

定期的なイベントについて、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が、すべての部分で null チェックが行われているため、表示されるか、このようなものになりますよね?

-1469913-4027何年も前のようなものを意味します!これは繰り返し発生するイベントであるため、「残り日数」の情報は常に 0 から 366 の間である必要があると考えました。このコードがこのような数値を生成する原因は何ですか? これはgetNextOccurrence()、4027 年前のデータを返すということですか? この振る舞いを説明することはできません。

あなたが私を助けてくれることを願っています。よろしくお願いします!

編集:参考になるかもしれません: 間違った日付の年が常に出力され1ます。それにもかかわらず、の結果は4k年のようなものです。DateFormat.getDateInstance().format()Jan 3, 1getDaysLeft()

編集#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. なんで?

4

2 に答える 2

1

日付を操作することは、実際にユーザーに発生する前に、これらのあいまいなエラーを把握するのが非常に難しい場合があります. 多くの場合、機械に数千万の日付を投げる小さな単体テストを作成し、極端な答えがポップアップするかどうかを確認することは、時間をかけて行う価値があります。

また、これは読む価値があるかもしれません。はるかに優れたものを試す前に、Javaの日付クラスがどれほど悪いかを理解することはできません。:)

編集:ユーザーが非常に高い入力値を指定した場合、結果を の整数にスローすると、数値オーバーフローが発生する可能性がありますgetDaysLeft()。そのまま長く続けてください。またはさらに良い: 適切な入力値のみを受け入れ、20120 年などを入力した場合はユーザーに警告します:)

EDIT2:最後の編集で間違っていました.ceil()。番号のオーバーフローから保護します。正直なところ、このバグがどのように発生するのか、もはやわかりません。

EDIT3: 3 番目の編集への対応: Unix timeDateCalendar使用することを忘れないでください。これは、ゼロで表される時間が 1970 年であることを意味します。1970 年より前のものはすべて、負の値で表されます。

EDIT4: javas  calendar-classes が悪いことを思い出してください。このコード スニペットは、エラーが実際には Calendar クラスにあることを示しています。

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を使用してください。あなたはそれを後悔しません:)

于 2012-06-12T13:31:13.910 に答える
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
            }
于 2012-06-15T07:22:14.033 に答える