diff --git a/lib/FWIF/FWIF.php b/lib/FWIF/FWIF.php index af39473..77a5659 100644 --- a/lib/FWIF/FWIF.php +++ b/lib/FWIF/FWIF.php @@ -23,7 +23,7 @@ class FWIF { public const TYPE_ARRAY = 0x05; // List of values, terminated with TRAILER public const TYPE_OBJECT = 0x06; // List of values with ASCII names, terminated with TRAILER public const TYPE_DATETIME = 0x07; // A gregorian year, month and day as well as an hour, minute, seconds and millisecond component, variable ranging from 4 to 7 bytes - public const TYPE_TIMESPAN = 0x08; // A period of time, follows the same format DATETIME but uses an additional flag to indicate negative periods + public const TYPE_TIMESPAN = 0x08; // A period of time, containing days, hours, minutes, seconds and milliseconds, variable ranging from 4 to 6 bytes public const TRAILER = 0xFF; // Termination byte @@ -409,8 +409,7 @@ class FWIF { return new DateTimeImmutable($dt . 'UTC'); } - private const TIMESPAN_FLAG_YEAR = 0x10000000; - private const TIMESPAN_FLAG_DMY = 0x20000000; + private const TIMESPAN_FLAG_DAYS = 0x20000000; private const TIMESPAN_FLAG_NEGA = 0x40000000; private const TIMESPAN_MILLI_SHIFT = 16; // << @@ -419,19 +418,16 @@ class FWIF { private const TIMESPAN_SECS_SHIFT = 11; // << private const TIMESPAN_MINS_SHIFT = 5; // << - private const TIMESPAN_DAYS_SHIFT = 10; // << - private const TIMESPAN_MONTH_SHIFT = 6; // << + private const TIMESPAN_DAYS_MASK = 0x7FFF; - private const TIMESPAN_YEAR_HI_MASK = 0x3F00; - private const TIMESPAN_YEAR_HI_SHIFT = 8; // >> - private const TIMESPAN_YEAR_LO_MASK = 0x00FF; + private const TIMESPAN_YEAR_DAYS = 365.0; + private const TIMESPAN_MONTH_DAYS = 31.0; - /* +--------+--------+ w - unsigned 10-bit millisecs y - Y enable flag - * |.ndy.www|wwwwwwwS| S - unsigned 6-bit seconds D - unsigned 5-bit day - * |SSSSSmmm|mmmHHHHH| m - unsigned 6-bit minutes M - unsigned 4-bit month - * |.DDDDDMM|MMYYYYYY| H - unsigned 5-bit hours Y - unsigned 14-bit year - * |YYYYYYYY| | n - Negative flag - * +--------+--------+ d - DMY enable flag + /* +--------+--------+ w - unsigned 10-bit millisecs n - Negative flag + * |.nd..www|wwwwwwwS| S - unsigned 6-bit seconds d - DMY enable flag + * |SSSSSmmm|mmmHHHHH| m - unsigned 6-bit minutes D - unsigned 15-bit day + * |.DDDDDDD|DDDDDDDD| H - unsigned 5-bit hours + * +--------+--------+ */ private static function encodeTimeSpan(DateInterval $di, int $flags): string { @@ -441,24 +437,18 @@ class FWIF { $wsmh |= ( $di->i & self::DATETIME_MINS_MASK) << self::TIMESPAN_MINS_SHIFT; $wsmh |= ( $di->h & self::DATETIME_HOUR_MASK); - if($di->d > 0 || $di->m > 0 || $di->y > 0) { - $wsmh |= self::TIMESPAN_FLAG_DMY; - $dmy = ($di->d & self::DATETIME_DAY_MASK) << self::TIMESPAN_DAYS_SHIFT; - $dmy |= ($di->m & self::DATETIME_MONTH_MASK) << self::TIMESPAN_MONTH_SHIFT; + $days = $di->days; + if($days === false) // Best I can come up with on short notice, fuck it + $days = (int)($di->d + ($di->m * self::TIMESPAN_YEAR_DAYS) + ($di->y * self::TIMESPAN_MONTH_DAYS)); - if($di->y > 0) { - $wsmh |= self::TIMESPAN_FLAG_YEAR; - $dmy |= ($di->y & self::TIMESPAN_YEAR_HI_MASK) >> self::TIMESPAN_YEAR_HI_SHIFT; - $y = ($di->y & self::TIMESPAN_YEAR_LO_MASK); - } + if($days > 0) { + $wsmh |= self::TIMESPAN_FLAG_DAYS; + $days &= self::TIMESPAN_DAYS_MASK; } $packed = pack('N', $wsmh); - if($wsmh & self::TIMESPAN_FLAG_DMY) { - $packed .= pack('n', $dmy); - if($wsmh & self::TIMESPAN_FLAG_YEAR) - $packed .= chr($y); - } + if($wsmh & self::TIMESPAN_FLAG_DAYS) + $packed .= pack('n', $days); return $packed; } @@ -472,16 +462,13 @@ class FWIF { $di->i = ($wsmh >> self::TIMESPAN_MINS_SHIFT) & self::DATETIME_MINS_MASK; $di->h = $wsmh & self::DATETIME_HOUR_MASK; - if($wsmh & self::TIMESPAN_FLAG_DMY) { - $dmy = unpack('n', fread($data, 2))[1]; - $di->d = ($dmy >> self::TIMESPAN_DAYS_SHIFT) & self::DATETIME_DAY_MASK; - - // OOPS! THESE WILL BE HORRIBLY INACCURATE! - // Perhaps go back to the drawing board and tack months and years onto days? - $di->m = ($dmy >> self::TIMESPAN_MONTH_SHIFT) & self::DATETIME_MONTH_MASK; - if($wsmh & self::TIMESPAN_FLAG_YEAR) - $di->y = ord(fgetc($data)) - | (($dmy << self::TIMESPAN_YEAR_HI_SHIFT) & self::TIMESPAN_YEAR_HI_MASK); + if($wsmh & self::TIMESPAN_FLAG_DAYS) { + $days = unpack('n', fread($data, 2))[1] & self::TIMESPAN_DAYS_MASK; + $di->days = $days; + $di->y = (int)floor($days / self::TIMESPAN_YEAR_DAYS); + $days -= $di->y * self::TIMESPAN_YEAR_DAYS; + $di->m = (int)ceil($days / self::TIMESPAN_MONTH_DAYS); + $di->d = max(0, $days - ($di->m * self::TIMESPAN_MONTH_DAYS)); } return $di;