[schema] Use TZDatetime for creation_date and modification_date
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 18 Dec 2015 09:23:23 +0100
changeset 11033 63d860a14a17
parent 11032 38afb7e23c6c
child 11034 75d752e6daf7
[schema] Use TZDatetime for creation_date and modification_date No work has been done for form fields/widgets since creation_date / modification_date are by default not editable through the default UI. One may want to add such feature at some point. Time will be displayed as UTC, let the end-application decide otherwise by customizing the tzdatetime printer if desired. Closes #4848923
dataimport/massive_store.py
dataimport/stores.py
dataimport/test/test_stores.py
hooks/metadata.py
hooks/test/unittest_hooks.py
hooks/workflow.py
misc/migration/3.22.0_Any.py
misc/scripts/ldapuser2ldapfeed.py
schemas/base.py
server/checkintegrity.py
server/test/unittest_rql2sql.py
web/views/editcontroller.py
web/views/forms.py
--- a/dataimport/massive_store.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/dataimport/massive_store.py	Fri Dec 18 09:23:23 2015 +0100
@@ -119,7 +119,7 @@
         self._dbh = PGHelper(self._cnx, pg_schema)
         self._data_entities = defaultdict(list)
         self._data_relations = defaultdict(list)
-        self._now = datetime.now()
+        self._now = datetime.utcnow()
         self._default_cwuri = make_uid('_auto_generated')
         self._count_cwuri = 0
         self.on_commit_callback = on_commit_callback
--- a/dataimport/stores.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/dataimport/stores.py	Fri Dec 18 09:23:23 2015 +0100
@@ -259,7 +259,7 @@
             source = cnx.repo.system_source
         self.source = source
         self.create_eid = cnx.repo.system_source.create_eid
-        self.time = datetime.now()
+        self.time = datetime.utcnow()
         # attributes/relations shared by all entities of the same type
         self.etype_attrs = []
         self.etype_rels = []
--- a/dataimport/test/test_stores.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/dataimport/test/test_stores.py	Fri Dec 18 09:23:23 2015 +0100
@@ -75,7 +75,7 @@
             metagen = stores.MetaGenerator(cnx)
             # hijack gen_modification_date to ensure we don't go through it
             metagen.gen_modification_date = None
-            md = DT.datetime.now() - DT.timedelta(days=1)
+            md = DT.datetime.utcnow() - DT.timedelta(days=1)
             entity, rels = metagen.base_etype_dicts('CWUser')
             entity.cw_edited.update(dict(modification_date=md))
             with cnx.ensure_cnx_set:
--- a/hooks/metadata.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/hooks/metadata.py	Fri Dec 18 09:23:23 2015 +0100
@@ -41,7 +41,7 @@
     events = ('before_add_entity',)
 
     def __call__(self):
-        timestamp = datetime.now()
+        timestamp = datetime.utcnow()
         edited = self.entity.cw_edited
         if not edited.get('creation_date'):
             edited['creation_date'] = timestamp
@@ -64,7 +64,7 @@
         # XXX to be really clean, we should turn off modification_date update
         # explicitly on each command where we do not want that behaviour.
         if not self._cw.vreg.config.repairing:
-            self.entity.cw_edited.setdefault('modification_date', datetime.now())
+            self.entity.cw_edited.setdefault('modification_date', datetime.utcnow())
 
 
 class SetCreatorOp(hook.DataOperationMixIn, hook.Operation):
--- a/hooks/test/unittest_hooks.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/hooks/test/unittest_hooks.py	Fri Dec 18 09:23:23 2015 +0100
@@ -115,7 +115,7 @@
 
     def test_metadata_creation_modification_date(self):
         with self.admin_access.repo_cnx() as cnx:
-            _now = datetime.now()
+            _now = datetime.utcnow()
             entity = cnx.create_entity('Workflow', name=u'wf1')
             self.assertEqual((entity.creation_date - _now).seconds, 0)
             self.assertEqual((entity.modification_date - _now).seconds, 0)
--- a/hooks/workflow.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/hooks/workflow.py	Fri Dec 18 09:23:23 2015 +0100
@@ -320,7 +320,7 @@
             return
         entity = self._cw.entity_from_eid(self.eidfrom)
         try:
-            entity.cw_set(modification_date=datetime.now())
+            entity.cw_set(modification_date=datetime.utcnow())
         except RepositoryError as ex:
             # usually occurs if entity is coming from a read-only source
             # (eg ldap user)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.22.0_Any.py	Fri Dec 18 09:23:23 2015 +0100
