Java Calendar 实例递增 DAY_OF_MONTH 作为递减(仅)HOUR 或 MINUTE 的副作用

标签 java android date datetime calendar

我有an unfinished Android app主要 Activity 包含一个 TimePicker (@+id/tyme),一个 DatePicker (@+id/date),以及一个 TextView (@+id/dropTime),用于显示类似“DateTime”的数据,出于某种原因我需要两个 View 来指定。

因为 Javadocs 反对所有从 Date 实例中提取 DateTime 字段类型值的尝试,我想使用 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);

您在 onTimeChanged() 中获得的 h 是 24 小时制的一天中的小时。 Calendar.HOUR 表示 AM 或 PM 中的小时,即 12 小时制。

这怎么会导致您的问题?示例:原始时间是 20:25(如果您愿意,也可以是晚上 8:25)。用户将其更改为 13:28(下午 1:28)。您将 Calendar.HOUR 设置为 13。由于原始时间是下午并且您没有更改它,您实际上是将时间设置为下午 13:28。这是无稽之谈,但 Calendar 并不关心,它只是将其解释为 1:28 AM 第二天。设置日期没有帮助,因为在你这样做的时候,字段还没有计算,所以内部时间仍然是正确日期的下午 13:28。只有当您最后调用 getTimeInMillis() 时,才会计算所有字段、检测到溢出并增加日期。

Calendar 默认宽松

如果您想收到有关此类错误的通知(我们都会不时发生),请在创建后对 Calendar 对象调用 setLenient(false)它。这将导致它不接受超出范围的值,例如 HOUR 的 13。

java.time

如果您根本不想为 Calendar 类操心,那么您肯定不是第一个,还有一个更好、对程序员更友好的替代方案。现代 Java 日期和时间 API 内置于 Java 8 及更高版本中,在 Android 上,您可以在 ThreeTenABP 中找到它。 , 请参阅 How to use ThreeTenABP in Android Project .我建议将日期保存在 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);
}

关于Java Calendar 实例递增 DAY_OF_MONTH 作为递减(仅)HOUR 或 MINUTE 的副作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45260922/

相关文章:

java - 树结构的正则表达式?

Android AppBarLayout 与 listview 重叠

mysql - 将 int 字段转换为日期字段(mysql)

java - 无法通过 EditText 接受整数值

java - 使用 keystore api 'unable to find valid certification path to requested target' 的内存 keystore

java - 为什么这两个字节操作不相等?

android - 在Android手机上模拟USB鼠标指针输入?

javascript - 如何在 Firefox 中保存全屏状态?

php - 获取下个月的第一天

php - Mysql和php中使用foreach显示两个数组的值并保存到数据库中