I know nothing about Python, so if you can read VB here is a sample from
another post:
In Active Directory the accountExpires and pwdLastSet attributes store a
FILETIME
which is a 64-bit integer (LargeInteger) value that specifies the number of
100-nanosecond intervals since January 1, 1601 00:00:00 UTC (GMT).
The Net APIs such as NetUserSetInfo and NetUserGetInfo use a structure such
as
USER_INFO_3 which contains a useri3_acct_expires field which stores a
32-bit
unsigned integer value that specifies the number of seconds since January
1, 1970
00:00:00 UTC (GMT).
A DateTime also represents time with a 64 bit integer but specifies the
number of
100-nanosecond intervals since January 1, 1 00:00:00 instead and has an
upper bound
of December 31, 9999 23:59:59. It is possible to encounter
ArgumentOutOfRangeException exceptions when converting the FILETIME value
to a
DateTime value so I suggest having an exception handler to handle this.
The following C# code is an example of how to get and set time values such
as
accountExpires.
using System.DirectoryServices;
using ActiveDs;
// Get the accountExpires property value and cast to a IADsLargeInteger
interface
as the
// underlying ADSI code returns this property as a LargeInteger object.
DirectoryEntry entry = new
DirectoryEntry("LDAP://<domain_or_server>/<distinguished_name>");
PropertyCollection properties = entry.Properties;
IADsLargeInteger largeInteger =
(IADsLargeInteger)properties["accountExpires"].Value;
// Calculate the 64 bit integer (Int64)(long) value from the low and high
32 bit
values.
long filetime = ((long)largeInteger.HighPart << 32) + largeInteger.LowPart;
// Convert to local DateTime. Note that the retrieved value above is in UTC
time
therefore must
// use FromFileTimeUtc.
DateTime dateTime = DateTime.FromFileTimeUtc(filetime);
// Convert a local DateTime to a UTC FILETIME.
dateTime = DateTime.Parse("11/1/03");
filetime = dateTime.ToFileTimeUtc();
// Calculate high and low 32 bit values from 64 bit value.
largeInteger.HighPart = (int)((filetime >> 32) & 0xFFFFFFFF);
largeInteger.LowPart = (int)(filetime & 0xFFFFFFFF);
// Assign updated LargeInteger value to property and commit changes.
properties["accountExpires"].Value = largeInteger;
entry.CommitChanges();
Note that I needed to add a reference to the Active DS Type Library
(Interop.ActiveDs) which provides interoperability with ADSI interfaces
such as
IADsLargeInteger above.
I have also attached a VBScript file that converts a LargeInteger to a Date
value.
It also contains an explanation of the math required to convert from the
FILETIME
64 bit integer time format to the double Date format.
vbscript:
'---------------------------------------------------------------------------
--------
----------------
' LargeIntegerToDate Function
'
' Converts a LargeInteger object representing a FILETIME to a Date data
type.
'
' A FILETIME is a 64-bit integer value that specifies the number of
100-nanosecond
intervals
' since January 1, 1601 (UTC).
'
' A LargeInteger object provides access to the low and high 32 bits of a
64-bit
integer value.
'
' A Date is a 64-bit floating point value that represents the number of
days
since
' December 30, 1899.
'
' Given the low and high 32 bits of a 64-bit value representing the number
of
100-nanosecond
' intervals since January 1, 1601 the following conversions are performed
to
convert this value to
' a value representing the number of days since December 30, 1899 which is
how the
Date data type
' represents time.
'
' The first step is to convert the 64-bit integer value to a 64-bit
floating point
value by
' multiplying the high 32 bits by 4294967296.0 (2^32) and then adding the
low 32
bits. This is
' equivalent to shifting the high 32-bit integer value by 32 bits and then
adding
the low 32-bit
' integer value to form a 64-bit integer value and then converting to a
64-bit
floating point value.
'
' The second step is to perform a unit conversion from 100-nanosecond
intervals to
days. Multiplying
' by 1/10^7 (1E-7) seconds per 100-nanosecond interval converts the value
to
seconds. Multiplying by
' 1/86400 (8.64E-4) days per second converts the value to days. Therefore
multiplying by the
' combined value of 8.64E-11 (8.64E-4 * 1E-7) or dividing by 8.64E11
converts the
value from
' 100-nanosecond intervals to days.
'
' The third and final step is to adjust the epoch by subtracting the
difference
between the epochs
' so that a value of zero (0.0) represents December 30, 1899. As there are
109205
days between
' January 1, 1601 and December 30, 1899, 109205.0 is subtracted from the
value. The
value of 109205
' was arrived at by obtaining the FILETIME value for December 30, 1899
(94353120000000000) and
' converting to days per the unit conversion performed in the second step
above.
'
' Note that the resulting Double data type is converted to a Date data type
by the
CDate() function.
'
' Author: Mark Oluper
' Date: September 6, 2003
'---------------------------------------------------------------------------
--------
----------------
Function LargeIntegerToDate(objTime)
LargeIntegerToDate = CDate(((4294967296.0 * objTime.HighPart +
objTime.LowPart) /
8.64E11) - 109205.0)
End Function
Function LargeIntegerToDouble(objLargeInteger)
LargeIntegerToDouble = 4294967296.0 * objLargeInteger.HighPart +
objLargeInteger.LowPart
End Function
'The following is some example code that uses the conversion function. The
example
is an excerpt for printing
'object properties. Note that overflow must be handled. The example simply
converts
to Double instead for
'display purposes.
'Set objLargeInteger = objPropertyValue.LargeInteger
'Select Case objPropertyEntry.Name
' Case "accountExpires","lastLogon","lastLogonTimestamp","pwdLastSet"
' On Error Resume Next
' vntValue = LargeIntegerToDate(objLargeInteger)
' If Err.Number = 6 Then 'Overflow
' vntValue = LargeIntegerToDouble(objLargeInteger)
' End If
' On Error GoTo 0
' Case Else
' vntValue = LargeIntegerToDouble(objLargeInteger)
'End Select
Jason Tyler
Microsoft Developer Support