Skip to content

Commit c1eb219

Browse files
committed
Merge branch 'date-time-dst-rfc' into PHP-5.4
This is a partial resolve of some DST issues, plenty of things to do, but as this *does* fix a few issues it's worth merging it already.
2 parents fb0c2cf + aab0b2c commit c1eb219

13 files changed

+615
-450
lines changed

ext/date/lib/interval.c

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
2525
{
2626
timelib_rel_time *rt;
2727
timelib_time *swp;
28-
timelib_sll dst_h_corr = 0, dst_m_corr = 0;
28+
timelib_sll dst_corr = 0 ,dst_h_corr = 0, dst_m_corr = 0;
2929
timelib_time one_backup, two_backup;
3030

3131
rt = timelib_rel_time_ctor();
@@ -43,8 +43,9 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
4343
&& (strcmp(one->tz_info->name, two->tz_info->name) == 0)
4444
&& (one->z != two->z))
4545
{
46-
dst_h_corr = (two->z - one->z) / 3600;
47-
dst_m_corr = ((two->z - one->z) % 3600) / 60;
46+
dst_corr = two->z - one->z;
47+
dst_h_corr = dst_corr / 3600;
48+
dst_m_corr = (dst_corr % 3600) / 60;
4849
}
4950

5051
/* Save old TZ info */
@@ -57,16 +58,108 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
5758
rt->y = two->y - one->y;
5859
rt->m = two->m - one->m;
5960
rt->d = two->d - one->d;
60-
rt->h = two->h - one->h + dst_h_corr;
61-
rt->i = two->i - one->i + dst_m_corr;
61+
rt->h = two->h - one->h;
62+
rt->i = two->i - one->i;
6263
rt->s = two->s - one->s;
64+
if (one_backup.dst == 0 && two_backup.dst == 1 && two->sse >= one->sse + 86400 - dst_corr) {
65+
rt->h += dst_h_corr;
66+
rt->i += dst_m_corr;
67+
}
68+
6369
rt->days = abs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400));
6470

6571
timelib_do_rel_normalize(rt->invert ? one : two, rt);
6672

73+
/* We need to do this after normalisation otherwise we can't get "24H" */
74+
if (one_backup.dst == 1 && two_backup.dst == 0 && two->sse >= one->sse + 86400) {
75+
if (two->sse < one->sse + 86400 - dst_corr) {
76+
rt->d--;
77+
rt->h = 24;
78+
} else {
79+
rt->h += dst_h_corr;
80+
rt->i += dst_m_corr;
81+
}
82+
}
83+
6784
/* Restore old TZ info */
6885
memcpy(one, &one_backup, sizeof(one_backup));
6986
memcpy(two, &two_backup, sizeof(two_backup));
7087

7188
return rt;
7289
}
90+
91+
timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
92+
{
93+
int bias = 1;
94+
timelib_time *t = timelib_time_clone(old_time);
95+
96+
if (interval->have_weekday_relative || interval->have_special_relative) {
97+
memcpy(&t->relative, interval, sizeof(struct timelib_rel_time));
98+
} else {
99+
if (interval->invert) {
100+
bias = -1;
101+
}
102+
memset(&t->relative, 0, sizeof(struct timelib_rel_time));
103+
t->relative.y = interval->y * bias;
104+
t->relative.m = interval->m * bias;
105+
t->relative.d = interval->d * bias;
106+
t->relative.h = interval->h * bias;
107+
t->relative.i = interval->i * bias;
108+
t->relative.s = interval->s * bias;
109+
}
110+
t->have_relative = 1;
111+
t->sse_uptodate = 0;
112+
113+
timelib_update_ts(t, NULL);
114+
115+
// printf("%lld %lld %d\n", old_time->dst, t->dst, (t->sse - old_time->sse));
116+
/* Adjust for backwards DST changeover */
117+
if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
118+
t->sse -= old_time->z;
119+
t->sse += t->z;
120+
}
121+
122+
timelib_update_from_sse(t);
123+
t->have_relative = 0;
124+
125+
return t;
126+
}
127+
128+
timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
129+
{
130+
int bias = 1;
131+
timelib_time *t = timelib_time_clone(old_time);
132+
133+
if (interval->invert) {
134+
bias = -1;
135+
}
136+
137+
memset(&t->relative, 0, sizeof(struct timelib_rel_time));
138+
t->relative.y = 0 - (interval->y * bias);
139+
t->relative.m = 0 - (interval->m * bias);
140+
t->relative.d = 0 - (interval->d * bias);
141+
t->relative.h = 0 - (interval->h * bias);
142+
t->relative.i = 0 - (interval->i * bias);
143+
t->relative.s = 0 - (interval->s * bias);
144+
t->have_relative = 1;
145+
t->sse_uptodate = 0;
146+
147+
timelib_update_ts(t, NULL);
148+
149+
/* Adjust for backwards DST changeover */
150+
if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
151+
t->sse -= old_time->z;
152+
t->sse += t->z;
153+
}
154+
/* Adjust for forwards DST changeover */
155+
if (old_time->dst == 0 && t->dst == 1 && !interval->y && !interval->m && !interval->d ) {
156+
t->sse -= old_time->z;
157+
t->sse += t->z;
158+
}
159+
160+
timelib_update_from_sse(t);
161+
162+
t->have_relative = 0;
163+
164+
return t;
165+
}

