Commit 0140598b authored by Jérome Perrin's avatar Jérome Perrin

ERP5Type.tests.utils: introduce timeZoneContext

This is a context manager which temporary allow to change the time zone.

It is not thread safe and intended to be used in tests only, to replace
setTimeZoneToUTC and setTimezone methods by something unified which
makes the transition to DateTime 4 easier, because changing timezone
was done by patching private attributes and the DateTime 4 use different
attributes.
parent 61298b26
......@@ -27,12 +27,15 @@
#
##############################################################################
import os
import unittest
from DateTime import DateTime
from erp5.component.module.DateUtils import addToDate, getIntervalListBetweenDates, \
atTheEndOfPeriod, getClosestDate
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import timeZoneContext
class TestDateUtils(unittest.TestCase):
"""
......@@ -199,8 +202,51 @@ class TestPinDateTime(ERP5TypeTestCase):
self.assertGreaterEqual(DateTime(), actual_begin_date)
class TestTimeZoneContext(ERP5TypeTestCase):
def afterSetUp(self):
self.reference_date_in_utc = DateTime('2001/02/03 00:00:00 UTC')
self.actual_timezone = DateTime().timezone()
self.actual_environ_tz = os.environ.get('TZ')
def test_timezone_context_UTC(self):
with timeZoneContext('UTC'):
self.assertEqual(DateTime().timezone(), 'UTC')
self.assertEqual(
DateTime(2001, 2, 3).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_context_with_dst(self):
with timeZoneContext('Europe/Paris'):
self.assertEqual(DateTime(2021, 2, 1).timezone(), 'CET')
self.assertEqual(DateTime(2021, 7, 1).timezone(), 'CEST')
self.assertEqual(
DateTime(2001, 2, 3, 1, 0, 0).toZone('UTC'),
self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_context_without_dst(self):
with timeZoneContext('Asia/Tokyo'):
self.assertEqual(DateTime().timezone(), 'JST')
self.assertEqual(
DateTime(2001, 2, 3, 9, 0, 0).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_timezone_abbreviation(self):
with timeZoneContext('GMT-7'):
self.assertEqual(DateTime(2021, 2, 1).timezone(), 'GMT-7')
self.assertEqual(DateTime(2021, 7, 1).timezone(), 'GMT-7')
self.assertEqual(
DateTime(2001, 2, 2, 17, 0, 0).toZone('UTC'), self.reference_date_in_utc)
self.assertEqual(DateTime().timezone(), self.actual_timezone)
self.assertEqual(os.environ.get('TZ'), self.actual_environ_tz)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestDateUtils))
suite.addTest(unittest.makeSuite(TestPinDateTime))
suite.addTest(unittest.makeSuite(TestTimeZoneContext))
return suite
import os, time
from DateTime import DateTime
from Products.ERP5Type.tests.utils import timeZoneContext
current_timezone_contexts = []
def setTimezone(timezone):
# timezone must be for example GMT-7
os.environ['TZ'] = timezone
time.tzset()
DateTime._isDST = False
DateTime._localzone = DateTime._localzone0 = DateTime._localzone1 = timezone
"""Change the default timezone to `timezone`.
"""
if current_timezone_contexts:
resetTimeZone()
tzc = timeZoneContext(timezone)
tzc.__enter__()
current_timezone_contexts.append(tzc)
return "Timezone Updated"
def resetTimeZone():
"""Reset the timezone that might have been set by `setTimezone`
"""
current_timezone_contexts.pop().__exit__(None, None, None)
......@@ -66,6 +66,7 @@ from Products.ERP5Type.Utils import convertToUpperCase, str2bytes
from Products.ERP5Type.tests.backportUnittest import SetupSiteError
from Products.ERP5Type.tests.utils import addUserToDeveloperRole
from Products.ERP5Type.tests.utils import parseListeningAddress
from Products.ERP5Type.tests.utils import timeZoneContext
# Quiet messages when installing business templates
install_bt5_quiet = 0
......@@ -376,15 +377,10 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
self.pinDateTime(None)
def setTimeZoneToUTC(self):
# Make sure tests runs with UTC timezone. Some tests are checking values
# based on now, and this could give unexpected results:
# DateTime("2016/10/31") - DateTime("2016/10/30") = 1.0416666666666667 if
# you are running on a timezone like Europe/Paris, while it return 1.0 for
# UTC
os.environ['TZ'] = "UTC"
time.tzset()
DateTime._isDST = False
DateTime._localzone = DateTime._localzone0 = DateTime._localzone1 = "UTC"
# Deprecated, prefer using `timeZoneContext` context manager instead.
timezone = timeZoneContext('UTC')
timezone.__enter__()
self.addCleanup(timezone.__exit__, None, None, None)
def getDefaultSystemPreference(self):
id = 'default_system_preference'
......
......@@ -28,12 +28,16 @@
"""Utility functions and classes for unit testing
"""
import contextlib
from datetime import datetime
import errno
import os
import logging
import mock
import random
import socket
import sys
import time
import unittest
import ZODB
import zLOG
......@@ -49,6 +53,7 @@ from email import message_from_string
from Products.ERP5Type.Globals import PersistentMapping
from Products.ERP5Type.Utils import simple_decorator
from Products.ZSQLCatalog.SQLCatalog import Catalog
import pytz
import six
class FileUpload(file):
......@@ -707,3 +712,35 @@ class SubcontentReindexingWrapper(object):
self.assertEqual(expected_path_set, catalogged_object_path_set)
finally:
Catalog.catalogObjectList = orig_catalogObjectList
@contextlib.contextmanager
def timeZoneContext(timezone):
"""Context manager to change timezone in tests.
"""
saved_TZ = os.environ.get('TZ')
os.environ['TZ'] = timezone
time.tzset()
if timezone in pytz.all_timezones:
_multipleZones = time.daylight
_localzone0 = time.tzname[0]
_localzone1 = time.tzname[1] if time.daylight else time.tzname[0]
else:
_multipleZones = False
_localzone0 = _localzone1 = timezone
if hasattr(sys.modules['DateTime.DateTime'].DateTime, '_localzone0'):
patch_target = sys.modules['DateTime.DateTime'].DateTime
else:
# BBB DateTime 2
patch_target = sys.modules['DateTime.DateTime']
try:
with mock.patch.object(patch_target, '_localzone0', new=_localzone0), \
mock.patch.object(patch_target, '_localzone1', new=_localzone1), \
mock.patch.object(patch_target, '_multipleZones', new=_multipleZones):
yield
finally:
os.environ.pop('TZ')
if saved_TZ:
os.environ['TZ'] = saved_TZ
time.tzset()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment