Commit e7746f48 authored by Leonard Hecker's avatar Leonard Hecker Committed by Paul Yang

php: Added nanosecond support for Timestamp (#3972)

* php: Added nanosecond support for Timestamp

* php: Fixed compatibility test
parent 656f64ec
......@@ -30,6 +30,7 @@
#include <php.h>
#include <stdlib.h>
#include <inttypes.h>
#include "protobuf.h"
#include "utf8.h"
......@@ -1249,28 +1250,62 @@ PHP_METHOD(Timestamp, fromDateTime) {
return;
}
// Get timestamp from Datetime object.
zval retval;
zval function_name;
int64_t timestamp;
int64_t timestamp_seconds;
{
zval retval;
zval function_name;
#if PHP_MAJOR_VERSION < 7
INIT_ZVAL(retval);
INIT_ZVAL(function_name);
INIT_ZVAL(retval);
INIT_ZVAL(function_name);
#endif
PHP_PROTO_ZVAL_STRING(&function_name, "date_timestamp_get", 1);
PHP_PROTO_ZVAL_STRING(&function_name, "date_timestamp_get", 1);
if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
ZVAL_PTR_TO_CACHED_PTR(datetime) TSRMLS_CC) == FAILURE) {
zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
return;
if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
ZVAL_PTR_TO_CACHED_PTR(datetime) TSRMLS_CC) == FAILURE) {
zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
return;
}
protobuf_convert_to_int64(&retval, &timestamp_seconds);
zval_dtor(&retval);
zval_dtor(&function_name);
}
protobuf_convert_to_int64(&retval, &timestamp);
int64_t timestamp_micros;
{
zval retval;
zval function_name;
zval format_string;
zval_dtor(&retval);
zval_dtor(&function_name);
#if PHP_MAJOR_VERSION < 7
INIT_ZVAL(retval);
INIT_ZVAL(function_name);
INIT_ZVAL(format_string);
#endif
PHP_PROTO_ZVAL_STRING(&function_name, "date_format", 1);
PHP_PROTO_ZVAL_STRING(&format_string, "u", 1);
CACHED_VALUE params[2] = {
ZVAL_PTR_TO_CACHED_VALUE(datetime),
ZVAL_TO_CACHED_VALUE(format_string),
};
if (call_user_function(EG(function_table), NULL, &function_name, &retval,
ARRAY_SIZE(params), params TSRMLS_CC) == FAILURE) {
zend_error(E_ERROR, "Cannot format DateTime.");
return;
}
protobuf_convert_to_int64(&retval, &timestamp_micros);
zval_dtor(&retval);
zval_dtor(&function_name);
zval_dtor(&format_string);
}
// Set seconds
MessageHeader* self = UNBOX(MessageHeader, getThis());
......@@ -1278,13 +1313,13 @@ PHP_METHOD(Timestamp, fromDateTime) {
upb_msgdef_ntofz(self->descriptor->msgdef, "seconds");
void* storage = message_data(self);
void* memory = slot_memory(self->descriptor->layout, storage, field);
*(int64_t*)memory = timestamp;
*(int64_t*)memory = timestamp_seconds;
// Set nanos
field = upb_msgdef_ntofz(self->descriptor->msgdef, "nanos");
storage = message_data(self);
memory = slot_memory(self->descriptor->layout, storage, field);
*(int32_t*)memory = 0;
*(int32_t*)memory = timestamp_micros * 1000;
RETURN_NULL();
}
......@@ -1303,38 +1338,41 @@ PHP_METHOD(Timestamp, toDateTime) {
memory = slot_memory(self->descriptor->layout, storage, field);
int32_t nanos = *(int32_t*)memory;
// Get formated time string.
char formated_time[50];
time_t raw_time = seconds;
struct tm *utc_time = gmtime(&raw_time);
strftime(formated_time, sizeof(formated_time), "%Y-%m-%dT%H:%M:%SUTC",
utc_time);
// Get formatted time string.
char formatted_time[32];
snprintf(formatted_time, sizeof(formatted_time), "%" PRId64 ".%06" PRId32,
seconds, nanos / 1000);
// Create Datetime object.
zval datetime;
zval formated_time_php;
zval function_name;
int64_t timestamp = 0;
zval format_string;
zval formatted_time_php;
#if PHP_MAJOR_VERSION < 7
INIT_ZVAL(function_name);
INIT_ZVAL(formated_time_php);
INIT_ZVAL(format_string);
INIT_ZVAL(formatted_time_php);
#endif
PHP_PROTO_ZVAL_STRING(&function_name, "date_create", 1);
PHP_PROTO_ZVAL_STRING(&formated_time_php, formated_time, 1);
PHP_PROTO_ZVAL_STRING(&function_name, "date_create_from_format", 1);
PHP_PROTO_ZVAL_STRING(&format_string, "U.u", 1);
PHP_PROTO_ZVAL_STRING(&formatted_time_php, formatted_time, 1);
CACHED_VALUE params[1] = {ZVAL_TO_CACHED_VALUE(formated_time_php)};
CACHED_VALUE params[2] = {
ZVAL_TO_CACHED_VALUE(format_string),
ZVAL_TO_CACHED_VALUE(formatted_time_php),
};
if (call_user_function(EG(function_table), NULL,
&function_name, &datetime, 1,
params TSRMLS_CC) == FAILURE) {
if (call_user_function(EG(function_table), NULL, &function_name, &datetime,
ARRAY_SIZE(params), params TSRMLS_CC) == FAILURE) {
zend_error(E_ERROR, "Cannot create DateTime.");
return;
}
zval_dtor(&formated_time_php);
zval_dtor(&function_name);
zval_dtor(&format_string);
zval_dtor(&formatted_time_php);
#if PHP_MAJOR_VERSION < 7
zval* datetime_ptr = &datetime;
......
......@@ -42,6 +42,10 @@
#define MAX_LENGTH_OF_INT64 20
#define SIZEOF_INT64 8
/* From Chromium. */
#define ARRAY_SIZE(x) \
((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
// -----------------------------------------------------------------------------
// PHP7 Wrappers
// ----------------------------------------------------------------------------
......
......@@ -182,18 +182,19 @@ class Timestamp extends \Google\Protobuf\Internal\Message
*/
public function fromDateTime(\DateTime $datetime)
{
$this->seconds = $datetime->format('U');
$this->nanos = 0;
$this->seconds = $datetime->getTimestamp();
$this->nanos = 1000 * $datetime->format('u');
}
/**
* Converts Timestamp to PHP DateTime. Nano second is ignored.
* Converts Timestamp to PHP DateTime.
*
* @return \DateTime $datetime
*/
public function toDateTime()
{
return \DateTime::createFromFormat('U', $this->seconds);
$time = sprintf('%s.%06d', $this->seconds, $this->nanos / 1000);
return \DateTime::createFromFormat('U.u', $time);
}
}
......@@ -124,6 +124,7 @@ sed -i.bak '/php_implementation_test.php/d' phpunit.xml
sed -i.bak '/generated_phpdoc_test.php/d' phpunit.xml
sed -i.bak 's/generated_phpdoc_test.php//g' tests/test.sh
sed -i.bak '/memory_leak_test.php/d' tests/test.sh
sed -i.bak '/^ public function testTimestamp()$/,/^ }$/d' tests/well_known_test.php
for t in "${tests[@]}"
do
remove_error_test tests/$t
......
......@@ -152,7 +152,7 @@ date_default_timezone_set('UTC');
$from = new DateTime('2011-01-01T15:03:01.012345UTC');
$timestamp->fromDateTime($from);
assert($from->format('U') == $timestamp->getSeconds());
assert(0 == $timestamp->getNanos());
assert(1000 * $from->format('u') == $timestamp->getNanos());
$to = $timestamp->toDateTime();
assert(\DateTime::class == get_class($to));
......
......@@ -312,11 +312,12 @@ class WellKnownTest extends TestBase {
$from = new DateTime('2011-01-01T15:03:01.012345UTC');
$timestamp->fromDateTime($from);
$this->assertEquals($from->format('U'), $timestamp->getSeconds());
$this->assertSame(0, $timestamp->getNanos());
$this->assertEquals(1000 * $from->format('u'), $timestamp->getNanos());
$to = $timestamp->toDateTime();
$this->assertSame(\DateTime::class, get_class($to));
$this->assertSame($from->format('U'), $to->format('U'));
$this->assertSame($from->format('u'), $to->format('u'));
}
public function testType()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment