Skip to content

Commit

Permalink
Merge pull request #2041 from supriyagarg/rfc3339
Browse files Browse the repository at this point in the history
Update the helper _datetime_to_rfc3339 to handle time zones.
  • Loading branch information
dhermes authored Aug 1, 2016
2 parents 3478b9c + 1f55e4d commit 5a6ba14
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 70 deletions.
12 changes: 10 additions & 2 deletions gcloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,23 @@ def _rfc3339_nanos_to_datetime(dt_str):
return bare_seconds.replace(microsecond=micros, tzinfo=UTC)


def _datetime_to_rfc3339(value):
"""Convert a native timestamp to a string.
def _datetime_to_rfc3339(value, ignore_zone=True):
"""Convert a timestamp to a string.
:type value: :class:`datetime.datetime`
:param value: The datetime object to be converted to a string.
:type ignore_zone: boolean
:param ignore_zone: If True, then the timezone (if any) of the datetime
object is ignored.
:rtype: str
:returns: The string representing the datetime stamp.
"""
if not ignore_zone and value.tzinfo is not None:
# Convert to UTC and remove the time zone info.
value = value.replace(tzinfo=None) - value.utcoffset()

return value.strftime(_RFC3339_MICROS)


Expand Down
25 changes: 6 additions & 19 deletions gcloud/monitoring/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import six

from gcloud._helpers import _datetime_to_rfc3339
from gcloud.monitoring._dataframe import _build_dataframe
from gcloud.monitoring.timeseries import TimeSeries

Expand Down Expand Up @@ -498,9 +499,12 @@ def _build_query_params(self, headers_only=False,
"""
yield 'filter', self.filter

yield 'interval.endTime', _format_timestamp(self._end_time)
yield 'interval.endTime', _datetime_to_rfc3339(
self._end_time, ignore_zone=False)

if self._start_time is not None:
yield 'interval.startTime', _format_timestamp(self._start_time)
yield 'interval.startTime', _datetime_to_rfc3339(
self._start_time, ignore_zone=False)

if self._per_series_aligner is not None:
yield 'aggregation.perSeriesAligner', self._per_series_aligner
Expand Down Expand Up @@ -651,20 +655,3 @@ def _build_label_filter(category, *args, **kwargs):
terms.append(term.format(key=key, value=value))

return ' AND '.join(sorted(terms))


def _format_timestamp(timestamp):
"""Convert a datetime object to a string as required by the API.
:type timestamp: :class:`datetime.datetime`
:param timestamp: A datetime object.
:rtype: string
:returns: The formatted timestamp. For example:
``"2016-02-17T19:18:01.763000Z"``
"""
if timestamp.tzinfo is not None:
# Convert to UTC and remove the time zone info.
timestamp = timestamp.replace(tzinfo=None) - timestamp.utcoffset()

return timestamp.isoformat() + 'Z'
5 changes: 3 additions & 2 deletions gcloud/monitoring/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def _makeOne(self, *args, **kwargs):

def test_query(self):
import datetime
from gcloud._helpers import _datetime_to_rfc3339
from gcloud.exceptions import NotFound

START_TIME = datetime.datetime(2016, 4, 6, 22, 5, 0)
Expand Down Expand Up @@ -119,8 +120,8 @@ def P(timestamp, value):
'path': '/projects/{project}/timeSeries/'.format(project=PROJECT),
'query_params': [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', END_TIME.isoformat() + 'Z'),
('interval.startTime', START_TIME.isoformat() + 'Z'),
('interval.endTime', _datetime_to_rfc3339(END_TIME)),
('interval.startTime', _datetime_to_rfc3339(START_TIME)),
],
}

Expand Down
47 changes: 16 additions & 31 deletions gcloud/monitoring/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ def _getTargetClass(self):
def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs)

@staticmethod
def _make_timestamp(value):
from gcloud._helpers import _datetime_to_rfc3339
return _datetime_to_rfc3339(value)

def test_constructor_minimal(self):
client = _Client(project=PROJECT, connection=_Connection())
query = self._makeOne(client)
Expand Down Expand Up @@ -217,7 +222,7 @@ def test_request_parameters_minimal(self):
actual = list(query._build_query_params())
expected = [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
]
self.assertEqual(actual, expected)

Expand Down Expand Up @@ -246,8 +251,8 @@ def test_request_parameters_maximal(self):
page_token=PAGE_TOKEN))
expected = [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.startTime', T0.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
('interval.startTime', self._make_timestamp(T0)),
('aggregation.perSeriesAligner', ALIGNER),
('aggregation.alignmentPeriod', PERIOD),
('aggregation.crossSeriesReducer', REDUCER),
Expand Down Expand Up @@ -318,8 +323,8 @@ def test_iteration(self):
'path': '/projects/{project}/timeSeries/'.format(project=PROJECT),
'query_params': [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.startTime', T0.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
('interval.startTime', self._make_timestamp(T0)),
],
}

