Fixed bug #81458: Regression: Incorrect difference after timezone change

This commit is contained in:
Derick Rethans 2021-11-08 09:40:27 +00:00
parent b1b6440d84
commit 904933e918
7 changed files with 108 additions and 4 deletions

4
NEWS
View File

@ -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)

View File

@ -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) {
@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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>
@ -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'.
*/
@ -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
*/
@ -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'.
*

View File

@ -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

View File

@ -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)