I'm working with pulling information out of Active Directory which means I'm dealing with some COMObjects. One of them has a date that is stored as a 64-bit integer (which is actually two 32-bit ints). I've figured out how to change it into a datetime object by using the following code.
def convert_to_datetime(time_object):
# http://docs.activestate.com/activepython/2.6/pywin32/html/com/help/active_directory.html
d = 116444736000000000L #diference between 1601 and 1970
time_int = (((long(time_object.highpart) << 32) + long(time_object.lowpart)) - d)/10000000
return datetime.datetime.fromtimestamp(time_int)
Now with this in place I'm wanting to build a unit test for this. However, I can't seem to figure out how to generate a 64-bit integer from a datetime object. What I am doing currently is I have created a class to replicate a COMObject, which stores its value as two 32-bit integers, and then hard-coded the values for the date and integer that match. However, I would like something that creates these values based on the time of "right now".
Here is what the unit test looks like currently, which passes.
def test_convert_to_datetime_for_64bit_int_returns_value(self):
now_com_time = COMObjectDate(30375774,216380170)
self.assertEqual(datetime.datetime(2014, 6, 3, 14, 0, 13), AD_AG.convert_to_datetime(now_com_time))
class COMObjectDate:
def __init__(self, high, low):
self.highpart = high
self.lowpart = low
Is there a way to generate the two 32-bit values from a datetime object?
Side note: I realize I may be over thinking this some since my unit test does pass but I would prefer to not have hard-coded values so that the tests are stronger and not reliant on values that "just work".
We'll start with a quote from the documentation you linked to:
Time in active directory is stored in a 64 bit integer that keeps track of the number of 100-nanosecond intervals which have passed since January 1, 1601.
So consider the parts as two 32 bit integers (high and low, leading zeroes added):
bin(30375774) =
00000001110011110111111101011110
bin(216380170) =
00001100111001011011001100001010
Join them together (the 'high part' and the 'low part') to get the 64 bit integer:
0000000111001111011111110101111000001100111001011011001100001010
In decimal, that is 130462956137067274
.
To get the number of seconds passed since the 1st of january 1601, we'll have to divide by
10000000
.
130462956137067274 / 10000000
= 13.046.295.613 seconds since the 1st of january 1601
Simple date arithmetic should get you the reverse should you wish to construct the corresponding 32 bit integer parts: start from the number of seconds since the 1st of january 1601, multiply by 10000000 and split the 64 bit integer in it's two 32 bit parts (which can be done easily by bit shifting).
The convert_to_datetime
method implementation should make sense too. The only thing you'll need to now is that the datetime.fromtimestamp()
method expects a POSIX timestamp, which is a float (in this case) that contains the number of seconds elapsed since epoch
(the 1st of january 1970). This explains the offset based calculation present in the method body.
====> 1. Creating a Unix timestamp (seconds since epoch) from a datetime
>>> import time
>>> import datetime
>>> time.mktime(datetime.datetime.now().timetuple())
1401828155.0
====> 2. Adding the difference in seconds between 01/01/1601 and 01/01/1970
====> (11644473600 seconds)
>>> 11644473600 + 1401828155
13046301755
====> 3. Multiply by 10000000
>>> 13046301755 * 10000000
130463017550000000
====> 4. Interpret as binary. Add leading zero's until you have 64 bits.
>>> bin(130463017550000000)
'0b111001111011111110110110001011001011001001010111110000000'
====> 5. Split the high part (32 bits) and the low part (32 bits)
>>> ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With