@@ -0,0 +1,21 @@
+if confirm('use Europe/Paris as timezone?'):
+    timezone = 'Europe/Paris'
+else:
+    import pytz
+    while True:
+        timezone = raw_input('enter your timezone')
+        if timezone in pytz.common_timezones:
+            break
+
+dbdriver = repo.system_source.dbdriver
+if dbdriver == 'postgres':
+    sql("SET TIME ZONE '%s'" % timezone)
+
+for entity in schema.entities():
+    if entity.final:
+        continue
+    change_attribute_type(entity.type, 'creation_date', 'TZDatetime', ask_confirm=False)
+    change_attribute_type(entity.type, 'modification_date', 'TZDatetime', ask_confirm=False)
+
+if dbdriver == 'postgres':
+    sql("SET TIME ZONE UTC")
--- a/misc/scripts/ldapuser2ldapfeed.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/misc/scripts/ldapuser2ldapfeed.py	Fri Dec 18 09:23:23 2015 +0100
@@ -54,9 +54,9 @@
     print('get back', etype, entity.eid)
     entity.cw_edited = EditedEntity(entity, **entity.cw_attr_cache)
     if not entity.creation_date:
-        entity.cw_edited['creation_date'] = datetime.now()
+        entity.cw_edited['creation_date'] = datetime.utcnow()
     if not entity.modification_date:
-        entity.cw_edited['modification_date'] = datetime.now()
+        entity.cw_edited['modification_date'] = datetime.utcnow()
     if not entity.upassword:
         entity.cw_edited['upassword'] = generate_password()
     extid = entity.cw_metainformation()['extid']
--- a/schemas/base.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/schemas/base.py	Fri Dec 18 09:23:23 2015 +0100
@@ -150,14 +150,16 @@
     __permissions__ = PUB_SYSTEM_ATTR_PERMS
     cardinality = '11'
     subject = '*'
-    object = 'Datetime'
+    object = 'TZDatetime'
+
 
 class modification_date(RelationType):
     """latest modification time of an entity"""
     __permissions__ = PUB_SYSTEM_ATTR_PERMS
     cardinality = '11'
     subject = '*'
-    object = 'Datetime'
+    object = 'TZDatetime'
+
 
 class cwuri(RelationType):
     """internal entity uri"""
--- a/server/checkintegrity.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/server/checkintegrity.py	Fri Dec 18 09:23:23 2015 +0100
@@ -372,8 +372,8 @@
                                    {'type': etype})
             continue
         table = SQL_PREFIX + etype
-        for rel, default in ( ('creation_date', datetime.now()),
-                              ('modification_date', datetime.now()), ):
+        for rel, default in ( ('creation_date', datetime.utcnow()),
+                              ('modification_date', datetime.utcnow()), ):
             column = SQL_PREFIX + rel
             cursor = cnx.system_sql("SELECT %s FROM %s WHERE %s is NULL"
                                         % (eidcolumn, table, column))
--- a/server/test/unittest_rql2sql.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/server/test/unittest_rql2sql.py	Fri Dec 18 09:23:23 2015 +0100
@@ -1027,10 +1027,10 @@
 FROM cw_Societe AS _S, travaille_relation AS rel_travaille0
 WHERE rel_travaille0.eid_to=_S.cw_eid AND _S.cw_tel=_S.cw_fax'''),
 
-    ("Personne P where X eid 0, X creation_date D, P datenaiss < D, X is Affaire",
+    ("Personne P where X eid 0, X creation_date D, P tzdatenaiss < D, X is Affaire",
      '''SELECT _P.cw_eid
 FROM cw_Affaire AS _X, cw_Personne AS _P
-WHERE _X.cw_eid=0 AND _P.cw_datenaiss<_X.cw_creation_date'''),
+WHERE _X.cw_eid=0 AND _P.cw_tzdatenaiss<_X.cw_creation_date'''),
 
     ("Any N,T WHERE N is Note, N type T;",
      '''SELECT _N.cw_eid, _N.cw_type
--- a/web/views/editcontroller.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/web/views/editcontroller.py	Fri Dec 18 09:23:23 2015 +0100
@@ -373,7 +373,7 @@
     def check_concurrent_edition(self, formparams, eid):
         req = self._cw
         try:
-            form_ts = datetime.fromtimestamp(float(formparams['__form_generation_time']))
+            form_ts = datetime.utcfromtimestamp(float(formparams['__form_generation_time']))
         except KeyError:
             # Backward and tests compatibility : if no timestamp consider edition OK
             return
--- a/web/views/forms.py	Fri Dec 18 09:37:15 2015 +0100
+++ b/web/views/forms.py	Fri Dec 18 09:23:23 2015 +0100
@@ -369,8 +369,8 @@
             self.add_hidden('_cwmsgid', msgid)
 
     def add_generation_time(self):
-        # NB repr is critical to avoid truncation of the timestamp
-        self.add_hidden('__form_generation_time', repr(time.time()),
+        # use %f to prevent (unlikely) display in exponential format
+        self.add_hidden('__form_generation_time', '%.6f' % time.time(),
                         eidparam=True)
 
     def add_linkto_hidden(self):