Commit ac9090d0 authored by Max Kanat-Alexander's avatar Max Kanat-Alexander Committed by Justin Wood

Bug 545299, XML-RPC WebService should take and return dates and times in UTC. POD

parents b30aeba0 9a9405da
......@@ -21,6 +21,8 @@ package Bugzilla::WebService;
use strict;
use Date::Parse;
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.
use constant DATE_FIELDS => {};
......@@ -36,21 +38,24 @@ sub login_exempt {
sub type {
my ($self, $type, $value) = @_;
if ($type eq 'dateTime') {
$value = datetime_format($value);
$value = $self->datetime_format_outbound($value);
}
return XMLRPC::Data->type($type)->value($value);
}
sub datetime_format {
my ($date_string) = @_;
my $time = str2time($date_string);
my ($sec, $min, $hour, $mday, $mon, $year) = localtime $time;
# This format string was stolen from SOAP::Utils->format_datetime,
# which doesn't work but which has almost the right format string.
my $iso_datetime = sprintf('%d%02d%02dT%02d:%02d:%02d',
$year + 1900, $mon + 1, $mday, $hour, $min, $sec);
return $iso_datetime;
sub datetime_format_outbound {
my ($self, $date) = @_;
my $time = $date;
if (blessed($date)) {
# We expect this to mean we were sent a datetime object
$time->set_time_zone('UTC');
} else {
# We always send our time in UTC, for consistency.
# 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;
use strict;
use base qw(Bugzilla::WebService);
use Bugzilla::Constants;
use Bugzilla::Util qw(datetime_from);
use DateTime;
......@@ -49,32 +50,27 @@ sub extensions {
sub timezone {
my $self = shift;
my $offset = Bugzilla->local_timezone->offset_for_datetime(DateTime->now());
$offset = (($offset / 60) / 60) * 100;
$offset = sprintf('%+05d', $offset);
return { timezone => $self->type('string', $offset) };
# All Webservices return times in UTC; Use UTC here for backwards compat.
return { timezone => $self->type('string', "+0000") };
}
sub time {
my ($self) = @_;
# All Webservices return times in UTC; Use UTC here for backwards compat.
# Hardcode values where appropriate
my $dbh = Bugzilla->dbh;
my $db_time = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$db_time = datetime_from($db_time, 'UTC');
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 {
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),
tz_name => $self->type('string', $tz->name),
tz_offset => $self->type('string',
$tz->offset_as_string($tz_offset)),
tz_short_name => $self->type('string',
$now_local->time_zone_short_name),
tz_name => $self->type('string', 'UTC'),
tz_offset => $self->type('string', '+0000'),
tz_short_name => $self->type('string', 'UTC'),
};
}
......@@ -172,9 +168,7 @@ Use L</time> instead.
=item B<Description>
Returns the timezone of the server Bugzilla is running on. This is
important because all dates/times that the webservice interface
returns will be in this timezone.
Returns the timezone that Bugzilla expects dates and times in.
=item B<Params> (none)
......@@ -183,12 +177,21 @@ returns will be in this timezone.
A hash with a single item, C<timezone>, that is the timezone offset as a
string in (+/-)XXXX (RFC 2822) format.
=item B<History>
=over
=item As of Bugzilla B<3.6>, the timezone returned is always C<+0000>
(the UTC timezone).
=back
=back
=item C<time>
B<UNSTABLE>
B<EXPERIMENTAL>
=over
......@@ -207,8 +210,8 @@ A struct with the following items:
=item C<db_time>
C<dateTime> The current time in Bugzilla's B<local time zone>, according
to the Bugzilla I<database server>.
C<dateTime> The current time in UTC, according to the Bugzilla
I<database server>.
Note that Bugzilla assumes that the database and the webserver are running
in the same time zone. However, if the web server and the database server
......@@ -217,8 +220,8 @@ rely on for doing searches and other input to the WebService.
=item C<web_time>
C<dateTime> This is the current time in Bugzilla's B<local time zone>,
according to Bugzilla's I<web server>.
C<dateTime> This is the current time in UTC, according to Bugzilla's
I<web server>.
This might be different by a second from C<db_time> since this comes from
a different source. If it's any more different than a second, then there is
......@@ -227,26 +230,23 @@ rely on the C<db_time>, not the C<web_time>.
=item C<web_time_utc>
The same as C<web_time>, but in the B<UTC> time zone instead of the local
time zone.
Identical to C<web_time>. (Exists only for backwards-compatibility with
versions of Bugzilla before 3.6.)
=item C<tz_name>
C<string> The long name of the time zone that the Bugzilla web server is
in. Will usually look something like: C<America/Los Angeles>
C<string> The literal string C<UTC>. (Exists only for backwards-compatibility
with versions of Bugzilla before 3.6.)
=item C<tz_short_name>
C<string> The "short name" of the time zone that the Bugzilla web server
is in. This should only be used for display, and not relied on for your
programs, because different time zones can have the same short name.
(For example, there are two C<EST>s.)
This will look something like: C<PST>.
C<string> The literal string C<UTC>. (Exists only for backwards-compatibility
with versions of Bugzilla before 3.6.)
=item C<tz_offset>
C<string> The timezone offset as a string in (+/-)XXXX (RFC 2822) format.
C<string> The literal string C<+0000>. (Exists only for backwards-compatibility
with versions of Bugzilla before 3.6.)
=back
......@@ -256,6 +256,10 @@ C<string> The timezone offset as a string in (+/-)XXXX (RFC 2822) format.
=item Added in Bugzilla B<3.4>.
=item As of Bugzilla B<3.6>, this method returns all data as though the server
were in the UTC timezone, instead of returning information in the server's
local timezone.
=back
=back
......
......@@ -19,6 +19,7 @@ package Bugzilla::WebService::Server;
use strict;
use Bugzilla::Error;
use Bugzilla::Util qw(datetime_from);
sub handle_login {
my ($self, $class, $method, $full_method) = @_;
......@@ -29,4 +30,12 @@ sub handle_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;
......@@ -27,7 +27,6 @@ use base qw(JSON::RPC::Server::CGI Bugzilla::WebService::Server);
use Bugzilla::Error;
use Bugzilla::WebService::Constants;
use Bugzilla::WebService::Util qw(taint_data);
use Bugzilla::Util qw(datetime_from);
sub new {
my $class = shift;
......@@ -77,20 +76,17 @@ sub type {
}
elsif ($type eq 'dateTime') {
# 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.
return $retval;
}
sub datetime_format {
my ($self, $date_string) = @_;
# YUI expects ISO8601 in UTC time; uncluding TZ specifier
my $time = datetime_from($date_string, 'UTC');
my $iso_datetime = $time->iso8601() . 'Z';
return $iso_datetime;
sub datetime_format_outbound {
my $self = shift;
# YUI expects ISO8601 in UTC time; including TZ specifier
return $self->SUPER::datetime_format_outbound(@_) . 'Z';
}
......@@ -192,10 +188,10 @@ sub _argument_type_check {
my $value = $params->{$field};
if (ref $value eq 'ARRAY') {
$params->{$field} =
[ map { $self->_bz_convert_datetime($_) } @$value ];
[ map { $self->datetime_format_inbound($_) } @$value ];
}
else {
$params->{$field} = $self->_bz_convert_datetime($value);
$params->{$field} = $self->datetime_format_inbound($value);
}
}
}
......@@ -220,14 +216,6 @@ sub _argument_type_check {
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 {
my $self = shift;
......
......@@ -106,10 +106,12 @@ sub decode_value {
# We convert dateTimes to a DB-friendly date format.
if ($type eq 'dateTime.iso8601') {
# We leave off the $ from the end of this regex to allow for possible
# extensions to the XML-RPC date standard.
$value =~ /^(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
$value = "$1-$2-$3 $4:$5:$6";
if ($value !~ /T.*[\-+Z]/i) {
# The caller did not specify a timezone, so we assume UTC.
# pass 'Z' specifier to datetime_from to force it
$value = $value . 'Z';
}
$value = $self->datetime_format_inbound($value);
}
return $value;
......@@ -288,7 +290,9 @@ API via: C<http://bugzilla.yourdomain.com/xmlrpc.cgi>
=head1 PARAMETERS
C<dateTime> fields are the standard C<dateTime.iso8601> XML-RPC field. They
should be in C<YYYY-MM-DDTHH:MM:SS> format (where C<T> is a literal T).
should be in C<YYYY-MM-DDTHH:MM:SS> format (where C<T> is a literal T). As
of Bugzilla B<3.6>, Bugzilla always expects C<dateTime> fields to be in the
UTC timezone, and all returned C<dateTime> values are in the UTC timezone.
All other fields are standard XML-RPC types.
......@@ -306,6 +310,14 @@ Normally, XML-RPC does not allow empty values for C<int>, C<double>, or
C<dateTime.iso8601> fields. Bugzilla does--it treats empty values as
C<undef> (called C<NULL> or C<None> in some programming languages).
Bugzilla accepts a timezone specifier at the end of C<dateTime.iso8601>
fields that are specified as method arguments. The format of the timezone
specifier is specified in the ISO-8601 standard. If no timezone specifier
is included, the passed-in time is assumed to be in the UTC timezone.
Bugzilla will never output a timezone specifier on returned data, because
doing so would violate the XML-RPC specification. All returned times are in
the UTC timezone.
Bugzilla also accepts an element called C<< <nil> >>, as specified by the
XML-RPC extension here: L<http://ontosys.com/xml-rpc/extensions.php>, which
is always considered to be C<undef>, no matter what it contains.
......
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