0

我有一个日期值,我被告知是 8 个字节,一个“长”(又名 int64)值,并转换为十六进制:

60f347d15798c901

如何使用 PHP 将这个和类似的值转换为时间/日期?

将其转换为十进制给我: 96 243 71 209 87 152 201 1

更多信息:原始值是 C# DateTime,应该表示大约 2 或 3 周前的时间/日期。

4

5 回答 5

4

我找到了另一个参考,它可能指向一些更相关的信息。在这种情况下,注册表中使用的 Windows 日期和时间。显然,这些也存储为 64 位值。

这看起来更有帮助,因为它提到以 C?01 结尾是此类日期的指示符。请注意,这是小端序(即右侧的最高有效字节),因此 hexdec() 的字节顺序错误,而不会一次将它们反转 2 个十六进制数字(即,从最右边的 2 个十六进制数字开始,然后是接下来的两个,等等)。

而且,根据这个消息来源

一个滴答表示一百纳秒或百万分之一秒。此属性的值表示自 0001 年 1 月 1 日午夜 12:00:00 以来经过的 100 纳秒间隔数,它表示。

不幸的是,PHP 无法处理 64 位整数,这可能会让您感到困惑。它的最大值为 32 位。浮点数会有所帮助,但这并不能阻止 hexdec() 本身仅处理整数。所以做这个数学可能很复杂;您可能仍需要将其分成两部分,然后通过将更重要的值转换为浮点数并将其乘以 2^32 将它们组合成浮点数,也将其转换为浮点数。 为 64 位硬件构建的 PHP 可能没有此限制,但我不确定。

于 2009-03-04T14:56:37.697 回答
2

(感谢 thomasrutter 的帖子,它给了我 Windows 文件时间时代):

给定的日期似乎是 Windows 64 位 little-endian 文件日期时间,60 f3 47 d1 57 98 c9 01,相当于四字 01c99857d147f360,整数为 128801567297500000

这是“自公元 1601 年 1 月 1 日 (CE) 协调世界时 (UTC) 午夜 12:00 以来经过的 100 纳秒间隔数”

转换后,它给出了 Thu, 26 February 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>
于 2009-03-04T17:56:34.830 回答
1

您给出的值是 36 个十六进制数字,即 18 个字节。那不是Int64转换为十六进制。

回到给你解释格式的人那里,让他们这次告诉你真相:)

编辑:好的,下一步:找出这个数字的实际含义。自 1970 年以来的蜱虫?公元 1 年以来的米利斯?你知道你的示例字符串是什么意思吗?

于 2009-03-04T13:47:10.250 回答
1

我相信您可以将其转换为十进制整数,hexdec(). 这将是您可以使用的时间戳date().

于 2009-03-04T13:56:57.527 回答
0

如果它与此处描述的 64 位十六进制 NTP 时间戳类型相同,那么您应该将十六进制字符串一分为二,也就是说,想象前 8 个十六进制数字和其他 8 个数字之间的分隔符。

使用 hexdec() 将前半部分(前 32 位的值)转换为整数,该整数将是 Unix 时间戳格式的日期值(自 1970 年 1 月 1 日 GMT 以来的秒数),然后您可以在 date() 函数中使用, ETC。

第二部分是小数部分,如果您需要小于一秒的精度(虽然没有多少应用程序这样做,PHP 的内置日期和时间函数除了 microtime() 之外没有),这很有用。您还可以使用 hexdec() 将其转换为十进制。

于 2009-03-04T14:01:32.353 回答