Commit 9a9405da authored by Justin Wood's avatar Justin Wood

Bug 545299, XML-RPC WebService should take and return dates and times in UTC. Code part, r+=mkanat

parent b30aeba0
...@@ -21,6 +21,8 @@ package Bugzilla::WebService; ...@@ -21,6 +21,8 @@ package Bugzilla::WebService;
use strict; use strict;
use Date::Parse; use Date::Parse;
use XMLRPC::Lite; use XMLRPC::Lite;
use Bugzilla::Util qw(datetime_from);
use Scalar::Util qw(blessed)
# Used by the JSON-RPC server to convert incoming date fields apprpriately. # Used by the JSON-RPC server to convert incoming date fields apprpriately.
use constant DATE_FIELDS => {}; use constant DATE_FIELDS => {};
...@@ -36,21 +38,24 @@ sub login_exempt { ...@@ -36,21 +38,24 @@ sub login_exempt {
sub type { sub type {
my ($self, $type, $value) = @_; my ($self, $type, $value) = @_;
if ($type eq 'dateTime') { if ($type eq 'dateTime') {
$value = datetime_format($value); $value = $self->datetime_format_outbound($value);
} }
return XMLRPC::Data->type($type)->value($value); return XMLRPC::Data->type($type)->value($value);
} }
sub datetime_format { sub datetime_format_outbound {
my ($date_string) = @_; my ($self, $date) = @_;
my $time = str2time($date_string); my $time = $date;
my ($sec, $min, $hour, $mday, $mon, $year) = localtime $time; if (blessed($date)) {
# This format string was stolen from SOAP::Utils->format_datetime, # We expect this to mean we were sent a datetime object
# which doesn't work but which has almost the right format string. $time->set_time_zone('UTC');
my $iso_datetime = sprintf('%d%02d%02dT%02d:%02d:%02d', } else {
$year + 1900, $mon + 1, $mday, $hour, $min, $sec); # We always send our time in UTC, for consistency.
return $iso_datetime; # passed in value is likely a string, create a datetime object
$time = datetime_from($date, 'UTC');
}
return $iso_datetime = $time->iso8601();
} }
......
...@@ -21,6 +21,7 @@ package Bugzilla::WebService::Bugzilla; ...@@ -21,6 +21,7 @@ package Bugzilla::WebService::Bugzilla;
use strict; use strict;
use base qw(Bugzilla::WebService); use base qw(Bugzilla::WebService);
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Util qw(datetime_from);
use DateTime; use DateTime;
...@@ -49,32 +50,27 @@ sub extensions { ...@@ -49,32 +50,27 @@ sub extensions {
sub timezone { sub timezone {
my $self = shift; my $self = shift;
my $offset = Bugzilla->local_timezone->offset_for_datetime(DateTime->now()); # All Webservices return times in UTC; Use UTC here for backwards compat.
$offset = (($offset / 60) / 60) * 100; return { timezone => $self->type('string', "+0000") };
$offset = sprintf('%+05d', $offset);
return { timezone => $self->type('string', $offset) };
} }
sub time { sub time {
my ($self) = @_; my ($self) = @_;
# All Webservices return times in UTC; Use UTC here for backwards compat.
# Hardcode values where appropriate
my $dbh = Bugzilla->dbh; my $dbh = Bugzilla->dbh;
my $db_time = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); my $db_time = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$db_time = datetime_from($db_time, 'UTC');
my $now_utc = DateTime->now(); my $now_utc = DateTime->now();
my $tz = Bugzilla->local_timezone;
my $now_local = $now_utc->clone->set_time_zone($tz);
my $tz_offset = $tz->offset_for_datetime($now_local);
return { return {
db_time => $self->type('dateTime', $db_time), db_time => $self->type('dateTime', $db_time),
web_time => $self->type('dateTime', $now_local), web_time => $self->type('dateTime', $now_utc),
web_time_utc => $self->type('dateTime', $now_utc), web_time_utc => $self->type('dateTime', $now_utc),
tz_name => $self->type('string', $tz->name), tz_name => $self->type('string', 'UTC'),
tz_offset => $self->type('string', tz_offset => $self->type('string', '+0000'),
$tz->offset_as_string($tz_offset)), tz_short_name => $self->type('string', 'UTC'),
tz_short_name => $self->type('string',
$now_local->time_zone_short_name),
}; };
} }
......
...@@ -19,6 +19,7 @@ package Bugzilla::WebService::Server; ...@@ -19,6 +19,7 @@ package Bugzilla::WebService::Server;
use strict; use strict;
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::Util qw(datetime_from);
sub handle_login { sub handle_login {
my ($self, $class, $method, $full_method) = @_; my ($self, $class, $method, $full_method) = @_;
...@@ -29,4 +30,12 @@ sub handle_login { ...@@ -29,4 +30,12 @@ sub handle_login {
Bugzilla->login(); Bugzilla->login();
} }
sub datetime_format_inbound {
my ($self, $time) = @_;
my $converted = datetime_from($time, Bugzilla->local_timezone);
$time = $converted->ymd() . ' ' . $converted->hms();
return $time
}
1; 1;
...@@ -27,7 +27,6 @@ use base qw(JSON::RPC::Server::CGI Bugzilla::WebService::Server); ...@@ -27,7 +27,6 @@ use base qw(JSON::RPC::Server::CGI Bugzilla::WebService::Server);
use Bugzilla::Error; use Bugzilla::Error;
use Bugzilla::WebService::Constants; use Bugzilla::WebService::Constants;
use Bugzilla::WebService::Util qw(taint_data); use Bugzilla::WebService::Util qw(taint_data);
use Bugzilla::Util qw(datetime_from);
sub new { sub new {
my $class = shift; my $class = shift;
...@@ -77,20 +76,17 @@ sub type { ...@@ -77,20 +76,17 @@ sub type {
} }
elsif ($type eq 'dateTime') { elsif ($type eq 'dateTime') {
# ISO-8601 "YYYYMMDDTHH:MM:SS" with a literal T # ISO-8601 "YYYYMMDDTHH:MM:SS" with a literal T
$retval = $self->datetime_format($value); $retval = $self->datetime_format_outbound($value);
} }
# XXX Will have to implement base64 if Bugzilla starts using it. # XXX Will have to implement base64 if Bugzilla starts using it.
return $retval; return $retval;
} }
sub datetime_format { sub datetime_format_outbound {
my ($self, $date_string) = @_; my $self = shift;
# YUI expects ISO8601 in UTC time; including TZ specifier
# YUI expects ISO8601 in UTC time; uncluding TZ specifier return $self->SUPER::datetime_format_outbound(@_) . 'Z';
my $time = datetime_from($date_string, 'UTC');
my $iso_datetime = $time->iso8601() . 'Z';
return $iso_datetime;
} }
...@@ -192,10 +188,10 @@ sub _argument_type_check { ...@@ -192,10 +188,10 @@ sub _argument_type_check {
my $value = $params->{$field}; my $value = $params->{$field};
if (ref $value eq 'ARRAY') { if (ref $value eq 'ARRAY') {
$params->{$field} = $params->{$field} =
[ map { $self->_bz_convert_datetime($_) } @$value ]; [ map { $self->datetime_format_inbound($_) } @$value ];
} }
else { else {
$params->{$field} = $self->_bz_convert_datetime($value); $params->{$field} = $self->datetime_format_inbound($value);
} }
} }
} }
...@@ -220,14 +216,6 @@ sub _argument_type_check { ...@@ -220,14 +216,6 @@ sub _argument_type_check {
return $params; return $params;
} }
sub _bz_convert_datetime {
my ($self, $time) = @_;
my $converted = datetime_from($time, Bugzilla->local_timezone);
$time = $converted->ymd() . ' ' . $converted->hms();
return $time
}
sub handle_login { sub handle_login {
my $self = shift; my $self = shift;
......
...@@ -106,10 +106,12 @@ sub decode_value { ...@@ -106,10 +106,12 @@ sub decode_value {
# We convert dateTimes to a DB-friendly date format. # We convert dateTimes to a DB-friendly date format.
if ($type eq 'dateTime.iso8601') { if ($type eq 'dateTime.iso8601') {
# We leave off the $ from the end of this regex to allow for possible if ($value !~ /T.*[\-+Z]/i) {
# extensions to the XML-RPC date standard. # The caller did not specify a timezone, so we assume UTC.
$value =~ /^(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/; # pass 'Z' specifier to datetime_from to force it
$value = "$1-$2-$3 $4:$5:$6"; $value = $value . 'Z';
}
$value = $self->datetime_format_inbound($value);
} }
return $value; return $value;
......
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