Skip to content

Commit

Permalink
Fixed bug #81458: Regression: Incorrect difference after timezone change
Browse files Browse the repository at this point in the history
  • Loading branch information
derickr committed Nov 8, 2021
1 parent b1b6440 commit 904933e
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 4 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ PHP NEWS
. Fixed bug #78647 (SEGFAULT in zend_do_perform_implementation_check).
(Nikita)

- Date:
. Fixed bug #81458 (Regression Incorrect difference after timezone change).
(Derick)

- GD:
. Fixed bug #71316 (libpng warning from imagecreatefromstring). (cmb)

Expand Down
33 changes: 32 additions & 1 deletion ext/date/lib/interval.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
rt->s = two->s - one->s;
rt->us = two->us - one->us;

rt->days = fabs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400));
rt->days = timelib_diff_days(one, two);

/* Fall Back: Cater for transition period, where rt->invert is 0, but there are negative numbers */
if (one->dst == 1 && two->dst == 0) {
Expand Down Expand Up @@ -147,6 +147,37 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
return rt;
}


int timelib_diff_days(timelib_time *one, timelib_time *two)
{
int days = 0;

if (timelib_same_timezone(one, two)) {
timelib_time *earliest, *latest;
double earliest_time, latest_time;

if (timelib_time_compare(one, two) < 0) {
earliest = one;
latest = two;
} else {
earliest = two;
latest = one;
}
timelib_hmsf_to_decimal_hour(earliest->h, earliest->i, earliest->s, earliest->us, &earliest_time);
timelib_hmsf_to_decimal_hour(latest->h, latest->i, latest->s, latest->us, &latest_time);

days = llabs(timelib_epoch_days_from_time(one) - timelib_epoch_days_from_time(two));
if (latest_time < earliest_time && days > 0) {
days--;
}
} else {
days = fabs(floor(one->sse - two->sse) / 86400);
}

return days;
}


timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
{
int bias = 1;
Expand Down
20 changes: 20 additions & 0 deletions ext/date/lib/parse_tz.c
Original file line number Diff line number Diff line change
Expand Up @@ -930,3 +930,23 @@ timelib_sll timelib_get_current_offset(timelib_time *t)
return 0;
}
}

int timelib_same_timezone(timelib_time *one, timelib_time *two)
{
if (one->zone_type != two->zone_type) {
return 0;
}

if (one->zone_type == TIMELIB_ZONETYPE_ABBR || one->zone_type == TIMELIB_ZONETYPE_OFFSET) {
if ((one->z + (one->dst * 3600)) == (two->z + (two->dst * 3600))) {
return 1;
}
return 0;
}

if (one->zone_type == TIMELIB_ZONETYPE_ID && strcmp(one->tz_info->name, two->tz_info->name) == 0) {
return 1;
}

return 0;
}
9 changes: 9 additions & 0 deletions ext/date/lib/timelib.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
}
}

void timelib_hmsf_to_decimal_hour(int hour, int min, int sec, int us, double *h)
{
if (hour > 0) {
*h = ((double)hour + (double)min / MINS_PER_HOUR + (double)sec / SECS_PER_HOUR) + (double)us / USECS_PER_HOUR;
} else {
*h = ((double)hour - (double)min / MINS_PER_HOUR - (double)sec / SECS_PER_HOUR) - (double)us / USECS_PER_HOUR;
}
}

timelib_sll timelib_hms_to_seconds(timelib_sll h, timelib_sll m, timelib_sll s)
{
return (h * SECS_PER_HOUR) + (m * 60) + s;
Expand Down
30 changes: 27 additions & 3 deletions ext/date/lib/timelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
# include "timelib_config.h"
#endif

#define TIMELIB_VERSION 202108
#define TIMELIB_EXTENDED_VERSION 20210801
#define TIMELIB_ASCII_VERSION "2021.08"
#define TIMELIB_VERSION 202110
#define TIMELIB_EXTENDED_VERSION 20211001
#define TIMELIB_ASCII_VERSION "2021.10"

#include <stdlib.h>
#include <stdbool.h>
Expand Down Expand Up @@ -799,6 +799,16 @@ timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *
*/
timelib_sll timelib_get_current_offset(timelib_time *t);

/**
* Returns whether the timezone information in *one and *two are the same
*
* A timezone is considered the same if:
* - the ->zone_type values are the same for *one and *two
* - for TYPE_ABBR and TYPE_OFFSET, ->z + (->dst * 3600), is the same
* - for TYPE_ID, the zone's names are the same
*/
int timelib_same_timezone(timelib_time *one, timelib_time *two);

/**
* Displays debugging information about the time zone information in 'tz'.
*/
Expand Down Expand Up @@ -961,6 +971,11 @@ void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec);
*/
void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h);

/**
* Converts hour/min/sec/micro sec values into a decimal hour
*/
void timelib_hmsf_to_decimal_hour(int hour, int min, int sec, int us, double *h);

/**
* Converts hour/min/sec values into seconds
*/
Expand Down Expand Up @@ -1027,6 +1042,15 @@ int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat,
*/
timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);

/**
* Calculates the difference in full days between two times
*
* The result is the number of full days between 'one' and 'two'. It does take
* into account 23 and 25 hour (and variants) days when the zone_type
* is TIMELIB_ZONETYPE_ID and have the same TZID for 'one' and 'two'.
*/
int timelib_diff_days(timelib_time *one, timelib_time *two);

/**
* Adds the relative time information 'interval' to the base time 't'.
*
Expand Down
3 changes: 3 additions & 0 deletions ext/date/lib/timelib_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@
#define TIMELIB_TIME_PART_DONT_KEEP 0x00
#define TIMELIB_TIME_PART_KEEP 0x01

#define MINS_PER_HOUR 60
#define SECS_PER_ERA TIMELIB_LL_CONST(12622780800)
#define SECS_PER_DAY 86400
#define SECS_PER_HOUR 3600
#define USECS_PER_HOUR TIMELIB_LL_CONST(3600000000)

#define DAYS_PER_WEEK 7
#define DAYS_PER_YEAR 365
#define DAYS_PER_LYEAR 366
Expand Down
13 changes: 13 additions & 0 deletions ext/date/tests/bug81458.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Test for bug #81458: Regression in PHP 8.1: Incorrect difference after timezone change
--FILE--
<?php
$first = (new DateTime('2018-07-01 00:00:00.000000 America/Toronto'))->setTimezone(new DateTimeZone('UTC'));
$second = new DateTime('2018-07-02 00:00:00.000000 America/Toronto');

var_dump($first->diff($second)->days);
var_dump($first->diff($second)->d);
?>
--EXPECT--
int(1)
int(1)

0 comments on commit 904933e

Please sign in to comment.