Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigQuery: Ensure timestamps retain microsecond precision #372

Merged
merged 1 commit into from
Mar 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions src/BigQuery/ValueMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,7 @@ public function fromBigQuery(array $value, array $schema)
case self::TYPE_TIME:
return new Time(new \DateTime($value));
case self::TYPE_TIMESTAMP:
$timestamp = new \DateTime();
$timestamp->setTimestamp((float) $value);

return new Timestamp($timestamp);
return $this->timestampFromBigQuery($value);
case self::TYPE_RECORD:
return $this->recordFromBigQuery($value, $schema['fields']);
default:
Expand Down Expand Up @@ -325,4 +322,48 @@ private function assocArrayToParameter(array $struct)
['structValues' => $values]
];
}

/**
* Converts a timestamp in string format received from BigQuery to a
* {@see Google\Cloud\BigQuery\Timestamp}.
*
* @param string $value The timestamp.
* @return Timestamp
*/
private function timestampFromBigQuery($value)
{
// If the string contains 'E' convert from exponential notation to
// decimal notation. This doesn't cast to a float because precision can
// be lost.
if (strpos($value, 'E')) {
list($value, $exponent) = explode('E', $value);
list($firstDigit, $remainingDigits) = explode('.', $value);

if (strlen($remainingDigits) > $exponent) {
$value = $firstDigit . substr_replace($remainingDigits, '.', $exponent, 0);
} else {
$value = $firstDigit . str_pad($remainingDigits, $exponent, '0') . '.0';
}
}

list($unixTimestamp, $microSeconds) = explode('.', $value);
$dateTime = new \DateTime("@$unixTimestamp");

// If the timestamp is before the epoch, make sure we account for that
// before concatenating the microseconds.
if ($microSeconds > 0 && $unixTimestamp[0] === '-') {
$microSeconds = 1000000 - (int) str_pad($microSeconds, 6, '0');
$dateTime->modify('-1 second');
}

return new Timestamp(
new \DateTime(
sprintf(
'%s.%s+00:00',
$dateTime->format('Y-m-d H:i:s'),
$microSeconds
)
)
);
}
}
17 changes: 16 additions & 1 deletion tests/unit/BigQuery/ValueMapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,25 @@ public function bigQueryValueProvider()
new Time(new \DateTime('12:15:15'))
],
[
['v' => '1438712914'],
['v' => '1.438712914E9'],
['type' => 'TIMESTAMP'],
new Timestamp(new \DateTime('2015-08-04 18:28:34Z'))
],
[
['v' => '2678400.0'],
['type' => 'TIMESTAMP'],
new Timestamp(new \DateTime('1970-02-01'))
],
[
['v' => '-3.1561919984985E8'],
['type' => 'TIMESTAMP'],
new Timestamp(new \DateTime('1960-01-01 00:00:00.150150Z'))
],
[
['v' => '9.4668480015015E8'],
['type' => 'TIMESTAMP'],
new Timestamp(new \DateTime('2000-01-01 00:00:00.150150Z'))
],
[
[
'v' => [
Expand Down