ext/date/lib/timelib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,7 @@ int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat,
136136

137137
/* from interval.c */
138138
timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);
139+
timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);
140+
timelib_time *timelib_sub(timelib_time *t, timelib_rel_time *interval);
139141

140142
#endif

ext/date/lib/unixtime2tm.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,16 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts)
137137
void timelib_update_from_sse(timelib_time *tm)
138138
{
139139
timelib_sll sse;
140+
int z = tm->z;
141+
signed int dst = tm->dst;
140142

141143
sse = tm->sse;
142144

143145
switch (tm->zone_type) {
144146
case TIMELIB_ZONETYPE_ABBR:
145147
case TIMELIB_ZONETYPE_OFFSET: {
146-
int z = tm->z;
147-
signed int dst = tm->dst;
148-
149148
timelib_unixtime2gmt(tm, tm->sse - (tm->z * 60) + (tm->dst * 3600));
150149

151-
tm->z = z;
152-
tm->dst = dst;
153150
goto cleanup;
154151
}
155152

@@ -171,6 +168,8 @@ void timelib_update_from_sse(timelib_time *tm)
171168
tm->sse = sse;
172169
tm->is_localtime = 1;
173170
tm->have_zone = 1;
171+
tm->z = z;
172+
tm->dst = dst;
174173
}
175174

176175
void timelib_unixtime2local(timelib_time *tm, timelib_sll ts)

ext/date/php_date.c

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2487,6 +2487,7 @@ PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str,
24872487

24882488
timelib_fill_holes(dateobj->time, now, TIMELIB_NO_CLONE);
24892489
timelib_update_ts(dateobj->time, tzi);
2490+
timelib_update_from_sse(dateobj->time);
24902491

24912492
dateobj->time->have_relative = 0;
24922493

@@ -2888,7 +2889,7 @@ PHP_FUNCTION(date_add)
28882889
zval *object, *interval;
28892890
php_date_obj *dateobj;
28902891
php_interval_obj *intobj;
2891-
int bias = 1;
2892+
timelib_time *new_time;
28922893

28932894
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
28942895
RETURN_FALSE;
@@ -2898,27 +2899,9 @@ PHP_FUNCTION(date_add)
28982899
intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
28992900
DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
29002901

2901-
2902-
if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) {
2903-
memcpy(&dateobj->time->relative, intobj->diff, sizeof(struct timelib_rel_time));
2904-
} else {
2905-
if (intobj->diff->invert) {
2906-
bias = -1;
2907-
}
2908-
memset(&dateobj->time->relative, 0, sizeof(struct timelib_rel_time));
2909-
dateobj->time->relative.y = intobj->diff->y * bias;
2910-
dateobj->time->relative.m = intobj->diff->m * bias;
2911-
dateobj->time->relative.d = intobj->diff->d * bias;
2912-
dateobj->time->relative.h = intobj->diff->h * bias;
2913-
dateobj->time->relative.i = intobj->diff->i * bias;
2914-
dateobj->time->relative.s = intobj->diff->s * bias;
2915-
}
2916-
dateobj->time->have_relative = 1;
2917-
dateobj->time->sse_uptodate = 0;
2918-
2919-
timelib_update_ts(dateobj->time, NULL);
2920-
timelib_update_from_sse(dateobj->time);
2921-
dateobj->time->have_relative = 0;
2902+
new_time = timelib_add(dateobj->time, intobj->diff);
2903+
timelib_time_dtor(dateobj->time);
2904+
dateobj->time = new_time;
29222905

