Помогите мне перевести длинное значение, выраженное в шестнадцатеричном формате, обратно в дату/время

У меня есть значение даты, которое, как мне сказали, составляет 8 байтов, одно «длинное» (также известное как int64) значение и преобразовано в шестнадцатеричный формат:

60f347d15798c901

Как я могу преобразовать это и подобные значения, используя PHP, во время/дату?

Преобразование его в десятичное дает мне: 96 243 71 209 87 152 201 1

Еще немного информации: исходное значение — это C# DateTime, которое должно представлять время/дату примерно 2 или 3 недели назад.


person lynn    schedule 04.03.2009    source источник


Ответы (5)


(Спасибо сообщению thomasrutter, которое дало мне эпоху файлового времени Windows):

Данная дата выглядит как дата-время 64-битного файла Windows с обратным порядком байтов, 60 f3 47 d1 57 98 c9 01, что эквивалентно четверному слову 01c99857d147f360, которое в виде целого числа равно 128801567297500000.

Это «количество 100-наносекундных интервалов, прошедших с полуночи 1 января 1601 года нашей эры (н.э.) по всемирному координированному времени (UTC)».

После конвертации выдает Чт, 26 февраля 2009 г., 21:18:49 UTC.

Образец кода:

<?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>
person Hugh Bothwell    schedule 04.03.2009
comment
Вау, это потрясающе! Спасибо! - person lynn; 04.03.2009

Я нашел другую ссылку, которая может указывать на более важную информацию. В данном случае дата и время Windows, используемые в реестре. По-видимому, они также хранятся как 64-битные значения.

Это выглядит немного более полезным, так как упоминается, что окончание C?01 является индикатором этого типа даты. Обратите внимание, что это прямой порядок следования байтов (т. е. самые значащие байты справа), поэтому байты находятся в неправильном порядке байтов для hexdec() без изменения их местами по 2 шестнадцатеричных цифры за раз (т. е. начните с крайних 2 шестнадцатеричных цифр, затем следующие два и т. д.).

И, согласно этому источнику,

Один тик представляет собой сто наносекунд или одну десятимиллионную долю секунды. Значение этого свойства представляет количество 100-наносекундных интервалов, прошедших с полуночи 12:00:00 1 января 0001 г., что представляет собой.

Что может сбить вас с толку, так это то, что, к сожалению, PHP не может обрабатывать 64-битные целые числа. Максимум 32 бита. Помогут числа с плавающей запятой, но это не мешает самому hexdec() справляться только с целыми числами. Так что может быть сложно сделать эту математику; вам все равно может понадобиться разделить его на два, а затем объединить их в число с плавающей запятой, приведя более значимое значение к числу с плавающей запятой и умножив его на 2 ^ 32, также приведенное как число с плавающей запятой. Возможно, PHP, созданный для 64-разрядного оборудования, может не иметь этого ограничения, но я точно не знаю.

person thomasrutter    schedule 04.03.2009

Я считаю, что вы можете преобразовать его в десятичное целое с помощью hexdec(). Это будет отметка времени, с которой вы можете работать с date().

person Jeremy DeGroot    schedule 04.03.2009
comment
Хм. Я знаю, что это должно быть долго. Как мне получить преобразованное значение: 96 243 71 209 87 152 201 1 обратно в виде даты/времени? - person lynn; 04.03.2009
comment
Проверьте мой второй ответ - шестнадцатеричные символы могут быть прямыми, поэтому неправильный порядок байтов для hexdec. Затем вы можете обнаружить, что это количество десятимиллионных долей секунды с 1 января 0001 года. - person thomasrutter; 04.03.2009

Значение, которое вы указали, составляет 36 шестнадцатеричных цифр, то есть 18 байтов. Это не Int64, преобразованное в шестнадцатеричное.

Вернитесь к тому, кто дал вам объяснение формата, и попросите их сказать вам правду на этот раз :)

РЕДАКТИРОВАТЬ: Хорошо, следующий шаг: узнайте, что на самом деле означает это число. Клещи с 1970 года? Миллис с 1 г. н.э.? Знаете ли вы, что должна представлять строка вашего примера?

person Jon Skeet    schedule 04.03.2009
comment
Отредактированный вопрос содержит правильную строку, извините за это! - person lynn; 04.03.2009

Если это тот же тип 64-битного шестнадцатеричного Временная метка NTP, как описано здесь, тогда вы должны разделить шестнадцатеричную строку поровну на две части, то есть представьте себе разделитель между первыми 8 шестнадцатеричными цифрами и остальными 8.

Преобразуйте первую половину (первые 32 бита) в целое число с помощью hexdec(), и это целое число будет значением даты в формате временной метки Unix (секунды с 1 января 1970 года по Гринвичу), которое затем можно использовать в функции date(). , так далее.

Вторая часть - это дробные секции, полезные, если вам нужна точность меньше одной секунды (хотя не многие приложения делают это, а встроенные в PHP функции даты и времени не нужны, за исключением microtime()). Вы также можете преобразовать это в десятичное число с помощью hexdec().

person thomasrutter    schedule 04.03.2009
comment
Использование date() в первой половине не дает ничего полезного. Я знаю, что это должно быть длинное значение, как оно становится временем/датой? - person lynn; 04.03.2009
comment
Хорошо, я думаю, что нашел другое решение, см. другой ответ. - person thomasrutter; 04.03.2009