让我们说我的设备在澳大利亚(GMT + 10:00)。
我有约会" 2016-04-22T17:45:00 + 02:00"从API获得。
我想在原始时区(GMT + 02:00)很好地展示它。
所以,我使用了SimpleDateFormat和#34; yyyy-MM-dd' HH:mm:ssZZZZZ"。
String date = "2016-04-22T17:45:00+02:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ");
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse(date));
但不幸的是,我找不到一个干净的方式来显示日期:
sdf.format(cal.getTime());
使用设备时区。
所以我解析了#34;手动"提取时区以在日历中设置它的日期。
cal.setTimeZone(TimeZone.getTimeZone("GMT" + date.substring(date.length() - 6, date.length())));
但它似乎没有做到这一点。
我该怎么做?
答案 0 :(得分:4)
在原始时区(GMT + 02:00)。
不,不对。文字GMT+02:00
代表offset-from-UTC,不是时区。时区是偏移量加上用于处理诸如夏令时(DST)等异常的一组规则。
时区的格式为continent/region
,例如America/Montreal
或Asia/Kolkata
。始终使用these proper names。从未在主流媒体中看到3-4个缩写;这些都不是标准化的,也不是唯一的。
与最早版本的Java捆绑在一起的旧日期时间类已被证明设计糟糕,令人困惑且麻烦。避免他们。这些类被非常成功的Joda-Time库及其继承者java.time框架所取代。
java.time框架内置于Java 8 and later。它的大部分功能都被反向移植到Java 6& ThreeTen-Backport项目中的7,并在ThreeTenABP中进一步适应Android。
了解详情Oracle Tutorial。
OffsetDateTime
输入字符串只有UTC的偏移量,而不是完整的time zone。所以我们使用OffsetDateTime
类来表示这种值。
幸运的是,您的输入字符串采用标准ISO 8601格式。在解析/生成日期时间值的文本表示时,java.time类默认使用ISO 8601格式。所以我们可以直接解析而不必费心去定义格式化模式。
String input = "2016-04-22T17:45:00+02:00";
OffsetDateTime odt = OffsetDateTime.parse( input );
调用OffsetDateTime::toString()
生成相同格式的字符串。
String output = odt.toString(); // ISO 8601 formatted string.
Instant
程序员不应该考虑这种本地日期时间。通常,您应该在UTC中进行业务逻辑,数据库存储和数据交换。
在java.time中,Instant
代表UTC时间轴上的一个时刻,分辨率为nanoseconds。我们可以从Instant
中提取OffsetDateTime
。
Instant instant = odt.toInstant();
ZonedDateTime
由于处理诸如夏令时等异常的规则,因此总是更好地使用时区而不仅仅是偏移量。
通过应用ZoneId
获取Instant
,您可以轻松地将OffsetDateTime
或ZonedDateTime
调整为时区。有关更多讨论,请参阅my longer answer和diagram有关在这些类之间转换/调整的一般主题。
ZoneId zoneId_Montreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId_Montreal );
...或...
ZonedDateTime zdt_Montreal = odt.atZoneSameInstant( zoneId_Montreal );
请注意,在应用时区时,您可能会看到时间甚至更改日期。可能需要根据该区域的规则进行调整以适应Daylight Saving Time (DST)或其他问题。
您可以进一步调整到其他时区。你提到澳大利亚。该国家/地区有许多时区,因此您必须选择适合您(或您的用户)的时区,因为它们现在或过去或将来可能有不同的规则。
ZoneId zoneId_Melbourne = ZoneId.of( "Australia/Melbourne" );
ZonedDateTime zdt_Melbourne = zdt_Montreal.withZoneSameInstant( zoneId_Melbourne );
当调用ZonedDateTime::toString
时,您会注意到该方法通过在方括号中附加时区名称来扩展标准ISO 8601格式。
要生成其他格式的字符串,请搜索Stack Overflow以查找java.time.format
和DateTimeFormatter
的主题。 ofLocalized…
方法和Locale
可以自动本地化格式化,包括翻译人类语言日/月名称,遵循元素顺序的文化规范,句点对逗号等等。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:1)
我可以看到Date对象没有关于时区的信息。所以一种方法但不是最好的方法是从日期字符串中解析时区并将其设置为SimpleDateFormat。类似的东西:
String date = "2016-04-22T17:45:00+02:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ");
Calendar cal = Calendar.getInstance();
// tz is +02:00
String tz;
if (date.contains("+"))
tz = date.substring(date.indexOf("+"));
else
tz = date.substring(date.lastIndexOf("-"));
sdf.setTimeZone(TimeZone.getTimeZone("GMT" + tz));
System.out.println(sdf.format(cal.getTime()));