帮助我将以十六进制表示的长值转换回日期/时间

发布于 2024-07-14 16:09:26 字数 235 浏览 8 评论 0原文

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

60f347d15798c901

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

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

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

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 this and values like this, using PHP, into a time/date?

Converting it to decimal gives me:
96 243 71 209 87 152 201 1

A little more info: the original value is a C# DateTime, and should represent a time/date about 2 or 3 weeks ago.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

别闹i 2024-07-21 16:09:27

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

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

并且,根据此来源,

单个刻度代表一百纳秒或百万分之一秒。 该属性的值表示自 0001 年 1 月 1 日午夜 12:00:00 以来经过的 100 纳秒间隔数。

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

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.

各自安好 2024-07-21 16:09:27

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

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

这是“100纳秒间隔的数量
自 1 月 1 日午夜 12:00 起经过的时间,
1601 AD (CE) 协调世界时 (UTC)"

转换后,给出
2009 年 2 月 26 日星期四 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>

(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>
浊酒尽余欢 2024-07-21 16:09:27

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

I believe you can convert it to a decimal int with hexdec(). That will be a timestap that you can work on with date().

一向肩并 2024-07-21 16:09:27

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

回到向您解释格式的人,并要求他们这次告诉您真相:)

编辑:好的,下一步:找出该数字的实际含义。 自 1970 年以来一直存在? 米利斯从公元1年起? 你知道你的示例字符串代表什么吗?

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?

始终不够 2024-07-21 16:09:27

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

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

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

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().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文