From 0295ef85634db53c61a45f1603a587b8f88ec567 Mon Sep 17 00:00:00 2001 From: Cam Hutchison Date: Wed, 8 Apr 2015 21:09:58 +1000 Subject: [PATCH 1/2] archive: Add testcases for microsecond handling. datetime.isoformat() has different output depending on whether microseconds are zero or not. Add test cases to ensure we handle both cases correctly in an archive. --- attic/testsuite/archive.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/attic/testsuite/archive.py b/attic/testsuite/archive.py index 8d478f5f2..a0af6c534 100644 --- a/attic/testsuite/archive.py +++ b/attic/testsuite/archive.py @@ -1,7 +1,10 @@ import msgpack from attic.testsuite import AtticTestCase -from attic.archive import CacheChunkBuffer, RobustUnpacker +from attic.testsuite.mock import Mock +from attic.archive import Archive, CacheChunkBuffer, RobustUnpacker from attic.key import PlaintextKey +from attic.helpers import Manifest +from datetime import datetime, timezone class MockCache: @@ -14,6 +17,27 @@ def add_chunk(self, id, data, stats=None): return id, len(data), len(data) +class ArchiveTimestampTestCase(AtticTestCase): + + def _test_timestamp_parsing(self, isoformat, expected): + repository = Mock() + key = PlaintextKey() + manifest = Manifest(repository, key) + a = Archive(repository, key, manifest, 'test', create=True) + a.metadata = {b'time': isoformat} + self.assert_equal(a.ts, expected) + + def test_with_microseconds(self): + self._test_timestamp_parsing( + '1970-01-01T00:00:01.000001', + datetime(1970, 1, 1, 0, 0, 1, 1, timezone.utc)) + + def test_without_microseconds(self): + self._test_timestamp_parsing( + '1970-01-01T00:00:01', + datetime(1970, 1, 1, 0, 0, 1, 0, timezone.utc)) + + class ChunkBufferTestCase(AtticTestCase): def test(self): From 9f99aa1abfa265b562f2874b92ca6d2269bed12b Mon Sep 17 00:00:00 2001 From: Cam Hutchison Date: Mon, 6 Apr 2015 22:17:38 +1000 Subject: [PATCH 2/2] archive: Fix parsing with missing microseconds. Archive timestamps are stored as the output of datetime.isoformat(). This function omits microseconds in the string output if the microseconds are zero (as documented and explained at https://bugs.python.org/issue7342). Parsing of timestamps assumes there are always microseconds present after a decimal point. This is not always true. Handle this case where it is not true by explicitly using '0' microseconds when not present. This commit fixes #282 --- attic/archive.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/attic/archive.py b/attic/archive.py index d78ce4b85..df789ac24 100644 --- a/attic/archive.py +++ b/attic/archive.py @@ -163,8 +163,11 @@ def load(self, id): @property def ts(self): """Timestamp of archive creation in UTC""" - t, f = self.metadata[b'time'].split('.', 1) - return datetime.strptime(t, '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) + timedelta(seconds=float('.' + f)) + t = self.metadata[b'time'].split('.', 1) + dt = datetime.strptime(t[0], '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc) + if len(t) > 1: + dt += timedelta(seconds=float('.' + t[1])) + return dt def __repr__(self): return 'Archive(%r)' % self.name