wrends
wrends copied to clipboard
GeneralizedTime incorrectly handles years greater than 9999
Summary
The GeneralizedTime
component inconsistently formats / parses the date when the year of the date is greater than 9999. During serialization, the year is serialized as is (e.g. 292278994
), but the parsing process expects the year to be in YYYY
format.
Steps To Reproduce ("Repro Steps")
@Test
public void testGeneralizedTimeMaxYear() {
String serialized = GeneralizedTime.valueOf(Long.MAX_VALUE).toString();
GeneralizedTime parsed = GeneralizedTime.valueOf(serialized);
}
The preceding test ends with the following exception:
org.forgerock.i18n.LocalizedIllegalArgumentException: The provided value "2922789940817071255.807Z" is not a valid generalized time value because "78" is not a valid month specification
at org.forgerock.opendj.ldap.GeneralizedTime.valueOf(GeneralizedTime.java:221)
Expected Result (Behavior You Expected to See)
The behaviour of the GeneralizedTime
component will be consistent if the year is greater than 9999.
RFC 4517 is very clear on this -- a year is represented exactly by four digits. Anything else is invalid. So there are only a few approaches to this issue:
- either the incorrect timestamp is rejected with exception when
GeneralizedTime
is first created; - or the exception is thrown when creating invalid string representation in
#toString()
; - or the date is silently capped at max allowed date in
#toString()
The right thing seems to be to throw exception, however that would be a major breaking change and that should probably at least increase minor release version.
I have few additional remarks after considering alternatives from my previous comment:
- Checking for the max allowed value is not that simple if we want to take TZ into account (e.g.
99991231Z
vs99991231-10
). This needs to be investigated a bit more. - As
GeneralizedTime
is basically immutable we might want to check the value during construction. However we should be aware of potential performance cost. - If the value is truly invalid from the LDAP point of view, I think we can add that check and don't worry about backwards compatibility.
Sample code to illustrate the first point:
jshell> import java.time.*;
jshell> var date = ZonedDateTime.of(9999, 12, 31, 23, 59, 59, 0, ZoneId.of("UTC-10"));
date ==> 9999-12-31T23:59:59-10:00[UTC-10:00]
jshell> date.withZoneSameInstant(ZoneOffset.UTC);
$3 ==> +10000-01-01T09:59:59Z