1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > java zoneid 中国_关于时区:Java 8 Time API – ZonedDateTime – 在解析时指定默认的ZoneId...

java zoneid 中国_关于时区:Java 8 Time API – ZonedDateTime – 在解析时指定默认的ZoneId...

时间:2022-03-28 21:29:39

相关推荐

java zoneid 中国_关于时区:Java 8 Time API – ZonedDateTime – 在解析时指定默认的ZoneId...

我正在尝试编写一个泛型方法来返回ZonedDateTime给定日期为String及其格式。

如果String未在日期String中指定,我们如何使ZonedDateTime使用默认ZoneId?

它可以用java.util.Calendar完成,但我想使用Java 8时间API。

这里的问题是使用固定的时区。 我将格式指定为参数。 日期及其格式都是String参数。 更通用。

代码和输出如下:

public class DateUtil {

/** Convert a given String to ZonedDateTime. Use default Zone in string does not have zone. */

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {

//use java.time from java 8

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

ZonedDateTime zonedDateTime = ZonedDateTime.parse(date, formatter);

return zonedDateTime;

}

public static void main(String args[]) {

DateUtil dateUtil = new DateUtil();

System.out.println(dateUtil.parseToZonedDateTime("-09-14 15:00:00+0530","yyyy-MM-dd HH:mm:ssZ"));

System.out.println(dateUtil.parseToZonedDateTime("-09-14 15:00:00","yyyy-MM-dd HH:mm:ss"));

}

}

产量

-09-14T15:00+05:30

Exception in thread"main" java.time.format.DateTimeParseException: Text '-09-14 15:00:00' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to -09-14T15:00 of type java.time.format.Parsed

at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)

at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)

at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)

at com.nam.sfmerchstorefhs.util.DateUtil.parseToZonedDateTime(DateUtil.java:81)

at com.nam.sfmerchstorefhs.util.DateUtil.main(DateUtil.java:97)

Caused by: java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to -09-14T15:00 of type java.time.format.Parsed

at java.time.ZonedDateTime.from(ZonedDateTime.java:565)

at java.time.format.Parsed.query(Parsed.java:226)

at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)

... 3 more

Caused by: java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor: {},ISO resolved to -09-14T15:00 of type java.time.format.Parsed

at java.time.ZoneId.from(ZoneId.java:466)

at java.time.ZonedDateTime.from(ZonedDateTime.java:553)

... 5 more

如何使用默认区域解析ZonedDateTime可能重复?

谢谢各位的回应。 很有帮助。 不幸的是,我只能接受一个答案。

ZonedDateTime需要构建时区或偏移量,第二个输入不需要它。 (它只包含日期和时间)。

因此,您需要检查是否可以构建ZonedDateTime,如果不是,则必须为其选择任意区域(因为输入没有指示正在使用的时区,您必须选择一个使用)。

一种替代方法是首先尝试创建ZonedDateTime,如果不可能,则创建LocalDateTime并将其转换为时区:

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {

// use java.time from java 8

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

ZonedDateTime zonedDateTime = null;

try {

zonedDateTime = ZonedDateTime.parse(date, formatter);

} catch (DateTimeException e) {

// couldn't parse to a ZoneDateTime, try LocalDateTime

LocalDateTime dt = LocalDateTime.parse(date, formatter);

// convert to a timezone

zonedDateTime = dt.atZone(ZoneId.systemDefault());

}

return zonedDateTime;

}

在上面的代码中,我使用的是ZoneId.systemDefault(),它获取了JVM默认时区,但是即使在运行时也可以在不事先通知的情况下进行更改,因此最好始终明确指出您正在使用的是哪一个。

API使用IANA时区名称(始终采用Region/City格式,如America/Sao_Paulo或Europe/Berlin)。

避免使用3个字母的缩写(如CST或PST),因为它们不明确且不标准。

您可以通过调用ZoneId.getAvailableZoneIds()获取可用时区列表(并选择最适合您系统的时区)。

如果要使用特定时区,只需使用ZoneId.of("America/New_York")(或ZoneId.getAvailableZoneIds()返回的任何其他有效名称,纽约只是一个示例)而不是ZoneId.systemDefault()。

另一种方法是使用parseBest()方法,尝试创建一个合适的日期对象(使用TemporalQuery的列表),直到它创建所需的类型:

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

// try to create a ZonedDateTime, if it fails, try LocalDateTime

TemporalAccessor parsed = formatter.parseBest(date, ZonedDateTime::from, LocalDateTime::from);

// if it's a ZonedDateTime, return it

if (parsed instanceof ZonedDateTime) {

return (ZonedDateTime) parsed;

}

if (parsed instanceof LocalDateTime) {

// convert LocalDateTime to JVM default timezone

LocalDateTime dt = (LocalDateTime) parsed;

return dt.atZone(ZoneId.systemDefault());

}

// if it can't be parsed, return null or throw exception?

return null;

}

在这种情况下,我只使用ZonedDateTime::from和LocalDateTime::from,因此格式化程序将尝试首先创建ZonedDateTime,如果不可能,则尝试创建LocalDateTime。

然后我检查返回的类型是什么,并相应地执行操作。

您可以添加所需的任何类型(所有主要类型,例如LocalDate,LocalTime,OffsetDateTime等),使用from方法与parseBest一起使用 - 您还可以创建自己的自定义TemporalQuery如果你愿意,但我认为内置方法足以满足这种情况)。

夏令时

使用atZone()方法将LocalDateTime转换为ZonedDateTime时,有一些关于夏令时(DST)的棘手案例。

我将使用我所居住的时区(America/Sao_Paulo)作为示例,但这可能发生在使用DST的任何时区。

在S?o Paulo,DST于10月16日开始:在午夜,时钟从午夜向上移动1小时到凌晨1点(偏移从-03:00变为-02:00)。因此,在这个时区中,00:00到00:59之间的所有当地时间都不存在(你也可以认为时钟从23:59:59.999999999直接变为01:00)。如果我在此间隔中创建本地日期,则会将其调整为下一个有效时刻:

ZoneId zone = ZoneId.of("America/Sao_Paulo");

// October 16th at midnight, DST started in Sao Paulo

LocalDateTime d = LocalDateTime.of(, 10, 16, 0, 0, 0, 0);

ZonedDateTime z = d.atZone(zone);

System.out.println(z);// adjusted to -10-15T01:00-02:00[America/Sao_Paulo]

DST结束时:2月19日午夜时钟,时钟从18点的午夜到晚上23点向后移动1小时(偏移量从-02:00变为-03:00)。所以从23:00到23:59的所有当地时间都存在两次(在两个偏移中:-03:00和-02:00),你必须决定你想要哪一个。

默认情况下,它使用DST结束前的偏移量,但您可以使用withLaterOffsetAtOverlap()方法在DST结束后获取偏移量:

// February 19th at midnight, DST ends in Sao Paulo

// local times from 23:00 to 23:59 at 18th exist twice

LocalDateTime d = LocalDateTime.of(, 2, 18, 23, 0, 0, 0);

// by default, it gets the offset before DST ends

ZonedDateTime beforeDST = d.atZone(zone);

System.out.println(beforeDST); // before DST end: -02-17T23:00-02:00[America/Sao_Paulo]

// get the offset after DST ends

ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();

System.out.println(afterDST); // after DST end: -02-17T23:00-03:00[America/Sao_Paulo]

请注意,DST结束前后的日期具有不同的偏移量(-02:00和-03:00)。如果您正在使用具有DST的时区,请记住这些角落情况可能会发生。

根据Java 8 ZonedDateTime实现,您无法在ZonedDateTime中解析没有区域的日期。

为了满足给定的问题,你必须把try catch放在它将考虑默认时区的任何异常的情况下。

请找到修改后的程序如下:

public class DateUtil {

/** Convert a given String to ZonedDateTime. Use default Zone in string does not have zone. */

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {

//use java.time from java 8

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

ZonedDateTime zonedDateTime = null;

try {

zonedDateTime = ZonedDateTime.parse(date, formatter);

} catch (DateTimeException e) {

// If date doesn't contains Zone then parse with LocalDateTime

LocalDateTime localDateTime = LocalDateTime.parse(date, formatter);

zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());

}

return zonedDateTime;

}

public static void main(String args[]) {

DateUtil dateUtil = new DateUtil();

System.out.println(dateUtil.parseToZonedDateTime("-09-14 15:00:00+0530","yyyy-MM-dd HH:mm:ssZ"));

System.out.println(dateUtil.parseToZonedDateTime("-09-14 15:00:00","yyyy-MM-dd HH:mm:ss"));

}

}

有关即将推出的Java功能的更多详细信息,请参考/java-8-date-time-intro

java.time库中没有默认设置,这是一件好事 - 你所看到的就是你得到的东西,句号。

我建议如果你的日期字符串不包含Zone - 它是LocalDateTime,并且不能是ZonedDateTime,这就是你得到的异常的含义(即使由于过于灵活而导致措辞受到影响代码结构)。

我的主要建议是,如果您知道该模式没有区域信息,则解析为本地日期时间。

但是,如果你真的必须,这是另一种方法来做你想要的(一种不使用异常来控制流的替代解决方案):

TemporalAccessor parsed = f.parse(string);

if (parsed.query(TemporalQueries.zone()) == null) {

parsed = f.withZone(ZoneId.systemDefault()).parse(string);

}

return ZonedDateTime.from(parsed);

这里我们使用中间解析结果来确定字符串是否包含区域信息,如果没有,我们再次解析(使用相同的字符串,但不同的打印机解析器),这次它将包含一个区域。

或者,您可以创建此类,这将使您免于解析第二次,并且应该允许您解析分区日期时间,假设所有其他字段都在那里:

class TemporalWithZone implements TemporalAccessor {

private final ZoneId zone;

private final TemporalAccessor delegate;

public TemporalWithZone(TemporalAccessor delegate, ZoneId zone) {

this.delegate = requireNonNull(delegate);

this.zone = requireNonNull(zone);

}

public R query(TemporalQuery query) {

if (query == TemporalQueries.zone() || query == TemporalQueries.zoneId()) {

return (R) zone;

}

return delegate.query(query);

}

}

如果没有OFFSET_SECOND,您只需在DateTimeFormatterBuilder中添加默认值:

编辑:要获得系统的默认值ZoneOffset,您必须将ZoneRules应用于当前的Instant。结果如下:

class DateUtil {

public ZonedDateTime parseToZonedDateTime(String date, String dateFormat) {

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);

LocalDateTime localDateTime = LocalDateTime.parse(date, formatter);

ZoneOffset defaultOffset = ZoneId.systemDefault().getRules().getOffset(localDateTime);

DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()

.append(formatter)

.parseDefaulting(ChronoField.OFFSET_SECONDS, defaultOffset.getTotalSeconds())

.toFormatter();

return ZonedDateTime.parse(date, dateTimeFormatter);

}

}

输出:

-09-14T15:00+05:30

-09-14T15:00+02:00

关于夏令时,使用Instant.now()的偏移量有一个棘手的情况。 示例:在我的JVM中,默认区域为America/Sao_Paulo。 冬季偏移为-03:00,夏令时(10月开始)偏移为-02:00。 如果我用params "-11-14 15:00:00","yyyy-MM-dd HH:mm:ss"调用你的方法,它将在11月11日15:00和当前偏移量(-03:00)得到一个日期,但正确的(IMO)应该是当地日期有效的偏移量 对应于输入(在这种情况下,在十一月,当它已经是DST时,因此偏移应该是-02:00)。

然后查看API并选择正确的方法:ZoneRules::getOffset vs. ZoneRules::getStandardOffset。 OP并非那么具体。

可以使用DateTimeFormatter中的withZone方法指定ZoneId:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat).withZone("+0530");

只是从我拥有的项目中复制此解决方案:

formatter = DateTimeFormatter.ofPattern(dateFormat).withZone(ZONE_UTC);

编译格式化程序后,可以调用withZone(ZoneId)创建具有设置时区的新格式化程序。

这将忽略第一个输入中的偏移+0530

然后尝试OffsetDateTime,因为Zoned需要知道区域规则。

好吧,OP要求ZonedDateTime和默认时区(不是UTC)。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。