16
16
use App \Utils \Scoreboard \Filter ;
17
17
use App \Utils \Utils ;
18
18
use Collator ;
19
+ use DateInterval ;
19
20
use DateTime ;
20
21
use DateTimeImmutable ;
22
+ use DateTimeInterface ;
21
23
use DateTimeZone ;
22
24
use Doctrine \ORM \EntityManagerInterface ;
23
25
use Doctrine \ORM \NonUniqueResultException ;
@@ -83,10 +85,14 @@ public function getContestYamlData(Contest $contest, bool $includeProblems = tru
83
85
'formal_name ' => $ contest ->getName (),
84
86
'name ' => $ contest ->getShortname (),
85
87
'start_time ' => Utils::absTime ($ contest ->getStarttime (), true ),
86
- 'end_time ' => Utils::absTime ($ contest ->getEndtime (), true ),
88
+ 'end_time ' => Utils::isRelTime ($ contest ->getEndtimeString ())
89
+ ? $ contest ->getEndtimeString ()
90
+ : Utils::absTime ($ contest ->getEndtime (), true ),
87
91
'duration ' => Utils::relTime ($ contest ->getContestTime ((float )$ contest ->getEndtime ())),
88
92
'penalty_time ' => $ this ->config ->get ('penalty_time ' ),
89
- 'activate_time ' => Utils::absTime ($ contest ->getActivatetime (), true ),
93
+ 'activate_time ' => Utils::isRelTime ($ contest ->getActivatetimeString ())
94
+ ? $ contest ->getActivatetimeString ()
95
+ : Utils::absTime ($ contest ->getActivatetime (), true ),
90
96
];
91
97
if ($ warnMsg = $ contest ->getWarningMessage ()) {
92
98
$ data ['warning_message ' ] = $ warnMsg ;
@@ -100,21 +106,27 @@ public function getContestYamlData(Contest $contest, bool $includeProblems = tru
100
106
}
101
107
102
108
if ($ contest ->getFreezetime () !== null ) {
103
- $ data ['scoreboard_freeze_time ' ] = Utils::absTime ($ contest ->getFreezetime (), true );
109
+ $ data ['scoreboard_freeze_time ' ] = Utils::isRelTime ($ contest ->getFreezetimeString ())
110
+ ? $ contest ->getFreezetimeString ()
111
+ : Utils::absTime ($ contest ->getFreezetime (), true );
104
112
$ data ['scoreboard_freeze_duration ' ] = Utils::relTime (
105
113
$ contest ->getContestTime ((float )$ contest ->getEndtime ()) - $ contest ->getContestTime ((float )$ contest ->getFreezetime ()),
106
114
true ,
107
115
);
108
116
if ($ contest ->getUnfreezetime () !== null ) {
109
- $ data ['scoreboard_thaw_time ' ] = Utils::absTime ($ contest ->getUnfreezetime (), true );
117
+ $ data ['scoreboard_thaw_time ' ] = Utils::isRelTime ($ contest ->getUnfreezetimeString ())
118
+ ? $ contest ->getUnfreezetimeString ()
119
+ : Utils::absTime ($ contest ->getUnfreezetime (), true );
110
120
}
111
121
}
112
122
if ($ contest ->getFinalizetime () !== null ) {
113
123
$ data ['finalize_time ' ] = Utils::absTime ($ contest ->getFinalizetime (), true );
114
124
}
115
125
116
126
if ($ contest ->getDeactivatetime () !== null ) {
117
- $ data ['deactivate_time ' ] = Utils::absTime ($ contest ->getDeactivatetime (), true );
127
+ $ data ['deactivate_time ' ] = Utils::isRelTime ($ contest ->getDeactivatetimeString ())
128
+ ? $ contest ->getDeactivatetimeString ()
129
+ : Utils::absTime ($ contest ->getDeactivatetime (), true );
118
130
}
119
131
120
132
if ($ includeProblems ) {
@@ -146,9 +158,15 @@ public function getContestYamlData(Contest $contest, bool $includeProblems = tru
146
158
*
147
159
* @param array<string> $fields
148
160
* @param array<string, string|DateTime|DateTimeImmutable> $data
161
+ * @return array{time: DateTimeImmutable|null, isRelative: bool|null, relativeTime: string|null}
149
162
*/
150
- protected function convertImportedTime (array $ fields , array $ data , ?string &$ errorMessage = null ): ?DateTimeImmutable
151
- {
163
+ protected function convertImportedTime (
164
+ array $ fields ,
165
+ array $ data ,
166
+ bool $ allowRelative = true ,
167
+ ?DateTimeInterface $ startTime = null ,
168
+ ?string &$ errorMessage = null
169
+ ): array {
152
170
$ timeValue = null ;
153
171
$ usedField = null ;
154
172
foreach ($ fields as $ field ) {
@@ -160,22 +178,42 @@ protected function convertImportedTime(array $fields, array $data, ?string &$err
160
178
}
161
179
}
162
180
181
+ $ isRelative = false ;
182
+
163
183
if (is_string ($ timeValue )) {
164
- $ time = date_create_from_format (DateTime::ISO8601 , $ timeValue ) ?:
165
- // Make sure ISO 8601 but with the T replaced with a space also works.
166
- date_create_from_format ('Y-m-d H:i:sO ' , $ timeValue );
184
+ if ($ allowRelative && ($ isRelative = Utils::isRelTime ($ timeValue )) && $ startTime ) {
185
+ $ time = new DateTimeImmutable ($ startTime ->format ('Y-m-d H:i:s ' ), $ startTime ->getTimezone ());
186
+ $ seconds = Utils::relTimeToSeconds ($ timeValue );
187
+ if ($ seconds < 0 ) {
188
+ $ time = $ time ->sub (new DateInterval (sprintf ('PT%sS ' , abs ($ seconds ))));
189
+ } else {
190
+ $ time = $ time ->add (new DateInterval (sprintf ('PT%sS ' , $ seconds )));
191
+ }
192
+ } else {
193
+ $ time = date_create_from_format (DateTime::ISO8601 , $ timeValue ) ?:
194
+ // Make sure ISO 8601 but with the T replaced with a space also works.
195
+ date_create_from_format ('Y-m-d H:i:sO ' , $ timeValue );
196
+ }
167
197
} else {
168
198
/** @var DateTime|DateTimeImmutable $time */
169
199
$ time = $ timeValue ;
170
200
}
171
201
// If/When parsing fails we get a false instead of a null
172
202
if ($ time === false ) {
173
203
$ errorMessage = 'Can not parse ' .$ usedField ;
174
- return null ;
204
+ return [
205
+ 'time ' => null ,
206
+ 'isRelative ' => null ,
207
+ 'relativeTime ' => null ,
208
+ ];
175
209
} elseif ($ time ) {
176
210
$ time = $ time ->setTimezone (new DateTimeZone (date_default_timezone_get ()));
177
211
}
178
- return $ time instanceof DateTime ? DateTimeImmutable::createFromMutable ($ time ) : $ time ;
212
+ return [
213
+ 'time ' => $ time instanceof DateTime ? DateTimeImmutable::createFromMutable ($ time ) : $ time ,
214
+ 'isRelative ' => $ isRelative ,
215
+ 'relativeTime ' => $ isRelative ? $ timeValue : null ,
216
+ ];
179
217
}
180
218
181
219
public function importContestData (mixed $ data , ?string &$ errorMessage = null , ?string &$ cid = null ): bool
@@ -215,13 +253,17 @@ public function importContestData(mixed $data, ?string &$errorMessage = null, ?s
215
253
216
254
$ invalid_regex = str_replace (['/^[ ' , '+$/ ' ], ['/[^ ' , '/ ' ], DOMJudgeService::EXTERNAL_IDENTIFIER_REGEX );
217
255
218
- $ startTime = $ this ->convertImportedTime ($ startTimeFields , $ data , $ errorMessage );
256
+ [ ' time ' => $ startTime] = $ this ->convertImportedTime ($ startTimeFields , $ data , false , errorMessage: $ errorMessage );
219
257
if ($ errorMessage ) {
220
258
return false ;
221
259
}
222
260
223
261
// Activate time is special, it can return non empty message for parsing error or null if no field was provided
224
- $ activateTime = $ this ->convertImportedTime ($ activateTimeFields , $ data , $ errorMessage );
262
+ [
263
+ 'time ' => $ activateTime ,
264
+ 'isRelative ' => $ activateTimeIsRelative ,
265
+ 'relativeTime ' => $ activateRelativeTime ,
266
+ ] = $ this ->convertImportedTime ($ activateTimeFields , $ data , startTime: $ startTime , errorMessage: $ errorMessage );
225
267
if ($ errorMessage ) {
226
268
return false ;
227
269
} elseif (!$ activateTime ) {
@@ -231,7 +273,11 @@ public function importContestData(mixed $data, ?string &$errorMessage = null, ?s
231
273
}
232
274
}
233
275
234
- $ deactivateTime = $ this ->convertImportedTime ($ deactivateTimeFields , $ data , $ errorMessage );
276
+ [
277
+ 'time ' => $ deactivateTime ,
278
+ 'isRelative ' => $ deactivateTimeIsRelative ,
279
+ 'relativeTime ' => $ deactivateRelativeTime ,
280
+ ] = $ this ->convertImportedTime ($ deactivateTimeFields , $ data , startTime: $ startTime , errorMessage: $ errorMessage );
235
281
if ($ errorMessage ) {
236
282
return false ;
237
283
}
@@ -247,11 +293,11 @@ public function importContestData(mixed $data, ?string &$errorMessage = null, ?s
247
293
->setExternalid ($ contest ->getShortname ())
248
294
->setWarningMessage ($ data ['warning_message ' ] ?? $ data ['warning-message ' ] ?? null )
249
295
->setStarttimeString (date_format ($ startTime , 'Y-m-d H:i:s e ' ))
250
- ->setActivatetimeString (date_format ($ activateTime , 'Y-m-d H:i:s e ' ))
296
+ ->setActivatetimeString ($ activateTimeIsRelative ? $ activateRelativeTime : date_format ($ activateTime , 'Y-m-d H:i:s e ' ))
251
297
->setEndtimeString (sprintf ('+%s ' , $ data ['duration ' ]))
252
298
->setPublic ($ data ['public ' ] ?? true );
253
299
if ($ deactivateTime ) {
254
- $ contest ->setDeactivatetimeString (date_format ($ deactivateTime , 'Y-m-d H:i:s e ' ));
300
+ $ contest ->setDeactivatetimeString ($ deactivateTimeIsRelative ? $ deactivateRelativeTime : date_format ($ deactivateTime , 'Y-m-d H:i:s e ' ));
255
301
}
256
302
257
303
// Get all visible categories. For now, we assume these are the ones getting awards
0 commit comments