29232906
RETURN_ZVAL(object, 1, 0);
29242907
}
@@ -2932,7 +2915,7 @@ PHP_FUNCTION(date_sub)
29322915
zval *object, *interval;
29332916
php_date_obj *dateobj;
29342917
php_interval_obj *intobj;
2935-
int bias = 1;
2918+
timelib_time *new_time;
29362919

29372920
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
29382921
RETURN_FALSE;
@@ -2947,24 +2930,9 @@ PHP_FUNCTION(date_sub)
29472930
return;
29482931
}
29492932

2950-
if (intobj->diff->invert) {
2951-
bias = -1;
2952-
}
2953-
2954-
memset(&dateobj->time->relative, 0, sizeof(struct timelib_rel_time));
2955-
dateobj->time->relative.y = 0 - (intobj->diff->y * bias);
2956-
dateobj->time->relative.m = 0 - (intobj->diff->m * bias);
2957-
dateobj->time->relative.d = 0 - (intobj->diff->d * bias);
2958-
dateobj->time->relative.h = 0 - (intobj->diff->h * bias);
2959-
dateobj->time->relative.i = 0 - (intobj->diff->i * bias);
2960-
dateobj->time->relative.s = 0 - (intobj->diff->s * bias);
2961-
dateobj->time->have_relative = 1;
2962-
dateobj->time->sse_uptodate = 0;
2963-
2964-
timelib_update_ts(dateobj->time, NULL);
2965-
timelib_update_from_sse(dateobj->time);
2966-
2967-
dateobj->time->have_relative = 0;
2933+
new_time = timelib_sub(dateobj->time, intobj->diff);
2934+
timelib_time_dtor(dateobj->time);
2935+
dateobj->time = new_time;
29682936

