英文:
Android - GregorianCalendar displays wrong month
问题
我尝试在互联网上搜索,并在StackOverflow上找到了很多与同一主题有关的问题,但是找不到我能理解的内容...
所以,我有一个数据类,其中包含类型为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
}
这是我的数据类的样子:
data class Location (
val id : Int,
val name : String,
val image : String,
val street : String,
val city : String,
val zip : Int,
@SerializedName("date_of_origin")
val originDate : GregorianCalendar?,
val parking : Boolean = true,
var imageBitmap : Bitmap? = null
)
每当我尝试设置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}}
英文:
I tried searching the internet and found a lot of questions on StackOverlflow somewhat regarding the same topic, but couldn't find anything that I was able to understand...
So, I have this data class that contains a dateOfOrigin of type GregorianCalendar. Using gson I convert all json and return an Observable array with all locations. Inside the json file, I added the dateOfOrigin as an object like so:
{
"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
}
And this is what my data class looks like:
<pre>data class Location (
val id : Int,
val name : String,
val image : String,
val street : String,
val city : String,
val zip : Int,
@SerializedName("date_of_origin")
val originDate : GregorianCalendar?,
val parking : Boolean = true,
var imageBitmap : Bitmap? = null
)</pre>
Whenever I try to set the dateText like this:
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)
}
It outputs 16 May 1994
instead of 16 Apr 1994
I'm not able to figure out how to fix this...
EDIT
Subtracting 1 from the month seems to fix the problem for most cases. Still, I have one result that is supposed to output 30 Jan 2016
but displays 1 Feb 2016
.
"date_of_origin": {"year":2016,"month":1,"dayOfMonth":30}
答案1
得分: 3
GregorianCalendar
表示月份的数字范围是从 0
到 11
。这意味着数字 0
表示一月,数字 11
表示十二月。
因此,如果您的 API 不使用与 Java 实现相同的逻辑,您需要减去 1
。
更新:
GregorianCalendar(2016, 1, 30)
被理解为二月的第30天。这在内部转换为三月的第一天,因此当您从日期中减去一个月时,您会得到二月的第一天。您需要创建一个 GregorianCalendar
类的实例,其中已经减去了月份的数字,即一月为0,二月为1,依此类推。
英文:
GregorianCalendar
represents months with numbers in the range from 0
to 11
. This means that the number 0
is represented as January and 11
is represented as of December.
Therefore you need to subtract 1
if your API is not using the same logic as the Java implementation.
Updated:
GregorianCalendar(2016, 1, 30)
is understood as 30th of February. This is internally converted to 1st of March, therefore when you subtract one month from the date, you get 1st of February. You need to create an instance of GregorianCalendar
class already with the subtracted month number, ie. January as 0, February as 1 and so on.
答案2
得分: 1
Answer by Hawklike是正确的。你被GregorianCalendar
类使用的疯狂的月份编号方案所迷惑。这是避免使用这个类的众多原因之一。
简短总结
myGregCal
.toZonedDateTime() // 从废弃的 `GregorianCalendar` 类转换为现代的 `java.time.ZonedDateTime` 类。
.toLocalDate() // 提取日期部分,不包括时间和时区。
.format( // 生成表示此日期时间对象值的文本。
DateTimeFormatter
.ofLocalizedDate( FormatStyle.MEDIUM ) // 自动本地化。
.withLocale( // 指定用于本地化的人类语言和文化规范的区域。
Locale.UK
)
)
> 2021年1月23日
详细信息
永远不要使用 GregorianCalendar
。这个类是早期Java版本中捆绑的日期时间类的一部分。多年前,这些类被现代的 java.time 类(定义在 JSR 310 中)取代。
如果你必须与尚未更新为 java.time 的代码进行交互,可以进行转换。调用添加到旧类中的新的转换方法。
GregorianCalendar
被 ZonedDateTime
替代。
ZonedDateTime zdt = myGregCal.toZonedDateTime() ; // 从旧类到现代类的转换。
进行相反的转换。
ZonedDateTime zdt = ZonedDateTime.of( 2021 , Month.JANUARY , 23 , 12 , 0 , 0 , 0 , ZoneId.of( "America/Montreal" ) ) ;
GregorianCalendar myGregCal = GregorianCalendar.from( zdt ) ;
或者将其分解为多个部分。
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 分别代表一月到十二月,不像 GregorianCalendar
。
LocalDate.of( 2021 , 1 , 23 ) // 和 Month.JANUARY 效果相同。
要生成文本,使用 DateTimeFormatter
类。你所需的格式恰好与英国使用的本地化格式相匹配。因此,通过调用 DateTimeFormatter.ofLocalizedDate
,让 java.time 自动进行本地化。
Locale locale = Locale.UK ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale ) ;
String output = zdt2.toLocalDate().format( f ) ;
>2021年1月23日
关于 java.time
java.time 框架内置于 Java 8 及更高版本。这些类取代了旧的、麻烦的 遗留 日期时间类,如 java.util.Date
、Calendar
和 SimpleDateFormat
。
要了解更多信息,请参阅 Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是 JSR 310。
Joda-Time 项目,现在处于维护模式,建议迁移到 java.time 类。
你可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。无需使用字符串,也不需要 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 功能在 Java 6 和 7 中进行了后移,使用 ThreeTen-Backport。
- Android
- 较新版本的 Android 都提供了 java.time 类的实现。
- 对于早期的 Android(<26),ThreeTenABP 项目适配了 [ThreeTen-Backport](http://www.threeten
英文:
The Answer by Hawklike is correct. You were tricked by the crazy month-numbering scheme employed by the GregorianCalendar
class. One of many reasons to avoid this class.
tl;dr
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
)
)
> 23 Jan 2021
Details
Never use GregorianCalendar
. This class is part of the date-time classes bundled with the earliest versions of Java. These classes were years ago supplanted by the modern java.time classes defined in JSR 310.
If you must interoperate with code not yet updated to java.time, convert. Call new conversion methods added to the old classes.
GregorianCalendar
was replaced by ZonedDateTime
.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ; // From legacy class to modern class.
Going the other direction.
ZonedDateTime zdt = ZonedDateTime.of( 2021 , Month.JANUARY , 23 , 12 , 0 , 0 , 0 , ZoneId.of( "America/Montreal" ) ) ;
GregorianCalendar myGregCal = GregorianCalendar.from( zdt ) ;
Or break that int multiple parts.
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 ) ;
Or use month number rather than Month
enum for the month. Notice that java.time uses sane numbering, 1-12 for January-December, unlike GregorianCalendar
.
LocalDate.of( 2021 , 1 , 23 ) // Same effect as Month.JANUARY.
To generate text, use DateTimeFormatter
class. Your desired format happens to match that of localized format used in the UK. So let java.time automatically localize for you by calling DateTimeFormatter.ofLocalizedDate
.
Locale locale = Locale.UK ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale ) ;
String output = zdt2.toLocalDate().format( f ) ;
See this code run live at IdeOne.com.
>23 Jan 2021
About java.time
The 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
.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and Java SE 7
- Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论