java - Calendar 的 getActualMinimum 返回错误值

标签 java calendar

我需要将某个日期与当前日期/时间进行比较,以确保它位于当月的第一天/小时/分钟/秒之前。为了实现此功能,请使用其 getActualMinimum 方法配置一个 Calendar 实例,但是,今天(星期四,19/01/2023 - 10:40:18 BRT 2023 ),它呈现出一种我以前从未遇到过的行为。考虑以下代码:

    Calendar cal = Calendar.getInstance();
    System.out.println("After instantiation:                  " + cal.getTime());
    
    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
    System.out.println("After configuring the Day of Month:   " + cal.getTime());
    
    cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
    System.out.println("After configuring the Hour of day:    " + cal.getTime());
    
    cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
    System.out.println("After configuring the Minutes:        " + cal.getTime());
    
    cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
    System.out.println("After configuring the Seconds:        " + cal.getTime());
    
    cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
    System.out.println("After configuring the Millis:         " + cal.getTime());

上面的代码在创建这篇文章时将打印到控制台:

After instantiation:                  Thu Jan 19 10:40:18 BRT 2023
After configuring the Day of Month:   Sun Jan 01 10:40:18 BRT 2023
After configuring the Hour of day:    Sat Dec 31 23:40:18 BRT 2022
After configuring the Minutes:        Sat Dec 31 23:00:18 BRT 2022
After configuring the Seconds:        Sat Dec 31 23:00:00 BRT 2022
After configuring the Millis:         Sat Dec 31 23:00:00 BRT 2022

有人可以解释为什么在配置一天中的小时后,该值设置为 23 而不是 00 吗?

编辑:我正在使用 Java 8,特别是 JDK 1.8.0_241

我当前的默认时区是 Horário Padrão de Brasília(BRT 或 GMT-3)

最佳答案

java.time

java.util Date-Time API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用并切换到 modern Date-Time API .

使用现代日期时间 API java.time,您可以得到 specialized types出于不同的目的。一种非常常见的类型是 ZonedDateTime,它包含时区信息以及日期和时间信息。

注意:与java.util日期时间类型不同,java.time类型是不可变的,即您总是在设置时获得一个新实例新的值(value);因此,就像String一样,如果您希望引用指向新值,则需要将新值分配给引用。

演示:

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        // Replace ZoneId.systemDefault() with the applicable ZoneId e.g.
        // ZoneId.of("America/New_York")
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.systemDefault());
        System.out.println(zdt);

        zdt = zdt.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println(zdt);

        zdt = zdt.withHour(LocalTime.MIN.getHour());
        System.out.println(zdt);

        zdt = zdt.withMinute(LocalTime.MIN.getMinute());
        System.out.println(zdt);

        zdt = zdt.withSecond(LocalTime.MIN.getSecond());
        System.out.println(zdt);

        zdt = zdt.with(ChronoField.MILLI_OF_SECOND, LocalTime.MIN.getLong(ChronoField.MILLI_OF_SECOND));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
        System.out.println(zdt.format(formatter));

        // In a single statement
        String output = ZonedDateTime.now(ZoneId.systemDefault())
                .with(TemporalAdjusters.firstDayOfMonth())
                .withHour(LocalTime.MIN.getHour())
                .withMinute(LocalTime.MIN.getMinute())
                .withSecond(LocalTime.MIN.getSecond())
                .with(ChronoField.MILLI_OF_SECOND, LocalTime.MIN.getLong(ChronoField.MILLI_OF_SECOND))
                .format(formatter);
        System.out.println(output);

        // There is a better way if all you want is day-1 with minimum time
        zdt = LocalDate.now(ZoneId.systemDefault())
                .with(TemporalAdjusters.firstDayOfMonth())
                .atStartOfDay()
                .atZone(ZoneId.systemDefault());
        System.out.println(zdt.format(formatter));
    }
}

示例运行的输出:

2023-01-19T16:50:43.811714Z[GMT]
2023-01-01T16:50:43.811714Z[GMT]
2023-01-01T00:50:43.811714Z[GMT]
2023-01-01T00:00:43.811714Z[GMT]
2023-01-01T00:00:00.811714Z[GMT]
2023-01-01T00:00:00.000Z
2023-01-01T00:00:00.000Z
2023-01-01T00:00:00.000Z

ONLINE DEMO

Trail: Date Time 了解有关现代日期时间 API 的更多信息

如果您需要使用旧版 API 的解决方案:

Calendar#getTime返回 java.util.Date 的实例,它不是真正的日期时间对象;相反,它只包含从 1970 年 1 月 1 日 00:00:00 GMT 开始的毫秒数。 Date#toString应用系统时区来计算日期时间并返回相同的结果。

获取具有所需时区的日期时间字符串的方法是将时区应用于 SimpleDateFormat 并使用它来格式化 java.util.Date 的实例>.

演示:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

class Main {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss.SSS z yyyy", Locale.ENGLISH);
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));

        Calendar cal = Calendar.getInstance();
        System.out.println("After instantiation:                  " + sdf.format(cal.getTime()));

        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
        System.out.println("After configuring the Day of Month:   " + sdf.format(cal.getTime()));

        cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
        System.out.println("After configuring the Hour of day:    " + sdf.format(cal.getTime()));

        cal.set(Calendar.MINUTE, cal.getActualMinimum(Calendar.MINUTE));
        System.out.println("After configuring the Minutes:        " + sdf.format(cal.getTime()));

        cal.set(Calendar.SECOND, cal.getActualMinimum(Calendar.SECOND));
        System.out.println("After configuring the Seconds:        " + sdf.format(cal.getTime()));

        cal.set(Calendar.MILLISECOND, cal.getActualMinimum(Calendar.MILLISECOND));
        System.out.println("After configuring the Millis:         " + sdf.format(cal.getTime()));
    }
}

示例运行的输出:

After instantiation:                  Thu Jan 19 15:29:38.381 UTC 2023
After configuring the Day of Month:   Sun Jan 01 15:29:38.381 UTC 2023
After configuring the Hour of day:    Sun Jan 01 00:29:38.381 UTC 2023
After configuring the Minutes:        Sun Jan 01 00:00:38.381 UTC 2023
After configuring the Seconds:        Sun Jan 01 00:00:00.381 UTC 2023
After configuring the Millis:         Sun Jan 01 00:00:00.000 UTC 2023

ONLINE DEMO

关于java - Calendar 的 getActualMinimum 返回错误值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75173196/

相关文章:

java - SWT - 表与 TableViewer

angular - primeng 日历 - 以客户端时区显示

java - 计时器倒数为负

swift - 是否可以扩展 Calendar.Identifier 来创建自定义日历?

java - 谷歌日历,如 Swing 应用程序

Django 日历小部件?

java - 四个不同的 wsdl2java 应用程序无法导入 wsdl,但 SoapUI 可以正常导入

java - 分别有抽象类和接口(interface)的必要性是什么?

java - 初级 Java Applet — 无法播放声音文件

java - 尝试获取字符串是否有作者、标题、出版商等