29692937
RETURN_ZVAL(object, 1, 0);
29702938
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Test for Date/Time construction during a forward DST transition
3+
--FILE--
4+
<?php
5+
date_default_timezone_set('America/New_York');
6+
7+
$date = new DateTime('2010-03-14 01:30:00');
8+
echo $date->format('Y-m-d H:i:s T/e - U') . "\n";
9+
10+
$date = new DateTime('2010-03-14 02:00:00');
11+
echo $date->format('Y-m-d H:i:s T/e - U') . "\n";
12+
13+
$date = new DateTime('2010-03-14 02:30:00');
14+
echo $date->format('Y-m-d H:i:s T/e - U') . "\n";
15+
16+
$date = new DateTime('2010-03-14 03:00:00');
17+
echo $date->format('Y-m-d H:i:s T/e - U') . "\n";
18+
19+
$date = new DateTime('2010-03-14 03:30:00');
20+
echo $date->format('Y-m-d H:i:s T/e - U') . "\n";
21+
?>
22+
--EXPECT--
23+
2010-03-14 01:30:00 EST/America/New_York - 1268548200
24+
2010-03-14 03:00:00 EDT/America/New_York - 1268550000
25+
2010-03-14 03:30:00 EDT/America/New_York - 1268551800
26+
2010-03-14 03:00:00 EDT/America/New_York - 1268550000
27+
2010-03-14 03:30:00 EDT/America/New_York - 1268551800
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
--TEST--
2+
RFC: DateTime and Daylight Saving Time Transitions (zone type 3, ba)
3+
--CREDITS--
4+
Daniel Convissor <danielc@php.net>
5+
--FILE--
6+
<?php
7+
8+
date_default_timezone_set('America/New_York');
9+
$date_format = 'Y-m-d H:i:s T e';
10+
$interval_format = 'P%dDT%hH';
11+
12+
/*
13+
* Backward Transitions, add().
14+
*/
15+
16+
$start = new DateTime('2010-11-07 01:59:59');
17+
$interval_spec = 'PT1S';
18+
$interval = new DateInterval($interval_spec);
19+
echo 'ba1 ' . $start->format($date_format) . " + $interval_spec = "
20+
. $start->add($interval)->format($date_format) . "\n";
21+
22+
$start = new DateTime('2010-11-06 04:30:00');
23+
$interval_spec = 'P1D';
24+
$interval = new DateInterval($interval_spec);
25+
echo 'ba2 ' . $start->format($date_format) . " + $interval_spec = "
26+
. $start->add($interval)->format($date_format) . "\n";
27+
28+
$start = new DateTime('2010-11-06 04:30:00');
29+
$interval_spec = 'PT24H';
30+
$interval = new DateInterval($interval_spec);
31+
echo 'ba3 ' . $start->format($date_format) . " + $interval_spec = "
32+
. $start->add($interval)->format($date_format) . "\n";
33+
34+
$start = new DateTime('2010-11-06 04:30:00');
35+
$interval_spec = 'PT23H';
36+
$interval = new DateInterval($interval_spec);
37+
echo 'ba4 ' . $start->format($date_format) . " + $interval_spec = "
38+
. $start->add($interval)->format($date_format) . "\n";
39+
40+
$start = new DateTime('2010-11-06 04:30:00');
41+
$interval_spec = 'PT22H';
42+
$interval = new DateInterval($interval_spec);
43+
echo 'ba5 ' . $start->format($date_format) . " + $interval_spec = "
44+
. $start->add($interval)->format($date_format) . "\n";
45+
46+
$start = new DateTime('2010-11-06 04:30:00');
47+
$interval_spec = 'PT21H';
48+
$interval = new DateInterval($interval_spec);
49+
echo 'ba6 ' . $start->format($date_format) . " + $interval_spec = "
50+
. $start->add($interval)->format($date_format) . "\n";
51+
52+
$start = new DateTime('2010-11-06 01:30:00');
53+
$interval_spec = 'P1D';
54+
$interval = new DateInterval($interval_spec);
55+
echo 'ba7 ' . $start->format($date_format) . " + $interval_spec = "
56+
. $start->add($interval)->format($date_format) . "\n";
57+
58+
$start = new DateTime('2010-11-06 01:30:00');
59+
$interval_spec = 'P1DT1H';
60+
$interval = new DateInterval($interval_spec);
61+
echo 'ba8 ' . $start->format($date_format) . " + $interval_spec = "
62+
. $start->add($interval)->format($date_format) . "\n";
63+
64+
$start = new DateTime('2010-11-06 04:30:00');
65+
$interval_spec = 'PT25H';
66+
$interval = new DateInterval($interval_spec);
67+
echo 'ba9 ' . $start->format($date_format) . " + $interval_spec = "
68+
. $start->add($interval)->format($date_format) . "\n";
69+
70+
$start = new DateTime('2010-11-06 03:30:00');
71+
$interval_spec = 'P1D';
72+
$interval = new DateInterval($interval_spec);
73+
echo 'ba10 ' . $start->format($date_format) . " + $interval_spec = "
74+
. $start->add($interval)->format($date_format) . "\n";
75+
76+
$start = new DateTime('2010-11-06 02:30:00');
77+
$interval_spec = 'P1D';
78+
$interval = new DateInterval($interval_spec);
79+
echo 'ba11 ' . $start->format($date_format) . " + $interval_spec = "
80+
. $start->add($interval)->format($date_format) . "\n";
81+
82+
echo "\n";
83+
84+
?>
85+
--EXPECT--
86+
ba1 2010-11-07 01:59:59 EDT America/New_York + PT1S = 2010-11-07 01:00:00 EST America/New_York
87+
ba2 2010-11-06 04:30:00 EDT America/New_York + P1D = 2010-11-07 04:30:00 EST America/New_York
88+
ba3 2010-11-06 04:30:00 EDT America/New_York + PT24H = 2010-11-07 03:30:00 EST America/New_York
89+
ba4 2010-11-06 04:30:00 EDT America/New_York + PT23H = 2010-11-07 02:30:00 EST America/New_York
90+
ba5 2010-11-06 04:30:00 EDT America/New_York + PT22H = 2010-11-07 01:30:00 EST America/New_York
91+
ba6 2010-11-06 04:30:00 EDT America/New_York + PT21H = 2010-11-07 01:30:00 EDT America/New_York
92+
ba7 2010-11-06 01:30:00 EDT America/New_York + P1D = 2010-11-07 01:30:00 EDT America/New_York
93+
ba8 2010-11-06 01:30:00 EDT America/New_York + P1DT1H = 2010-11-07 02:30:00 EST America/New_York
94+
ba9 2010-11-06 04:30:00 EDT America/New_York + PT25H = 2010-11-07 04:30:00 EST America/New_York
95+
ba10 2010-11-06 03:30:00 EDT America/New_York + P1D = 2010-11-07 03:30:00 EST America/New_York
96+
ba11 2010-11-06 02:30:00 EDT America/New_York + P1D = 2010-11-07 02:30:00 EST America/New_York

0 commit comments

Comments
 (0)