【Android 国际化】碰到有关日期的时区问题,我们该怎么办?终极大招来了。。

###背景 为什么想到要写一篇关于时区问题的blog?因为。。 去年Colin参与开发了公司的一个独立app的开发,其中我只负责了“多目的地选择页”的开发,说白了就是去打了个酱油。。可这酱油还是没完全白打,我发现项目经理、产品、开发、测试们在项目后期都在纠结时区问题。虽然我也跟着开了几场关于时区问题的会,可由于我没有真正参与吧,并没有真正理解为啥时区会出问题。。 不过Colin从那时就告诉自己,下次等我遇到时区问题时,一定要好好研究,这块内容貌似挺有意思的。。 就这样,真让我等来了。。 ###为什么会有时区问题? 1. 国际化app中需要展示时间时 2. app中用到了Calendar类orDate类时

总的来说,当需要把Date转化成时间字符串时,还有就是用到了Calendar类来计算时间时,如果用户在国外使用你的app时就可能存在时区问题。这里说的时区问题到底是什么问题呢?
比如,北京时间5月23号5:30时,另外一个半球的国家很有可能是5月22号晚上,那么一个字符串“2018-5-23 5:30”经过转为Date就转成了当地的Date,当这个Date再转成字符串时就不再是“2018-5-23 5:30”了。
###该如何解决时区问题?
####1. 关于时区的几个基本概念
要想彻底理解时区问题,我们需要先了解几个基本概念。

1.协调世界时:Coordinated Universal Time(UTC):
是英国格林尼治时间,格林尼治时间被称为零时区时间,也被称为绝对时间。
2.格林尼治标准时:Greenwich Mean Time(GMT):
是指位于伦敦郊区的皇家格林尼治天文台的标准时间,由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能和实际的太阳时相差16分钟。 现在都采用UTC时间,是校正过的GMT时间。
####2. Android中处理时区问题的办法
把当地时间都转为北京时间处理,这里有两个办法
#####(1)全局设置app时区为北京时区

TimeZone chinaTimeZone = TimeZone.getTimeZone("GMT+8");
TimeZone.setDefault(chinaTimeZone);

这个方法比较暴力,我们之前的独立app就是采用这种办法。不过,考虑到可能影响其他页面的功能,我没有采用这种办法。
####(2)在需要处理时间时,把本地时区的Date转为北京地区的Date

//转换任意时区的办法(本篇blog精髓)
private fun changeTimeZone(date: Date, oldZone: TimeZone, newZone: TimeZone): Date {
        var dateTmp: Date
        val timeOffset = oldZone.rawOffset - newZone.rawOffset
        dateTmp = Date(date.time - timeOffset)
        return dateTmp
}
//将当地时间转为北京时间的方法
fun getDateOfBJTimeZone(date: Date): Date {
        var originTimeZone = TimeZone.getDefault()
        var bjTimeZone = TimeZone.getTimeZone("Asia/Shanghai")
        return changeTimeZone(date, originTimeZone, bjTimeZone)
}

上面提供了两个Date转换的方法,那需要用到Calendar类时,可以给Calendar设置一个时区的,办法如下:

val c = Calendar.getInstance()
c.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
c.timeInMillis = date.time
c.add(Calendar.DATE, days)

同样,String和Date互转时也可以设置时区,办法如下:

val dateFormat = SimpleDateFormat(type)
val timeZone = TimeZone.getTimeZone("Asia/Shanghai")
dateFormat.timeZone = timeZone
str = dateFormat.format(date)

###参考

http://www.cnblogs.com/endure/p/3485281.html
https://www.jianshu.com/p/098c16b39f9b
https://blog.youkuaiyun.com/l_serein/article/details/6273362
http://www.cnblogs.com/endure/p/3485281.html
https://www.cnblogs.com/wenfei123chai/p/4267998.html
https://blog.youkuaiyun.com/lovext4098477/article/details/46866879

