From e9b62b47dc32cb6fa300b0ced132bad3ca0e4f0b Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Tue, 2 Apr 2024 10:16:48 +0200 Subject: [PATCH] [FIX] *: adapt tz for Ubuntu Noble Some of the non canononical timezones are not present in Ubuntu Noble, it would be a better practice to only use canonical timezones in data and tests. Note that this is not a real fix for all cases since the database that ran on Ubuntu Jammy and are moved to an ubuntu Noble server will have the issue with timezones already in database. One of the possible fix would be to manage that during upgrades, but this isn't a verry flexible solution since upgrade are meant to manage chyange of version, not change of server. If an old 17.0 versions needs to be moved to a Noble server, this won't work. Another solution would be to install package like tzdata-legacy that may keep the old timezones but it is not the only think since TAI-10 are also in this package. This solution is not ideal because non canonical timezone will still be shown in the dropdown. We would need to filter them. A last solution would be to add the support for those old timezones by monkeypatching the lib. This way, only new timezones would be shown but non canonical one won't crash when used. This is not ideal either because we may need to keep this for a while. But in combination with the upgrade solution, it may work proprely. Part-of: odoo/odoo#160842 --- addons/calendar/models/calendar_recurrence.py | 8 ++--- addons/calendar/tests/test_calendar.py | 6 ++-- .../calendar/tests/test_event_recurrence.py | 14 ++++---- addons/event/data/event_demo.xml | 4 +-- addons/event/data/event_demo_misc.xml | 2 +- addons/event/tests/test_event_internals.py | 4 +-- addons/hr/tests/test_self_user_access.py | 4 +-- .../hr_holidays/tests/test_global_leaves.py | 6 ++-- .../hr_holidays/tests/test_leave_requests.py | 10 +++--- .../spreadsheet/tests/test_currency_rate.py | 2 +- addons/test_mail/tests/test_mail_activity.py | 2 +- addons/test_resource/tests/common.py | 2 +- addons/test_resource/tests/test_resource.py | 10 +++--- odoo/addons/base/tests/__init__.py | 1 + odoo/addons/base/tests/test_misc.py | 4 +-- odoo/addons/base/tests/test_tz.py | 33 +++++++++++++++++++ 16 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 odoo/addons/base/tests/test_tz.py diff --git a/addons/calendar/models/calendar_recurrence.py b/addons/calendar/models/calendar_recurrence.py index 2cfd25ec4e8..9434bfb89b7 100644 --- a/addons/calendar/models/calendar_recurrence.py +++ b/addons/calendar/models/calendar_recurrence.py @@ -529,14 +529,14 @@ class RecurrenceRule(models.Model): # Given the following recurrence: # - monthly # - 1st of each month - # - timezone US/Eastern (UTC−05:00) - # - at 6am US/Eastern = 11am UTC + # - timezone America/New_York (UTC−05:00) + # - at 6am America/New_York = 11am UTC # - from 2019/02/01 to 2019/05/01. # The naive way would be to store: # 2019/02/01 11:00 - 2019/03/01 11:00 - 2019/04/01 11:00 - 2019/05/01 11:00 (UTC) # - # But a DST change occurs on 2019/03/10 in US/Eastern timezone. US/Eastern is now UTC−04:00. - # From this point in time, 11am (UTC) is actually converted to 7am (US/Eastern) instead of the expected 6am! + # But a DST change occurs on 2019/03/10 in America/New_York timezone. America/New_York is now UTC−04:00. + # From this point in time, 11am (UTC) is actually converted to 7am (America/New_York) instead of the expected 6am! # What should be stored is: # 2019/02/01 11:00 - 2019/03/01 11:00 - 2019/04/01 10:00 - 2019/05/01 10:00 (UTC) # ***** ***** diff --git a/addons/calendar/tests/test_calendar.py b/addons/calendar/tests/test_calendar.py index 5b43fc2ec06..f4f6c4d06d5 100644 --- a/addons/calendar/tests/test_calendar.py +++ b/addons/calendar/tests/test_calendar.py @@ -218,14 +218,14 @@ class TestCalendar(SavepointCaseWithUserDemo): self.assertEqual(d.minute, 30) def test_recurring_ny(self): - self.user_demo.tz = 'US/Eastern' + self.user_demo.tz = 'America/New_York' event = self.CalendarEvent.create({'user_id': self.user_demo.id, 'name': 'test', 'partner_ids': [Command.link(self.user_demo.partner_id.id)]}) - f = Form(event.with_context(tz='US/Eastern').with_user(self.user_demo)) + f = Form(event.with_context(tz='America/New_York').with_user(self.user_demo)) f.name = 'test' f.start = '2022-07-07 01:00:00' # This is in UTC. In NY, it corresponds to the 6th of july at 9pm. f.recurrency = True self.assertEqual(f.weekday, 'WED') - self.assertEqual(f.event_tz, 'US/Eastern', "The value should correspond to the user tz") + self.assertEqual(f.event_tz, 'America/New_York', "The value should correspond to the user tz") self.assertEqual(f.count, 1, "The default value should be displayed") self.assertEqual(f.interval, 1, "The default value should be displayed") self.assertEqual(f.month_by, "date", "The default value should be displayed") diff --git a/addons/calendar/tests/test_event_recurrence.py b/addons/calendar/tests/test_event_recurrence.py index 7be8971160f..202116ed50a 100644 --- a/addons/calendar/tests/test_event_recurrence.py +++ b/addons/calendar/tests/test_event_recurrence.py @@ -216,7 +216,7 @@ class TestCreateRecurrentEvents(TestRecurrentEvents): 'rrule_type': 'weekly', 'mon': True, 'count': '2', - 'event_tz': 'US/Eastern', # DST change on 2002/10/27 + 'event_tz': 'America/New_York', # DST change on 2002/10/27 }) recurrence = self.env['calendar.recurrence'].search([('base_event_id', '=', self.event.id)]) self.assertEventDates(recurrence.calendar_event_ids, [ @@ -226,9 +226,9 @@ class TestCreateRecurrentEvents(TestRecurrentEvents): def test_ambiguous_dst_time_winter(self): """ Test hours stays the same, regardless of DST changes """ - eastern = pytz.timezone('US/Eastern') + eastern = pytz.timezone('America/New_York') dt = eastern.localize(datetime(2002, 10, 20, 1, 30, 00)).astimezone(pytz.utc).replace(tzinfo=None) - # Next occurence happens at 1:30am on 27th Oct 2002 which happened twice in the US/Eastern + # Next occurence happens at 1:30am on 27th Oct 2002 which happened twice in the America/New_York # timezone when the clocks where put back at the end of Daylight Saving Time self.event.start = dt self.event.stop = dt + relativedelta(hours=1) @@ -237,7 +237,7 @@ class TestCreateRecurrentEvents(TestRecurrentEvents): 'rrule_type': 'weekly', 'sun': True, 'count': '2', - 'event_tz': 'US/Eastern' # DST change on 2002/4/7 + 'event_tz': 'America/New_York' # DST change on 2002/4/7 }) events = self.event.recurrence_id.calendar_event_ids self.assertEqual(events.mapped('duration'), [1, 1]) @@ -248,10 +248,10 @@ class TestCreateRecurrentEvents(TestRecurrentEvents): def test_ambiguous_dst_time_spring(self): """ Test hours stays the same, regardless of DST changes """ - eastern = pytz.timezone('US/Eastern') + eastern = pytz.timezone('America/New_York') dt = eastern.localize(datetime(2002, 3, 31, 2, 30, 00)).astimezone(pytz.utc).replace(tzinfo=None) # Next occurence happens 2:30am on 7th April 2002 which never happened at all in the - # US/Eastern timezone, as the clocks where put forward at 2:00am skipping the entire hour + # America/New_York timezone, as the clocks where put forward at 2:00am skipping the entire hour self.event.start = dt self.event.stop = dt + relativedelta(hours=1) self.event._apply_recurrence_values({ @@ -259,7 +259,7 @@ class TestCreateRecurrentEvents(TestRecurrentEvents): 'rrule_type': 'weekly', 'sun': True, 'count': '2', - 'event_tz': 'US/Eastern' # DST change on 2002/4/7 + 'event_tz': 'America/New_York' # DST change on 2002/4/7 }) events = self.event.recurrence_id.calendar_event_ids self.assertEqual(events.mapped('duration'), [1, 1]) diff --git a/addons/event/data/event_demo.xml b/addons/event/data/event_demo.xml index 86174b45121..e041001af99 100644 --- a/addons/event/data/event_demo.xml +++ b/addons/event/data/event_demo.xml @@ -15,7 +15,7 @@ True 50 - US/Pacific + America/Los_Angeles @@ -196,7 +196,7 @@ True 4 - US/Pacific + America/Los_Angeles done diff --git a/addons/event/data/event_demo_misc.xml b/addons/event/data/event_demo_misc.xml index f5248677ea4..2740b7a867a 100644 --- a/addons/event/data/event_demo_misc.xml +++ b/addons/event/data/event_demo_misc.xml @@ -12,7 +12,7 @@ Sport - US/Pacific + America/Los_Angeles 5 diff --git a/addons/event/tests/test_event_internals.py b/addons/event/tests/test_event_internals.py index 56baf026e8f..e8e3ac7143d 100644 --- a/addons/event/tests/test_event_internals.py +++ b/addons/event/tests/test_event_internals.py @@ -84,7 +84,7 @@ class TestEventData(TestEventInternalsCommon): event.write({ 'date_begin': datetime(2020, 1, 31, 14, 30, 0), 'date_end': datetime(2020, 2, 1, 2, 0, 0), - 'date_tz': 'Mexico/General' + 'date_tz': 'America/Mexico_City' }) self.assertTrue(event.is_one_day) @@ -96,7 +96,7 @@ class TestEventData(TestEventInternalsCommon): event.write({ 'date_begin': datetime(2020, 1, 31, 14, 30, 0), 'date_end': datetime(2020, 2, 1, 2, 0, 0), - 'date_tz': 'Mexico/General' + 'date_tz': 'America/Mexico_City' }) self.assertTrue(event.is_one_day) self.assertFalse(event.is_ongoing) diff --git a/addons/hr/tests/test_self_user_access.py b/addons/hr/tests/test_self_user_access.py index cdeae64a59f..4528a546222 100644 --- a/addons/hr/tests/test_self_user_access.py +++ b/addons/hr/tests/test_self_user_access.py @@ -179,7 +179,7 @@ class TestSelfAccessRights(TestHrCommon): self.env['ir.config_parameter'].set_param('hr.hr_employee_self_edit', False) # should not raise vals = [ - {'tz': "Australia/ACT"}, + {'tz': "Australia/Sydney"}, {'email': "new@example.com"}, {'signature': "

