当前位置: 首页 > 面试题库 >

如何从ISO 8601格式的字符串中选择时区到Calendar instace

柴英博
2023-03-14
问题内容

作为输入,我有一个字符串,它是ISO 8601中表示日期的字符串。例如:

“ 2017-04-04T09:00:00-08:00”

的最后一部分String“ -08:00”, 表示“时区偏移”。我将此字符串转换为一个Calendar实例,如下所示:

Calendar calendar = GregorianCalendar.getInstance();
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(iso8601Date);
calendar.setTime(date);

iso8601日期为“ 2017-04-04T09:00:00-08:00”

但这不会选择时区,并且如果我从Calendar实例中获取时区,它将给出当前设置的笔记本电脑实例,并且不会从ISO
8601字符串中获取时间戳。我通过日历实例检查时区为:

calendar.getTimeZone().getDisplayName()

有人可以在Calendar实例中显示如何选择时区吗?


问题答案:

创建时Calendar,它将采用JVM的默认时区。而且,当您String将a
解析为a时Date,它只会设置一个值:自历元(1970-01-01T00:00Z)以来的毫秒数。A Date
没有任何时区信息,仅此毫秒值。因此,您需要在日历中设置时区。

在格式化程序中,您将其Z视为文字,因为它位于引号('Z')内。这将忽略偏移量,并获取JVM默认时区中的日期(如果对应的偏移量不是-08:00,则它将具有不同的值)。

在JDK> = 7中,可以使用该X模式来解析偏移量:

Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.US).parse(iso8601Date);

但这不会在日历中设置时区(它仍将使用JVM的默认值)。因此,“更好”的方法是从输入中去除偏移量并分别处理它:

Calendar calendar = GregorianCalendar.getInstance();
String iso8601Date = "2017-04-04T09:00:00-08:00";
// get the offset (-08:00)
String offset = iso8601Date.substring(19);
TimeZone tz = TimeZone.getTimeZone("GMT" + offset);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
// set the offset in the formatter
sdf.setTimeZone(tz);
// parse just date and time (without the offset)
Date date = sdf.parse(iso8601Date.substring(0, 19));
// set the offset in the calendar
calendar.setTimeZone(tz);
calendar.setTime(date);

这样,日历将-08:00设置偏移量。正如@BasilBourque的回答已经说的那样,它-08:00是一个偏移量,而不是时区(TimeZone该类将偏移量视为时区一样对待偏移量,这是一种解决方法/错误的设计选择)。

老班(DateCalendarSimpleDateFormat)有很多问题和设计问题,他们正在被新的API取代。

在Android中,您可以使用ThreeTen Backport,这是Java
8的新日期/时间类的绝佳反向端口。您还需要ThreeTenABP使其起作用(更多有关如何在此处使用的信息)。

@BasilBourque的答案已经告诉您有关的信息OffsetDateTime。但是要转换为a
Calendar,您可以使用a
org.threeten.bp.ZonedDateTime并使用以下org.threeten.bp.DateTimeUtils类将其转换:

String iso8601Date = "2017-04-04T09:00:00-08:00";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601Date);
Calendar cal = DateTimeUtils.toGregorianCalendar(zdt);

日历将已经设置了-08:00偏移量。

如果要从偏移量获取时区,恐怕不是那么简单。多个时区可以使用相同的offset,因此您无法确定要使用哪个时区(最好的方法是获取可能的候选者列表)。

java.util.Date

只是有关的更详细的说明java.util.Date。该链接对此有很多解释,所以我真的建议您阅读它。

如上所述,a Date没有时区信息。它只是让纪元以来的毫秒数(是1970-01-01T00:00Z年1月1 日
1970年,在午夜UTC
)。

这个值在世界各地都一样。示例:目前,我正在编写此代码,当前时间的Millis值为1504632865935。对于世界上任何人,无论我使用的是哪个时区,在与我相同的时间获取当前时间的人,此数字都是相同的。

与该毫秒值相对应的 当地日期和时间 有所不同。在UTC,它对应于2017-09-05T17:34:25.935Z在纽约,日期是相同的(9月5 日
2017年),但时间是不同的(13:34),并在东京为九月 6 日 2017年在上午2时34分。

尽管Date对象是相同的(因为对象的Millis值适用1504632865935于每个人),但是 相应的 日期和时间会根据使用的时区而变化。

人们倾向于认为a
Date具有时区,因为在打印时(通过System.out.println或通过日志记录)或在调试器中进行检查时,它隐式使用该toString()方法,并将日期转换为JVM的默认时区(并且还会打印时区名称)
)。这样给人的印象是,a Date设置了格式和时区,但没有。



 类似资料:
  • 在我的数据集中,我有一个变量< code>duration,其中有200万行< code>ISO 8601格式的数据。示例格式:< code>PT21S或< code>PT5M29S,< code>PT1M16S 我在将此字符串变量转换为时间时遇到问题。目前我正在使用查询: 通过该查询,可以区分持续时间是小时、分钟还是秒。我想将字符串持续时间转换为时间。我用过Regex和cast,但没有成功。当我

  • 我有一个格式为“Mon May 31 2021 23:59:59 GMT 0000(协调世界时)”的日期,但需要将其转换为本地时区并解析为日期时间格式。我该怎么做?

  • 假设我有一个有2列的表:name和number。Name是一个字符串,可以有一个或多个单词,我想要一个新的查询选择名称编号,但在Name列,它只有最长的词,原来的表。 SQL中是否有一个函数只从字符串中提取最长的单词?

  • 我是Android开发新手,我正在尝试以下代码: 我明白了: 隐式使用默认语言环境是常见的错误源: 使用字符串。格式(区域设置,…)相反 我不知道如何修改代码以实现建议。

  • 按照官方文件http://developer.android.com/guide/topics/ui/controls/pickers.html#DatePicker. 我只使用了相同的代码,只在onDateSet方法中添加了结果格式: 对于测试,调用者活动只是显示并在用户触摸小部件时调用选择器: 显示日期选择器,在将结果格式化为字符串的位置设置断点后,我可以看到它是有效的。 但是,我不知道如何将