From 42203d7a9c528bab7a5da7cccf7c4220431d4281 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 26 Aug 2022 13:03:19 -0300 Subject: [PATCH] chore: migrate timestamps for use with USE_TZ=True (#4370) * chore: add migration to change timestamps to UTC * chore: fill in Meeting.time_zone where it is blank Nearly all interim meetings on or before 2016-07-01 have blank time_zone values. This migration fills these in with PST8PDT. * chore: disallow blank Meeting.time_zone value * refactor: no need to handle blank time_zone case in TZ migration * refactor: remove now-unnecessary checks that meeting has time_zone * chore: renumber migrations * chore: update timestamp conversion migration The django-celery-beat package introduces tables with timestamp columns. These columns are stored in CELERY_TIMEZONE. Because we run with this set to UTC, the migration ignores these columns. * chore: fix pytz-related change in migration * chore: be explicit that Meeting.vtimezone can return None * refactor: remove unnecessary save() --- .../0057_fill_in_empty_meeting_time_zone.py | 47 ++++ .../0058_meeting_time_zone_not_blank.py | 18 ++ ietf/meeting/models.py | 49 ++-- ietf/meeting/tests_models.py | 18 ++ ietf/meeting/views.py | 5 +- .../0002_convert_timestamps_to_utc.py | 256 ++++++++++++++++++ 6 files changed, 361 insertions(+), 32 deletions(-) create mode 100644 ietf/meeting/migrations/0057_fill_in_empty_meeting_time_zone.py create mode 100644 ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py create mode 100644 ietf/utils/migrations/0002_convert_timestamps_to_utc.py diff --git a/ietf/meeting/migrations/0057_fill_in_empty_meeting_time_zone.py b/ietf/meeting/migrations/0057_fill_in_empty_meeting_time_zone.py new file mode 100644 index 000000000..f009b08f3 --- /dev/null +++ b/ietf/meeting/migrations/0057_fill_in_empty_meeting_time_zone.py @@ -0,0 +1,47 @@ +# Generated by Django 2.2.28 on 2022-08-08 11:37 + +import datetime + +from django.db import migrations + + +# date of last meeting with an empty time_zone before this migration +LAST_EMPTY_TZ = datetime.date(2022, 7, 1) + + +def forward(apps, schema_editor): + Meeting = apps.get_model('meeting', 'Meeting') + + # Check that we will be able to identify the migrated meetings later + old_meetings_in_pst8pdt = Meeting.objects.filter(type_id='interim', time_zone='PST8PDT', date__lte=LAST_EMPTY_TZ) + assert old_meetings_in_pst8pdt.count() == 0, 'not expecting interim meetings in PST8PDT time_zone' + + meetings_with_empty_tz = Meeting.objects.filter(time_zone='') + # check our expected conditions + for mtg in meetings_with_empty_tz: + assert mtg.type_id == 'interim', 'was not expecting non-interim meetings to be affected' + assert mtg.date <= LAST_EMPTY_TZ, 'affected meeting outside expected date range' + mtg.time_zone = 'PST8PDT' + + # commit the changes + Meeting.objects.bulk_update(meetings_with_empty_tz, ['time_zone']) + + +def reverse(apps, schema_editor): + Meeting = apps.get_model('meeting', 'Meeting') + meetings_to_restore = Meeting.objects.filter(time_zone='PST8PDT', date__lte=LAST_EMPTY_TZ) + for mtg in meetings_to_restore: + mtg.time_zone = '' + # commit the changes + Meeting.objects.bulk_update(meetings_to_restore, ['time_zone']) + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0056_use_timezone_now_for_meeting_models'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py b/ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py new file mode 100644 index 000000000..0adec77a2 --- /dev/null +++ b/ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-08-25 12:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0057_fill_in_empty_meeting_time_zone'), + ] + + operations = [ + migrations.AlterField( + model_name='meeting', + name='time_zone', + field=models.CharField(choices=[('', '---------'), ('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Sitka', 'America/Sitka'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Sydney', 'Australia/Sydney'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GMT', 'GMT'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('UTC', 'UTC')], default='UTC', max_length=255), + ), + ] diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index d7294f70a..df5876e27 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -86,7 +86,7 @@ class Meeting(models.Model): # We can't derive time-zone from country, as there are some that have # more than one timezone, and the pytz module doesn't provide timezone # lookup information for all relevant city/country combinations. - time_zone = models.CharField(blank=True, max_length=255, choices=timezones) + time_zone = models.CharField(max_length=255, choices=timezones, default='UTC') idsubmit_cutoff_day_offset_00 = models.IntegerField(blank=True, default=settings.IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_00, help_text = "The number of days before the meeting start date when the submission of -00 drafts will be closed.") @@ -355,19 +355,18 @@ class Meeting(models.Model): # timeslot = ts) def vtimezone(self): - if self.time_zone: - try: - tzfn = os.path.join(settings.TZDATA_ICS_PATH, self.time_zone + ".ics") - if os.path.exists(tzfn): - with io.open(tzfn) as tzf: - icstext = tzf.read() - vtimezone = re.search("(?sm)(\nBEGIN:VTIMEZONE.*\nEND:VTIMEZONE\n)", icstext).group(1).strip() - if vtimezone: - vtimezone += "\n" - return vtimezone - except IOError: - pass - return '' + try: + tzfn = os.path.join(settings.TZDATA_ICS_PATH, self.time_zone + ".ics") + if os.path.exists(tzfn): + with io.open(tzfn) as tzf: + icstext = tzf.read() + vtimezone = re.search("(?sm)(\nBEGIN:VTIMEZONE.*\nEND:VTIMEZONE\n)", icstext).group(1).strip() + if vtimezone: + vtimezone += "\n" + return vtimezone + except IOError: + pass + return None def set_official_schedule(self, schedule): if self.schedule != schedule: @@ -606,24 +605,15 @@ class TimeSlot(models.Model): def tz(self): if not hasattr(self, '_cached_tz'): - if self.meeting.time_zone: - self._cached_tz = pytz.timezone(self.meeting.time_zone) - else: - self._cached_tz = None + self._cached_tz = pytz.timezone(self.meeting.time_zone) return self._cached_tz def tzname(self): - if self.tz(): - return self.tz().tzname(self.time) - else: - return "" + return self.tz().tzname(self.time) def utc_start_time(self): - if self.tz(): - local_start_time = self.tz().localize(self.time) - return local_start_time.astimezone(pytz.utc) - else: - return None + local_start_time = self.tz().localize(self.time) + return local_start_time.astimezone(pytz.utc) def utc_end_time(self): utc_start = self.utc_start_time() @@ -631,10 +621,7 @@ class TimeSlot(models.Model): return None if utc_start is None else utc_start + self.duration def local_start_time(self): - if self.tz(): - return self.tz().localize(self.time) - else: - return None + return self.tz().localize(self.time) def local_end_time(self): local_start = self.local_start_time() diff --git a/ietf/meeting/tests_models.py b/ietf/meeting/tests_models.py index dea31dc04..4a2f42c16 100644 --- a/ietf/meeting/tests_models.py +++ b/ietf/meeting/tests_models.py @@ -3,6 +3,8 @@ """Tests of models in the Meeting application""" import datetime +from mock import patch + from ietf.meeting.factories import MeetingFactory, SessionFactory from ietf.stats.factories import MeetingRegistrationFactory from ietf.utils.test_utils import TestCase @@ -52,6 +54,22 @@ class MeetingTests(TestCase): self.assertEqual(attendance.online, 0) self.assertEqual(attendance.onsite, 5) + def test_vtimezone(self): + # normal time zone that should have a zoneinfo file + meeting = MeetingFactory(type_id='ietf', time_zone='America/Los_Angeles') + vtz = meeting.vtimezone() + self.assertIsNotNone(vtz) + self.assertGreater(len(vtz), 0) + # time zone that does not have a zoneinfo file should return None + meeting = MeetingFactory(type_id='ietf', time_zone='Fake/Time_Zone') + vtz = meeting.vtimezone() + self.assertIsNone(vtz) + # ioerror trying to read zoneinfo should return None + meeting = MeetingFactory(type_id='ietf', time_zone='America/Los_Angeles') + with patch('ietf.meeting.models.io.open', side_effect=IOError): + vtz = meeting.vtimezone() + self.assertIsNone(vtz) + class SessionTests(TestCase): def test_chat_archive_url_with_jabber(self): diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index e4fc9dc17..c8eac6f9f 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -3662,9 +3662,12 @@ def upcoming_ical(request): ietfs = [m for m in meetings if m.type_id == 'ietf'] preprocess_meeting_important_dates(ietfs) + meeting_vtz = {meeting.vtimezone() for meeting in meetings} + meeting_vtz.discard(None) + # icalendar response file should have '\r\n' line endings per RFC5545 response = render_to_string('meeting/upcoming.ics', { - 'vtimezones': ''.join(sorted(list({meeting.vtimezone() for meeting in meetings if meeting.vtimezone()}))), + 'vtimezones': ''.join(sorted(meeting_vtz)), 'assignments': assignments, 'ietfs': ietfs, }, request=request) diff --git a/ietf/utils/migrations/0002_convert_timestamps_to_utc.py b/ietf/utils/migrations/0002_convert_timestamps_to_utc.py new file mode 100644 index 000000000..6cc022538 --- /dev/null +++ b/ietf/utils/migrations/0002_convert_timestamps_to_utc.py @@ -0,0 +1,256 @@ +# Generated by Django 2.2.28 on 2022-06-21 11:44 + +from django.conf import settings +from django.db import migrations, connection + +# to generate the expected list: +# +# from django.db import connection +# from pprint import pp +# cursor = connection.cursor() +# cursor.execute(""" +# SELECT table_name, column_name +# FROM information_schema.columns +# WHERE table_schema='ietf_utf8' +# AND column_type LIKE 'datetime%' +# AND NOT table_name LIKE 'django_celery_beat_%' +# ORDER BY table_name, column_name; +# """) +# pp(cursor.fetchall()) +# +expected_datetime_columns = ( + ('auth_user', 'date_joined'), + ('auth_user', 'last_login'), + ('community_documentchangedates', 'new_version_date'), + ('community_documentchangedates', 'normal_change_date'), + ('community_documentchangedates', 'significant_change_date'), + ('django_admin_log', 'action_time'), + ('django_migrations', 'applied'), + ('django_session', 'expire_date'), + ('doc_ballotpositiondocevent', 'comment_time'), + ('doc_ballotpositiondocevent', 'discuss_time'), + ('doc_deletedevent', 'time'), + ('doc_docevent', 'time'), + ('doc_dochistory', 'expires'), + ('doc_dochistory', 'time'), + ('doc_docreminder', 'due'), + ('doc_document', 'expires'), + ('doc_document', 'time'), + ('doc_documentactionholder', 'time_added'), + ('doc_initialreviewdocevent', 'expires'), + ('doc_irsgballotdocevent', 'duedate'), + ('doc_lastcalldocevent', 'expires'), + ('group_group', 'time'), + ('group_groupevent', 'time'), + ('group_grouphistory', 'time'), + ('group_groupmilestone', 'time'), + ('group_groupmilestonehistory', 'time'), + ('ipr_iprdisclosurebase', 'time'), + ('ipr_iprevent', 'response_due'), + ('ipr_iprevent', 'time'), + ('liaisons_liaisonstatementevent', 'time'), + ('mailinglists_subscribed', 'time'), + ('mailinglists_whitelisted', 'time'), + ('meeting_floorplan', 'modified'), + ('meeting_room', 'modified'), + ('meeting_schedtimesessassignment', 'modified'), + ('meeting_schedulingevent', 'time'), + ('meeting_session', 'modified'), + ('meeting_session', 'scheduled'), + ('meeting_slidesubmission', 'time'), + ('meeting_timeslot', 'modified'), + ('meeting_timeslot', 'time'), + ('message_message', 'sent'), + ('message_message', 'time'), + ('message_sendqueue', 'send_at'), + ('message_sendqueue', 'sent_at'), + ('message_sendqueue', 'time'), + ('nomcom_feedback', 'time'), + ('nomcom_feedbacklastseen', 'time'), + ('nomcom_nomination', 'time'), + ('nomcom_nomineeposition', 'time'), + ('nomcom_topicfeedbacklastseen', 'time'), + ('oidc_provider_code', 'expires_at'), + ('oidc_provider_token', 'expires_at'), + ('oidc_provider_userconsent', 'date_given'), + ('oidc_provider_userconsent', 'expires_at'), + ('person_email', 'time'), + ('person_historicalemail', 'history_date'), + ('person_historicalemail', 'time'), + ('person_historicalperson', 'history_date'), + ('person_historicalperson', 'time'), + ('person_person', 'time'), + ('person_personalapikey', 'created'), + ('person_personalapikey', 'latest'), + ('person_personevent', 'time'), + ('request_profiler_profilingrecord', 'end_ts'), + ('request_profiler_profilingrecord', 'start_ts'), + ('review_historicalreviewassignment', 'assigned_on'), + ('review_historicalreviewassignment', 'completed_on'), + ('review_historicalreviewassignment', 'history_date'), + ('review_historicalreviewersettings', 'history_date'), + ('review_historicalreviewrequest', 'history_date'), + ('review_historicalreviewrequest', 'time'), + ('review_historicalunavailableperiod', 'history_date'), + ('review_reviewassignment', 'assigned_on'), + ('review_reviewassignment', 'completed_on'), + ('review_reviewrequest', 'time'), + ('review_reviewwish', 'time'), + ('south_migrationhistory', 'applied'), + ('submit_preapproval', 'time'), + ('submit_submissioncheck', 'time'), + ('submit_submissionevent', 'time'), + ('tastypie_apikey', 'created'), + ('utils_dumpinfo', 'date'), + ('utils_versioninfo', 'time'), +) + + +def forward(apps, schema_editor): + # Check that we can safely ignore celery beat columns - it defaults to UTC if CELERY_TIMEZONE is not set. + celery_timezone = getattr(settings, 'CELERY_TIMEZONE', None) + assert celery_timezone in ('UTC', None), 'update migration, celery is not using UTC' + # If the CELERY_ENABLE_UTC flag is set, abort because someone is using a strange configuration. + assert not hasattr(settings, 'CELERY_ENABLE_UTC'), 'update migration, settings.CELERY_ENABLE_UTC was not expected' + + with connection.cursor() as cursor: + # Check that we have timezones. + # If these assertions fail, the DB does not know all the necessary time zones. + # To load timezones, + # $ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql + # (on a dev system, first connect to the db image with `docker compose exec db bash`) + cursor.execute("SELECT CONVERT_TZ('2022-06-22T17:43:00', 'PST8PDT', 'UTC');") + assert not any(None in row for row in cursor.fetchall()), 'database does not recognize PST8PDT' + cursor.execute( + "SELECT CONVERT_TZ('2022-06-22T17:43:00', time_zone, 'UTC') FROM meeting_meeting WHERE time_zone != '';" + ) + assert not any(None in row for row in cursor.fetchall()), 'database does not recognize a meeting time zone' + + # Check that we have all and only the expected datetime columns to work with. + # If this fails, figure out what changed and decide how to proceed safely. + cursor.execute(""" + SELECT table_name, column_name + FROM information_schema.columns + WHERE table_schema='ietf_utf8' + AND column_type LIKE 'datetime%' + AND NOT table_name LIKE 'django_celery_beat_%' + ORDER BY table_name, column_name; + """) + assert cursor.fetchall() == expected_datetime_columns, 'unexpected or missing datetime columns in db' + + +class Migration(migrations.Migration): + dependencies = [ + ('utils', '0001_initial'), + ('meeting', '0058_meeting_time_zone_not_blank'), + ] + + # To generate the queries: + # + # pst8pdt_columns = [e for e in expected_datetime_columns if e != ('meeting_timeslot', 'time')] + # queries = [] + # for table, column in pst8pdt_columns: + # queries.append(f"UPDATE {table} SET {column} = CONVERT_TZ({column}, 'PST8PDT', 'UTC');") + # + # queries.append(""" + # UPDATE meeting_timeslot + # JOIN meeting_meeting + # ON meeting_meeting.id = meeting_id + # SET time = CONVERT_TZ(time, time_zone, 'UTC'); + # """) + # + # print("\n".join(queries)) + # + operations = [ + migrations.RunPython(forward), + migrations.RunSQL(""" +UPDATE auth_user SET date_joined = CONVERT_TZ(date_joined, 'PST8PDT', 'UTC'); +UPDATE auth_user SET last_login = CONVERT_TZ(last_login, 'PST8PDT', 'UTC'); +UPDATE community_documentchangedates SET new_version_date = CONVERT_TZ(new_version_date, 'PST8PDT', 'UTC'); +UPDATE community_documentchangedates SET normal_change_date = CONVERT_TZ(normal_change_date, 'PST8PDT', 'UTC'); +UPDATE community_documentchangedates SET significant_change_date = CONVERT_TZ(significant_change_date, 'PST8PDT', 'UTC'); +UPDATE django_admin_log SET action_time = CONVERT_TZ(action_time, 'PST8PDT', 'UTC'); +UPDATE django_migrations SET applied = CONVERT_TZ(applied, 'PST8PDT', 'UTC'); +UPDATE django_session SET expire_date = CONVERT_TZ(expire_date, 'PST8PDT', 'UTC'); +UPDATE doc_ballotpositiondocevent SET comment_time = CONVERT_TZ(comment_time, 'PST8PDT', 'UTC'); +UPDATE doc_ballotpositiondocevent SET discuss_time = CONVERT_TZ(discuss_time, 'PST8PDT', 'UTC'); +UPDATE doc_deletedevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE doc_docevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE doc_dochistory SET expires = CONVERT_TZ(expires, 'PST8PDT', 'UTC'); +UPDATE doc_dochistory SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE doc_docreminder SET due = CONVERT_TZ(due, 'PST8PDT', 'UTC'); +UPDATE doc_document SET expires = CONVERT_TZ(expires, 'PST8PDT', 'UTC'); +UPDATE doc_document SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE doc_documentactionholder SET time_added = CONVERT_TZ(time_added, 'PST8PDT', 'UTC'); +UPDATE doc_initialreviewdocevent SET expires = CONVERT_TZ(expires, 'PST8PDT', 'UTC'); +UPDATE doc_irsgballotdocevent SET duedate = CONVERT_TZ(duedate, 'PST8PDT', 'UTC'); +UPDATE doc_lastcalldocevent SET expires = CONVERT_TZ(expires, 'PST8PDT', 'UTC'); +UPDATE group_group SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE group_groupevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE group_grouphistory SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE group_groupmilestone SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE group_groupmilestonehistory SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE ipr_iprdisclosurebase SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE ipr_iprevent SET response_due = CONVERT_TZ(response_due, 'PST8PDT', 'UTC'); +UPDATE ipr_iprevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE liaisons_liaisonstatementevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE mailinglists_subscribed SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE mailinglists_whitelisted SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE meeting_floorplan SET modified = CONVERT_TZ(modified, 'PST8PDT', 'UTC'); +UPDATE meeting_room SET modified = CONVERT_TZ(modified, 'PST8PDT', 'UTC'); +UPDATE meeting_schedtimesessassignment SET modified = CONVERT_TZ(modified, 'PST8PDT', 'UTC'); +UPDATE meeting_schedulingevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE meeting_session SET modified = CONVERT_TZ(modified, 'PST8PDT', 'UTC'); +UPDATE meeting_session SET scheduled = CONVERT_TZ(scheduled, 'PST8PDT', 'UTC'); +UPDATE meeting_slidesubmission SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE meeting_timeslot SET modified = CONVERT_TZ(modified, 'PST8PDT', 'UTC'); +UPDATE message_message SET sent = CONVERT_TZ(sent, 'PST8PDT', 'UTC'); +UPDATE message_message SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE message_sendqueue SET send_at = CONVERT_TZ(send_at, 'PST8PDT', 'UTC'); +UPDATE message_sendqueue SET sent_at = CONVERT_TZ(sent_at, 'PST8PDT', 'UTC'); +UPDATE message_sendqueue SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE nomcom_feedback SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE nomcom_feedbacklastseen SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE nomcom_nomination SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE nomcom_nomineeposition SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE nomcom_topicfeedbacklastseen SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE oidc_provider_code SET expires_at = CONVERT_TZ(expires_at, 'PST8PDT', 'UTC'); +UPDATE oidc_provider_token SET expires_at = CONVERT_TZ(expires_at, 'PST8PDT', 'UTC'); +UPDATE oidc_provider_userconsent SET date_given = CONVERT_TZ(date_given, 'PST8PDT', 'UTC'); +UPDATE oidc_provider_userconsent SET expires_at = CONVERT_TZ(expires_at, 'PST8PDT', 'UTC'); +UPDATE person_email SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE person_historicalemail SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE person_historicalemail SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE person_historicalperson SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE person_historicalperson SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE person_person SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE person_personalapikey SET created = CONVERT_TZ(created, 'PST8PDT', 'UTC'); +UPDATE person_personalapikey SET latest = CONVERT_TZ(latest, 'PST8PDT', 'UTC'); +UPDATE person_personevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE request_profiler_profilingrecord SET end_ts = CONVERT_TZ(end_ts, 'PST8PDT', 'UTC'); +UPDATE request_profiler_profilingrecord SET start_ts = CONVERT_TZ(start_ts, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewassignment SET assigned_on = CONVERT_TZ(assigned_on, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewassignment SET completed_on = CONVERT_TZ(completed_on, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewassignment SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewersettings SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewrequest SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE review_historicalreviewrequest SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE review_historicalunavailableperiod SET history_date = CONVERT_TZ(history_date, 'PST8PDT', 'UTC'); +UPDATE review_reviewassignment SET assigned_on = CONVERT_TZ(assigned_on, 'PST8PDT', 'UTC'); +UPDATE review_reviewassignment SET completed_on = CONVERT_TZ(completed_on, 'PST8PDT', 'UTC'); +UPDATE review_reviewrequest SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE review_reviewwish SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE south_migrationhistory SET applied = CONVERT_TZ(applied, 'PST8PDT', 'UTC'); +UPDATE submit_preapproval SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE submit_submissioncheck SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE submit_submissionevent SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); +UPDATE tastypie_apikey SET created = CONVERT_TZ(created, 'PST8PDT', 'UTC'); +UPDATE utils_dumpinfo SET date = CONVERT_TZ(date, 'PST8PDT', 'UTC'); +UPDATE utils_versioninfo SET time = CONVERT_TZ(time, 'PST8PDT', 'UTC'); + +UPDATE meeting_timeslot + JOIN meeting_meeting + ON meeting_meeting.id = meeting_id + SET time = CONVERT_TZ(time, time_zone, 'UTC'); +"""), + ]