为啥日历值不同?

Posted

技术标签:

【中文标题】为啥日历值不同?【英文标题】:Why is Calendar value different?为什么日历值不同? 【发布时间】:2019-07-13 18:25:26 【问题描述】:

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

我选择了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);

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

有什么问题?

我没有使用“日期”。 因为,android Devloper 是这么说的。

日期(int year, int month, int date, int hrs, int min, int sec) 此构造函数在 API 级别 1 中已弃用。从 JDK 版本 1.1 开始,由 Calendar.set(year + 1900, month, date, hrs, min, sec) 或 GregorianCalendar(year + 1900, month, date, hrs, min,秒)。

【问题讨论】:

我建议你不要使用Calendar。该类设计不佳,这可能会给您带来麻烦,并且已经过时了。而是使用来自java.time, the modern Java date and time API 的LocalDateTime 【参考方案1】:

永远不要使用Calendar

可怕的Calendar 类在几年前被java.time 类取代,特别是ZonedDateTime

时区

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

ZonedDateTime

使用ZonedDateTime 类表示带有时区的日期和时间。

ZoneID

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

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

我想你会关心日期时间键的排序顺序,所以使用SortedMapTreeMap 就是这样一种实现。

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

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

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


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

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

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些次要功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 Android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

【讨论】:

感谢您的评论!!!我还有一个问题。你说“地图和堆栈溢出”。在那个过程之后,如果我想要下一个过程,我必须按“年&月&日”或“小时&分钟”或“值(整数)”对地图进行排序。在这种情况下我可以使用 sortedmap 吗?任何地图(排序、哈希图、树形图)都有堆栈溢出? 排序已经在 Stack Overflow 上被覆盖了无数次。搜索 compareToComparableComparator @현도연 在这种情况下,答案中显示的SortedMap 可能对您有利,但这取决于我们不完全清楚的要求。【参考方案2】:

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 方法的这种行为让很多人感到惊讶,这只是类设计不佳的众多问题中的一个小问题。你不应该使用它。我们从 2014 年开始就有 java.time,所以真的没有理由这样做。

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

是的,java.time 在较旧和较新的 Android 设备上运行良好。它只需要至少 Java 6

在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 起)中,现代 API 是内置的。 在 Java 6 和 7 中获得 ThreeTen Backport,这是现代类的后向端口(JSR 310 的 ThreeTen;请参阅底部的链接)。 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保从 org.threeten.bp 导入日期和时间类以及子包。

链接

Oracle tutorial: Date Time 解释如何使用 java.time。 Java Specification Request (JSR) 310,其中首次描述了 java.time。 ThreeTen Backport project,java.time 向后移植到 Java 6 和 7(JSR-310 的 ThreeTen)。 ThreeTenABP,Android 版 ThreeTen Backport Question: How to use ThreeTenABP in Android Project,解释得很透彻。

【讨论】:

以上是关于为啥日历值不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥相同值的内存地址不同[重复]

为啥 django 模型的空白和空值不同的选项?

为啥 django 模型的空白和空值不同的选项?

为啥与两者相比得到不同的毫秒值(QTIme 和 QueryPerformanceCounter)

在 keydown 上,为啥值的 console.log 与将值分配给对象不同?

为啥我的淘汰视图模型使用不同的值初始化?