now with timespans

This commit is contained in:
flash 2020-12-27 21:35:22 +00:00
parent a76a22f990
commit 111b74b20b

View file

@ -8,8 +8,6 @@ use DateTimeImmutable;
use DateTimeZone; use DateTimeZone;
use InvalidArgumentException; use InvalidArgumentException;
// TODO: IMPLEMENT TYPE_TIMESPAN
class FWIF { class FWIF {
public const CONTENT_TYPE = 'text/plain; charset=us-ascii'; // TODO: come up with a mime type public const CONTENT_TYPE = 'text/plain; charset=us-ascii'; // TODO: come up with a mime type
@ -304,7 +302,6 @@ class FWIF {
return fread($data, self::decodeInteger($data, $flags)); return fread($data, self::decodeInteger($data, $flags));
} }
private const DATETIME_FLAG_NEGA = 0x20;
private const DATETIME_FLAG_TIME = 0x40; private const DATETIME_FLAG_TIME = 0x40;
private const DATETIME_FLAG_MILLI = 0x4000; private const DATETIME_FLAG_MILLI = 0x4000;
@ -332,10 +329,10 @@ class FWIF {
/* +--------+--------+ Y - Signed 15-bit year W - w enable flag /* +--------+--------+ Y - Signed 15-bit year W - w enable flag
* |.YYYYYYY|YYYYYYYY| M - Unsigned 4-bit month m - unsigned 6-bit minutes * |.YYYYYYY|YYYYYYYY| M - Unsigned 4-bit month m - unsigned 6-bit minutes
* |MMMMDDDD|DTNHHHHH| D - Unsigned 5-bit Day S - unsigned 6-bit seconds * |MMMMDDDD|DT.HHHHH| D - Unsigned 5-bit Day S - unsigned 6-bit seconds
* |.Wmmmmmm|SSSSSSww| T - WmSw enable flag w - unsigned 10-bit millisecs * |.Wmmmmmm|SSSSSSww| T - WmSw enable flag w - unsigned 10-bit millisecs
* |wwwwwwww| | N - Negative flag (for TYPE_PERIOD) * |wwwwwwww| | H - Unsigned 5-bit hours
* +--------+--------+ H - Unsigned 5-bit hours * +--------+--------+
*/ */
private static function encodeDateTime(DateTimeInterface $dt, int $flags): string { private static function encodeDateTime(DateTimeInterface $dt, int $flags): string {
@ -346,10 +343,13 @@ class FWIF {
if($dt->getTimezone()->getOffset($dt) !== 0) if($dt->getTimezone()->getOffset($dt) !== 0)
$dt = DateTime::createFromInterface($dt)->setTimezone($utc); $dt = DateTime::createFromInterface($dt)->setTimezone($utc);
$year = (int)$dt->format('Y'); $year = (int)$dt->format('Y');
$month = (int)$dt->format('n'); $month = (int)$dt->format('n');
$day = (int)$dt->format('j'); $day = (int)$dt->format('j');
$hours = (int)$dt->format('G'); $hours = (int)$dt->format('G');
$mins = (int)$dt->format('i');
$secs = (int)$dt->format('s');
$millis = ($flags & self::DISCARD_MILLISECONDS) ? 0 : (int)$dt->format('v');
$subYear = $year < 0; $subYear = $year < 0;
if($subYear) if($subYear)
@ -361,10 +361,6 @@ class FWIF {
$ymdh |= ($day & self::DATETIME_DAY_MASK) << self::DATETIME_DAY_SHIFT; $ymdh |= ($day & self::DATETIME_DAY_MASK) << self::DATETIME_DAY_SHIFT;
$ymdh |= ($hours & self::DATETIME_HOUR_MASK); $ymdh |= ($hours & self::DATETIME_HOUR_MASK);
$mins = (int)$dt->format('i');
$secs = (int)$dt->format('s');
$millis = ($flags & self::DISCARD_MILLISECONDS) ? 0 : (int)$dt->format('v');
if($mins > 0 || $secs > 0 || $millis > 0) { if($mins > 0 || $secs > 0 || $millis > 0) {
$ymdh |= self::DATETIME_FLAG_TIME; $ymdh |= self::DATETIME_FLAG_TIME;
$msw = 0; $msw = 0;
@ -388,39 +384,103 @@ class FWIF {
return $packed; return $packed;
} }
private static function decodeDateTime($data, int $flags): DateTimeInterface { private static function decodeDateTime($data, int $flags): DateTimeInterface {
$ymdh = unpack('N', fread($data, 4))[1]; $ymdh = unpack('N', fread($data, 4))[1];
$hasMsw = $ymdh & self::DATETIME_FLAG_TIME; $years = ($ymdh >> self::DATETIME_YEAR_SHIFT) & self::DATETIME_YEAR_MASK;
$msw = $hasMsw ? unpack('n', fread($data, 2))[1] : 0; $months = ($ymdh >> self::DATETIME_MONTH_SHIFT) & self::DATETIME_MONTH_MASK;
$hasW = $hasMsw && ($msw & self::DATETIME_FLAG_MILLI); $days = ($ymdh >> self::DATETIME_DAY_SHIFT) & self::DATETIME_DAY_MASK;
$w = $hasW ? ord(fgetc($data)) : 0; $hours = $ymdh & self::DATETIME_HOUR_MASK;
$year = ($ymdh >> self::DATETIME_YEAR_SHIFT) & self::DATETIME_YEAR_MASK;
$month = ($ymdh >> self::DATETIME_MONTH_SHIFT) & self::DATETIME_MONTH_MASK;
$day = ($ymdh >> self::DATETIME_DAY_SHIFT) & self::DATETIME_DAY_MASK;
$hour = $ymdh & self::DATETIME_HOUR_MASK;
if($ymdh & self::DATETIME_YEAR_SIGN) if($ymdh & self::DATETIME_YEAR_SIGN)
$year = ~$year; $years = ~$years;
$dt = sprintf('%04d-%02d-%02dT%02d:', $year, $month, $day, $hour); $dt = sprintf('%04d-%02d-%02dT%02d:', $years, $months, $days, $hours);
if($hasMsw) { if($ymdh & self::DATETIME_FLAG_TIME) {
$msw = unpack('n', fread($data, 2))[1];
$mins = ($msw >> self::DATETIME_MINS_SHIFT) & self::DATETIME_MINS_MASK; $mins = ($msw >> self::DATETIME_MINS_SHIFT) & self::DATETIME_MINS_MASK;
$secs = ($msw >> self::DATETIME_SECS_SHIFT) & self::DATETIME_SECS_MASK; $secs = ($msw >> self::DATETIME_SECS_SHIFT) & self::DATETIME_SECS_MASK;
$dt .= sprintf('%02d:%02d', $mins, $secs); $dt .= sprintf('%02d:%02d', $mins, $secs);
if($hasW) { if($msw & self::DATETIME_FLAG_MILLI) {
$millis = ($msw << self::DATETIME_MILLI_HI_SHIFT) & self::DATETIME_MILLI_HI_MASK; $millis = ($msw << self::DATETIME_MILLI_HI_SHIFT) & self::DATETIME_MILLI_HI_MASK;
$millis |= $w; $millis |= ord(fgetc($data));
$dt .= sprintf('.%03d', $millis); $dt .= sprintf('.%03d', $millis);
} }
} else $dt .= '00:00'; } else $dt .= '00:00';
return new DateTimeImmutable($dt); return new DateTimeImmutable($dt . 'UTC');
} }
private const TIMESPAN_FLAG_YEAR = 0x10000000;
private const TIMESPAN_FLAG_DMY = 0x20000000;
private const TIMESPAN_FLAG_NEGA = 0x40000000;
private const TIMESPAN_MILLI_SHIFT = 16; // <<
private const TIMESPAN_MILLI_MASK = 0x3FF;
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_YEAR_HI_MASK = 0x3F00;
private const TIMESPAN_YEAR_HI_SHIFT = 8; // >>
private const TIMESPAN_YEAR_LO_MASK = 0x00FF;
/* +--------+--------+ 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
*/
private static function encodeTimeSpan(DateInterval $di, int $flags): string { private static function encodeTimeSpan(DateInterval $di, int $flags): string {
return ''; $wsmh = $di->invert ? self::TIMESPAN_FLAG_NEGA : 0;
$wsmh |= ((int)($di->f * 1000) & self::TIMESPAN_MILLI_MASK) << self::TIMESPAN_MILLI_SHIFT;
$wsmh |= ( $di->s & self::DATETIME_SECS_MASK) << self::TIMESPAN_SECS_SHIFT;
$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;
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);
}
}
$packed = pack('N', $wsmh);
if($wsmh & self::TIMESPAN_FLAG_DMY) {
$packed .= pack('n', $dmy);
if($wsmh & self::TIMESPAN_FLAG_YEAR)
$packed .= chr($y);
}
return $packed;
} }
private static function decodeTimeSpan($data, int $flags): DateInterval { private static function decodeTimeSpan($data, int $flags): DateInterval {
return new \DateInterval('P1Y'); $di = new DateInterval('P0Y');
$wsmh = unpack('N', fread($data, 4))[1];
$di->invert = ($wsmh & self::TIMESPAN_FLAG_NEGA) ? 1 : 0;
$di->f = (($wsmh >> self::TIMESPAN_MILLI_SHIFT) & self::TIMESPAN_MILLI_MASK) / 1000.0;
$di->s = ($wsmh >> self::TIMESPAN_SECS_SHIFT) & self::DATETIME_SECS_MASK;
$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;
$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);
}
return $di;
} }
} }