安卓 – GregorianCalendar 显示错误的月份

huangapple go评论79阅读模式
英文:

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 = &quot;${it.get(Calendar.DAY_OF_MONTH)} ${it.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault())} ${it.get(Calendar.YEAR)}&quot;

    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.

&quot;date_of_origin&quot;: {&quot;year&quot;:2016,&quot;month&quot;:1,&quot;dayOfMonth&quot;:30}

答案1

得分: 3

GregorianCalendar 表示月份的数字范围是从 011。这意味着数字 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 的代码进行交互,可以进行转换。调用添加到旧类中的新的转换方法。

GregorianCalendarZonedDateTime 替代。

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 ) ;

IdeOne.com 上运行此代码

>2021年1月23日


关于 java.time

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

要了解更多信息,请参阅 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 类?

英文:

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( &quot;America/Montreal&quot; ) ) ;
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( &quot;America/Montreal&quot; ) ;
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?

安卓 – GregorianCalendar 显示错误的月份

huangapple
  • 本文由 发表于 2020年5月19日 17:43:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/61887963.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定