Reputation: 195
I would like to create network timestamps in NT format.
I've been able to convert them to readable time with this function:
NetworkStamp = "\xc0\x65\x31\x50\xde\x09\xd2\x01"
def GetTime(NetworkStamp):
Ftime = int(struct.unpack('<Q',NetworkStamp)[0])
Epoch = divmod(Ftime - 116444736000000000, 10000000)
Actualtime = datetime.datetime.fromtimestamp(Epoch[0])
return Actualtime, Actualtime.strftime('%Y-%m-%d %H:%M:%S')
print GetTime(NetworkStamp)
Output:
(datetime.datetime(2016, 9, 8, 11, 35, 57), '2016-09-08 11:35:57')
Now I'd like to do the opposite, converting '2016/09/08 11:35:57'
sec to this format:
"\xc0\x65\x31\x50\xde\x09\xd2\x01"
Upvotes: 3
Views: 3509
Reputation: 6911
A basic conversion:
from datetime import datetime
from calendar import timegm
EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS = 10000000
def dt_to_filetime(dt):
return EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
The answer is based on this Gist I found: https://gist.github.com/Mostafa-Hamdy-Elgiar/9714475f1b3bc224ea063af81566d873
The Gist adds support for timezones and conversion in the other direction.
Upvotes: -1
Reputation: 123491
Your code that converts the Window's FILETIME
value into a datetime.datetime
isn't as accurate as it could be—it's truncating any fractional seconds there might have been (because it ignores the remainder of the divmod()
result). This isn't noticeable in the readable string your code creates since it only shows whole seconds.
Even if the fractional seconds are included, you can't do exactly what you want because the Windows FILETIME
structure has values in intervals of 100-nanosecond (.1 microsecond), but Python's datetime
only supports accuracy to whole microseconds. So best that's possible to do is approximate the original value due to this loss of information involved even doing the most accurate possible conversion.
Here's code, for both Python 2 and 3, demonstrating this using the NetworkStamp
test value in your question:
import datetime
import struct
import time
WINDOWS_TICKS = int(1/10**-7) # 10,000,000 (100 nanoseconds or .1 microseconds)
WINDOWS_EPOCH = datetime.datetime.strptime('1601-01-01 00:00:00',
'%Y-%m-%d %H:%M:%S')
POSIX_EPOCH = datetime.datetime.strptime('1970-01-01 00:00:00',
'%Y-%m-%d %H:%M:%S')
EPOCH_DIFF = (POSIX_EPOCH - WINDOWS_EPOCH).total_seconds() # 11644473600.0
WINDOWS_TICKS_TO_POSIX_EPOCH = EPOCH_DIFF * WINDOWS_TICKS # 116444736000000000.0
def get_time(filetime):
"""Convert windows filetime winticks to python datetime.datetime."""
winticks = struct.unpack('<Q', filetime)[0]
microsecs = (winticks - WINDOWS_TICKS_TO_POSIX_EPOCH) / WINDOWS_TICKS
return datetime.datetime.fromtimestamp(microsecs)
def convert_back(timestamp_string):
"""Convert a timestamp in Y=M=D H:M:S.f format into a windows filetime."""
dt = datetime.datetime.strptime(timestamp_string, '%Y-%m-%d %H:%M:%S.%f')
posix_secs = int(time.mktime(dt.timetuple()))
winticks = (posix_secs + int(EPOCH_DIFF)) * WINDOWS_TICKS
return winticks
def int_to_bytes(n, minlen=0): # helper function
""" int/long to bytes (little-endian byte order).
Note: built-in int.to_bytes() method could be used in Python 3.
"""
nbits = n.bit_length() + (1 if n < 0 else 0) # plus one for any sign bit
nbytes = (nbits+7) // 8 # number of whole bytes
ba = bytearray()
for _ in range(nbytes):
ba.append(n & 0xff)
n >>= 8
if minlen > 0 and len(ba) < minlen: # zero pad?
ba.extend([0] * (minlen-len(ba)))
return ba # with low bytes first
def hexbytes(s): # formatting helper function
"""Convert string to string of hex character values."""
ba = bytearray(s)
return ''.join('\\x{:02x}'.format(b) for b in ba)
win_timestamp = b'\xc0\x65\x31\x50\xde\x09\xd2\x01'
print('win timestamp: b"{}"'.format(hexbytes(win_timestamp)))
dtime = get_time(win_timestamp)
readable = dtime.strftime('%Y-%m-%d %H:%M:%S.%f') # includes fractional secs
print('datetime repr: "{}"'.format(readable))
winticks = convert_back(readable) # includes fractional secs
new_timestamp = int_to_bytes(winticks)
print('new timestamp: b"{}"'.format(hexbytes(new_timestamp)))
Output:
win timestamp: b"\xc0\x65\x31\x50\xde\x09\xd2\x01"
datetime repr: "2016-09-08 07:35:57.996998"
new timestamp: b"\x80\x44\x99\x4f\xde\x09\xd2\x01"
Upvotes: 5
Reputation: 155506
If you understand how to perform the conversion in one direction, doing it in reverse is basically using the inverse of each method in reverse order. Just look at the documentation for the modules/classes you're using:
strftime
has a strptime
counterpartfromtimestamp
is matched by timestamp
(if you're on pre-3.3 Python, timestamp
doesn't exist, but you could define FILETIME_epoch = datetime.datetime(1601, 1, 1) - datetime.timedelta(seconds=time.altzone if time.daylight else time.timezone)
outside the function to precompute a datetime
that represents the FILETIME
epoch for your timezone, then use int((mydatetime - FILETIME_epoch).total_seconds())
to get int
seconds since FILETIME
epoch directly, without manually adjusting for difference between FILETIME
and Unix epoches separately)divmod
(which you don't really need, since you only use the quotient, not the remainder, you could just do Epoch = (Ftime - 116444736000000000) // 10000000
and avoid indexing later) is trivially reversible (just multiply and add, with the add being unnecessary if you use my trick to convert to FILETIME
epoch seconds directly from #2)struct.unpack
is matched by struct.pack
I'm not providing the exact code because you really should learn to use these things yourself (and read the docs when necessary); I'm guessing your forward code was written without understanding what it is doing, because if you understood it, the reverse should have been obvious; every step has an inverse documented on the same page.
Upvotes: 3