<think>好的,我需要帮助用户解决在使用PostgreSQL的TO_CHAR函数格式化日期时遇到的时区问题。首先,我得理解用户的具体问题。用户提到在格式化日期时,时区导致结果不一致,想找到解决方案。 根据用户提供的引用内容,引用[1]提到优化时区处理的方法,比如合理设置数据库时区和使用AT TIME ZONE进行换。这可能是一个关键点。引用[3]指出本地时间和GMT时间不一致是因为时区,但没有找到处理时区偏移的函数,这说明用户可能尝试过一些方法但未成功。 接下来,我需要回忆PostgreSQL中日期时间处理的机制。TO_CHAR函数将时间换为字符串,但如果时间戳带有时区信息(TIMESTAMP WITH TIME ZONE),换时会自动换为当前会话的时区。如果用户没有正确管理时区设置,这可能导致意外结果。 解决方案可能包括以下几点: 1. 使用AT TIME ZONE明确指定时区,将带时区时间换为特定时区的本地时间,然后再格式化。例如,将时间换为UTC时区后再格式化。 2. 确保数据库和会话的时区设置正确,避免隐式换带来的问题。引用[1]中提到设置数据库时区的重要性。 3. 可能还需要处理存储数据时的一致性,确保所有时间数据都存储为UTC,减少换的复杂性。 另外,引用[2]提到FastDateFormat的缓存机制,虽然这是Java的内容,但可能用户误以为PostgreSQL有类似问题,需要澄清两者无关,但可以借鉴显式处理时区的思路。 用户可能遇到的问题案例是,存储的时间戳带有时区,但格式化时没有换到目标时区,导致显示的时间与预期不符。例如,存储的是UTC时间,但格式化时未换到本地时区,直接使用TO_CHAR导致结果错误。 需要分步骤说明解决方案,首先处理时区换,再格式化。同时提醒用户检查数据库和会话的时区设置,避免隐式换。还可能涉及使用CST等时区缩写的问题,建议使用IANA时区名称更明确。 最后,生成的相关问题需要围绕时区处理、函数使用和最佳实践展开,帮助用户进一步理解。</think>在使用PostgreSQL的`TO_CHAR`函数格式化日期时,时区问题可能导致显示结果与预期不符。以下是分步解决方案: --- ### **1. 理解数据类型差异** - **带时区类型**:`TIMESTAMP WITH TIME TIME ZONE`(简称`TIMESTAMPTZ`)存储时会自动换为UTC时间,查询时根据会话时区换。 - **不带时区类型**:`TIMESTAMP`直接存储原始时间值,不进行时区换。 **关键操作**:格式化前明确时区归属。若字段是`TIMESTAMPTZ`,需用`AT TIME ZONE`显式时区后再格式化[^1]。 --- ### **2. 显式指定目标时区** **语法**:将带时区时间换为目标时区的本地时间 ```sql SELECT TO_CHAR( timestamp_with_tz_column AT TIME ZONE '目标时区', 'YYYY-MM-DD HH24:MI:SS' ); ``` **示例**: ```sql -- 将UTC时间换为北京时间(UTC+8) SELECT TO_CHAR( '2023-10-01 12:00:00 UTC'::TIMESTAMPTZ AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD HH24:MI:SS' ); -- 输出:2023-10-01 20:00:00 ``` --- ### **3. 时区设置检查** - **数据库时区**:通过`SHOW timezone;`查看当前设置,建议统一设置为UTC。 - **会话时区**:用`SET timezone = 'Asia/Shanghai';`临时修改,影响后续查询的隐式换[^1]。 --- ### **4. 存储层统一时区** **最佳实践**: 1. 所有时间戳**存储为UTC**(使用`TIMESTAMPTZ`类型)。 2. 查询时根据需求换为目标时区,避免存储时混合不同时区数据。 --- ### **5. 避免时区缩写歧义** 时区缩写(如`CST`可表示中国标准时间或北美中部时间)易引发歧义。**推荐使用IANA时区名称**(如`Asia/Shanghai`)。 --- ### **案例:错误用法 vs 正确用法** ```sql -- 错误:直接格式化带时区字段,结果依赖会话时区 SELECT TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI') FROM orders; -- 正确:显式时区后再格式化 SELECT TO_CHAR( created_at AT TIME ZONE 'UTC', -- 假设存储为UTC 'YYYY-MM-DD HH24:MI' ) FROM orders; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值