now with timespans
This commit is contained in:
parent
a76a22f990
commit
111b74b20b
1 changed files with 93 additions and 33 deletions
|
@ -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 {
|
||||||
|
@ -350,6 +347,9 @@ class FWIF {
|
||||||
$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;
|
||||||
|
@ -389,38 +385,102 @@ class FWIF {
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue