""" Part of the tagit test suite. A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ # standard imports from datetime import datetime, timezone import math import os import shutil import sys import tempfile import unittest # external imports import pyexiv2 # inner-module imports from tagit.parsing import parse_datetime # objects to test from tagit.shared import time as ttime # constants sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')) from testdata import IMAGE_VALID ## code ## class TestTTime(unittest.TestCase): def setUp(self): self.img = tempfile.mkstemp(prefix='tagit_')[1] shutil.copy(IMAGE_VALID['path'], self.img) self.empty = tempfile.mkstemp(prefix='tagit_')[1] # ensure correct file date (fixed, in utc) #os.system('touch -d "29 Oct 2015 14:20:56" {}'.format(self.empty)) os.utime(self.empty, (1446124856, 1446124856)) def tearDown(self): if os.path.exists(self.img): os.unlink(self.img) if os.path.exists(self.empty): os.unlink(self.empty) def test_conversions(self): """ Files, via os.stat (Timestamp in UTC, Local timezone) Images, from Exif (Timestamp in camera local time, No timezone) Images, from Xmp (Timestamp in camera local time, Timezone from Xmp) now (Timestamp in UTC, Local timezone) Database (timestamp in UTC, timestamp in local time) """ # prepare stat = os.stat(self.empty) meta = pyexiv2.ImageMetadata(self.img) meta.read() # Create time objects in the proper format # Manually defined dt_manual = datetime(2015, 10, 29, 14, 20, 56) # os.stat dt_stat = datetime.fromtimestamp(stat.st_mtime) # exif dt_exif = meta['Exif.Photo.DateTimeOriginal'].value.replace(tzinfo=ttime.NoTimeZone) # xmp dt_xmp = meta['Xmp.exif.DateTimeOriginal'].value # user-specified dt_user = parse_datetime("29.10.2015, 14:20:56") # now dt_now = datetime.now() # UTC offset offset_ref = dt_manual.replace(tzinfo=timezone.utc).timestamp() - dt_manual.timestamp() offset_ref /= 3600 offset_now = dt_now.replace(tzinfo=timezone.utc).timestamp() - dt_now.timestamp() offset_now /= 3600 # Conversions # Comparable local time self.assertEqual(math.floor(ttime.timestamp_loc(dt_manual) % (3600 * 24) / 3600), 14 ) self.assertEqual(math.ceil( ttime.timestamp_loc(dt_manual) % (3600 * 24) / 3600), 15 ) self.assertEqual(ttime.timestamp_loc(dt_manual) % (3600 * 24), 51656 ) self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_stat) ) self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_exif) ) self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_xmp) ) self.assertEqual(ttime.timestamp_loc(dt_manual), ttime.timestamp_loc(dt_user) ) # UTC offset self.assertEqual(ttime.utcoffset(dt_manual), offset_ref) self.assertEqual(ttime.utcoffset(dt_stat), offset_ref) self.assertEqual(ttime.utcoffset(dt_exif), None) self.assertEqual(ttime.utcoffset(dt_xmp), 11) self.assertEqual(ttime.utcoffset(dt_user), offset_ref) self.assertEqual(ttime.utcoffset(dt_now), offset_now) # Comparable UTC self.assertEqual(ttime.timestamp_utc(dt_now), ttime.timestamp_loc(dt_now) - 3600 * offset_now) self.assertEqual(ttime.timestamp_utc(dt_manual), ttime.timestamp_loc(dt_manual) - 3600 * offset_ref ) self.assertEqual(ttime.timestamp_utc(dt_stat), ttime.timestamp_loc(dt_stat) - 3600 * offset_ref) self.assertEqual(ttime.timestamp_utc(dt_user), ttime.timestamp_loc(dt_user) - 3600 * offset_ref) self.assertEqual(ttime.timestamp_utc(dt_exif), ttime.timestamp_loc(dt_exif)) self.assertEqual(ttime.timestamp_utc(dt_xmp), ttime.timestamp_loc(dt_xmp) - 3600 * ttime.utcoffset(dt_xmp)) # Conversion back self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_now)).timestamp(), dt_now.timestamp()) self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_manual)).timestamp(), dt_manual.timestamp()) self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_stat)).timestamp(), dt_stat.timestamp()) self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_user)).timestamp(), dt_user.timestamp()) self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_exif)).timestamp(), dt_exif.timestamp()) self.assertEqual(ttime.from_timestamp_utc( ttime.timestamp_utc(dt_xmp)).timestamp(), dt_xmp.timestamp()) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_now))), ttime.timestamp_loc(dt_now)) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_manual))), ttime.timestamp_loc(dt_manual)) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_stat))), ttime.timestamp_loc(dt_stat)) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_user))), ttime.timestamp_loc(dt_user)) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_exif))), ttime.timestamp_loc(dt_exif)) self.assertEqual(ttime.timestamp_loc(ttime.from_timestamp_loc( ttime.timestamp_loc(dt_xmp))), ttime.timestamp_loc(dt_xmp)) # Conversion to local time + TZO for storage.library # This is used to store file infos # Retrieval in local time # This is used to search for files in a given time range # Retrieval in local time + TZO from storage.library # Retrieve in UTC from storage.library # This is used to compare files (e.g. syncing) across platforms ## main ## if __name__ == '__main__': unittest.main() ## EOF ##