3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?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"); $timeUuid->format(DateTime::ISO8601); $timeUuid = new TimeUUID("00000000-0000-1000-8fff-ffffffffffff"); $timeUuid->format(DateTime::ISO8601);
Output for 5.3.0 - 5.6.28, hhvm-3.10.0 - 3.12.0, 7.0.0 - 7.1.0