@ 2014-06-26T08:45:52Z <?php
class TimeUUID extends DateTime
{
// Interval between Julian and Gregorian calendar in 100nanoseconds
const Interval = "122192928000000000";
const Time = 1;
const DCE = 2;
/**
* Returns true if this UUID is parsable
*
* @param string $uuid
* @return boolean
*/
private static function isUUID($uuid)
{
if (preg_match('/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/', $uuid))
return true;
return false;
}
/**
* Ensure the value is not a signed extracted value
*
* @param char $value
* @return char
*/
private static function ensureNotSigned8bits($value)
{
return (256 + $value) % 256;
}
/**
* Create data according to the two given raw values
*
* @param integer $raw1
* @param integer $raw2
* @return char array[8]
*/
private static function createData($raw1, $raw2)
{
$b1 = ($raw1 & 0xFF000000) >> 24;
$b2 = ($raw1 & 0x00FF0000) >> 16;
$b3 = ($raw1 & 0x0000FF00) >> 8;
$b4 = ($raw1 & 0x000000FF);
$b5 = ($raw2 & 0xFF000000) >> 24;
$b6 = ($raw2 & 0x00FF0000) >> 16;
$b7 = ($raw2 & 0x0000FF00) >> 8;
$b8 = ($raw2 & 0x000000FF);
return array(
TimeUUID::ensureNotSigned8bits($b1), // 8bits
$b2, // 8bits
$b3, // 8bits
$b4, // 8bits
TimeUUID::ensureNotSigned8bits($b5), // 8bits
$b6, // 8bits
$b7, // 8bits
$b8, // 8bits
);
}
/**
* Extract fields from the given UUID
*
* @param string $uuid
* @return array
*/
private static function extractFields($uuid)
{
// remove dashes
$uuid = preg_replace("/[^a-f0-9]/is", "", $uuid);
$data1 = hexdec(substr($uuid, 0, 8));
$data2 = hexdec(substr($uuid, 8, 4));
$data3 = hexdec(substr($uuid, 12, 4));
$uuid332 = hexdec(substr($uuid, 16, 8));
$uuid432 = hexdec(substr($uuid, 24, 8));
return array(
$data1, // 32 bits
$data2, // 16 bits
$data3, // 16 bits
TimeUUID::createData($uuid332, $uuid432)
);
}
/**
* Build the UUID using the raw values
*
* @param integer $data1
* @param integer $data2
* @param integer $data3
* @param char array[8] $data4
* @return string
*/
private static function composeFields($data1, $data2, $data3, $data4)
{
$data1Str = str_pad(dechex($data1), 8, '0', STR_PAD_LEFT);
$data2Str = str_pad(dechex($data2), 4, '0', STR_PAD_LEFT);
$data3Str = str_pad(dechex($data3), 4, '0', STR_PAD_LEFT);
$data4Str0 = str_pad(dechex($data4[0]), 2, '0', STR_PAD_LEFT);
$data4Str1 = str_pad(dechex($data4[1]), 2, '0', STR_PAD_LEFT);
$result = "$data1Str-$data2Str-$data3Str-$data4Str0$data4Str1-";
foreach (range(2, 7) as $index)
$result .= str_pad(dechex($data4[$index]), 2, '0', STR_PAD_LEFT);
return $result;
}
/**
* Check if the variant of this UUID 4rd node is a DCE
*
* @param integer $data4
* @return boolean
*/
private static function checkVariantDCE($data4)
{
return ($data4[0] & 0xC0) == (TimeUUID::DCE << 6);
}
/**
* Check if the version of this UUID 3rd node is a Time
*
* @param integer $data3
* @return boolean
*/
private static function checkVersionTime($data3)
{
return ($data3 >> 12) == (TimeUUID::Time);
}
/**
* Parse the given uuid, check if it's a TimeUUID (RFC4144), and returns parsed data
*
* @param type $uuid
* @return array(
* Unix Timestamp,
* Data,
* 100nanoseconds
* )
*/
private static function parse($uuid)
{
list($data1, $data2, $data3, $data4) = TimeUUID::extractFields($uuid);
if (!TimeUUID::checkVariantDCE($data4))
throw new Exception("Not a valid TimeUUID: DCE not found (Variant)");
if (!TimeUUID::checkVersionTime($data3))
throw new Exception("Not a valid TimeUUID: Time not found (Version)");
// $time = ($data1 | ($data2 << 32) | (($data3 & 0xFFF) << 48)) - TimeUUID::Interval;
$time =
bcsub(
bcadd(
bcadd(
bcmul(
$data2,
"4294967296", // 0xFFFFFFFF
0
),
$data1
),
bcmul(
$data3 & 0xFFF,
"281474976710656", // 0xFFFFFFFFFFFF
0
)
),
TimeUUID::Interval
);
return array(
// intval($time / 10000000),
intval(bcdiv($time, 10000000, 0)),
$data4,
// intval($time % 10000000),
intval(bcmod($time, 10000000))
);
}
private $data = array(
0,
0,
0,
0,
0,
0,
0,
0
);
private $ticks = 0;
/**
* Returns $this data
*
* @return char array[8]
*/
public function getData()
{
return $this->data;
}
/**
* Specify the data this TimeUUID will holds. Check TimeUUIDMax for an example
*
* @param char array[8] $data
*/
public function setData($data)
{
$this->data = $data;
}
/**
* Returns $this 100nanoseconds
*
* @return integer
*/
public function getTicks()
{
return $this->ticks;
}
/**
* Since DateTime doesn't support 100nanoseconds, you can give here the
* missing accuracy of DateTime to build a complete TimeUUID
*
* @param integer $ticks
*/
public function setTicks($ticks)
{
$this->ticks = $ticks;
}
/**
* Attempt to construct a DateTime based on the given TimeUUID, fallback on
* default DateTime constructor if the given $time is not a TimeUUID
*
* @param type $time
* @param DateTimeZone $timezone (ignored when passing a TimeUUID in $time)
*/
public function __construct($time, $timezone = null)
{
if (TimeUUID::isUUID($time)) {
// TimeUUID are always UTC, and timezone parameter is ignored
// when ctoring with @ syntax
list($time, $this->data, $this->ticks) = TimeUUID::parse($time);
parent::__construct('@' . $time);
} else {
parent::__construct($time, $timezone);
}
}
/**
* Return $this TimeUUID
*
* @return string
*/
public function __toString()
{
// ((Timesteamp() * 10000000) + Interval) + Ticks
$time =
bcadd(
bcadd(
bcmul(
$this->getTimestamp(),
10000000
),
TimeUUID::Interval
),
$this->getTicks()
);
// $time & 32 (0xFFFFFFFF => 2^32)
$data1 = bcmod($time, "4294967296");
// $time >> 32
$timeHigh = bcdiv($time, "4294967296", 0);
$data2 = $timeHigh & 0xFFFF;
$data3 = (($timeHigh >> 16) & 0xFFF) | TimeUUID::Time << 12;
$data = $this->data;
$data[0] = ($data[0] & 0x3F) | TimeUUID::DCE << 6;
return TimeUUID::composeFields($data1, $data2, $data3, $data);
}
}
$timeUuid = new TimeUUID("ffffffff-ffff-1fff-8000-000000000000");
var_dump($timeUuid->format(DateTime::ISO8601));
$timeUuid = new TimeUUID("00000000-0000-1000-8fff-ffffffffffff");
var_dump($timeUuid->format(DateTime::ISO8601));
var_dump("PHP size: " . PHP_INT_SIZE);
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Output for 5.3.0 - 5.3.29 , 5.4.0 - 5.4.45 , 5.5.0 - 5.5.38 , 5.6.0 - 5.6.28 , 7.0.0 - 7.0.20 , 7.1.0 - 7.1.20 , 7.2.6 - 7.2.33 , 7.3.12 - 7.3.31 , 7.4.0 - 7.4.32 , 8.0.0 - 8.0.12 , 8.0.14 - 8.0.30 , 8.1.0 - 8.1.27 , 8.2.0 - 8.2.17 , 8.3.0 - 8.3.4 string(24) "5236-03-31T21:21:00+0000"
string(24) "1582-10-15T00:00:00+0000"
string(11) "PHP size: 8"
Output for 7.3.32 - 7.3.33 , 7.4.33 , 8.0.13 Fatal error: Uncaught Error: Call to undefined function bcsub() in /in/2QQMf:158
Stack trace:
#0 /in/2QQMf(252): TimeUUID::parse('ffffffff-ffff-1...')
#1 /in/2QQMf(295): TimeUUID->__construct('ffffffff-ffff-1...')
#2 {main}
thrown in /in/2QQMf on line 158
Process exited with code 255 . preferences:dark mode live preview
215.5 ms | 401 KiB | 297 Q