I'm Richard!

"}, {'notification_type': "email"}, @@ -193,7 +193,7 @@ class TestSelfAccessRights(TestHrCommon): # they are in SELF_READABLE_FIELDS self.env['ir.config_parameter'].set_param('hr.hr_employee_self_edit', False) vals = [ - {'tz': "Australia/ACT"}, + {'tz': "Australia/Sydney"}, {'email': "new@example.com"}, {'signature': "

I'm Richard!

"}, {'notification_type': "email"}, diff --git a/addons/hr_holidays/tests/test_global_leaves.py b/addons/hr_holidays/tests/test_global_leaves.py index a81c7ff715d..6a8d491d38f 100644 --- a/addons/hr_holidays/tests/test_global_leaves.py +++ b/addons/hr_holidays/tests/test_global_leaves.py @@ -111,7 +111,7 @@ class TestGlobalLeaves(TestHrHolidaysCommon): """ calendar_asia = self.env['resource.calendar'].create({ 'name': 'Asia calendar', - 'tz': 'Asia/Calcutta', # UTC +05:30 + 'tz': 'Asia/Kolkata', # UTC +05:30 'hours_per_day': 8.0, 'attendance_ids': [] }) @@ -124,12 +124,12 @@ class TestGlobalLeaves(TestHrHolidaysCommon): }) # Expectation: # 6:00:00 in UTC (data from the browser) --> 8:00:00 for Europe/Brussel (UTC +02:00) - # 8:00:00 for Asia/Calcutta (UTC +05:30) --> 2:30:00 in UTC + # 8:00:00 for Asia/Kolkata (UTC +05:30) --> 2:30:00 in UTC self.assertEqual(global_leave.date_from, datetime(2023, 5, 15, 2, 30)) self.assertEqual(global_leave.date_to, datetime(2023, 5, 15, 11, 30)) # Note: # The user in Europe/Brussels timezone see 4:30 and not 2:30 because he is in UTC +02:00. - # The user in Asia/Calcutta timezone (determined via the browser) see 8:00 because he is in UTC +05:30 + # The user in Asia/Kolkata timezone (determined via the browser) see 8:00 because he is in UTC +05:30 def test_global_leave_number_of_days_with_new(self): """ diff --git a/addons/hr_holidays/tests/test_leave_requests.py b/addons/hr_holidays/tests/test_leave_requests.py index 52ecd50673d..ebafee1b4d1 100644 --- a/addons/hr_holidays/tests/test_leave_requests.py +++ b/addons/hr_holidays/tests/test_leave_requests.py @@ -256,7 +256,7 @@ class TestLeaveRequests(TestHrHolidaysCommon): @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_timezone_employee_leave_request(self): """ Create a leave request for an employee in another timezone """ - self.employee_emp.tz = 'NZ' # GMT+12 + self.employee_emp.tz = 'Pacific/Auckland' # GMT+12 leave = self.env['hr.leave'].new({ 'employee_id': self.employee_emp.id, 'holiday_status_id': self.holidays_type_1.id, @@ -273,7 +273,7 @@ class TestLeaveRequests(TestHrHolidaysCommon): def test_timezone_company_leave_request(self): """ Create a leave request for a company in another timezone """ company = self.env['res.company'].create({'name': "Hergé"}) - company.resource_calendar_id.tz = 'NZ' # GMT+12 + company.resource_calendar_id.tz = 'Australia/Sydney' # GMT+12 leave = self.env['hr.leave'].new({ 'employee_id': self.employee_emp.id, 'holiday_status_id': self.holidays_type_1.id, @@ -291,7 +291,7 @@ class TestLeaveRequests(TestHrHolidaysCommon): @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_timezone_company_validated(self): """ Create a leave request for a company in another timezone and validate it """ - self.env.user.tz = 'NZ' # GMT+12 + self.env.user.tz = 'Australia/Sydney' # GMT+12 company = self.env['res.company'].create({'name': "Hergé"}) employee = self.env['hr.employee'].create({'name': "Remi", 'company_id': company.id}) leave_form = Form(self.env['hr.leave'], view='hr_holidays.hr_leave_view_form_manager') @@ -313,7 +313,7 @@ class TestLeaveRequests(TestHrHolidaysCommon): def test_timezone_department_leave_request(self): """ Create a leave request for a department in another timezone """ company = self.env['res.company'].create({'name': "Hergé"}) - company.resource_calendar_id.tz = 'NZ' # GMT+12 + company.resource_calendar_id.tz = 'Australia/Sydney' # GMT+12 department = self.env['hr.department'].create({'name': "Museum", 'company_id': company.id}) leave = self.env['hr.leave'].new({ 'employee_id': self.employee_emp.id, @@ -470,7 +470,7 @@ class TestLeaveRequests(TestHrHolidaysCommon): @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') def test_leave_defaults_with_timezones(self): """ Make sure that leaves start with correct defaults for non-UTC timezones """ - timezones_to_test = ('UTC', 'Pacific/Midway', 'US/Pacific', 'Asia/Taipei', 'Pacific/Kiritimati') # UTC, UTC -11, UTC -8, UTC +8, UTC +14 + timezones_to_test = ('UTC', 'Pacific/Midway', 'America/Los_Angeles', 'Asia/Taipei', 'Pacific/Kiritimati') # UTC, UTC -11, UTC -8, UTC +8, UTC +14 # January 2020 # Su Mo Tu We Th Fr Sa diff --git a/addons/spreadsheet/tests/test_currency_rate.py b/addons/spreadsheet/tests/test_currency_rate.py index 239f71f93e0..a4d8b89d8a4 100644 --- a/addons/spreadsheet/tests/test_currency_rate.py +++ b/addons/spreadsheet/tests/test_currency_rate.py @@ -117,7 +117,7 @@ class TestCurrencyRates(TransactionCase): "rate": CAD_UTC, } ) - self.env.user.tz = "Australia/ACT" + self.env.user.tz = "Australia/Sydney" self.env["res.currency.rate"].create( { "currency_id": cad.id, diff --git a/addons/test_mail/tests/test_mail_activity.py b/addons/test_mail/tests/test_mail_activity.py index 1c6052fd214..5495d3fb11f 100644 --- a/addons/test_mail/tests/test_mail_activity.py +++ b/addons/test_mail/tests/test_mail_activity.py @@ -305,7 +305,7 @@ class TestActivityMixin(TestActivityCommon): name='user Australia', login='user Australia', ) - cls.user_australia.tz = 'Australia/ACT' + cls.user_australia.tz = 'Australia/Sydney' @mute_logger('odoo.addons.mail.models.mail_mail') def test_activity_mixin(self): diff --git a/addons/test_resource/tests/common.py b/addons/test_resource/tests/common.py index 74ec56528ac..8b2c8d38c72 100644 --- a/addons/test_resource/tests/common.py +++ b/addons/test_resource/tests/common.py @@ -60,7 +60,7 @@ class TestResourceCommon(TransactionCase): (0, 0, 0, '1', 'line_section', 10), (8, 16, 0, '1', False, 11), (7, 15, 2, '1', False, 12), (8, 16, 3, '1', False, 13), (10, 16, 4, '1', False, 14)], 'Europe/Brussels') - cls.calendar_paul = cls._define_calendar('Morning and evening shifts', sum([((2, 7, i, 0.5), (10, 16, i, 0.5)) for i in range(5)], ()), 'Brazil/DeNoronha') + cls.calendar_paul = cls._define_calendar('Morning and evening shifts', sum([((2, 7, i, 0.5), (10, 16, i, 0.5)) for i in range(5)], ()), 'America/Noronha') cls.calendar_bob = cls._define_calendar('Calendar with adjacent attendances', sum([((8, 12, i, 0.5), (12, 16, i, 0.5)) for i in range(5)], ()), 'Europe/Brussels') diff --git a/addons/test_resource/tests/test_resource.py b/addons/test_resource/tests/test_resource.py index 3c21a16483c..00bb473b7aa 100644 --- a/addons/test_resource/tests/test_resource.py +++ b/addons/test_resource/tests/test_resource.py @@ -651,16 +651,16 @@ class TestResMixin(TestResourceCommon): def test_adjust_calendar_timezone_before(self): # Calendar: # Every day 8-16 - self.jean.tz = 'Japan' + self.jean.tz = 'Asia/Tokyo' self.calendar_jean.tz = 'Europe/Brussels' result = self.jean._adjust_to_calendar( - datetime_tz(2020, 4, 1, 0, 0, 0, tzinfo='Japan'), - datetime_tz(2020, 4, 1, 23, 59, 59, tzinfo='Japan'), + datetime_tz(2020, 4, 1, 0, 0, 0, tzinfo='Asia/Tokyo'), + datetime_tz(2020, 4, 1, 23, 59, 59, tzinfo='Asia/Tokyo'), ) self.assertEqual(result[self.jean], ( - datetime_tz(2020, 4, 1, 8, 0, 0, tzinfo='Japan'), - datetime_tz(2020, 4, 1, 16, 0, 0, tzinfo='Japan'), + datetime_tz(2020, 4, 1, 8, 0, 0, tzinfo='Asia/Tokyo'), + datetime_tz(2020, 4, 1, 16, 0, 0, tzinfo='Asia/Tokyo'), ), "It should have found a starting time the 1st") def test_adjust_calendar_timezone_after(self): diff --git a/odoo/addons/base/tests/__init__.py b/odoo/addons/base/tests/__init__.py index 6b40ed8e455..d61483e8439 100644 --- a/odoo/addons/base/tests/__init__.py +++ b/odoo/addons/base/tests/__init__.py @@ -46,6 +46,7 @@ from . import test_search from . import test_split_table from . import test_sql from . import test_translate +from . import test_tz # from . import test_uninstall # loop from . import test_user_has_group from . import test_views diff --git a/odoo/addons/base/tests/test_misc.py b/odoo/addons/base/tests/test_misc.py index 8c3e638bf7c..0f45d3d1d5a 100644 --- a/odoo/addons/base/tests/test_misc.py +++ b/odoo/addons/base/tests/test_misc.py @@ -291,7 +291,7 @@ class TestFormatLangDate(TransactionCase): # -- test `time` time_part = datetime.time(16, 30, 22) - time_part_tz = datetime.time(16, 30, 22, tzinfo=pytz.timezone('US/Eastern')) # 4:30 PM timezoned + time_part_tz = datetime.time(16, 30, 22, tzinfo=pytz.timezone('America/New_York')) # 4:30 PM timezoned self.assertEqual(misc.format_time(lang.with_context(lang='fr_FR').env, time_part, time_format='HH:mm:ss'), '16:30:22') self.assertEqual(misc.format_time(lang.with_context(lang='zh_CN').env, time_part, time_format="ah:m:ss"), '\u4e0b\u53484:30:22') @@ -306,7 +306,7 @@ class TestFormatLangDate(TransactionCase): #Check timezone conversion in format_time self.assertEqual(misc.format_time(lang.with_context(lang='fr_FR').env, datetime_str, 'Europe/Brussels', time_format='HH:mm:ss Z'), '11:33:00 +0100') - self.assertEqual(misc.format_time(lang.with_context(lang='fr_FR').env, datetime_str, 'US/Eastern', time_format='HH:mm:ss Z'), '05:33:00 -0500') + self.assertEqual(misc.format_time(lang.with_context(lang='fr_FR').env, datetime_str, 'America/New_York', time_format='HH:mm:ss Z'), '05:33:00 -0500') # Check given `lang_code` overwites context lang self.assertEqual(misc.format_time(lang.with_context(lang='fr_FR').env, time_part, time_format='ah:mm', lang_code='zh_CN'), '\u4e0b\u53484:30') diff --git a/odoo/addons/base/tests/test_tz.py b/odoo/addons/base/tests/test_tz.py new file mode 100644 index 00000000000..741a3575ea2 --- /dev/null +++ b/odoo/addons/base/tests/test_tz.py @@ -0,0 +1,33 @@ +import pytz + +from odoo.tests.common import TransactionCase + + +class TestTZ(TransactionCase): + + def test_tz_legacy(self): + #See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + def assertTZEqual(tz1, tz2): + self.assertEqual(tz1._utcoffset, tz2._utcoffset) + self.assertEqual(tz1._transition_info, tz2._transition_info) + + assertTZEqual(pytz.timezone('US/Eastern'), pytz.timezone('America/New_York')) + assertTZEqual(pytz.timezone('US/Central'), pytz.timezone('America/Chicago')) + assertTZEqual(pytz.timezone('US/Mountain'), pytz.timezone('America/Denver')) + assertTZEqual(pytz.timezone('US/Pacific'), pytz.timezone('America/Los_Angeles')) + assertTZEqual(pytz.timezone('US/Alaska'), pytz.timezone('America/Anchorage')) + assertTZEqual(pytz.timezone('US/Hawaii'), pytz.timezone('Pacific/Honolulu')) + assertTZEqual(pytz.timezone('Canada/Atlantic'), pytz.timezone('America/Halifax')) + assertTZEqual(pytz.timezone('Canada/Pacific'), pytz.timezone('America/Vancouver')) + assertTZEqual(pytz.timezone('Mexico/BajaNorte'), pytz.timezone('America/Tijuana')) + assertTZEqual(pytz.timezone('Mexico/General'), pytz.timezone('America/Mexico_City')) + assertTZEqual(pytz.timezone('Brazil/East'), pytz.timezone('America/Sao_Paulo')) + # This one is not correct for a strange reason + #assertTZEqual(pytz.timezone('Pacific/Midway'), pytz.timezone('Pacific/Pago_Pago')) + + def test_cannot_set_deprecated_timezone(self): + # this should be ok + self.env.user.tz = "America/New_York" + if "US/Eastern" not in pytz.all_timezones: + with self.assertRaises(ValueError): + self.env.user.tz = "US/Eastern" \ No newline at end of file