Skip to content

Commit 37a1eb5

Browse files
authored
fix: correctly decode times without microseconds (#375)
Currently custom_time is not being decoded correctly if the value has a zero in the microseconds field. This fixes the issue for custom_time as well as elsewhere by replacing _datetime_to_rfc3339 with _rfc3339_nanos_to_datetime. Fixes #363
1 parent 921553c commit 37a1eb5

File tree

4 files changed

+28
-13
lines changed

4 files changed

+28
-13
lines changed

google/cloud/storage/blob.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from google.cloud import exceptions
5656
from google.cloud._helpers import _bytes_to_unicode
5757
from google.cloud._helpers import _datetime_to_rfc3339
58-
from google.cloud._helpers import _rfc3339_to_datetime
58+
from google.cloud._helpers import _rfc3339_nanos_to_datetime
5959
from google.cloud._helpers import _to_bytes
6060
from google.cloud.exceptions import NotFound
6161
from google.cloud.storage._helpers import _add_generation_match_parameters
@@ -3645,7 +3645,7 @@ def retention_expiration_time(self):
36453645
"""
36463646
value = self._properties.get("retentionExpirationTime")
36473647
if value is not None:
3648-
return _rfc3339_to_datetime(value)
3648+
return _rfc3339_nanos_to_datetime(value)
36493649

36503650
@property
36513651
def self_link(self):
@@ -3731,7 +3731,7 @@ def time_deleted(self):
37313731
"""
37323732
value = self._properties.get("timeDeleted")
37333733
if value is not None:
3734-
return _rfc3339_to_datetime(value)
3734+
return _rfc3339_nanos_to_datetime(value)
37353735

37363736
@property
37373737
def time_created(self):
@@ -3746,7 +3746,7 @@ def time_created(self):
37463746
"""
37473747
value = self._properties.get("timeCreated")
37483748
if value is not None:
3749-
return _rfc3339_to_datetime(value)
3749+
return _rfc3339_nanos_to_datetime(value)
37503750

37513751
@property
37523752
def updated(self):
@@ -3761,7 +3761,7 @@ def updated(self):
37613761
"""
37623762
value = self._properties.get("updated")
37633763
if value is not None:
3764-
return _rfc3339_to_datetime(value)
3764+
return _rfc3339_nanos_to_datetime(value)
37653765

37663766
@property
37673767
def custom_time(self):
@@ -3776,7 +3776,7 @@ def custom_time(self):
37763776
"""
37773777
value = self._properties.get("customTime")
37783778
if value is not None:
3779-
return _rfc3339_to_datetime(value)
3779+
return _rfc3339_nanos_to_datetime(value)
37803780

37813781
@custom_time.setter
37823782
def custom_time(self, value):

google/cloud/storage/bucket.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from google.api_core import datetime_helpers
2929
from google.cloud._helpers import _datetime_to_rfc3339
3030
from google.cloud._helpers import _NOW
31-
from google.cloud._helpers import _rfc3339_to_datetime
31+
from google.cloud._helpers import _rfc3339_nanos_to_datetime
3232
from google.cloud.exceptions import NotFound
3333
from google.api_core.iam import Policy
3434
from google.cloud.storage import _signing
@@ -499,7 +499,7 @@ def uniform_bucket_level_access_locked_time(self):
499499
ubla = self.get("uniformBucketLevelAccess", {})
500500
stamp = ubla.get("lockedTime")
501501
if stamp is not None:
502-
stamp = _rfc3339_to_datetime(stamp)
502+
stamp = _rfc3339_nanos_to_datetime(stamp)
503503
return stamp
504504

505505
@property
@@ -2556,7 +2556,7 @@ def retention_policy_effective_time(self):
25562556
if policy is not None:
25572557
timestamp = policy.get("effectiveTime")
25582558
if timestamp is not None:
2559-
return _rfc3339_to_datetime(timestamp)
2559+
return _rfc3339_nanos_to_datetime(timestamp)
25602560

25612561
@property
25622562
def retention_policy_locked(self):
@@ -2675,7 +2675,7 @@ def time_created(self):
26752675
"""
26762676
value = self._properties.get("timeCreated")
26772677
if value is not None:
2678-
return _rfc3339_to_datetime(value)
2678+
return _rfc3339_nanos_to_datetime(value)
26792679

26802680
@property
26812681
def versioning_enabled(self):

google/cloud/storage/hmac_key.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from google.cloud.exceptions import NotFound
16-
from google.cloud._helpers import _rfc3339_to_datetime
16+
from google.cloud._helpers import _rfc3339_nanos_to_datetime
1717

1818
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
1919
from google.cloud.storage.retry import DEFAULT_RETRY
@@ -151,7 +151,7 @@ def time_created(self):
151151
"""
152152
value = self._properties.get("timeCreated")
153153
if value is not None:
154-
return _rfc3339_to_datetime(value)
154+
return _rfc3339_nanos_to_datetime(value)
155155

156156
@property
157157
def updated(self):
@@ -164,7 +164,7 @@ def updated(self):
164164
"""
165165
value = self._properties.get("updated")
166166
if value is not None:
167-
return _rfc3339_to_datetime(value)
167+
return _rfc3339_nanos_to_datetime(value)
168168

169169
@property
170170
def path(self):

tests/system/test_system.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,21 @@ def test_upload_blob_custom_time(self):
10631063
custom_time = same_blob.custom_time.replace(tzinfo=None)
10641064
self.assertEqual(custom_time, current_time)
10651065

1066+
def test_blob_custom_time_no_micros(self):
1067+
# Test that timestamps without microseconds are treated correctly by
1068+
# custom_time encoding/decoding.
1069+
blob = self.bucket.blob("CustomTimeNoMicrosBlob")
1070+
file_contents = b"Hello World"
1071+
time_without_micros = datetime.datetime(2021, 2, 10, 12, 30)
1072+
blob.custom_time = time_without_micros
1073+
blob.upload_from_string(file_contents)
1074+
self.case_blobs_to_delete.append(blob)
1075+
1076+
same_blob = self.bucket.blob(("CustomTimeNoMicrosBlob"))
1077+
same_blob.reload(projection="full")
1078+
custom_time = same_blob.custom_time.replace(tzinfo=None)
1079+
self.assertEqual(custom_time, time_without_micros)
1080+
10661081
def test_blob_crc32_md5_hash(self):
10671082
blob = self.bucket.blob("MyBuffer")
10681083
file_contents = b"Hello World"

0 commit comments

Comments
 (0)