如何从ISO 8601格式字符串中选择时区到日历实例中 [英] How to Pick Timezone from ISO 8601 format String into a Calendar instace
问题描述
作为输入,我有一个字符串,它是ISO 8601中表示日期的字符串。例如:
As an input I have a string which is a String in ISO 8601 to represent date. For example:
2017-04-04T09:00:00-08:00
"2017-04-04T09:00:00-08:00"
String
的最后一部分, - 08:00表示TimeZone Offset 。我将此字符串转换为日历
实例,如下所示:
The last part of String
, which is "-08:00" denotes TimeZone Offset. I convert this string into a Calendar
instance as shown below:
Calendar calendar = GregorianCalendar.getInstance();
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(iso8601Date);
calendar.setTime(date);
iso8601Date是2017-04-04T09:00:00-08:00
但是这不会选择时区,如果我从 Calendar
实例获得时区,它会提供当前设置笔记本电脑的实例,并没有从ISO 8601字符串中获取时间戳。我通过日历实例检查时区为:
But this does not pick timezone and if I get timezone from Calendar
instance, it gives currently set instance of the laptop and does not pick up timestamp from ISO 8601 String. I check for timezone via calendar instance as:
calendar.getTimeZone().getDisplayName()
有人可以在日历
实例中显示如何选择时区吗?
Can someone show how to pick timezone also in the Calendar
instance?
推荐答案
当您创建日历
时,它将采用JVM的默认时区。当你将 String
解析为 Date
时,它只设置一个值:自纪元以来的毫秒数( 1970-01-01T00:00Z
)。 A 日期
没有任何时区信息,只有这个毫秒值。因此,您需要在日历中设置时区。
When you create a Calendar
, it takes the JVM's default timezone. And when you parse a String
to a Date
, it just sets one value: the number of milliseconds since epoch (1970-01-01T00:00Z
). A Date
doesn't have any timezone information, just this milliseconds value. So you need to set the timezone in the calendar.
在格式化程序中,您将 Z
视为字面值,因为它在引号内('Z'
)。这会忽略偏移量并在JVM默认时区中获取日期(如果相应的偏移量不是-08:00,则会有不同的值)。
In your formatter, you're treating Z
as a literal, because it's inside quotes ('Z'
). This ignores the offset and gets the date in the JVM default timezone (which will have a different value if the corresponding offset is not -08:00).
在JDK中> = 7,您可以使用 X
模式来解析偏移量:
In JDK >= 7, you can use the X
pattern to parse the offset:
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.US).parse(iso8601Date);
但是这不会在日历中设置时区(它仍将使用JVM的默认值)。因此,更好的方法是从输入中去除偏移并单独处理:
But this doesn't set the timezone in the calendar (it will still use the JVM's default). So, a "better" way is to strip the offset from the input and handle it separately:
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
类对待偏移量就像它们是时区,这是一种解决方法/糟糕的设计选择。)
With this, the calendar will have the offset -08:00
set. As @BasilBourque's answer already said, -08:00
is an offset, not a timezone (the TimeZone
class treats offsets just like they were timezones, which is a workaround/bad design choice).
旧班级(日期
,日历
和 SimpleDateFormat
)许多问题和设计问题,它们将被新的API取代。
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
在Android中你可以使用 ThreeTen Backport ,是Java 8新日期/时间类的绝佳后端。您还需要 ThreeTenABP 才能使其正常运行(更多关于如何使用它在其他地方。
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. You'll also need the ThreeTenABP to make it work (more on how to use it here).
@ BasilBourque的答案已经告诉你 OffsetDateTime
。但要转换为日历
,您可以使用 org.threeten.bp.ZonedDateTime
并使用<转换它code> org.threeten.bp.DateTimeUtils class:
@BasilBourque's answer already tells you about OffsetDateTime
. But to convert to a Calendar
, you can use a org.threeten.bp.ZonedDateTime
and convert it using the org.threeten.bp.DateTimeUtils
class:
String iso8601Date = "2017-04-04T09:00:00-08:00";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601Date);
Calendar cal = DateTimeUtils.toGregorianCalendar(zdt);
日历将设置为 -08:00
偏移。
如果你想从偏移量中获取时区,恐怕不是那样的简单。 多个时区可以使用相同的偏移量,因此您无法确定哪个时区使用(你可以做的最好的事情是获得可能的候选人名单)。
If you want to get the timezone from the offset, I'm afraid it's not that simple. More than one timezone can use the same offset, so you can't know for sure which timezone to use (the best you can do is to get a list of possible candidates).
关于 java.util.Date
的更详细说明。 此链接解释了很多相关信息,所以我真的建议你阅读它。
Just a more detailed note about java.util.Date
. This link explains a lot about it, so I really recommend you to read it.
如上所述,日期
没有时区信息。它只保留了自纪元以来的毫秒数(这是 1970-01-01T00:00Z
,或 1月1日 st 1970年午夜在UTC )。
As already said above, a Date
has no timezone information. It just keeps the number of milliseconds since epoch (which is 1970-01-01T00:00Z
, or January 1st 1970 at midnight in UTC).
这个值在世界各地都是一样的。示例:在我写这篇文章时,当前时间的millis值为 1504632865935
。对于世界上任何人来说,无论他们使用的是什么时区,这个数字都是相同的。
This value is the same everywhere in the world. Example: at the moment I'm writing this, the millis value for the current time is 1504632865935
. This number is the same for anyone in the world who gets the current time at the same instant I did, regardless of what timezone they're using.
有什么不同是与此millis值对应的本地日期和时间。在UTC中,它对应于 2017-09-05T17:34:25.935Z
,在纽约,日期是相同的(9月5日 th 2017)但时间不同(13:34),东京是9月 6 th 2017年02:34 AM。
What is different is the local date and time that corresponds to this millis value. In UTC, it corresponds to 2017-09-05T17:34:25.935Z
, in New York, the date is the same (September 5th 2017) but the time is different (13:34), and in Tokyo is September 6th 2017 at 02:34 AM.
虽然 Date
对象是相同的(因为每个人的millis值为 1504632865935
), 相应的日期和时间会根据使用的时区发生变化。
Although the Date
object is the same (because its millis value is 1504632865935
for everyone), the corresponding date and time changes according to the timezone used.
人们倾向于认为日期
有一个时区,因为在打印它时(使用 System.out.println
或通过loggging)或在调试器中检查时,它隐含使用 toString()
方法,这会将日期转换为JVM的默认时区(并且还会打印区域名称)。这给人的印象是 Date
有一个格式和时区设置,但它没有。
People tend to think that a Date
has a timezone because when printing it (with System.out.println
or by loggging) or when inspecting in a debugger, it implicity uses the toString()
method, and this converts the date to the JVM's default timezone (and it also prints the zone name). This gives the impression that a Date
has a format and a timezone set to it, but it doesn't.
这篇关于如何从ISO 8601格式字符串中选择时区到日历实例中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!