Expand Down Expand Up @@ -398,8 +403,8 @@ def test_iteration_paged(self):
'path': '/projects/{project}/timeSeries/'.format(project=PROJECT),
'query_params': [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.startTime', T0.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
('interval.startTime', self._make_timestamp(T0)),
],
}

Expand Down Expand Up @@ -432,8 +437,8 @@ def test_iteration_empty(self):
'path': '/projects/{project}/timeSeries/'.format(project=PROJECT),
'query_params': [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.startTime', T0.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
('interval.startTime', self._make_timestamp(T0)),
],
}
request, = connection._requested
Expand Down Expand Up @@ -482,8 +487,8 @@ def test_iteration_headers_only(self):
'path': '/projects/{project}/timeSeries/'.format(project=PROJECT),
'query_params': [
('filter', 'metric.type = "{type}"'.format(type=METRIC_TYPE)),
('interval.endTime', T1.isoformat() + 'Z'),
('interval.startTime', T0.isoformat() + 'Z'),
('interval.endTime', self._make_timestamp(T1)),
('interval.startTime', self._make_timestamp(T0)),
('view', 'HEADERS'),
],
}
Expand Down Expand Up @@ -596,26 +601,6 @@ def test_resource_type_suffix(self):
self.assertEqual(actual, expected)


class Test__format_timestamp(unittest2.TestCase):

def _callFUT(self, timestamp):
from gcloud.monitoring.query import _format_timestamp
return _format_timestamp(timestamp)

def test_naive(self):
from datetime import datetime
TIMESTAMP = datetime(2016, 4, 5, 13, 30, 0)
timestamp = self._callFUT(TIMESTAMP)
self.assertEqual(timestamp, '2016-04-05T13:30:00Z')

def test_with_timezone(self):
from datetime import datetime
from gcloud._helpers import UTC
TIMESTAMP = datetime(2016, 4, 5, 13, 30, 0, tzinfo=UTC)
timestamp = self._callFUT(TIMESTAMP)
self.assertEqual(timestamp, '2016-04-05T13:30:00Z')


class _Connection(object):

def __init__(self, *responses):
Expand Down
56 changes: 40 additions & 16 deletions gcloud/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,28 +648,52 @@ def test_w_naonseconds(self):

class Test__datetime_to_rfc3339(unittest2.TestCase):

def _callFUT(self, value):
def _callFUT(self, *args, **kwargs):
from gcloud._helpers import _datetime_to_rfc3339
return _datetime_to_rfc3339(value)
return _datetime_to_rfc3339(*args, **kwargs)

def test_it(self):
@staticmethod
def _make_timezone(offset):
from gcloud._helpers import _UTC

class CET(_UTC):
_tzname = 'CET'
_utcoffset = offset

return CET()

def test_w_utc_datetime(self):
import datetime
from gcloud._helpers import UTC

year = 2009
month = 12
day = 17
hour = 12
minute = 44
seconds = 32
micros = 123456
TIMESTAMP = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=UTC)
result = self._callFUT(TIMESTAMP, ignore_zone=False)
self.assertEqual(result, '2016-04-05T13:30:00.000000Z')

to_convert = datetime.datetime(
year, month, day, hour, minute, seconds, micros, UTC)
dt_str = '%d-%02d-%02dT%02d:%02d:%02d.%06dZ' % (
year, month, day, hour, minute, seconds, micros)
result = self._callFUT(to_convert)
self.assertEqual(result, dt_str)
def test_w_non_utc_datetime(self):
import datetime
from gcloud._helpers import _UTC

zone = self._make_timezone(offset=datetime.timedelta(hours=-1))
TIMESTAMP = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=zone)
result = self._callFUT(TIMESTAMP, ignore_zone=False)
self.assertEqual(result, '2016-04-05T14:30:00.000000Z')

def test_w_non_utc_datetime_and_ignore_zone(self):
import datetime
from gcloud._helpers import _UTC

zone = self._make_timezone(offset=datetime.timedelta(hours=-1))
TIMESTAMP = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=zone)
result = self._callFUT(TIMESTAMP)
self.assertEqual(result, '2016-04-05T13:30:00.000000Z')

def test_w_naive_datetime(self):
import datetime

TIMESTAMP = datetime.datetime(2016, 4, 5, 13, 30, 0)
result = self._callFUT(TIMESTAMP)
self.assertEqual(result, '2016-04-05T13:30:00.000000Z')


class Test__to_bytes(unittest2.TestCase):
Expand Down

0 comments on commit 5a6ba14

Please sign in to comment.