Skip to content

Allow more absolute times in Contest validation. #3050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions webapp/src/Controller/Jury/ContestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -956,9 +956,24 @@ private function checkTimezones(FormInterface $form): ?Response
foreach (['Activate', 'Deactivate', 'Start', 'End', 'Freeze', 'Unfreeze'] as $timeString) {
$tmpValue = $formData->{'get' . $timeString . 'timeString'}();
if ($tmpValue !== '' && !is_null($tmpValue)) {
$fields = explode(' ', $tmpValue);
if (count($fields) > 1) {
$timeZones[] = $fields[2];
if (preg_match("/\d{2}-\d{2}-\d{2}.*/", $tmpValue) === 1) {
$chr = $tmpValue[10]; // The separator between date & time
$fields = explode($chr, $tmpValue);
// First field is the time, 2nd/3th might be timezone or offset
$tmpValue = substr(str_replace($fields[0], '', $tmpValue), 1); // Also remove the separator
if (str_contains($tmpValue, ' ')) {
$fields = explode(' ', $tmpValue);
} elseif (str_contains($tmpValue, '+')) {
$fields = explode('+', $tmpValue);
} elseif (str_contains($tmpValue, '-')) {
$fields = explode('-', $tmpValue);
} elseif (substr($tmpValue, -1) === 'Z') {
$timeZones[] = 'UTC';
continue;
}
if (count($fields) > 1) {
$timeZones[] = $fields[1];
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/Entity/Contest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1275,12 +1275,12 @@
$this->updateTimes();
if (Utils::difftime((float)$this->getEndtime(), (float)$this->getStarttime(true)) <= 0) {
$context
->buildViolation('Contest ends before it even starts')
->buildViolation('Contest ends before it even starts.')
->atPath('endtimeString')
->addViolation();
}
if (!empty($this->getFreezetime())) {
if (Utils::difftime((float)$this->getFreezetime(), (float)$this->getEndtime()) > 0 ||

Check failure on line 1283 in webapp/src/Entity/Contest.php

View workflow job for this annotation

GitHub Actions / phpcs

Inline control structures are not allowed
Utils::difftime((float)$this->getFreezetime(), (float)$this->getStarttime()) < 0) {
$context
->buildViolation('Freezetime is out of start/endtime range')
Expand Down
10 changes: 7 additions & 3 deletions webapp/src/Validator/Constraints/TimeStringValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class TimeStringValidator extends ConstraintValidator
{
public function validate(mixed $value, Constraint $constraint): void
{
$timezoneRegex = "[A-Za-z][A-Za-z0-9_\/+-]{1,35}"; # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
$offsetRegex = "[+-]\d{1,2}(:\d\d)?";
$absoluteRegex = "\d\d\d\d-\d\d-\d\d( |T)\d\d:\d\d:\d\d(\.\d{1,6})?( " . $timezoneRegex . "|" . $offsetRegex . "|Z)";
$relativeRegex = "\d+:\d\d(:\d\d(\.\d{1,6})?)?";
if (!$constraint instanceof TimeString) {
throw new UnexpectedTypeException($constraint, TimeString::class);
}
Expand All @@ -24,11 +28,11 @@ public function validate(mixed $value, Constraint $constraint): void

if ($constraint->allowRelative) {
$regex = $constraint->relativeIsPositive ?
"/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d{1,6})? [A-Za-z][A-Za-z0-9_\/+-]{1,35}|\+\d+:\d\d(:\d\d(\.\d{1,6})?)?)$/" :
"/^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d{1,6})? [A-Za-z][A-Za-z0-9_\/+-]{1,35}|-\d+:\d\d(:\d\d(\.\d{1,6})?)?)$/";
"/^(" . $absoluteRegex . "|\+?" . $relativeRegex . ")$/" :
"/^(" . $absoluteRegex . "|-" . $relativeRegex . ")$/";
$message = $constraint->absoluteRelativeMessage;
} else {
$regex = "/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d{1,6})? [A-Za-z][A-Za-z0-9_\/+-]{1,35}$/";
$regex = "/^" . $absoluteRegex . "$/";
$message = $constraint->absoluteMessage;
}
if (preg_match($regex, $value) !== 1) {
Expand Down
82 changes: 81 additions & 1 deletion webapp/tests/Unit/Controller/Jury/ContestControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,54 @@ class ContestControllerTest extends JuryControllerTestCase
'silverMedals' => '1',
'bronzeMedals' => '1',
'medalCategories' => ['0' => '2']],
['shortname' => 'CLICS_offset_HMM',
'name' => 'No Timezone but only offset',
'activatetimeString' => '2021-07-17 16:08:00+1:11',
'starttimeString' => '2021-07-17 16:09:00+1:11',
'freezetimeString' => '2021-07-17 16:10:00+1:11',
'endtimeString' => '2021-07-17 16:11:00+1:11',
'unfreezetimeString' => '2021-07-17T16:12:00+1:11',
'deactivatetimeString' => '2021-07-17T16:13:00+1:11'],
['shortname' => 'CLICS_offset_HHMM',
'name' => 'No Timezone but only offset',
'activatetimeString' => '2021-07-17 16:08:00+11:11',
'starttimeString' => '2021-07-17 16:09:00+11:11',
'freezetimeString' => '2021-07-17 16:10:00+11:11',
'endtimeString' => '2021-07-17 16:11:00+11:11',
'unfreezetimeString' => '2021-07-17T16:12:00+11:11',
'deactivatetimeString' => '2021-07-17T16:13:00+11:11'],
['shortname' => 'CLICS_offset_0H00',
'name' => 'No Timezone but only offset',
'activatetimeString' => '2021-07-17 16:08:00+01:00',
'starttimeString' => '2021-07-17 16:09:00+01:00',
'freezetimeString' => '2021-07-17 16:10:00+01:00',
'endtimeString' => '2021-07-17 16:11:00+01:00',
'unfreezetimeString' => '2021-07-17T16:12:00+01:00',
'deactivatetimeString' => '2021-07-17T16:13:00+01:00'],
['shortname' => 'CLICS_offset_H',
'name' => 'No Timezone but only offset',
'activatetimeString' => '2021-07-17 16:08:00+1',
'starttimeString' => '2021-07-17 16:09:00+1',
'freezetimeString' => '2021-07-17 16:10:00+1',
'endtimeString' => '2021-07-17 16:11:00+1',
'unfreezetimeString' => '2021-07-17T16:12:00+1',
'deactivatetimeString' => '2021-07-17T16:13:00+1'],
['shortname' => 'CLICS_offset_-HHH',
'name' => 'No Timezone but only offset',
'activatetimeString' => '2021-07-17 16:08:00-01',
'starttimeString' => '2021-07-17 16:09:00-01',
'freezetimeString' => '2021-07-17 16:10:00-01',
'endtimeString' => '2021-07-17 16:11:00-01',
'unfreezetimeString' => '2021-07-17T16:12:00-01',
'deactivatetimeString' => '2021-07-17T16:13:00-01'],
['shortname' => 'utc_Z',
'name' => 'UTC (Z)',
'activatetimeString' => '2021-07-17 16:08:00Z',
'starttimeString' => '2021-07-17 16:09:00.0Z',
'freezetimeString' => '2021-07-17 16:10:00.00Z',
'endtimeString' => '2021-07-17 16:11:00.000Z',
'unfreezetimeString' => '2021-07-17T16:12:00.1Z',
'deactivatetimeString' => '2021-07-17T16:13:00.2Z'],
['shortname' => 'otzcet',
'name' => 'Other timezone (CET)',
'activatetimeString' => '2021-07-17 16:08:00 CET',
Expand All @@ -57,6 +105,14 @@ class ContestControllerTest extends JuryControllerTestCase
'endtimeString' => '2021-07-17 16:11:00 CET',
'unfreezetimeString' => '2021-07-17 16:12:00 CET',
'deactivatetimeString' => '2021-07-17 16:13:00 CET'],
['shortname' => 'otzAfricaPorto-Novo',
'name' => 'Other timezone (Africa/Porto-Novo)',
'activatetimeString' => '2021-07-17 16:08:00 Africa/Porto-Novo',
'starttimeString' => '2021-07-17 16:09:00 Africa/Porto-Novo',
'freezetimeString' => '2021-07-17 16:10:00 Africa/Porto-Novo',
'endtimeString' => '2021-07-17 16:11:00 Africa/Porto-Novo',
'unfreezetimeString' => '2021-07-17 16:12:00 Africa/Porto-Novo',
'deactivatetimeString' => '2021-07-17 16:13:00 Africa/Porto-Novo'],
['shortname' => 'otzunder',
'name' => 'Other timezone (Underscore)',
'activatetimeString' => '2021-07-17 16:08:00 America/Porto_Velho',
Expand All @@ -73,6 +129,14 @@ class ContestControllerTest extends JuryControllerTestCase
'endtimeString' => '2021-07-17 16:11:00 Etc/GMT-3',
'unfreezetimeString' => '',
'deactivatetimeString' => ''],
['shortname' => 'otzGMT2',
'name' => 'Other timezone (GMT)',
'activatetimeString' => '2021-07-17 16:08:00 Etc/GMT+3',
'starttimeString' => '2021-07-17 16:09:00 Etc/GMT+3',
'freezetimeString' => '2021-07-17 16:10:00 Etc/GMT+3',
'endtimeString' => '2021-07-17 16:11:00 Etc/GMT+3',
'unfreezetimeString' => '',
'deactivatetimeString' => ''],
['shortname' => 'otzrel',
'name' => 'Other timezone (Relative)',
'activatetimeString' => '-10:00',
Expand All @@ -81,6 +145,14 @@ class ContestControllerTest extends JuryControllerTestCase
'endtimeString' => '+1111:11',
'unfreezetimeString' => '',
'deactivatetimeString' => ''],
['shortname' => 'other_split_char',
'name' => 'Other CLICS splitchar',
'activatetimeString' => '-10:00',
'starttimeString' => '2021-07-17T16:09:00 Atlantic/Reykjavik',
'freezetimeString' => '+0:01',
'endtimeString' => '+1111:11',
'unfreezetimeString' => '',
'deactivatetimeString' => ''],
['shortname' => 'nofr',
'name' => 'No Freeze',
'freezetimeString' => '',
Expand All @@ -93,7 +165,7 @@ class ContestControllerTest extends JuryControllerTestCase
'endtimeString' => '2021-07-17 16:11:00 Europe/Amsterdam',
'unfreezetimeString' => '',
'deactivatetimeString' => ''],
['shortname' => 'dirfreeze',
['shortname' => 'dirfreeze',
'name' => 'Direct freeze minimal',
'activatetimeString' => '2021-07-17 16:07:59 Europe/Amsterdam',
'starttimeString' => '2021-07-17 16:08:00 Europe/Amsterdam',
Expand All @@ -109,6 +181,14 @@ class ContestControllerTest extends JuryControllerTestCase
'endtimeString' => '+10:00',
'unfreezetimeString' => '+25:00',
'deactivatetimeString' => ''],
['shortname' => 'dirfreezerelnoplus',
'name' => 'Direct freeze minimal relative',
'activatetimeString' => '-0:00',
'starttimeString' => '2021-07-17 16:08:00 Europe/Amsterdam',
'freezetimeString' => '0:00',
'endtimeString' => '+10:00',
'unfreezetimeString' => '25:00',
'deactivatetimeString' => ''],
['shortname' => 'rel',
'name' => 'Relative contest',
'activatetimeString' => '-1:00',
Expand Down
Loading