Uploaded image for project: 'Directory Client API'
  1. Directory Client API
  2. DIRAPI-284

DateUtils throws an exception for certain timestamps

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 1.0.0-RC1
    • Fix Version/s: 1.0.0-RC2
    • Labels:
      None
    • Environment:
      All

      Description

      The DateUtils class can't handle certain timestamps.
      The following code

      DateUtils.getDate("9223372036854775807");

      throws an "java.lang.RuntimeException: java.text.ParseException: ERR_04363 Invalid Time too short, expected field 'fraction of second' or 'timezone'."

      The value in microsofts active directory indicates that e.g. an account never expires...

        Activity

        Hide
        elecharny Emmanuel Lecharny added a comment -

        Grrr... Another example of Micro$oft not being able to respect the standars :/

        GeneralizedTime must respect this format :

        GeneralizedTime = century year month day hour
                              [ minute [ second / leap-second ] ]
                              [ fraction ]
                              g-time-zone
        g-time-zone     = %x5A  ; "Z" | g-differential
        g-differential  = ( MINUS / PLUS ) hour [ minute ]
        

        9223372036854775807 is certainly not valid wrt to the ISO8601 spec :/

        I can't wait for M$ to go bankrupt...

        Ok, at this point, teh only solution would be to add a specific check for this stupid number, and accept it.

        PS : Bill Gates and co, you are a bunch of morons !

        Show
        elecharny Emmanuel Lecharny added a comment - Grrr... Another example of Micro$oft not being able to respect the standars :/ GeneralizedTime must respect this format : GeneralizedTime = century year month day hour [ minute [ second / leap-second ] ] [ fraction ] g-time-zone g-time-zone = %x5A ; "Z" | g-differential g-differential = ( MINUS / PLUS ) hour [ minute ] 9223372036854775807 is certainly not valid wrt to the ISO8601 spec :/ I can't wait for M$ to go bankrupt... Ok, at this point, teh only solution would be to add a specific check for this stupid number, and accept it. PS : Bill Gates and co, you are a bunch of morons !
        Hide
        elecharny Emmanuel Lecharny added a comment -

        Here is a workaround :

            /** A Date far in the future, when Micro$oft would have vanished for a long time... */
            private static final Date INFINITE = new Date( 0x7FFFFFFFFFFFFFFFL );
        
            public static Date getDate( String zuluTime ) throws ParseException
            {
                try 
                {
                    return new GeneralizedTime( zuluTime ).calendar.getTime();
                }
                catch ( ParseException pe )
                {
                    // Maybe one of the multiple Micro$oft ineptness to cope with Standards ?
                    if ( "9223372036854775807".equals( zuluTime) )
                    {
                        // This 0x7FFFFFFFFFFFFFFF, never ending date
                        return INFINITE;
                    }
                    else
                    {
                        throw pe;
                    }
                }
            }
        

        That should fix your issue.

        Show
        elecharny Emmanuel Lecharny added a comment - Here is a workaround : /** A Date far in the future, when Micro$oft would have vanished for a long time... */ private static final Date INFINITE = new Date( 0x7FFFFFFFFFFFFFFFL ); public static Date getDate( String zuluTime ) throws ParseException { try { return new GeneralizedTime( zuluTime ).calendar.getTime(); } catch ( ParseException pe ) { // Maybe one of the multiple Micro$oft ineptness to cope with Standards ? if ( "9223372036854775807".equals( zuluTime) ) { // This 0x7FFFFFFFFFFFFFFF, never ending date return INFINITE; } else { throw pe; } } } That should fix your issue.
        Show
        elecharny Emmanuel Lecharny added a comment - Fixed with http://svn.apache.org/viewvc?rev=1758246&view=rev
        Hide
        TeeWeTee Lonzak added a comment - - edited

        Agreed on the *icrosoft comments :-D

        I just realized that there are different timestamps - the one defined in the ISO8601 and there is a 18-digit timestamp (http://www.epochconverter.com/ldap)

        The 18-digit Active Directory timestamps, also named 'Windows NT time format' and 'Win32 FILETIME or SYSTEMTIME'. These are used in Microsoft Active Directory for pwdLastSet, accountExpires, LastLogon, LastLogonTimestamp and LastPwdSet. The timestamp is the number of 100-nanoseconds intervals (1 nanosecond = one billionth of a second) since Jan 1, 1601 UTC. The current LDAP/Win32 FILETIME is: 131169589170000000.

        I need to convert the content of 'accountExpires' so can DateUtils handle the 18-digit timestamp? (Currently only exceptions are thrown)
        Some more info: https://support.microsoft.com/en-us/kb/555936

        Show
        TeeWeTee Lonzak added a comment - - edited Agreed on the *icrosoft comments :-D I just realized that there are different timestamps - the one defined in the ISO8601 and there is a 18-digit timestamp ( http://www.epochconverter.com/ldap ) The 18-digit Active Directory timestamps, also named 'Windows NT time format' and 'Win32 FILETIME or SYSTEMTIME'. These are used in Microsoft Active Directory for pwdLastSet , accountExpires , LastLogon , LastLogonTimestamp and LastPwdSet . The timestamp is the number of 100-nanoseconds intervals (1 nanosecond = one billionth of a second) since Jan 1, 1601 UTC. The current LDAP/Win32 FILETIME is: 131169589170000000. I need to convert the content of 'accountExpires' so can DateUtils handle the 18-digit timestamp? (Currently only exceptions are thrown) Some more info: https://support.microsoft.com/en-us/kb/555936
        Hide
        elecharny Emmanuel Lecharny added a comment -

        The new Dateutils version I just committed will allow you to get a Date from what you pull from a LDAP server, but only when it's infinite.

        At this point, supporting something else would require that the API supports the interval syntax, which it is currently not supporting by the API.
        The only solution for you would be to pull the textual representation of the attribute, and to convert it to a long. We can write a specific DateUtils method that does the job.

        Show
        elecharny Emmanuel Lecharny added a comment - The new Dateutils version I just committed will allow you to get a Date from what you pull from a LDAP server, but only when it's infinite. At this point, supporting something else would require that the API supports the interval syntax, which it is currently not supporting by the API. The only solution for you would be to pull the textual representation of the attribute, and to convert it to a long. We can write a specific DateUtils method that does the job.
        Hide
        TeeWeTee Lonzak added a comment - - edited

        Yeah that convenience method would be nice to have since many people have that problem...
        A rudimentary method could look like this:

        private Date convertMicrosoftFileTimeDate(String adDate){
            long offset = 11644473600000L;  // offset milliseconds from Jan 1, 1601 to Jan 1, 1970
            // convert 100-nanosecond intervals to milliseconds
            long javaTime = (Long.parseLong(adDate) / 10000 - offset);
        	
            return new Date(javaTime);
        }
        

        One last remark: I think the INFINITE (0x7FFFFFFFFFFFFFFF) ... is only used for the 18 digit timestamp and not for the ISO-8601 timestamp. However can't hurt to use it for both...

        Show
        TeeWeTee Lonzak added a comment - - edited Yeah that convenience method would be nice to have since many people have that problem... A rudimentary method could look like this: private Date convertMicrosoftFileTimeDate( String adDate){ long offset = 11644473600000L; // offset milliseconds from Jan 1, 1601 to Jan 1, 1970 // convert 100-nanosecond intervals to milliseconds long javaTime = ( Long .parseLong(adDate) / 10000 - offset); return new Date(javaTime); } One last remark: I think the INFINITE (0x7FFFFFFFFFFFFFFF) ... is only used for the 18 digit timestamp and not for the ISO-8601 timestamp. However can't hurt to use it for both...
        Hide
        elecharny Emmanuel Lecharny added a comment -

        Could you provide a patch that I can apply ? (svn diff would do).

        Thanks !

        Show
        elecharny Emmanuel Lecharny added a comment - Could you provide a patch that I can apply ? (svn diff would do). Thanks !
        Hide
        TeeWeTee Lonzak added a comment -

        — DateUtils.java-revBASE.svn002.tmp.java Di Aug 30 17:27:06 2016
        +++ DateUtils.java Di Aug 30 17:31:50 2016
        @@ -89,0 +90,21 @@ public final class DateUtils
        +
        + /**
        + * Converts the 18-digit Active Directory timestamps, also named 'Windows NT time format' or 'Win32 FILETIME or SYSTEMTIME'.
        + * These are used in Microsoft Active Directory for pwdLastSet, accountExpires, LastLogon, LastLogonTimestamp and LastPwdSet.
        + * The timestamp is the number of 100-nanoseconds intervals (1 nanosecond = one billionth of a second) since Jan 1, 1601 UTC.
        + * <p>
        + *
        + * @param intervalDate 18-digit number. Time in 100-nanoseconds intervals since 1.1.1601
        + * @return Date the converted date
        + */
        + private Date convertIntervalDate(String intervalDate){
        + if ( intervalDate == null )

        { + throw new ParseException( I18n.err( I18n.ERR_04359 ), 0 ); + }

        +
        + long offset = 11644473600000L; // offset milliseconds from Jan 1, 1601 to Jan 1, 1970
        + // convert 100-nanosecond intervals to milliseconds
        + long javaTime = (Long.parseLong(intervalDate) / 10000 - offset);
        +
        + return new Date(javaTime);
        + }

        Show
        TeeWeTee Lonzak added a comment - — DateUtils.java-revBASE.svn002.tmp.java Di Aug 30 17:27:06 2016 +++ DateUtils.java Di Aug 30 17:31:50 2016 @@ -89,0 +90,21 @@ public final class DateUtils + + /** + * Converts the 18-digit Active Directory timestamps, also named 'Windows NT time format' or 'Win32 FILETIME or SYSTEMTIME'. + * These are used in Microsoft Active Directory for pwdLastSet, accountExpires, LastLogon, LastLogonTimestamp and LastPwdSet. + * The timestamp is the number of 100-nanoseconds intervals (1 nanosecond = one billionth of a second) since Jan 1, 1601 UTC. + * <p> + * + * @param intervalDate 18-digit number. Time in 100-nanoseconds intervals since 1.1.1601 + * @return Date the converted date + */ + private Date convertIntervalDate(String intervalDate){ + if ( intervalDate == null ) { + throw new ParseException( I18n.err( I18n.ERR_04359 ), 0 ); + } + + long offset = 11644473600000L; // offset milliseconds from Jan 1, 1601 to Jan 1, 1970 + // convert 100-nanosecond intervals to milliseconds + long javaTime = (Long.parseLong(intervalDate) / 10000 - offset); + + return new Date(javaTime); + }
        Hide
        TeeWeTee Lonzak added a comment -

        Is the patch ok this way?

        Show
        TeeWeTee Lonzak added a comment - Is the patch ok this way?
        Hide
        elecharny Emmanuel Lecharny added a comment -

        Patch applied, after a few fixes :

        • exception added
        • made the method static
        • fixed the Javadoc
        • formated the code

        Thanks !

        Show
        elecharny Emmanuel Lecharny added a comment - Patch applied, after a few fixes : exception added made the method static fixed the Javadoc formated the code Thanks !

          People

          • Assignee:
            Unassigned
            Reporter:
            TeeWeTee Lonzak
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development