Android - GregorianCalendar 显示错误的月份
Posted
技术标签:
【中文标题】Android - GregorianCalendar 显示错误的月份【英文标题】:Android - GregorianCalendar displays wrong month 【发布时间】:2020-09-05 08:03:39 【问题描述】:我尝试在互联网上搜索并在 StackOverlflow 上发现了很多关于同一主题的问题,但找不到任何我能够理解的内容...
所以,我有一个包含 GregorianCalendar 类型的 dateOfOrigin 的数据类。使用 gson 我转换所有 json 并返回一个包含所有位置的 Observable 数组。在 json 文件中,我将 dateOfOrigin 添加为一个对象,如下所示:
"id": 6,
"name": "Fuse",
"image": "fuse.jpg",
"street": "Blaesstraat 208",
"city": "Brussels Hoofdstedelijk Gewest",
"zip": 1000,
"date_of_origin": "year":1994,"month":4,"dayOfMonth":16,
"parking": true
这就是我的数据类的样子:
数据类位置 ( 有效ID:整数, 值名称:字符串, val图像:字符串, val 街道:字符串, val 城市:字符串, val zip:整数, @SerializedName("date_of_origin") val originDate : GregorianCalendar?, val停车:布尔=真, var imageBitmap : 位图? =空 )
每当我尝试像这样设置 dateText 时:
originDate?.let
dateText = "$it.get(Calendar.DAY_OF_MONTH) $it.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()) $it.get(Calendar.YEAR)"
dateText = resources.getString(R.string.origin_date, dateText)
它输出16 May 1994
而不是16 Apr 1994
我不知道如何解决这个问题...
编辑
在大多数情况下,从月份中减去 1 似乎可以解决问题。不过,我有一个结果应该输出30 Jan 2016
,但显示1 Feb 2016
。
"date_of_origin": "year":2016,"month":1,"dayOfMonth":30
【问题讨论】:
Calender.MONTH 从 0 开始(对于一月,link),因此您必须减去一个才能获得正确的日期。 你好,我认为它是 this 的副本。那是因为 android 日历 MONTH 从 0 位置开始 使用java.time
...
在这里它有多直观(或不直观)并不重要,因为在这种情况下它工作正常,它只是不是 OP 所期望的输出,但它不会错了@Hawklike
我建议你不要使用GregorianCalendar
。该课程设计不良且早已过时。而是使用来自java.time, the modern Java date and time API 的LocalDate
。
【参考方案1】:
GregorianCalendar
表示月份,数字范围从0
到11
。这意味着数字 0
表示为 1 月,11
表示为 12 月。
因此,如果您的 API 未使用与 Java 实现相同的逻辑,则需要减去 1
。
更新:
GregorianCalendar(2016, 1, 30)
被理解为 2 月 30 日。这在内部转换为 3 月 1 日,因此当您从日期中减去一个月时,您将得到 2 月 1 日。您需要使用减去的月份数创建 GregorianCalendar
类的实例,即。一月为 0,二月为 1,依此类推。
【讨论】:
没有理由使用 Joda Time 另外,你不需要subtract
而不是add
吗?
我认为你的答案的最后一部分根本没有必要,因为 api 和实现都使用相同的逻辑 - 只是因为它没有显示 OP 认为它会在这种情况下实际上并没有出错
谢谢!我仍然不明白为什么它会在内部转换为 3 月 1 日...尝试如下创建 GregorianCalendar 的实例,但仍返回 2 月 1 日而不是 2 月 30 日: originDate?.let val d = GregorianCalendar( it.get(Calendar.YEAR), it.get(Calendar.MONTH) - 1, it.get(Calendar.DAY_OF_MONTH)) //....
因为 2 月 30 日是无稽之谈。具体来说,2016 年是闰年,所以 2 月有 29 天。因此,2 月 30 日转换为 3 月 1 日(溢出)。【参考方案2】:
Answer by Hawklike 是正确的。你被GregorianCalendar
类使用的疯狂月份编号方案欺骗了。避免此类的众多原因之一。
tl;博士
myGregCal
.toZonedDateTime() // Convert from obsolete `GregorianCalendar` class to modern `java.time.ZonedDateTime` class.
.toLocalDate() // Extract the date portion, without time-of-day and without time zone.
.format( // Generate text representing the value of this date-time object.
DateTimeFormatter
.ofLocalizedDate( FormatStyle.MEDIUM ) // Automatically localize.
.withLocale( // Specify a locale for the human language and cultural norms used in localization.
Locale.UK
)
)
2021 年 1 月 23 日
详情
永远不要使用GregorianCalendar
。此类是与 Java 的最早版本捆绑在一起的日期时间类的一部分。这些类在几年前被 JSR 310 中定义的现代 java.time 类所取代。
如果您必须与尚未更新到 java.time 的代码进行互操作,请转换。调用添加到旧类的新转换方法。
GregorianCalendar
已替换为 ZonedDateTime
。
ZonedDateTime zdt = myGregCal.toZonedDateTime() ; // From legacy class to modern class.
走向另一个方向。
ZonedDateTime zdt = ZonedDateTime.of( 2021 , Month.JANUARY , 23 , 12 , 0 , 0 , 0 , ZoneId.of( "America/Montreal" ) ) ;
GregorianCalendar myGregCal = GregorianCalendar.from( zdt ) ;
或者打破那个 int 多个部分。
LocalDate ld = LocalDate.of( 2021 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 12 , 0 ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
GregorianCalendar myGregCal = GregorianCalendar.from( zdt ) ;
或者使用月份编号而不是 Month
枚举作为月份。请注意,java.time 使用正常编号,1-12 表示 1 月至 12 月,这与 GregorianCalendar
不同。
LocalDate.of( 2021 , 1 , 23 ) // Same effect as Month.JANUARY.
要生成文本,请使用DateTimeFormatter
类。您想要的格式恰好与英国使用的本地化格式相匹配。所以让 java.time 通过调用DateTimeFormatter.ofLocalizedDate
为您自动本地化。
Locale locale = Locale.UK ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale ) ;
String output = zdt2.toLocalDate().format( f ) ;
看到这个code run live at IdeOne.com。
2021 年 1 月 23 日
关于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.*
类。 Hibernate 5 和 JPA 2.2 支持 java.time。
从哪里获得 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…。【讨论】:
以上是关于Android - GregorianCalendar 显示错误的月份的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )