为啥日历值不同?
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/Paris
比Asia/Tokyo
的中午晚得多,而America/Montreal
的中午早得多。
ZonedDateTime
使用ZonedDateTime
类表示带有时区的日期和时间。
ZoneID
以Continent/Region
的格式指定proper time zone name,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。切勿使用 2-4 个字母的缩写,例如 EST
或 IST
,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。
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
如果出于计数目的,您只想考虑日期和时间而忽略时区,请提取LocalDateTime
。 LocalDateTime
只是一个带有时间的日期,没有任何时区概念或与 UTC 的偏移量。
LocalDateTime ldt = zdt.toLocalDateTime() ;
Map
➙ SortedMap
➙ TreeMap
有了LocalDateTime
,您就可以数数了。创建一个Map
,其中键是LocalDateTime
,值是Integer
。
我想你会关心日期时间键的排序顺序,所以使用SortedMap
。 TreeMap
就是这样一种实现。
SortedMap< LocalDateTime , Integer > map = new TreeMap() ;
对于每个LocalDateTime
,从Map
中检索一个Integer
对象。增加计数,并将旧的 Integer
对象替换为新对象。
使用 Map
已经在 Stack Overflow 上被报道了数百次,甚至数千次。因此,如果您需要更多讨论和示例,请搜索。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date
、Calendar
和 SimpleDateFormat
。
要了解更多信息,请参阅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 8、Java SE 9、Java SE 10、Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些次要功能和修复。 Java SE 6 和 Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 Android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…。【讨论】:
感谢您的评论!!!我还有一个问题。你说“地图和堆栈溢出”。在那个过程之后,如果我想要下一个过程,我必须按“年&月&日”或“小时&分钟”或“值(整数)”对地图进行排序。在这种情况下我可以使用 sortedmap 吗?任何地图(排序、哈希图、树形图)都有堆栈溢出? 排序已经在 Stack Overflow 上被覆盖了无数次。搜索compareTo
、Comparable
和 Comparator
。
@현도연 在这种情况下,答案中显示的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
由于您将mMonth
与Calendar
一起使用,我认为它是从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,解释得很透彻。
【讨论】:
以上是关于为啥日历值不同?的主要内容,如果未能解决你的问题,请参考以下文章
为啥与两者相比得到不同的毫秒值(QTIme 和 QueryPerformanceCounter)