Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a ldap date

Tags:

java

ldap

I'm exporting users from an ldap programmatically. Therefor I'm retrieving the users from ldap. One of the attributes is whenCreated.

One of the values I have to convert is: 20090813145607.0Z Directly splitting it up I get the following format: yyyyMMddHHmmss+.0Z. The problem is that the application is running in CET timezone and the time stored is UTC which is probably indicated by the .0Z . It is 14:56 UTC and the local representation is 16:56. For summer time it seems to be 2 hours and for winter time 1 hour.

I checked the SimpleDateFormat and there is a placeholder for the timezone, however its a different format.

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.parse("20090813145607.0Z");

Will show the wrong date as it ignores the dates time zone.

Is there a way to convert it directly?

like image 410
Udo Held Avatar asked Mar 21 '12 14:03

Udo Held


People also ask

What is LDAP timestamp?

The timestamp is the number of 100-nanosecond intervals (1 nanosecond = one billionth of a second) since Jan 1, 1601 UTC.


2 Answers

ISO 8601

As a couple of other Answers mentioned, the date-time format in question is defined by RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules. See section 3.3.13, Generalized Time.

That section explains this LDAP format is a restricted version of one of the date-time formats defined by ISO 8601. This style using a minimum of separators is known as “basic” in ISO 8601.

In these formats, the Z on the end is short for Zulu and means UTC (basically same as GMT).

The decimal point and digit at the end represents a fraction of a second. Note that a comma is possible instead of the dot (period) in both RFC 4517 and ISO 8601. The comma is actually recommended over the dot in ISO 8601. The RFC 4517 spec allows for only a single digit fraction (some tenths of a fraction) or no dot/comma & digit at all. Note that in contrast: (a) ISO 8601 allows for any number of fractional digits, and (b) java.time objects have nanosecond resolution for up to nine digits of fractional second.

java.time

The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.

Now in maintenance mode, the Joda-Time project also advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.

Parsing

Define a formatting pattern to fit RFC 4517. Study the DateTimeFormatter class for the pattern coding. This should work: uuuuMMddHHmmss[,S][.S]X. The square brackets mean optional. We accommodate either a dot or comma. Note the singular digit for fraction of second. The X on the end allows for either a Z or an offset-from-UTC such as -08 or -0830 or -08:30 or -083015 or -08:30:15.

String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
Instant instant = odt.toInstant ();

Dump to console.

System.out.println ( "input: " + input + " | odt: " + odt + " | instant: " + instant );

input: 20090813145607.0Z | odt: 2009-08-13T14:56:07Z | instant: 2009-08-13T14:56:07Z

Of course you should also be coding a check for java.time.format.DateTimeParseException in case of unexpected input.

like image 142
Basil Bourque Avatar answered Sep 23 '22 09:09

Basil Bourque


Checking the RFC mentioned above it seems like using UTC is the recommended default behavior for ldap dates. Therefor I converted it directly:

public Date parseLdapDate(String ldapDate){
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
        return sdf.parse(ldapDate);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}
like image 42
Udo Held Avatar answered Sep 22 '22 09:09

Udo Held