我们的数据存储区中的记录有效且有效。到期时间。此信息使用Instant
的字符串表示形式存储。
有些记录永远不会过期。但由于到期日的值是强制性的,我们决定存储Instant.MAX
的字符串表示。
到目前为止一切顺利。我们有搜索用例来返回在输入时间范围[s,e]
内有效的所有记录。我们查询数据存储区并返回满足条件[Si, Ei]
的所有此类记录Si < s && e < Ei
。请注意,此处将比较字符串表示。
现在问题是+
被添加到Instant.MAX
的字符串表示之前。从e < Ei
开始,条件ASCII('+') < ASCII(digit)
失败。
我已经编写了一段代码,知道second
之后+
开始变为前导:
Long e = Instant.now().getEpochSecond()*1000;
for (int i = 0; i < 5; i++) {
System.out.println(e + "->" + Instant.ofEpochMilli(e));
e *= 10;
}
打印:
1471925168000->2016-08-23T04:06:08Z
14719251680000->2436-06-07T17:01:20Z
147192516800000->6634-05-07T02:13:20Z
1471925168000000->+48613-06-14T22:13:20Z
14719251680000000->+468404-07-08T06:13:20Z
我可以选择在保留数据存储区之前截断+
。我对为什么会发生这种情况更感兴趣,我们如何明确地避免它呢?
答案 0 :(得分:10)
你的问题'为什么?'的答案隐藏在DateTimeFormatterBuilder.InstantPrinterParser.format()
的实现中(为了简洁,我遗漏了不相关的代码):
// use INSTANT_SECONDS, thus this code is not bound by Instant.MAX
Long inSec = context.getValue(INSTANT_SECONDS);
if (inSec >= -SECONDS_0000_TO_1970) {
// current era
long zeroSecs = inSec - SECONDS_PER_10000_YEARS + SECONDS_0000_TO_1970;
long hi = Math.floorDiv(zeroSecs, SECONDS_PER_10000_YEARS) + 1;
long lo = Math.floorMod(zeroSecs, SECONDS_PER_10000_YEARS);
LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, 0, ZoneOffset.UTC);
if (hi > 0) {
buf.append('+').append(hi);
}
buf.append(ldt);
}
如您所见,它检查10000年期间的边界,如果该值超过其中至少一个,则会增加+
以及此类期间的数量。
因此,为了防止此类行为,请将最大日期保留在纪元范围内,请勿使用Instant.MAX
。
答案 1 :(得分:2)
如果您希望能够支持字符串值的排序,则需要确保永远不会超过0000
到9999
的年份范围。
这意味着将Instant.MAX
替换为Instant.parse("9999-12-31T23:59:59Z")
,这也是大多数RDBMS可以处理的最长日期。
要跳过解析步骤,请使用Instant.ofEpochSecond(253402300799L)
。
然而,不是设置&#34; max&#34;开放日期范围的值,使用空值,即是否&#34; max&#34;值。
您将Si < s && e < Ei
条件更改为:
Si < s && (Ei == null || e < Ei)
此外,您可能在这种情况下错过=
。在Java中,范围通常是低级别的,高级别的(例如,请参阅substring()
,subList()
等),因此请使用:
Si <= s && (Ei == null || e < Ei)
答案 2 :(得分:2)
大多数日期格式化程序的默认行为是,如果长度超过4位,则将加号添加到年份。这适用于解析和格式化。例如,请参阅Year.parse()
和年部分here。该格式几乎已融入DateTimeFormatter.ISO_INSTANT
(请参阅@ AndrewLygin&#39;答案),因此如果您想要更改它,则必须将您的即时转换为ZonedDateTime
(因为瞬间没有自然字段)并使用自定义格式化程序:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NORMAL)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral('T')
.append(DateTimeFormatter.ISO_OFFSET_TIME)
.toFormatter();
String instant = formatter
.format(Instant.ofEpochMilli(e).atOffset(ZoneOffset.UTC));
此处的密钥为SignStyle.NORMAL
,而不是默认SignStyle.EXCEEDS_PAD
,如果年份超过4位数填充,则会+
。{/ p>