You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中GregorianCalendar月份显示错误问题求助

问题分析与解决方案

这个问题的核心原因其实很常见:GregorianCalendar的月份是0-based索引(0代表一月,11代表十二月),但你的JSON里的month字段是1-based的(1代表一月,12代表十二月),直接用Gson映射会导致月份整体偏移1,还会触发Calendar自动调整无效日期的机制,这就是你遇到第二个异常的根源。

为什么会出现1 Feb 2016的异常?

你提到的JSON数据{"year":2016,"month":1,"dayOfMonth":30},如果直接让Gson解析到GregorianCalendar里,它会把month=1直接赋值给Calendar的月份字段(此时对应的是二月)。但二月没有30天,GregorianCalendar会自动进行日期滚动调整,把这个无效日期转换成三月1号。之后你在显示时给月份减1,就变成了二月1号,所以最终显示1 Feb 2016。

正确的解决方法

不能只在显示阶段给月份减1,必须在解析阶段就把JSON的1-based月份转换成0-based,这样才能避免Calendar自动调整日期的问题。这里给你两个可行的方案:

方案一:自定义Gson TypeAdapter(推荐)

写一个专门的TypeAdapter来处理GregorianCalendar的反序列化,在解析时直接修正月份的偏移:

class GregorianCalendarTypeAdapter : TypeAdapter<GregorianCalendar>() {
    override fun write(out: JsonWriter, value: GregorianCalendar?) {
        // 如果需要序列化Calendar到JSON,这里可以实现把0-based月份转回1-based
        value?.let {
            out.beginObject()
            out.name("year").value(it.get(Calendar.YEAR))
            out.name("month").value(it.get(Calendar.MONTH) + 1)
            out.name("dayOfMonth").value(it.get(Calendar.DAY_OF_MONTH))
            out.endObject()
        } ?: out.nullValue()
    }

    override fun read(`in`: JsonReader): GregorianCalendar? {
        var year = 0
        var month = 0
        var dayOfMonth = 0
        `in`.beginObject()
        while (`in`.hasNext()) {
            when (`in`.nextName()) {
                "year" -> year = `in`.nextInt()
                "month" -> month = `in`.nextInt() - 1 // 关键:将1-based转为0-based
                "dayOfMonth" -> dayOfMonth = `in`.nextInt()
                else -> `in`.skipValue()
            }
        }
        `in`.endObject()
        val calendar = GregorianCalendar()
        calendar.clear() // 清除默认的时分秒等冗余信息
        calendar.set(year, month, dayOfMonth)
        return calendar
    }
}

然后在创建Gson实例时注册这个Adapter:

val gson = GsonBuilder()
    .registerTypeAdapter(GregorianCalendar::class.java, GregorianCalendarTypeAdapter())
    .create()

这样解析出来的GregorianCalendar就是完全正确的日期,显示时直接调用get()方法即可,不需要再调整月份:

originDate?.let {
    val day = it.get(Calendar.DAY_OF_MONTH)
    val monthName = it.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault())
    val year = it.get(Calendar.YEAR)
    dateText = resources.getString(R.string.origin_date, "$day $monthName $year")
}

方案二:使用中间数据类转换

如果不想自定义TypeAdapter,可以先把JSON的日期解析成一个简单的数据类,再转换成GregorianCalendar:

// 定义用于解析JSON的中间日期类
data class DateOfOrigin(
    val year: Int,
    val month: Int,
    val dayOfMonth: Int
)

// 修改Location数据类
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 originDateRaw: DateOfOrigin?,
    val parking : Boolean = true,
    var imageBitmap : Bitmap? = null
) {
    // 延迟转换为GregorianCalendar,同时修正月份偏移
    val originDate: GregorianCalendar? get() = originDateRaw?.let {
        val calendar = GregorianCalendar()
        calendar.clear()
        calendar.set(it.year, it.month - 1, it.dayOfMonth)
        calendar
    }
}

这种方式更轻量化,适合不需要频繁序列化GregorianCalendar的场景。

内容的提问来源于stack exchange,提问作者DanteC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:52:51