@@ -14,7 +14,7 @@ require __DIR__ . '/../bootstrap.php';
14
14
date_default_timezone_set ('Europe/Prague ' );
15
15
16
16
17
- test ('Basic operations ' , function () {
17
+ test ('Basic operations (no DST involved) ' , function () {
18
18
$ base = new DateTime ('2024-07-15 10:00:00 ' ); // Summer time (CEST)
19
19
20
20
$ dt = clone $ base ;
@@ -35,6 +35,68 @@ test('Basic operations', function () {
35
35
});
36
36
37
37
38
+ test ('Spring DST transition (2025-03-30 02:00 -> 03:00) ' , function () {
39
+ $ startSpring = new DateTime ('2025-03-30 01:45:00 ' ); // Before the jump (CET +01:00)
40
+
41
+ // Modification ending BEFORE the jump
42
+ $ dt = clone $ startSpring ;
43
+ $ dt ->modify ('+10 minutes ' );
44
+ Assert::same ('2025-03-30 01:55:00 CET (+01:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+10 min (ends before jump) ' );
45
+
46
+ // Modification crossing the jump (duration logic thanks to Nette fix)
47
+ $ dt = clone $ startSpring ;
48
+ $ dt ->modify ('+30 minutes ' ); // 01:45 CET + 30 min duration = 01:15 UTC = 03:15 CEST
49
+ Assert::same ('2025-03-30 03:15:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+30 min (crosses jump) ' );
50
+
51
+ $ dt = clone $ startSpring ;
52
+ $ dt ->modify ('+90 minutes ' ); // 01:45 CET + 90 min duration = 02:15 UTC = 04:15 CEST (Key test!)
53
+ Assert::same ('2025-03-30 04:15:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+90 min (crosses jump) ' );
54
+
55
+ // Adding a day across the jump (day has only 23 hours)
56
+ $ dt = clone $ startSpring ;
57
+ $ dt ->modify ('+1 day ' );
58
+ Assert::same ('2025-03-31 01:45:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+1 day ' );
59
+
60
+ // Combination of day + hours across the jump
61
+ $ dt = clone $ startSpring ;
62
+ $ dt ->modify ('+1 day +1 hour ' );
63
+ Assert::same ('2025-03-31 02:45:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+1 day + 1 hour ' );
64
+
65
+ $ dt = clone $ startSpring ;
66
+ $ dt ->modify ('+2 hours ' ); // 01:45 CET + 2h duration = 02:45 UTC = 04:45 CEST
67
+ Assert::same ('2025-03-30 04:45:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+2 hours (crosses jump) ' );
68
+ });
69
+
70
+
71
+ test ('Autumn DST transition (2024-10-27 03:00 -> 02:00) ' , function () {
72
+ $ startAutumn = new DateTime ('2024-10-27 01:45:00 ' ); // Before the fallback (CEST +02:00)
73
+
74
+ // Modification ending BEFORE the fallback (still CEST)
75
+ $ dt = clone $ startAutumn ;
76
+ $ dt ->modify ('+30 minutes ' );
77
+ Assert::same ('2024-10-27 02:15:00 CEST (+02:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+30 min (ends before fallback) ' );
78
+
79
+ // Modification crossing the fallback (lands in the second 2:xx hour - CET)
80
+ $ dt = clone $ startAutumn ;
81
+ $ dt ->modify ('+90 minutes ' ); // 01:45 CEST + 90 min duration = 01:15 UTC = 02:15 CET
82
+ Assert::same ('2024-10-27 02:15:00 CET (+01:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+90 min (crosses fallback, lands in CET) ' );
83
+
84
+ $ dt = clone $ startAutumn ;
85
+ $ dt ->modify ('+1 hour + 30 minutes ' ); // Same as +90 minutes
86
+ Assert::same ('2024-10-27 02:15:00 CET (+01:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+1 hour + 30 minutes (crosses fallback) ' );
87
+
88
+ // Adding a day across the fallback (day has 25 hours)
89
+ $ dt = clone $ startAutumn ;
90
+ $ dt ->modify ('+1 day ' );
91
+ Assert::same ('2024-10-28 01:45:00 CET (+01:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+1 day ' );
92
+
93
+ // Combination of day + hours across the fallback
94
+ $ dt = clone $ startAutumn ;
95
+ $ dt ->modify ('+1 day +2 hours ' );
96
+ Assert::same ('2024-10-28 03:45:00 CET (+01:00) ' , $ dt ->format ('Y-m-d H:i:s T (P) ' ), '+1 day + 2 hours ' );
97
+ });
98
+
99
+
38
100
test ('Complex and varied format strings ' , function () {
39
101
$ dt = new DateTime ('2024-04-10 12:00:00 ' ); // CEST
40
102
// Expected: -2m -> 2024-02-10 12:00 CET | +7d -> 2024-02-17 12:00 CET | +23h 59m 59s -> 2024-02-18 11:59:59 CET
0 commit comments