@@ -198,4 +198,151 @@ public function testInvalidDates(string $date, string $format): void
198198 {
199199 self ::assertFalse (StringManipulation::isValidDate ($ date , $ format ));
200200 }
201+
202+
203+ /**
204+ * Test edge cases and boundary conditions for date validation.
205+ */
206+ public function testDateValidationEdgeCases (): void
207+ {
208+ // Leap year edge cases
209+ self ::assertTrue (StringManipulation::isValidDate ('2000-02-29 ' , 'Y-m-d ' )); // Leap year (divisible by 400)
210+ self ::assertTrue (StringManipulation::isValidDate ('2004-02-29 ' , 'Y-m-d ' )); // Leap year (divisible by 4)
211+ self ::assertFalse (StringManipulation::isValidDate ('1900-02-29 ' , 'Y-m-d ' )); // Not leap year (divisible by 100, not 400)
212+ self ::assertFalse (StringManipulation::isValidDate ('2023-02-29 ' , 'Y-m-d ' )); // Not leap year
213+
214+ // Month boundary cases
215+ self ::assertTrue (StringManipulation::isValidDate ('2023-01-31 ' , 'Y-m-d ' )); // January 31 days
216+ self ::assertFalse (StringManipulation::isValidDate ('2023-02-31 ' , 'Y-m-d ' )); // February doesn't have 31 days
217+ self ::assertTrue (StringManipulation::isValidDate ('2023-03-31 ' , 'Y-m-d ' )); // March 31 days
218+ self ::assertFalse (StringManipulation::isValidDate ('2023-04-31 ' , 'Y-m-d ' )); // April only has 30 days
219+ self ::assertTrue (StringManipulation::isValidDate ('2023-05-31 ' , 'Y-m-d ' )); // May 31 days
220+ self ::assertFalse (StringManipulation::isValidDate ('2023-06-31 ' , 'Y-m-d ' )); // June only has 30 days
221+ self ::assertTrue (StringManipulation::isValidDate ('2023-12-31 ' , 'Y-m-d ' )); // December 31 days
222+
223+ // Time boundary cases
224+ self ::assertTrue (StringManipulation::isValidDate ('00:00:00 ' , 'H:i:s ' )); // Midnight
225+ self ::assertTrue (StringManipulation::isValidDate ('23:59:59 ' , 'H:i:s ' )); // Last second of day
226+ self ::assertFalse (StringManipulation::isValidDate ('24:00:00 ' , 'H:i:s ' )); // Invalid hour
227+ self ::assertFalse (StringManipulation::isValidDate ('23:60:00 ' , 'H:i:s ' )); // Invalid minute
228+ self ::assertFalse (StringManipulation::isValidDate ('23:59:60 ' , 'H:i:s ' )); // Invalid second
229+
230+ // Year boundary cases
231+ self ::assertTrue (StringManipulation::isValidDate ('0001-01-01 ' , 'Y-m-d ' )); // Minimum year
232+ self ::assertTrue (StringManipulation::isValidDate ('9999-12-31 ' , 'Y-m-d ' )); // Maximum year
233+ self ::assertFalse (StringManipulation::isValidDate ('0000-01-01 ' , 'Y-m-d ' )); // Year 0 doesn't exist
234+
235+ // Complex format edge cases
236+ self ::assertTrue (StringManipulation::isValidDate ('31-12-2023 23:59:59 ' , 'd-m-Y H:i:s ' ));
237+ self ::assertFalse (StringManipulation::isValidDate ('32-12-2023 23:59:59 ' , 'd-m-Y H:i:s ' ));
238+
239+ // Different separators
240+ self ::assertTrue (StringManipulation::isValidDate ('2023/12/31 ' , 'Y/m/d ' ));
241+ self ::assertTrue (StringManipulation::isValidDate ('2023.12.31 ' , 'Y.m.d ' ));
242+ self ::assertTrue (StringManipulation::isValidDate ('31.12.2023 ' , 'd.m.Y ' ));
243+ }
244+
245+
246+ /**
247+ * Test performance and stress scenarios for date validation.
248+ */
249+ public function testDateValidationPerformance (): void
250+ {
251+ // Large batch of date validations
252+ $ dates = [];
253+ for ($ year = 2020 ; $ year <= 2025 ; ++$ year ) {
254+ for ($ month = 1 ; $ month <= 12 ; ++$ month ) {
255+ for ($ day = 1 ; $ day <= 28 ; ++$ day ) { // Safe range for all months
256+ $ dates [] = sprintf ('%04d-%02d-%02d ' , $ year , $ month , $ day );
257+ }
258+ }
259+ }
260+
261+ $ startTime = microtime (true );
262+ $ validCount = 0 ;
263+ foreach ($ dates as $ date ) {
264+ if (StringManipulation::isValidDate ($ date , 'Y-m-d ' )) {
265+ ++$ validCount ;
266+ }
267+ }
268+
269+ $ duration = microtime (true ) - $ startTime ;
270+
271+ self ::assertEquals (count ($ dates ), $ validCount ); // All should be valid
272+ self ::assertLessThan (2.0 , $ duration , 'Batch date validation should complete efficiently ' );
273+
274+ // Stress test with complex formats
275+ $ complexDates = [
276+ '2023-12-31 23:59:59 ' ,
277+ '31-12-2023 00:00:00 ' ,
278+ '2023/01/01 12:30:45 ' ,
279+ '01.01.2023 06:15:30 ' ,
280+ ];
281+
282+ $ formats = [
283+ 'Y-m-d H:i:s ' ,
284+ 'd-m-Y H:i:s ' ,
285+ 'Y/m/d H:i:s ' ,
286+ 'd.m.Y H:i:s ' ,
287+ ];
288+
289+ $ startTime = microtime (true );
290+ foreach ($ complexDates as $ index => $ date ) {
291+ self ::assertTrue (StringManipulation::isValidDate ($ date , $ formats [$ index ]));
292+ }
293+
294+ $ duration = microtime (true ) - $ startTime ;
295+ self ::assertLessThan (0.1 , $ duration , 'Complex format validation should be fast ' );
296+ }
297+
298+
299+ /**
300+ * Test negative flow scenarios for date validation.
301+ */
302+ public function testDateValidationNegativeFlow (): void
303+ {
304+ // Malformed dates
305+ $ malformedDates = [
306+ ['' , 'Y-m-d ' ],
307+ ['not-a-date ' , 'Y-m-d ' ],
308+ ['2023-13-01 ' , 'Y-m-d ' ], // Invalid month
309+ ['2023-00-01 ' , 'Y-m-d ' ], // Invalid month
310+ ['2023-01-00 ' , 'Y-m-d ' ], // Invalid day
311+ ['2023-01-32 ' , 'Y-m-d ' ], // Invalid day
312+ ['2023/01/01 ' , 'Y-m-d ' ], // Wrong separator
313+ ['2023-1-1 ' , 'Y-m-d ' ], // Missing zero padding
314+ ];
315+
316+ foreach ($ malformedDates as [$ date , $ format ]) {
317+ self ::assertFalse (StringManipulation::isValidDate ($ date , $ format ), sprintf ("Date '%s' should be invalid for format '%s' " , $ date , $ format ));
318+ }
319+
320+ // Time validation edge cases
321+ $ invalidTimes = [
322+ ['25:00:00 ' , 'H:i:s ' ], // Hour too high
323+ ['12:60:00 ' , 'H:i:s ' ], // Minute too high
324+ ['12:30:60 ' , 'H:i:s ' ], // Second too high
325+ ['-1:30:00 ' , 'H:i:s ' ], // Negative hour
326+ ['12:-1:00 ' , 'H:i:s ' ], // Negative minute
327+ ['12:30:-1 ' , 'H:i:s ' ], // Negative second
328+ ];
329+
330+ foreach ($ invalidTimes as [$ time , $ format ]) {
331+ self ::assertFalse (StringManipulation::isValidDate ($ time , $ format ), sprintf ("Time '%s' should be invalid for format '%s' " , $ time , $ format ));
332+ }
333+
334+ // Format mismatch scenarios
335+ $ formatMismatches = [
336+ ['2023-01-01 ' , 'd-m-Y ' ], // Date format doesn't match
337+ ['01-01-2023 ' , 'Y-m-d ' ], // Date format doesn't match
338+ ['2023-01-01 12:30:00 ' , 'Y-m-d ' ], // Extra time component
339+ ['12:30:00 ' , 'Y-m-d H:i:s ' ], // Missing date component
340+ ['2023 ' , 'Y-m-d ' ], // Incomplete date
341+ ['01-01 ' , 'Y-m-d ' ], // Incomplete date
342+ ];
343+
344+ foreach ($ formatMismatches as [$ date , $ format ]) {
345+ self ::assertFalse (StringManipulation::isValidDate ($ date , $ format ), sprintf ("Date '%s' should not match format '%s' " , $ date , $ format ));
346+ }
347+ }
201348}
0 commit comments