I have a date value, which I\'m told is 8 bytes, a single \"long\" (aka int64) value, and converted to hex:
60f347d15798c901
How can I convert
I found another reference which might point to some more relevant information. In this case, a Windows date and time as used in the registry. Apparently these are also stored as 64 bit values.
This is looking a bit more helpful, as it mentions that ending in C?01 is an indicator of this type of date. Note that this is little endian (ie, most significant bytes on the right) so the bytes are in the wrong byte order for hexdec() without reversing them 2 hex digits at a time (ie, start with right-most 2 hex digits, then next two, etc).
And, according to this source,
A single tick represents one hundred nanoseconds or one ten-millionth of a second. The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001, which represents.
What might trip you up is that unfortunately PHP cannot handle 64 bit integers. It maxes out at 32 bit. Floats will help, but that doesn't stop hexdec() itself from only coping with ints. So it may be complicated to do this math; you may still need to split it into two, then combine them into a float by casting the more significant value to float and multiplying it by 2^32 also cast as float. It's possible that PHP built for 64-bit hardware may not have this limitation, but I don't know for sure.
If it is the same type of 64 bit hex NTP timestamp as described here, then you should be splitting the hex string equally in two, that is, imagine a separator between the first 8 hex digits and the other 8.
Convert the first half (first 32 bits' worth) into an integer with hexdec(), and this integer will be the date value in Unix timestamp format (seconds since Jan 1 1970 GMT) which you can then use in the date() function, etc.
The second part is fractional sections, useful if you need accuracy finer than one second (though not many applications do, and PHP's built in date and time functions don't except for microtime()). You would also convert this to decimal with hexdec().
I believe you can convert it to a decimal int with hexdec().
That will be a timestap that you can work on with date().
(Thanks to thomasrutter's post, which gave me the Windows filetime epoch):
The given date appears to be a Windows 64-bit little-endian file date-time, 60 f3 47 d1 57 98 c9 01, which is equivalent to the quadword 01c99857d147f360 which as an integer is 128801567297500000
This is "the number of 100-nanosecond intervals that have elapsed since 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC)"
After conversion, it gives Thu, 26 February 2009 21:18:49 UTC
Sample code:
<?php
// strip non-hex characters
function hexstring($str) {
$hex = array(
'0'=>'0', '1'=>'1', '2'=>'2', '3'=>'3', '4'=>'4',
'5'=>'5', '6'=>'6', '7'=>'7', '8'=>'8', '9'=>'9',
'a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'e', 'f'=>'f',
'A'=>'a', 'B'=>'b', 'C'=>'c', 'D'=>'d', 'E'=>'e', 'F'=>'f'
);
$t = '';
$len = strlen($str);
for ($i=0; $i<$len; ++$i) {
$ch = $str[$i];
if (isset($hex[$ch]))
$t .= $hex[$ch];
}
return $t;
}
// swap little-endian to big-endian
function flip_endian($str) {
// make sure #digits is even
if ( strlen($str) & 1 )
$str = '0' . $str;
$t = '';
for ($i = strlen($str)-2; $i >= 0; $i-=2)
$t .= substr($str, $i, 2);
return $t;
}
// convert hex string to BC-int
function hex_to_bcint($str) {
$hex = array(
'0'=>'0', '1'=>'1', '2'=>'2', '3'=>'3', '4'=>'4',
'5'=>'5', '6'=>'6', '7'=>'7', '8'=>'8', '9'=>'9',
'a'=>'10', 'b'=>'11', 'c'=>'12', 'd'=>'13', 'e'=>'14', 'f'=>'15',
'A'=>'10', 'B'=>'11', 'C'=>'12', 'D'=>'13', 'E'=>'14', 'F'=>'15'
);
$bci = '0';
$len = strlen($str);
for ($i=0; $i<$len; ++$i) {
$bci = bcmul($bci, '16');
$ch = $str[$i];
if (isset($hex[$ch]))
$bci = bcadd($bci, $hex[$ch]);
}
return $bci;
}
// WARNING! range clipping
// Windows date time has range from 29000 BC to 29000 AD
// Unix time only has range from 1901 AD to 2038 AD
// WARNING! loss of accuracy
// Windows date time has accuracy to 0.0000001s
// Unix time only has accuracy to 1.0s
function win64_to_unix($bci) {
// Unix epoch as a Windows file date-time value
$magicnum = '116444735995904000';
$t = bcsub($bci, $magicnum); // Cast to Unix epoch
$t = bcdiv($t, '10000000', 0); // Convert from ticks to seconds
return $t;
}
// get input
$dtval = isset($_GET["dt"]) ? strval($_GET["dt"]) : "0";
$dtval = hexstring($dtval); // strip non-hex chars
// convert to quadword
$dtval = substr($dtval, 0, 16); // clip overlength string
$dtval = str_pad($dtval, 16, '0'); // pad underlength string
$quad = flip_endian($dtval);
// convert to int
$win64_datetime = hex_to_bcint($quad);
// convert to Unix timestamp value
$unix_datetime = win64_to_unix($win64_datetime);
?><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Windows datetime test code</title>
</head>
<form method="get">
<label>Datetime value: <input name="dt" type="text" value="<?php echo $dtval; ?>"/></label>
<input type="submit" />
</form>
<hr />
Result:
Quad: <?php echo $quad; ?><br />
Int: <?php echo $win64_datetime; ?><br />
Unix timestamp: <?php echo $unix_datetime; ?><br />
Date: <?php echo date("D, d F Y H:i:s e", $unix_datetime); ?><br />
<body>
</body>
</html>
The value you've given is 36 hex digits, i.e. 18 bytes-worth. That's not an Int64
converted to hex.
Go back to whoever gave you the explanation of the format and ask them to tell you the truth this time :)
EDIT: Okay, next step: find out what that number is actually meant to mean. Ticks since 1970? Millis since AD 1? Do you know what your example string is meant to represent?