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

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

60f347d15798c901

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

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

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

Ответы [ 5 ]

4 голосов
/ 04 марта 2009

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

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

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

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

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

2 голосов
/ 04 марта 2009

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

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

Это «количество интервалов в 100 наносекунд». прошедшие с 12:00 до полуночи 1 января, 1601 г. н.э. (C.E.) Всемирное координированное время (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>
1 голос
/ 04 марта 2009

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

1 голос
/ 04 марта 2009

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

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

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

0 голосов
/ 04 марта 2009

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

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

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

...