Repository: googleapis/google-cloud-python

Issue:
pubsub Message class does not set timestamp
`pubsub.Message` timestamps are not set.  There are two object properties, neither of them is set.  What's even more confusing is that attempting to access an attribute of an instance throws an error. 

To reproduce:

``` python
from gcloud import pubsub
t = pubsub.Client(project='...').topic('${TOPIC}')
# MAY NEED t.create()
s = t.subscription('${SUBSCRIPTION}')
# MAY NEED s.create()
t.publish('test data')  # RETURNS A MSG ID
ack_id, m = s.pull()  # WAS t.pull()
```



Discussion:
Trying to repro right now.

@kir-google-cloud-pubsub I can't reproduce. `ack_id, m = s.pull()` is essentially invalid, but maybe you meant to say

``` python
pairs = s.pull()
ack_id, m = pairs[0]
```

When I run the slightly modified code, I get

``` python
>>> pub_id = t.publish('test data')
>>> pub_id
u'44126525330808'
>>> msgs = s.pull()
>>> assert len(msgs) == 1
>>> ack_id, m = msgs[0]
>>> m.message_id
u'44126525330808'
>>> m.message_id == pub_id
True
>>> m.data
'test data'
```

---

Any more details you can provide?

You are right -- about the mistake I meant.  In your code, try m.timestamp -- it should be set, but is not.

also, m.service_timestamp

There are two timestamps possible on a message:
- `message.timestamp` is in the `attributes` of the message, and must therefore be set by the topic:  setting those is controlled by the `topic.timestamp_messages` attribute, which defaults to `False`.
- `message.service_timestamp` is set by the back-end, and parsed from the `publishTime` field of the pulled message resource.

To activate the first kind:

``` python
from gcloud.pubsub.client import Client
client = Client(project='...')
topic = client.topic('${TOPIC}', timestamp_messages=True)
t.create()
s = t.subscription('${SUBSCRIPTION}')
s.create()
t.publish('test data')
ack_ids = []
for ack_id, m in s.pull():
    ack_ids.append(ack_id)
    print(m.data, m.timestamp, m.service_timetamp)
sub.acknowledge(ack_ids)
```

On my box, the previous example yields:

```
(datetime.datetime(2016, 9, 7, 20, 42, 18, 810563, tzinfo=<UTC>), 'test_data', None)
```

That tells me that for some reason the back-end is not setting the `publishTime` field in the message resource it is returning, but that the timestamp attribute set by the publishing application _is_ being set.

It appears that the GAX/gRPC adapter for pubsub is not mediating between the `publish_time` [field](https://cloud.google.com/pubsub/reference/rpc/google.pubsub.v1#google.pubsub.v1.PubsubMessage.FIELDS.google.protobuf.Timestamp.google.pubsub.v1.PubsubMessage.publish_time) in the `PublishedMessage` proto and the expected `publishTime` field (as returned from the JSON-over-HTTP API).


Resolving patch:
diff --git a/google/cloud/pubsub/_gax.py b/google/cloud/pubsub/_gax.py
--- a/google/cloud/pubsub/_gax.py
+++ b/google/cloud/pubsub/_gax.py
@@ -24,6 +24,7 @@
 # pylint: disable=ungrouped-imports
 from google.cloud._helpers import _to_bytes
 from google.cloud._helpers import exc_to_code
+from google.cloud._helpers import _pb_timestamp_to_rfc3339
 from google.cloud.exceptions import Conflict
 from google.cloud.exceptions import NotFound
 # pylint: enable=ungrouped-imports
@@ -482,6 +483,7 @@ def _message_pb_to_mapping(message_pb):
         'messageId': message_pb.message_id,
         'data': message_pb.data,
         'attributes': message_pb.attributes,
+        'publishTime': _pb_timestamp_to_rfc3339(message_pb.publish_time),
     }
 
 


Test patch:
diff --git a/unit_tests/pubsub/test__gax.py b/unit_tests/pubsub/test__gax.py
--- a/unit_tests/pubsub/test__gax.py
+++ b/unit_tests/pubsub/test__gax.py
@@ -585,13 +585,25 @@ def test_subscription_modify_push_config_error(self):
 
     def test_subscription_pull_explicit(self):
         import base64
+        import datetime
+        from google.cloud._helpers import UTC
+        from google.cloud._helpers import _datetime_to_pb_timestamp
+        from google.cloud._helpers import _datetime_to_rfc3339
+        NOW = datetime.datetime.utcnow().replace(tzinfo=UTC)
+        NOW_PB = _datetime_to_pb_timestamp(NOW)
+        NOW_RFC3339 = _datetime_to_rfc3339(NOW)
         PAYLOAD = b'This is the message text'
         B64 = base64.b64encode(PAYLOAD).decode('ascii')
         ACK_ID = 'DEADBEEF'
         MSG_ID = 'BEADCAFE'
-        MESSAGE = {'messageId': MSG_ID, 'data': B64, 'attributes': {'a': 'b'}}
+        MESSAGE = {
+            'messageId': MSG_ID,
+            'data': B64,
+            'attributes': {'a': 'b'},
+            'publishTime': NOW_RFC3339,
+        }
         RECEIVED = [{'ackId': ACK_ID, 'message': MESSAGE}]
-        message_pb = _PubsubMessagePB(MSG_ID, B64, {'a': 'b'})
+        message_pb = _PubsubMessagePB(MSG_ID, B64, {'a': 'b'}, NOW_PB)
         response_pb = _PullResponsePB([_ReceivedMessagePB(ACK_ID, message_pb)])
         gax_api = _GAXSubscriberAPI(_pull_response=response_pb)
         api = self._makeOne(gax_api)
@@ -891,10 +903,11 @@ def __init__(self, push_endpoint):
 
 class _PubsubMessagePB(object):
 
-    def __init__(self, message_id, data, attributes):
+    def __init__(self, message_id, data, attributes, publish_time):
         self.message_id = message_id
         self.data = data
         self.attributes = attributes
+        self.publish_time = publish_time
 
 
 class _ReceivedMessagePB(object):