为什么日历值不同?

Why is Calendar value different?

我要按年、月、日、时、分计算次数。(秒统一为零,我不需要秒)

我选择了HashMap作为数据结构

HashMap<Calendar,Integer> arr_time; 

如果已经有相同的时间(年,月,日,小时,分钟),我想增加整数,或者添加一个新的时间(年,月,日,小时,分钟)。

Calendar calendar = Calendar.getInstance();
calendar.set(mYear,mMonth,mDay,mHour,mMinute,0);
if(arr_time.containsKey(calendar)){
  // increase Integer value
  // ++1;
}else{
  // add new time 
  // arr_time.put(calendar,1);
}

我认为如果年、月、日、小时和分钟相同,它会识别相同的日历。 但事实并非如此。

有什么问题?

我没有使用 "Date"。 因为,Android开发者是这样说的

Date(int year, int month, int date, int hrs, int min, int sec) This constructor was deprecated in API level 1. As of JDK version 1.1, replaced by Calendar.set(year + 1900, month, date, hrs, min, sec) or GregorianCalendar(year + 1900, month, date, hrs, min, sec).

从不使用 Calendar

可怕的Calendarclass在几年前被java.timeclass所取代,特别是ZonedDateTime.

时区

你忽略了时区这个关键问题。在您提供时区上下文(或与 UTC 的偏移量)之前,日期和时间没有实际意义。例如,中午 Europe/ParisAsia/Tokyo 的中午晚得多,比 America/Montreal 的中午早得多。

ZonedDateTime

ZonedDateTime class.

表示带时区的日期和时间

ZoneID

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 ESTIST 等 2-4 字母缩写,因为它们 不是 真实时区,未标准化,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

truncatedTo

如果要将秒和小数秒都设置为零,请截断到分钟。

ZonedDateTime zdt = ZonedDateTime.now( z ).truncatedTo( ChronoUnit.MINUTES ) ;  // Set the whole second and the fractional second both to zero.

LocalDateTime

如果出于计数目的,您只想考虑带时间的日期而忽略时区,请提取 LocalDateTimeLocalDateTime 只是一个带有时间的日期,没有任何时区或与 UTC 的偏移量的概念。

LocalDateTime ldt = zdt.toLocalDateTime() ;

MapSortedMapTreeMap

手头有LocalDateTime,您可以数数了。创建一个 Map,其中键是 LocalDateTime,值是 Integer

我想您会关心日期时间键的排序顺序,因此使用 SortedMap. A TreeMap 就是这样一种实现方式。

SortedMap< LocalDateTime , Integer > map = new TreeMap() ;

对于每个 LocalDateTime,从 Map 中检索一个 Integer 对象。增加计数,并用新对象替换旧的 Integer 对象。

使用 Map 已经在 Stack Overflow 上被提及了数百次,甚至数千次。如果您需要更多讨论和示例,请搜索。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

在哪里获取java.time classes?

java.time

    Map<LocalDateTime, Integer> arr_time = new HashMap<>();
    ZoneId zone = ZoneId.of("Antarctica/Vostok");

    for (int i = 0; i < 10; i++) {
        LocalDateTime now = LocalDateTime.now(zone).truncatedTo(ChronoUnit.MINUTES);
        arr_time.compute(now, (ldt, oldCount) -> oldCount == null ? Integer.valueOf(1) : oldCount + 1);

        TimeUnit.MILLISECONDS.sleep(103);
    }
    System.out.println(arr_time);

当我运行刚才的代码时,得到:

{2019-02-20T13:42=10}

我录了 10 次,在中间睡觉以确保它们不完全相同。但是因为我将每分钟都截断为整分钟,所以它们最终都是 2019-02-20T13:42 并一起计算。

int 个变量创建 LocalDateTime

    int mYear = 2019;
    int mMonth = Calendar.JANUARY;
    int mDay = 31;
    int mHour = 23;
    int mMinute = 45;
    LocalDateTime ldt = LocalDateTime.of(mYear, mMonth + 1, mDay, mHour, mMinute);
    System.out.println(ldt);

2019-01-31T23:45

由于您将 mMonthCalendar 一起使用,我认为它是从 0 开始的。 LocalDateTime 月数从 1 开始,就像人类一样,所以我需要加 1。

你的代码出了什么问题?

calendar.set(mYear,mMonth,mDay,mHour,mMinute,0) 设置年月日时分秒 但不设置毫秒 。由于每个 Calendar 对象都是使用当前时间创建的,因此毫秒数通常会有所不同,因此即使您设置相同的值,Calendar 个对象仍然不相等。

6-arg set 方法的行为令很多人感到惊讶,这只是 class 设计不佳的众多问题中的一个小问题。你不应该使用它。自 2014 年以来,我们已经 java.time,所以真的没有理由这样做。

问题:我可以在 Android 上使用 java.time 吗?

是的,java.time 在新旧 Android 设备上都能很好地工作。它只需要至少 Java 6.

  • 在 Java 8 和更新的 Android 设备上(从 API 级别 26)内置了现代 API。
  • 在 Java 6 和 7 中获取 ThreeTen Backport,现代 classes 的 backport(ThreeTen 用于 JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 classes。

链接