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 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;
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue