1

Can someone please explain the ldatDatetime to date code especially :

(((1970-1601) * 365) - 3 + Math.round((1970-1601)/4) ) * 86400L;

The code available is here under which converts windows nt timeformat(ldap-accountexpires time format) to Java datetime?

long nanoseconds = 132590231990000000l; //Targeted Time
long mills = (nanoseconds/10000000);
long unix = (((1970-1601) * 365) - 3 + Math.round((1970-1601)/4) ) * 86400L;
long timeStamp = mills - unix ;
Date date = new Date(timeStamp*1000L); // *1000 is to convert seconds to milliseconds
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // The format of your date
sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // Give a Timezone reference for formating (see comment at the bottom

String formattedDate = sdf.format(date);
System.out.println(formattedDate);

Is this only one way to do this or other available calculations too?

fatherazrael
  • 5,511
  • 16
  • 71
  • 155
  • I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `Instant`, `ZonedDateTime` and `DateTimeFormatter`, all from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Feb 21 '21 at 12:52
  • The opposite conversion is asked about [in this question: How to convert local time to LDAP timestamp in Java](https://stackoverflow.com/questions/57686752/how-to-convert-local-time-to-ldap-timestamp-in-java). – Ole V.V. Feb 21 '21 at 19:06

1 Answers1

1

An LDAP (or Active Directory) timestamp is the number of 100-nanosecond intervals since Jan 1, 1601 UTC. The number is usually 18 digits like yours.

java.time

I recommend that you use java.time, the modern Java date and time API. for your date and time work. While the following conversion is not shorter than yours, I find it much more readable. Let’s first define some constants both for use in the conversion and for formatting afterward.

private static final Instant LDAP_EPOCH = Instant.parse("1601-01-01T00:00:00Z");
// A unit in LDAP time is 100 nanoseconds
private static final int UNITS_PER_SECOND = 10_000_000;
private static final long NANOS_PER_UNIT = Duration.ofSeconds(1).toNanos() / UNITS_PER_SECOND;

private static final DateTimeFormatter FORMATTER
        = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss zzz", Locale.US);

Now the conversion can go like this:

    long units = 132_590_231_990_000_000L; // Targeted Time
    long seoncds = units / UNITS_PER_SECOND;
    long nanos = units % UNITS_PER_SECOND * NANOS_PER_UNIT;
    Duration sinceLdapEpoch = Duration.ofSeconds(seoncds, nanos);
    ZonedDateTime dateTime = LDAP_EPOCH.plus(sinceLdapEpoch).atZone(ZoneOffset.UTC);
    String formattedDate = dateTime.format(FORMATTER);
    System.out.println(formattedDate);

Output is:

2021-02-28 21:59:59 Z

It’s the same point in time as I get from your code.

Explaining your code

Since LDAP time units are 100 nanoseconds, when your code uses the variable name nanoseconds, it’s wrong. First you divide by 10 000 000 (10 million), which gets you seconds, so the variable name mills is wrong too. The formula you asked about in your title, (((1970-1601) * 365) - 3 + Math.round((1970-1601)/4) ) * 86400L calculates the number of seconds from the LDAP eopch in 1601 to the Unix and Java epoch in 1970. It does this by first calculating the number of days, taking into account that one fourth of the years are leap years, and then multiplying by the number of seconds in a day in UTC, 86 400. I agree with you that it’s very hard to read and understand. Subtracting this number form the seconds you had in the ill-names mills variable gives you the number of seconds since the Java epoch of January 1, 1970 at 00:00 UTC. Finally you multiply by 1000 to convert from seconds to milliseconds since this is what the constructor of the outdated Date class requires.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161