android - 如何在android中转换具有不同时区的时间字符串格式
问题描述
我有一种时间字符串格式,例如yyyy-MM-dd'T'HH:mm:ss.SSSSSS
时区“GMT+05:30”。我需要将此格式转换为yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
具有不同时区的格式 - “GMT0:00”。我写下面的函数来转换具有不同时区的时间字符串。它工作正常,但不能改变时间。
public static String getUTCDate(String dateString) {
String oldDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS";
String newDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
String result = "";
SimpleDateFormat simpleDateFormatOld;
SimpleDateFormat simpleDateFormatNew;
try {
simpleDateFormatOld = new SimpleDateFormat(oldDateFormat,Locale.US);
simpleDateFormatNew = new SimpleDateFormat(newDateFormat,Locale.US);
simpleDateFormatNew.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
result = simpleDateFormatNew.format(simpleDateFormatOld.parse(dateString));
}
catch(Exception e) {
ExceptionHandler.handleException(e);
}
return result;
}
示例:我传递了2019-07-11T21:28:02.8469576
日期时间字符串。但作为回报,我得到了2019-07-11T21:28:02.846Z
日期时间字符串而不改变时间。
如何更新字符串的时间?
解决方案
对于大多数目的,您不应该将时间点从一个时区的一种字符串格式转换为不同时区的另一种字符串格式。在您的程序中,将日期和时间保存在正确的日期时间对象中,而不是字符串(就像您不会在字符串中保存整数或浮点值一样)。如果您只需要时间点(不是原始的 GMT 偏移量,+05:30),那么Instant
该类是正确的。当您的程序接受字符串输入时,解析并将其转换为Instant
第一件事并保持原样。只有当您需要给出字符串输出时,才将时间格式化回字符串并将其传递出去。
java.time 和 ThreeTenABP
解析和转换输入
ZoneId originalZone = ZoneId.of("Asia/Kolkata");
Instant time = LocalDateTime.parse("2019-07-11T21:28:02.8469576")
.atZone(originalZone)
.toInstant();
System.out.println(time);
转换后的时间打印为:
2019-07-11T15:58:02.846957600Z
在大多数情况下,不要将时区作为裸 GMT 偏移量。一个命名的时区可以更好地向读者解释为什么选择这个时区,并且在偏移量发生变化的情况下更具前瞻性(这比你想象的更频繁地发生)。我正在利用您的字符串采用 ISO 8601 格式这一事实。在这种情况下,我们不需要提供显式格式化程序。顺便说一句,您的示例字符串在秒上有 7 个小数,而您oldDateFormat
似乎想要 6 个。在这里没关系,因为LocalDateTime.parse
接受从 0 到 9 个小数的任何内容。
格式化输出
您要求的输出是 ISO 8601 的不同变体。上面的输出非常相似,因为它也是 ISO 8601,只是小数点太多。所以这次让我们应用一个明确的格式:
ZoneOffset newOffset = ZoneOffset.UTC;
DateTimeFormatter newFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss.SSSX")
.toFormatter();
String formattedUtcDateTime = time.atOffset(newOffset).format(newFormatter);
System.out.println(formattedUtcDateTime);
2019-07-11T15:58:02.846Z
我们看到现代 Java 日期和时间 API java.time 强制我们指定时区偏移量,所以忘记这样做(就像您在问题的代码中所做的那样,导致意外输出)根本不是可能的。
我建议反对 SimpleDateFormat 和 TimeZone
您尝试使用的日期时间类SimpleDateFormat
和TimeZone
设计不佳且早已过时,前者尤其是出了名的麻烦。也没有办法正确SimpleDateFormat
解析秒的 6 位或 7 位小数;它只支持毫秒,正好是三位小数。相反,我使用的是现代 Java 日期和时间 API java.time。我发现使用它要好得多。
问题:我可以在 Android 上使用 java.time 吗?
是的,java.time 在较旧和较新的 Android 设备上运行良好。它只需要至少Java 6。
- 在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 开始)中,现代 API 是内置的。
- 在 Java 6 和 7 中获得 ThreeTen Backport,现代类的后向端口(ThreeTen 用于 JSR 310;请参阅底部的链接)。
- 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保从
org.threeten.bp
子包中导入日期和时间类。
链接
- Oracle 教程:日期时间解释如何使用 java.time。
- Java 规范请求 (JSR) 310,
java.time
第一次描述的地方。 - ThreeTen Backport 项目,
java.time
Java 6 和 7 的反向移植(ThreeTen for JSR-310)。 - ThreeTenABP , ThreeTen Backport 的 Android 版
- 问题:如何在Android项目中使用ThreeTenABP,有非常详尽的解释。
- 维基百科文章:ISO 8601
推荐阅读
- r - if 语句根据数据帧中的其他值为数据帧分配值
- python - 为什么线程不等待输入就结束了?
- android - 当我尝试使用自定义 EditText 时应用程序崩溃
- magento - 证明与 magento 2.3 的集成
- amazon-web-services - 如何使用 Aws SQS 作为事件源并根据事件属性调用不同的 Lamda 函数
- javascript - 如何将函数作为参数传递给 Javascript 中的另一个函数?
- android - 如何在 android studio 中从 HEX/Integer 获取颜色名称
- javascript - “node filename.js”在 vs 代码中不起作用
- javascript - 对某些回调具有优先级的 Promise 回调队列
- laravel - 资源:使用 whenPivotLoaded 作为条件