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 InvalidArgumentException;
// TODO: IMPLEMENT TYPE_TIMESPAN
class FWIF {
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));
}
private const DATETIME_FLAG_NEGA = 0x20;
private const DATETIME_FLAG_TIME = 0x40;
private const DATETIME_FLAG_MILLI = 0x4000;
@ -332,10 +329,10 @@ class FWIF {
/* +--------+--------+ Y - Signed 15-bit year W - w enable flag
* |.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
* |wwwwwwww| | N - Negative flag (for TYPE_PERIOD)
* +--------+--------+ H - Unsigned 5-bit hours
* |wwwwwwww| | H - Unsigned 5-bit hours
* +--------+--------+
*/
private static function encodeDateTime(DateTimeInterface $dt, int $flags): string {
@ -346,10 +343,13 @@ class FWIF {
if($dt->getTimezone()->getOffset($dt) !== 0)
$dt = DateTime::createFromInterface($dt)->setTimezone($utc);
$year = (int)$dt->format('Y');
$month = (int)$dt->format('n');
$day = (int)$dt->format('j');
$hours = (int)$dt->format('G');
$year = (int)$dt->format('Y');
$month = (int)$dt->format('n');
$day = (int)$dt->format('j');
$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;
if($subYear)
@ -361,10 +361,6 @@ class FWIF {
$ymdh |= ($day & self::DATETIME_DAY_MASK) << self::DATETIME_DAY_SHIFT;
$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) {
$ymdh |= self::DATETIME_FLAG_TIME;
$msw = 0;
@ -388,39 +384,103 @@ class FWIF {
return $packed;
}
private static function decodeDateTime($data, int $flags): DateTimeInterface {
$ymdh = unpack('N', fread($data, 4))[1];
$hasMsw = $ymdh & self::DATETIME_FLAG_TIME;
$msw = $hasMsw ? unpack('n', fread($data, 2))[1] : 0;
$hasW = $hasMsw && ($msw & self::DATETIME_FLAG_MILLI);
$w = $hasW ? ord(fgetc($data)) : 0;
$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;
$ymdh = unpack('N', fread($data, 4))[1];
$years = ($ymdh >> self::DATETIME_YEAR_SHIFT) & self::DATETIME_YEAR_MASK;
$months = ($ymdh >> self::DATETIME_MONTH_SHIFT) & self::DATETIME_MONTH_MASK;
$days = ($ymdh >> self::DATETIME_DAY_SHIFT) & self::DATETIME_DAY_MASK;
$hours = $ymdh & self::DATETIME_HOUR_MASK;
if($ymdh & self::DATETIME_YEAR_SIGN)
$year = ~$year;
$dt = sprintf('%04d-%02d-%02dT%02d:', $year, $month, $day, $hour);
$years = ~$years;
$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;
$secs = ($msw >> self::DATETIME_SECS_SHIFT) & self::DATETIME_SECS_MASK;
$dt .= sprintf('%02d:%02d', $mins, $secs);
if($hasW) {
$dt .= sprintf('%02d:%02d', $mins, $secs);
if($msw & self::DATETIME_FLAG_MILLI) {
$millis = ($msw << self::DATETIME_MILLI_HI_SHIFT) & self::DATETIME_MILLI_HI_MASK;
$millis |= $w;
$millis |= ord(fgetc($data));
$dt .= sprintf('.%03d', $millis);
}
} 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 {
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 {
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;
}
}