簡體   English   中英

Java Calendar實例增加DAY_OF_MONTH作為遞減(僅)HOUR或MINUTE的副作用

[英]Java Calendar instance increments DAY_OF_MONTH as a side effect of decrementing (only) HOUR or MINUTE

我有一個未完成的Android應用程序 ,主要活動有TimePicker@+id/tyme ), DatePicker@+id/date )和TextView@+id/dropTime ),用於顯示類似“DateTime”的數據由於某種原因,我需要指定兩個視圖。

因為Javadocs不贊成從Date實例中提取DateTime-field-type值的所有嘗試,所以我想使用Calendar類來表示用戶選擇的年,月,日,小時,分鍾,秒的組合。 我發現我經常(但不總是)不能減少小時或分鍾字段而不同時增加日期字段。 這是DatePicker事件處理程序:

        date = (DatePicker) findViewById(R.id.date);
        // h/t O'one https://stackoverflow.com/a/34952495/948073
        date.init(
                calendar.get(Calendar.YEAR),
                calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH),
                new DatePicker.OnDateChangedListener() {
                    @Override
                    public void onDateChanged(DatePicker datePicker, int y, int m, int d) {
                        System.out.println("onDateChanged(tp,"+y+","+m+","+d+")");
                        calendar.set(Calendar.YEAR, y);
                        calendar.set(Calendar.MONTH, m);
                        calendar.set(Calendar.DAY_OF_MONTH, d);
                        updateDropTime();
                    }
                }
        );

TimePicker 注意y / m / d字段的顯式備份和恢復是徒勞的,試圖在h:m:s變化期間消除y:m:d的可疑交叉污染:

        private TimePicker time;
        dropTime = (TextView) findViewById(R.id.dropTime);
        updateDropTime();
        time = (TimePicker) findViewById(R.id.tyme);


        time.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
            public void onTimeChanged(TimePicker tp, int h, int m) {
                System.out.println("onTimeChanged(tp,"+h+","+m+")");
                // Save state of y/m/d portion of calendar
                int y = calendar.get(Calendar.YEAR);
                int mo = calendar.get(Calendar.MONTH);
                int d = calendar.get(Calendar.DAY_OF_MONTH);
                // Set h and m fields of calendar based on user input
                calendar.set(Calendar.HOUR, h);
                calendar.set(Calendar.MINUTE, m);
                // Restore state of y/m/d fields
                calendar.set(Calendar.YEAR, y);
                calendar.set(Calendar.MONTH, mo);
                calendar.set(Calendar.DAY_OF_MONTH, d);
                // In theory y/m/d portion of datetime should be unchanged
                updateDropTime();
            }
        });

這兩個事件處理程序都調用updateDropTime() ,如下所示:

private void updateDropTime() {
    String disp = DateFormat.getDateTimeInstance().format(calendar.getTimeInMillis());
    dropTime.setText(disp);
}

我懷疑方法鏈中導致disp值的某些東西是看似混亂的行為發生的地方,我認為我已經系統地標記了對日期字段的任何更改,這些更改可能是時間字段更改的副作用。

這條線錯了:

            calendar.set(Calendar.HOUR, h);

相反,你想要:

            calendar.set(Calendar.HOUR_OF_DAY, h);

h你得到onTimeChanged()是在24小時時鍾一天的小時。 Calendar.HOUR表示AM或PM內的小時,即12小時制。

這怎么會導致你的問題? 示例:原始時間為20:25(如果您願意,則為8:25 PM)。 用戶將其更改為13:28(1:28 PM)。 您將Calendar.HOUR設置為13.由於原始時間是在PM並且您不更改此項,因此您實際上將時間設置為下午13:28。 這是胡說八道,但Calendar並不關心,它只是將其解釋為第二天凌晨11:28分。 設置日期並沒有幫助,因為當你這樣做時,字段尚未計算,因此內部時間仍然是正確的一天下午13:28。 只有在最后調用getTimeInMillis() ,才會計算所有字段,檢測到溢出並增加日期。

Calendar是默認的寬松

如果您希望收到有關此類錯誤的通知(我們不時制作的setLenient(false) ,請在創建Calendar對象后調用setLenient(false) 這將導致它不接受超出范圍的值,如13 HOUR

java.time

如果您根本不想使用Calendar類,那么您肯定遠離第一個,並且有一個更好,更程序友好的替代方案。 現代Java日期和時間API內置於Java 8及更高版本中,在Android上您可以在ThreeTenABP 中使用它,請參閱如何在Android項目中使用ThreeTenABP 我建議將日期保存在LocalDate對象中,並將時間保存在LocalTime這樣用戶可以獨立更新每個對象而不會有交叉污染的風險。 只有在updateDropTime()才能使用LocalDate.atTime()將兩者合並為LocalDateTime ,然后使用DateTimeFormatter將其格式化為所需的顯示格式。

public void onTimeChanged(TimePicker tp, int h, int m) {
    System.out.println("onTimeChanged(tp," + h + "," + m + ")");
    time = LocalTime.of(h, m);
    updateDropTime();
}

更新可能會像:

private void updateDropTime() {
    String disp = date.atTime(time)
            .format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT));
    dropTime.setText(disp);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM