java gmt-0_java – GMT0的区域解析

我将GMT0作为系统中的默认时区,当我将其序列化并在此之后反序列化时会导致问题.

System.setProperty("user.timezone","GMT0");

DateTimeFormatter zoneFormatter = new DateTimeFormatterBuilder()

.appendZoneOrOffsetId()

.toFormatter();

String formatted = zoneFormatter.format(ZonedDateTime.now());

System.out.println(formatted);

System.out.println(zoneFormatter.parse(formatted));

第一个System.out.println打印GMT0,而第二个抛出以下问题.

Exception in thread "main" java.time.format.DateTimeParseException: Text 'GMT0' could not be parsed, unparsed text found at index 3

at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)

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

这是一种预期的行为吗?有没有办法以安全的方式做到这一点?

最佳答案 正如您在评论中注意到的那样,这是一个

bug in JDK 8,仅在版本> = 9中修复.

如果您正在使用JDK 8而不能/不会升级它,那么就有一种解决方法.您可以将“GMT”部分视为文字(文本“GMT”本身),并使用相应的ChronoField将0视为偏移秒:

DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()

// text "GMT"

.appendLiteral("GMT")

// offset seconds

.appendValue(ChronoField.OFFSET_SECONDS)

.toFormatter();

System.out.println(zoneParser.parse("GMT0"));

请记住,这仅适用于零偏移.对于任何其他值(例如“GMT2”或“GMT-2”),这将不起作用,因为它将值“2”和“-2”视为秒,但它们实际上意味着“小时”.

如果您需要以此格式解析所有偏移值(“GMTn”)

那么,JDK 8也是can’t handle one-digit offsets,它总是需要一个信号,或者 – .因此“GMT2”和“GMT-2”不适用于当前的API.

但是,有一个更难的选择:创建自己的TemporalField,表示“偏移小时”.有关如何操作的所有细节都在documentation中,我不确定是否所有方法都正确实现 – 我只是确定isSupportedBy,getFrom和adjustInto,其他人可能需要一些改进/调整:

public class OffsetHours implements TemporalField {

@Override

public TemporalUnit getBaseUnit() {

return ChronoUnit.HOURS;

}

@Override

public TemporalUnit getRangeUnit() {

return ChronoUnit.FOREVER;

}

@Override

public ValueRange range() {

return ValueRange.of(ZoneOffset.MIN.getTotalSeconds() / 3600, ZoneOffset.MAX.getTotalSeconds() / 3600);

}

@Override

public boolean isDateBased() {

return false;

}

@Override

public boolean isTimeBased() {

return true;

}

@Override

public boolean isSupportedBy(TemporalAccessor temporal) {

return temporal.isSupported(ChronoField.OFFSET_SECONDS);

}

@Override

public ValueRange rangeRefinedBy(TemporalAccessor temporal) {

ValueRange rangeInSecs = temporal.range(ChronoField.OFFSET_SECONDS);

return ValueRange.of(rangeInSecs.getMinimum() / 3600, rangeInSecs.getMaximum() / 3600);

}

@Override

public long getFrom(TemporalAccessor temporal) {

return temporal.getLong(ChronoField.OFFSET_SECONDS) / 3600;

}

@Override

public R adjustInto(R temporal, long newValue) {

return (R) temporal.with(ChronoField.OFFSET_SECONDS, newValue * 3600);

}

}

现在,您创建此字段的实例并在解析器中使用它:

// the new field

OffsetHours offsetHoursField = new OffsetHours();

DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()

// text "GMT"

.appendLiteral("GMT")

// offset hours

.appendValue(offsetHoursField)

.toFormatter();

我还建议创建一个TemporalQuery来将解析后的结果转换为ZoneOffset:

// get hours and create offset from hours value

TemporalQuery getOffsetFromHours = temporal -> {

return ZoneOffset.ofHours((int) temporal.getLong(offsetHoursField));

};

现在你可以解析它:

ZoneOffset offsetZero = zoneParser.parse("GMT0", getOffsetFromHours);

ZoneOffset offsetTwo = zoneParser.parse("GMT2", getOffsetFromHours);

ZoneOffset offsetMinusTwo = zoneParser.parse("GMT-2", getOffsetFromHours);

您可以改进它,使OffsetHours字段成为静态实例(或者可能是枚举),因此您无需一直创建它.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值