1
0
mirror of https://github.com/php/php-src.git synced 2026-04-24 16:38:25 +02:00

Merge branch 'timelib-and-date-time-fixes'

This commit is contained in:
Derick Rethans
2021-04-06 20:50:58 +01:00
48 changed files with 51601 additions and 28294 deletions
+37
View File
@@ -10,6 +10,43 @@ PHP NEWS
. Fixed bug #53826 (__callStatic fired in base class through a parent call if
the method is private). (Nikita)
- Date:
. Fixed bug #52480 (Incorrect difference using DateInterval) (Derick)
. Fixed bug #62326 (date_diff() function returns false result) (Derick)
. Fixed bug #64992 (dst not handled past 2038) (Derick)
. Fixed bug #65003 (Wrong date diff) (Derick)
. Fixed bug #66545 (DateTime. diff returns negative values) (Derick)
. Fixed bug #68503 (date_diff on two dates with timezone set localised
returns wrong results) (Derick)
. Fixed bug #69806 (Incorrect date from timestamp) (Derick)
. Fixed bug #71700 (Extra day on diff between begin and end of march 2016)
(Derick)
. Fixed bug #71826 (DateTime::diff confuse on timezone 'Asia/Tokyo') (Derick)
. Fixed bug #73460 (Datetime add not realising it already applied DST
change) (Derick)
. Fixed bug #74173 (DateTimeImmutable::getTimestamp() triggers DST switch in
incorrect time) (Derick)
. Fixed bug #74274 (Handling DST transitions correctly) (Derick)
. Fixed bug #74524 (Date diff is bad calculated, in same time zone) (Derick)
. Fixed bug #75167 (DateTime::add does only care about backward DST
transition, not forward) (Derick)
. Fixed bug #76032 (DateTime->diff having issues with leap days for
timezones ahead of UTC) (Derick)
. Fixed bug #76374 (Date difference varies according day time) (Derick)
. Fixed bug #77571 (DateTime's diff DateInterval incorrect in timezones from
UTC+01:00 to UTC+12:00 (Derick)
. Fixed bug #78452 (diff makes wrong in hour for Asia/Tehran) (Derick)
. Fixed bug #79452 (DateTime::diff() generates months differently between
time zones) (Derick)
. Fixed bug #79698 (timelib mishandles future timestamps (triggered by 'zic
-b slim')) (Derick)
. Fixed bug #79716 (Invalid date time created (with day "00")) (Derick)
. Fixed bug #80610 (DateTime calculate wrong with DateInterval) (Derick)
. Fixed bug #80664 (DateTime objects behave incorrectly around DST
transition) (Derick)
. Fixed bug #80913 (DateTime(Immutable)::sub around DST yield incorrect
time) (Derick)
- FPM:
. Added openmetrics status format. (Cees-Jan Kiewiet)
. Enable process renaming on macOS. (devnexen)
+2 -1
View File
@@ -2,7 +2,7 @@
EXTENSION("date", "php_date.c", false, "/Iext/date/lib /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /DHAVE_TIMELIB_CONFIG_H=1");
PHP_DATE = "yes";
ADD_SOURCES("ext/date/lib", "astro.c timelib.c dow.c parse_date.c parse_tz.c tm2unixtime.c unixtime2tm.c parse_iso_intervals.c interval.c", "date");
ADD_SOURCES("ext/date/lib", "astro.c timelib.c dow.c parse_date.c parse_posix.c parse_tz.c tm2unixtime.c unixtime2tm.c parse_iso_intervals.c interval.c", "date");
ADD_FLAG('CFLAGS_DATE', "/wd4244");
@@ -14,6 +14,7 @@ tl_config.WriteLine("#define timelib_malloc emalloc");
tl_config.WriteLine("#define timelib_realloc erealloc");
tl_config.WriteLine("#define timelib_calloc ecalloc");
tl_config.WriteLine("#define timelib_strdup estrdup");
tl_config.WriteLine("#define timelib_strndup estrndup");
tl_config.WriteLine("#define timelib_free efree");
tl_config.Close();
+2 -1
View File
@@ -5,7 +5,7 @@ dnl Check for strtoll, atoll
AC_CHECK_FUNCS(strtoll atoll)
PHP_DATE_CFLAGS="-I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DHAVE_TIMELIB_CONFIG_H=1"
timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c
timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c lib/parse_posix.c
lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c"
PHP_NEW_EXTENSION(date, php_date.c $timelib_sources, no,, $PHP_DATE_CFLAGS)
@@ -31,5 +31,6 @@ cat > $ext_builddir/lib/timelib_config.h <<EOF
#define timelib_realloc erealloc
#define timelib_calloc ecalloc
#define timelib_strdup estrdup
#define timelib_strndup estrndup
#define timelib_free efree
EOF
+2 -2
View File
@@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright (c) 2015-2019 Derick Rethans
Copyright (c) 2017-2019 MongoDB, Inc.
Copyright (c) 2015-2021 Derick Rethans
Copyright (c) 2017-2019,2021 MongoDB, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+5
View File
@@ -7,3 +7,8 @@ information.
It is the library supporting PHP's Date/Time extension and MongoDB's time zone
support.
Build Requirements
------------------
On Debian: ``apt install libcpputest-dev``
+160 -54
View File
@@ -30,8 +30,9 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
{
timelib_rel_time *rt;
timelib_time *swp;
timelib_sll dst_corr = 0 ,dst_h_corr = 0, dst_m_corr = 0;
timelib_time one_backup, two_backup;
timelib_sll dst_corr = 0, dst_h_corr = 0, dst_m_corr = 0;
timelib_time_offset *trans = NULL;
rt = timelib_rel_time_ctor();
rt->invert = 0;
@@ -45,23 +46,10 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
rt->invert = 1;
}
/* Calculate correction for DST change over, but only if the TZ type is ID
* and it's the same */
if (one->zone_type == 3 && two->zone_type == 3
&& (strcmp(one->tz_info->name, two->tz_info->name) == 0)
&& (one->z != two->z))
{
dst_corr = two->z - one->z;
dst_h_corr = dst_corr / 3600;
dst_m_corr = (dst_corr % 3600) / 60;
}
/* Save old TZ info */
memcpy(&one_backup, one, sizeof(one_backup));
memcpy(&two_backup, two, sizeof(two_backup));
timelib_apply_localtime(one, 0);
timelib_apply_localtime(two, 0);
/* Calculate correction for UTC offset changes between first and second SSE */
dst_corr = two->z - one->z;
dst_h_corr = dst_corr / 3600;
dst_m_corr = (dst_corr % 3600) / 60;
rt->y = two->y - one->y;
rt->m = two->m - one->m;
@@ -70,29 +58,87 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
rt->i = two->i - one->i;
rt->s = two->s - one->s;
rt->us = two->us - one->us;
if (one_backup.dst == 0 && two_backup.dst == 1 && two->sse >= one->sse + 86400 - dst_corr) {
rt->h += dst_h_corr;
rt->i += dst_m_corr;
}
rt->days = fabs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400));
timelib_do_rel_normalize(rt->invert ? one : two, rt);
/* We need to do this after normalisation otherwise we can't get "24H" */
if (one_backup.dst == 1 && two_backup.dst == 0 && two->sse >= one->sse + 86400) {
if (two->sse < one->sse + 86400 - dst_corr) {
rt->d--;
rt->h = 24;
} else {
rt->h += dst_h_corr;
rt->i += dst_m_corr;
/* Fall Back: Cater for transition period, where rt->invert is 0, but there are negative numbers */
if (one->dst == 1 && two->dst == 0) {
/* First for two "Type 3" times */
if (one->zone_type == 3 && two->zone_type == 3) {
trans = timelib_get_time_zone_info(two->sse, two->tz_info);
if (trans) {
if (one->sse >= trans->transition_time + dst_corr && one->sse < trans->transition_time) {
timelib_sll flipped = SECS_PER_HOUR + (rt->i * 60) + (rt->s);
rt->h = flipped / SECS_PER_HOUR;
rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
rt->s = flipped % 60;
}
timelib_time_offset_dtor(trans);
trans = NULL;
}
} else if (rt->h == 0 && (rt->i < 0 || rt->s < 0)) {
/* Then for all the others */
timelib_sll flipped = SECS_PER_HOUR + (rt->i * 60) + (rt->s);
rt->h = flipped / SECS_PER_HOUR;
rt->i = (flipped - rt->h * SECS_PER_HOUR) / 60;
rt->s = flipped % 60;
dst_corr += SECS_PER_HOUR;
dst_h_corr++;
}
}
/* Restore old TZ info */
memcpy(one, &one_backup, sizeof(one_backup));
memcpy(two, &two_backup, sizeof(two_backup));
timelib_do_rel_normalize(rt->invert ? one : two, rt);
/* Do corrections for "Type 3" times */
if (one->zone_type == 3 && two->zone_type == 3) {
if (one->dst == 1 && two->dst == 0) { /* Fall Back */
if (two->tz_info) {
trans = timelib_get_time_zone_info(two->sse, two->tz_info);
if (
trans &&
two->sse >= trans->transition_time &&
((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans->transition_time)
) {
rt->h -= dst_h_corr;
rt->i -= dst_m_corr;
}
}
} else if (one->dst == 0 && two->dst == 1) { /* Spring Forward */
if (two->tz_info) {
trans = timelib_get_time_zone_info(two->sse, two->tz_info);
if (
trans &&
!((one->sse + SECS_PER_DAY > trans->transition_time) && (one->sse + SECS_PER_DAY <= (trans->transition_time + dst_corr))) &&
two->sse >= trans->transition_time &&
((two->sse - one->sse + dst_corr) % SECS_PER_DAY) > (two->sse - trans->transition_time)
) {
rt->h -= dst_h_corr;
rt->i -= dst_m_corr;
}
}
} else if (two->sse - one->sse >= SECS_PER_DAY) {
/* Check whether we're in the period to the next transition time */
trans = timelib_get_time_zone_info(two->sse - two->z, two->tz_info);
dst_corr = one->z - trans->offset;
if (two->sse >= trans->transition_time - dst_corr && two->sse < trans->transition_time) {
rt->d--;
rt->h = 24;
}
}
} else {
/* Then for all the others */
rt->h -= dst_h_corr + (two->dst - one->dst);
rt->i -= dst_m_corr;
timelib_do_rel_normalize(rt->invert ? one : two, rt);
}
if (trans) {
timelib_time_offset_dtor(trans);
}
return rt;
}
@@ -122,13 +168,6 @@ timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
timelib_update_ts(t, NULL);
// printf("%lld %lld %d\n", old_time->dst, t->dst, (t->sse - old_time->sse));
/* Adjust for backwards DST changeover */
if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
t->sse -= old_time->z;
t->sse += t->z;
}
timelib_update_from_sse(t);
t->have_relative = 0;
@@ -157,20 +196,87 @@ timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
timelib_update_ts(t, NULL);
/* Adjust for backwards DST changeover */
if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
t->sse -= old_time->z;
t->sse += t->z;
}
/* Adjust for forwards DST changeover */
if (old_time->dst == 0 && t->dst == 1 && !interval->y && !interval->m && !interval->d ) {
t->sse -= old_time->z;
t->sse += t->z;
}
timelib_update_from_sse(t);
t->have_relative = 0;
return t;
}
timelib_time *timelib_add_wall(timelib_time *old_time, timelib_rel_time *interval)
{
int bias = 1;
timelib_time *t = timelib_time_clone(old_time);
t->have_relative = 1;
t->sse_uptodate = 0;
if (interval->have_weekday_relative || interval->have_special_relative) {
memcpy(&t->relative, interval, sizeof(timelib_rel_time));
timelib_update_ts(t, NULL);
} else {
if (interval->invert) {
bias = -1;
}
memset(&t->relative, 0, sizeof(timelib_rel_time));
t->relative.y = interval->y * bias;
t->relative.m = interval->m * bias;
t->relative.d = interval->d * bias;
if (t->relative.y || t->relative.m || t->relative.d) {
timelib_update_ts(t, NULL);
}
t->sse += bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
t->us += interval->us * bias;
timelib_do_normalize(t);
}
timelib_update_from_sse(t);
if (t->zone_type == TIMELIB_ZONETYPE_ID) {
timelib_set_timezone(t, t->tz_info);
}
t->have_relative = 0;
return t;
}
timelib_time *timelib_sub_wall(timelib_time *old_time, timelib_rel_time *interval)
{
int bias = 1;
timelib_time *t = timelib_time_clone(old_time);
t->have_relative = 1;
t->sse_uptodate = 0;
if (interval->have_weekday_relative || interval->have_special_relative) {
memcpy(&t->relative, interval, sizeof(timelib_rel_time));
timelib_update_ts(t, NULL);
} else {
if (interval->invert) {
bias = -1;
}
memset(&t->relative, 0, sizeof(timelib_rel_time));
t->relative.y = 0 - (interval->y * bias);
t->relative.m = 0 - (interval->m * bias);
t->relative.d = 0 - (interval->d * bias);
if (t->relative.y || t->relative.m || t->relative.d) {
timelib_update_ts(t, NULL);
}
t->sse -= bias * timelib_hms_to_seconds(interval->h, interval->i, interval->s);
t->us -= interval->us * bias;
timelib_do_normalize(t);
}
timelib_update_from_sse(t);
if (t->zone_type == TIMELIB_ZONETYPE_ID) {
timelib_set_timezone(t, t->tz_info);
}
t->have_relative = 0;
return t;
}
+37 -24
View File
@@ -331,44 +331,57 @@ uchar *fill(Scanner *s, uchar *cursor){
}
#endif
static timelib_error_message *alloc_error_message(timelib_error_message **messages, int *count)
{
/* Realloc in power of two increments */
int is_pow2 = (*count & (*count - 1)) == 0;
if (is_pow2) {
size_t alloc_size = *count ? (*count * 2) : 1;
*messages = timelib_realloc(*messages, alloc_size * sizeof(timelib_error_message));
}
return *messages + (*count)++;
}
static void add_warning(Scanner *s, int error_code, char *error)
{
s->errors->warning_count++;
s->errors->warning_messages = timelib_realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
s->errors->warning_messages[s->errors->warning_count - 1].error_code = error_code;
s->errors->warning_messages[s->errors->warning_count - 1].position = s->tok ? s->tok - s->str : 0;
s->errors->warning_messages[s->errors->warning_count - 1].character = s->tok ? *s->tok : 0;
s->errors->warning_messages[s->errors->warning_count - 1].message = timelib_strdup(error);
timelib_error_message *message = alloc_error_message(&s->errors->warning_messages, &s->errors->warning_count);
message->error_code = error_code;
message->position = s->tok ? s->tok - s->str : 0;
message->character = s->tok ? *s->tok : 0;
message->message = timelib_strdup(error);
}
static void add_error(Scanner *s, int error_code, char *error)
{
s->errors->error_count++;
s->errors->error_messages = timelib_realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
s->errors->error_messages[s->errors->error_count - 1].error_code = error_code;
s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0;
s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0;
s->errors->error_messages[s->errors->error_count - 1].message = timelib_strdup(error);
timelib_error_message *message = alloc_error_message(&s->errors->error_messages, &s->errors->error_count);
message->error_code = error_code;
message->position = s->tok ? s->tok - s->str : 0;
message->character = s->tok ? *s->tok : 0;
message->message = timelib_strdup(error);
}
static void add_pbf_warning(Scanner *s, int error_code, char *error, const char *sptr, const char *cptr)
{
s->errors->warning_count++;
s->errors->warning_messages = timelib_realloc(s->errors->warning_messages, s->errors->warning_count * sizeof(timelib_error_message));
s->errors->warning_messages[s->errors->warning_count - 1].error_code = error_code;
s->errors->warning_messages[s->errors->warning_count - 1].position = cptr - sptr;
s->errors->warning_messages[s->errors->warning_count - 1].character = *cptr;
s->errors->warning_messages[s->errors->warning_count - 1].message = timelib_strdup(error);
timelib_error_message *message = alloc_error_message(&s->errors->warning_messages, &s->errors->warning_count);
message->error_code = error_code;
message->position = cptr - sptr;
message->character = *cptr;
message->message = timelib_strdup(error);
}
static void add_pbf_error(Scanner *s, int error_code, char *error, const char *sptr, const char *cptr)
{
s->errors->error_count++;
s->errors->error_messages = timelib_realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message));
s->errors->error_messages[s->errors->error_count - 1].error_code = error_code;
s->errors->error_messages[s->errors->error_count - 1].position = cptr - sptr;
s->errors->error_messages[s->errors->error_count - 1].character = *cptr;
s->errors->error_messages[s->errors->error_count - 1].message = timelib_strdup(error);
timelib_error_message *message = alloc_error_message(&s->errors->error_messages, &s->errors->error_count);
message->error_code = error_code;
message->position = cptr - sptr;
message->character = *cptr;
message->message = timelib_strdup(error);
}
static timelib_sll timelib_meridian(const char **ptr, timelib_sll h)
+542
View File
@@ -0,0 +1,542 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 MongoDB, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "timelib.h"
#include "timelib_private.h"
/* Forwards declrations */
static timelib_posix_trans_info *timelib_posix_trans_info_ctor(void);
static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts);
/* "<" [+-]? .+? ">" */
static char *read_description_numeric_abbr(char **ptr)
{
const char *begin = *ptr + 1;
// skip '<'
(*ptr)++;
while (**ptr != '\0' && **ptr != '>') {
(*ptr)++;
}
if (**ptr == '\0') {
return NULL;
}
if (**ptr == '>') {
(*ptr)++;
}
// Abbreviation may not be empty
if (*ptr - begin - 1 < 1) {
return NULL;
}
return timelib_strndup(begin, *ptr - begin - 1);
}
/* [A-Z]+ */
static char *read_description_abbr(char **ptr)
{
const char *begin = *ptr;
// Find the end
while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
(*ptr)++;
}
// Abbreviation may not be empty
if (*ptr - begin < 1) {
return NULL;
}
return timelib_strndup(begin, *ptr - begin);
}
/* "<" [+-]? .+? ">" | [A-Z]+ */
static char *read_description(char **ptr)
{
if (**ptr == '<') {
return read_description_numeric_abbr(ptr);
} else {
return read_description_abbr(ptr);
}
}
/* [+-]? */
static int read_sign(char **ptr)
{
int bias = 1;
if (**ptr == '+') {
(*ptr)++;
} else if (**ptr == '-') {
bias = -1;
(*ptr)++;
}
return bias;
}
/* [0-9]+ */
static timelib_sll read_number(char **ptr)
{
const char *begin = *ptr;
int acc = 0;
// skip leading 0's
while (**ptr == '0') {
(*ptr)++;
}
while (**ptr >= '0' && **ptr <= '9') {
acc = acc * 10;
acc += (**ptr) - '0';
(*ptr)++;
}
if (begin == *ptr) {
return TIMELIB_UNSET;
}
return acc;
}
/* [+-]? [0-9]+ ( ":" [0-9]+ ( ":" [0-9]+ )? )? */
static timelib_sll read_offset(char **ptr)
{
const char *begin;
int bias = read_sign(ptr);
int hours = 0;
int minutes = 0;
int seconds = 0;
begin = *ptr;
// read through to : or non-digit for hours
hours = read_number(ptr);
if (hours == TIMELIB_UNSET) {
return hours;
}
// check for optional minutes
if (**ptr == ':') {
(*ptr)++; // skip ':'
minutes = read_number(ptr);
if (minutes == TIMELIB_UNSET) {
return minutes;
}
}
// check for optional seconds
if (**ptr == ':') {
(*ptr)++; // skip ':'
seconds = read_number(ptr);
if (seconds == TIMELIB_UNSET) {
return seconds;
}
}
if (begin == *ptr) {
return TIMELIB_UNSET;
}
// multiplication with -1, because the offset in the identifier is the
// 'wrong' way around as for example EST5 is UTC-5 (and not +5)
return -1 * bias * (hours * 3600 + minutes * 60 + seconds);
}
// Mw.m.d
static timelib_posix_trans_info* read_trans_spec_mwd(char **ptr)
{
timelib_posix_trans_info *tmp = timelib_posix_trans_info_ctor();
tmp->type = TIMELIB_POSIX_TRANS_TYPE_MWD;
// Skip 'M'
(*ptr)++;
tmp->mwd.month = read_number(ptr);
if (tmp->mwd.month == TIMELIB_UNSET) {
goto fail;
}
// check for '.' and skip it
if (**ptr != '.') {
goto fail;
}
(*ptr)++;
tmp->mwd.week = read_number(ptr);
if (tmp->mwd.week == TIMELIB_UNSET) {
goto fail;
}
// check for '.' and skip it
if (**ptr != '.') {
goto fail;
}
(*ptr)++;
tmp->mwd.dow = read_number(ptr);
if (tmp->mwd.dow == TIMELIB_UNSET) {
goto fail;
}
return tmp;
fail:
timelib_posix_trans_info_dtor(tmp);
return NULL;
}
// (Jn | n | Mw.m.d) ( /time )?
static timelib_posix_trans_info* read_transition_spec(char **ptr)
{
timelib_posix_trans_info *tmp;
if (**ptr == 'M') {
tmp = read_trans_spec_mwd(ptr);
if (!tmp) {
return NULL;
}
} else {
tmp = timelib_posix_trans_info_ctor();
if (**ptr == 'J') {
tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29;
(*ptr)++;
}
tmp->days = read_number(ptr);
if (tmp->days == TIMELIB_UNSET) {
goto fail;
}
}
// Check for the optional hour
if (**ptr == '/') {
(*ptr)++;
tmp->hour = read_offset(ptr);
if (tmp->hour == TIMELIB_UNSET) {
goto fail;
}
// as the bias for normal offsets = -1, we need to reverse it here
tmp->hour = -tmp->hour;
}
return tmp;
fail:
timelib_posix_trans_info_dtor(tmp);
return NULL;
}
static timelib_posix_trans_info* timelib_posix_trans_info_ctor(void)
{
timelib_posix_trans_info *tmp;
tmp = timelib_calloc(1, sizeof(timelib_posix_trans_info));
tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29;
tmp->hour = 2 * 3600;
return tmp;
}
static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts)
{
timelib_free(ts);
}
void timelib_posix_str_dtor(timelib_posix_str *ps)
{
if (ps->std) {
timelib_free(ps->std);
}
if (ps->dst) {
timelib_free(ps->dst);
}
if (ps->dst_begin) {
timelib_posix_trans_info_dtor(ps->dst_begin);
}
if (ps->dst_end) {
timelib_posix_trans_info_dtor(ps->dst_end);
}
timelib_free(ps);
}
timelib_posix_str* timelib_parse_posix_str(const char *posix)
{
timelib_posix_str *tmp = timelib_calloc(1, sizeof(timelib_posix_str));
char *ptr = (char*) posix;
// read standard description (ie. EST or <-03>)
tmp->std = read_description(&ptr);
if (!tmp->std) {
timelib_posix_str_dtor(tmp);
return NULL;
}
// read required offset
tmp->std_offset = read_offset(&ptr);
if (tmp->std_offset == TIMELIB_UNSET) {
timelib_posix_str_dtor(tmp);
return NULL;
}
// if we're at the end return, otherwise we'll continue to try to parse
// the dst abbreviation and spec
if (*ptr == '\0') {
return tmp;
}
// assume dst is there, and initialise offset
tmp->dst_offset = tmp->std_offset + 3600;
tmp->dst = read_description(&ptr);
if (!tmp->dst) {
timelib_posix_str_dtor(tmp);
return NULL;
}
// if we have a "," here, then the dst offset is the standard offset +
// 3600 seconds, otherwise, try to parse the dst offset
if (*ptr != ',' && *ptr != '\0') {
tmp->dst_offset = read_offset(&ptr);
if (tmp->dst_offset == TIMELIB_UNSET) {
timelib_posix_str_dtor(tmp);
return NULL;
}
}
// if we *don't* have a "," here, we're missing the dst transitions
// ,start[/time],end[/time]
if (*ptr != ',') {
timelib_posix_str_dtor(tmp);
return NULL;
}
ptr++; // skip ','
// start[/time]
tmp->dst_begin = read_transition_spec(&ptr);
if (!tmp->dst_begin) {
timelib_posix_str_dtor(tmp);
return NULL;
}
// if we *don't* have a "," here, we're missing the dst end transition
// ,end[/time]
if (*ptr != ',') {
timelib_posix_str_dtor(tmp);
return NULL;
}
ptr++; // skip ','
// end[/time]
tmp->dst_end = read_transition_spec(&ptr);
if (!tmp->dst_end) {
timelib_posix_str_dtor(tmp);
return NULL;
}
// make sure there is no trailing data
if (*ptr != '\0') {
timelib_posix_str_dtor(tmp);
return NULL;
}
return tmp;
}
typedef struct _posix_transitions {
size_t count;
timelib_sll times[6];
timelib_sll types[6];
} posix_transitions;
static const int month_lengths[2][MONTHS_PER_YEAR] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // normal year
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year
};
/* This function is adapted from the 'localtime.c' function 'transtime' as bundled with the 'tzcode' project
* from IANA, and is public domain licensed. */
static timelib_sll calc_transition(timelib_posix_trans_info *psi, timelib_sll year)
{
int leap_year = timelib_is_leap(year);
switch (psi->type) {
case TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29: {
timelib_sll value = (psi->days - 1);
if (leap_year && psi->days >= 60) {
value++;
}
return value * SECS_PER_DAY;
}
case TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29: {
return psi->days * SECS_PER_DAY;
}
case TIMELIB_POSIX_TRANS_TYPE_MWD: {
/*
* Mm.n.d - nth "dth day" of month m.
*/
int i, d, m1, yy0, yy1, yy2, dow;
timelib_sll value = 0;
/* Use Zeller's Congruence to get day-of-week of first day of
* month. */
m1 = (psi->mwd.month + 9) % 12 + 1;
yy0 = (psi->mwd.month <= 2) ? (year - 1) : year;
yy1 = yy0 / 100;
yy2 = yy0 % 100;
dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
if (dow < 0) {
dow += DAYS_PER_WEEK;
}
/* "dow" is the day-of-week of the first day of the month. Get the
* day-of-month (zero-origin) of the first "dow" day of the month. */
d = psi->mwd.dow - dow;
if (d < 0) {
d += DAYS_PER_WEEK;
}
for (i = 1; i < psi->mwd.week; ++i) {
if (d + DAYS_PER_WEEK >= month_lengths[leap_year][psi->mwd.month - 1]) {
break;
}
d += DAYS_PER_WEEK;
}
/* "d" is the day-of-month (zero-origin) of the day we want. */
value = d * SECS_PER_DAY;
for (i = 0; i < psi->mwd.month - 1; ++i) {
value += month_lengths[leap_year][i] * SECS_PER_DAY;
}
return value;
} break;
}
return 0;
}
static timelib_sll count_leap_years(timelib_sll y)
{
/* Because we want this for Jan 1, the leap day hasn't happend yet, so
* subtract one of year before we calculate */
y--;
return (y/4) - (y/100) + (y/400);
}
timelib_sll timelib_ts_at_start_of_year(timelib_sll year)
{
timelib_sll epoch_leap_years = count_leap_years(1970);
timelib_sll current_leap_years = count_leap_years(year);
return SECS_PER_DAY * (
((year-1970) * DAYS_PER_YEAR)
+ current_leap_years
- epoch_leap_years
);
}
static void calc_transitions_for_year(timelib_tzinfo *tz, timelib_sll year, posix_transitions *transitions)
{
timelib_sll trans_begin; /* Since start of the year */
timelib_sll trans_end;
timelib_sll year_begin_ts = timelib_ts_at_start_of_year(year);
trans_begin = year_begin_ts;
trans_begin += calc_transition(tz->posix_info->dst_begin, year);
trans_begin += tz->posix_info->dst_begin->hour;
trans_begin -= tz->posix_info->std_offset;
trans_end = year_begin_ts;
trans_end += calc_transition(tz->posix_info->dst_end, year);
trans_end += tz->posix_info->dst_end->hour;
trans_end -= tz->posix_info->dst_offset;
if (trans_begin < trans_end) {
transitions->times[transitions->count ] = trans_begin;
transitions->times[transitions->count+1] = trans_end;
transitions->types[transitions->count ] = tz->posix_info->type_index_dst_type;
transitions->types[transitions->count+1] = tz->posix_info->type_index_std_type;
} else {
transitions->times[transitions->count+1] = trans_begin;
transitions->times[transitions->count ] = trans_end;
transitions->types[transitions->count+1] = tz->posix_info->type_index_dst_type;
transitions->types[transitions->count ] = tz->posix_info->type_index_std_type;
}
transitions->count += 2;
}
ttinfo* timelib_fetch_posix_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
{
timelib_sll year;
timelib_time dummy;
posix_transitions transitions = { 0 };
size_t i;
/* Find 'year' (UTC) for 'ts' */
timelib_unixtime2gmt(&dummy, ts);
year = dummy.y;
/* If there is no second (dst_end) information, the UTC offset is valid for the whole year, so no need to
* do clever logic */
if (!tz->posix_info->dst_end) {
if (transition_time) {
*transition_time = tz->trans[tz->bit64.timecnt - 1];
}
return &(tz->type[tz->posix_info->type_index_std_type]);
}
/* Calculate transition times for 'year-1', 'year', and 'year+1' */
calc_transitions_for_year(tz, year - 1, &transitions);
calc_transitions_for_year(tz, year, &transitions);
calc_transitions_for_year(tz, year + 1, &transitions);
/* Check where the 'ts' falls in the 4 transitions */
for (i = 1; i < transitions.count; i++) {
if (ts < transitions.times[i]) {
if (transition_time) {
*transition_time = transitions.times[i - 1];
}
return &(tz->type[transitions.types[i - 1]]);
}
}
return NULL;
}
+251 -51
View File
@@ -27,6 +27,7 @@
#include "timelib_private.h"
#define TIMELIB_SUPPORTS_V2DATA
#define TIMELIB_SUPPORT_SLIM_FILE
#include "timezonedb.h"
#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
@@ -128,6 +129,9 @@ static int read_tzif_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
case '3':
version = 3;
break;
case '4':
version = 4;
break;
default:
return -1;
}
@@ -174,6 +178,22 @@ static void read_32bit_header(const unsigned char **tzf, timelib_tzinfo *tz)
*tzf += sizeof(buffer);
}
static int detect_slim_file(timelib_tzinfo *tz)
{
if (
(tz->_bit32.ttisgmtcnt == 0) &&
(tz->_bit32.ttisstdcnt == 0) &&
(tz->_bit32.leapcnt == 0) &&
(tz->_bit32.timecnt == 0) &&
(tz->_bit32.typecnt == 1) &&
(tz->_bit32.charcnt == 1)
) {
return 1;
}
return 0;
}
static int read_64bit_transitions(const unsigned char **tzf, timelib_tzinfo *tz)
{
int64_t *buffer = NULL;
@@ -232,7 +252,9 @@ static int read_64bit_types(const unsigned char **tzf, timelib_tzinfo *tz)
memcpy(buffer, *tzf, sizeof(unsigned char) * 6 * tz->bit64.typecnt);
*tzf += sizeof(unsigned char) * 6 * tz->bit64.typecnt;
tz->type = (ttinfo*) timelib_malloc(tz->bit64.typecnt * sizeof(ttinfo));
// We add two extra to have space for potential new ttinfo entries due to new types defined in the
// POSIX string
tz->type = (ttinfo*) timelib_calloc(1, (tz->bit64.typecnt + 2) * sizeof(ttinfo));
if (!tz->type) {
timelib_free(buffer);
return TIMELIB_ERROR_CANNOT_ALLOCATE;
@@ -333,16 +355,92 @@ static void skip_32bit_types(const unsigned char **tzf, timelib_tzinfo *tz)
}
}
static void skip_posix_string(const unsigned char **tzf, timelib_tzinfo *tz)
static void read_posix_string(const unsigned char **tzf, timelib_tzinfo *tz)
{
int n_count = 0;
const unsigned char *begin;
do {
if (*tzf[0] == '\n') {
n_count++;
}
// POSIX string is delimited by \n
(*tzf)++;
begin = *tzf;
while (*tzf[0] != '\n') {
(*tzf)++;
} while (n_count < 2);
}
tz->posix_string = timelib_calloc(1, *tzf - begin + 1);
memcpy(tz->posix_string, begin, *tzf - begin);
// skip over closing \n
(*tzf)++;
}
static signed int find_ttinfo_index(timelib_tzinfo *tz, int32_t offset, int isdst, char *abbr)
{
uint64_t i;
for (i = 0; i < tz->bit64.typecnt; i++) {
if (
(offset == tz->type[i].offset) &&
(isdst == tz->type[i].isdst) &&
(strcmp(abbr, &tz->timezone_abbr[tz->type[i].abbr_idx]) == 0)
) {
return i;
}
}
return TIMELIB_UNSET;
}
static unsigned int add_abbr(timelib_tzinfo *tz, char *abbr)
{
size_t old_length = tz->bit64.charcnt;
size_t new_length = old_length + strlen(abbr) + 1;
tz->timezone_abbr = (char*) timelib_realloc(tz->timezone_abbr, new_length);
memcpy(tz->timezone_abbr + old_length, abbr, strlen(abbr));
tz->bit64.charcnt = new_length;
tz->timezone_abbr[new_length - 1] = '\0';
return old_length;
}
static signed int add_new_ttinfo_index(timelib_tzinfo *tz, int32_t offset, int isdst, char *abbr)
{
tz->type[tz->bit64.typecnt].offset = offset;
tz->type[tz->bit64.typecnt].isdst = isdst;
tz->type[tz->bit64.typecnt].abbr_idx = add_abbr(tz, abbr);
tz->type[tz->bit64.typecnt].isstdcnt = 0;
tz->type[tz->bit64.typecnt].isgmtcnt = 0;
++tz->bit64.typecnt;
return tz->bit64.typecnt - 1;
}
static int integrate_posix_string(timelib_tzinfo *tz)
{
tz->posix_info = timelib_parse_posix_str(tz->posix_string);
if (!tz->posix_info) {
return 0;
}
tz->posix_info->type_index_std_type = find_ttinfo_index(tz, tz->posix_info->std_offset, 0, tz->posix_info->std);
if (tz->posix_info->type_index_std_type == TIMELIB_UNSET) {
tz->posix_info->type_index_std_type = add_new_ttinfo_index(tz, tz->posix_info->std_offset, 0, tz->posix_info->std);
return 1;
}
/* If there is no DST set for this zone, return */
if (!tz->posix_info->dst) {
return 1;
}
tz->posix_info->type_index_dst_type = find_ttinfo_index(tz, tz->posix_info->dst_offset, 1, tz->posix_info->dst);
if (tz->posix_info->type_index_dst_type == TIMELIB_UNSET) {
tz->posix_info->type_index_dst_type = add_new_ttinfo_index(tz, tz->posix_info->dst_offset, 1, tz->posix_info->dst);
return 1;
}
return 1;
}
static void read_location(const unsigned char **tzf, timelib_tzinfo *tz)
@@ -373,14 +471,52 @@ static void set_default_location_and_comments(const unsigned char **tzf, timelib
tz->location.comments[1] = '\0';
}
static char *format_ut_time(timelib_sll ts, timelib_tzinfo *tz)
{
char *tmp = timelib_calloc(1, 64);
timelib_time *t = timelib_time_ctor();
timelib_unixtime2gmt(t, ts);
snprintf(
tmp, 64,
"%04lld-%02lld-%02lld %02lld:%02lld:%02lld UT",
t->y, t->m, t->d,
t->h, t->i, t->s
);
timelib_time_dtor(t);
return tmp;
}
static char *format_offset_type(timelib_tzinfo *tz, int i)
{
char *tmp = timelib_calloc(1, 64);
snprintf(
tmp, 64,
"%3d [%6ld %1d %3d '%s' (%d,%d)]",
i,
(long int) tz->type[i].offset,
tz->type[i].isdst,
tz->type[i].abbr_idx,
&tz->timezone_abbr[tz->type[i].abbr_idx],
tz->type[i].isstdcnt,
tz->type[i].isgmtcnt
);
return tmp;
}
void timelib_dump_tzinfo(timelib_tzinfo *tz)
{
uint32_t i;
uint32_t i;
char *date_str, *trans_str;
printf("Country Code: %s\n", tz->location.country_code);
printf("Geo Location: %f,%f\n", tz->location.latitude, tz->location.longitude);
printf("Comments:\n%s\n", tz->location.comments);
printf("BC: %s\n", tz->bc ? "" : "yes");
printf("BC: %s\n", tz->bc ? "no" : "yes");
printf("Slim File: %s\n", detect_slim_file(tz) ? "yes" : "no");
printf("\n64-bit:\n");
printf("UTC/Local count: " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.ttisgmtcnt);
@@ -390,31 +526,43 @@ void timelib_dump_tzinfo(timelib_tzinfo *tz)
printf("Local types count: " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.typecnt);
printf("Zone Abbr. count: " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.charcnt);
printf ("%16s (%20s) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
"", "", 0,
(long int) tz->type[0].offset,
tz->type[0].isdst,
tz->type[0].abbr_idx,
&tz->timezone_abbr[tz->type[0].abbr_idx],
tz->type[0].isstdcnt,
tz->type[0].isgmtcnt
);
trans_str = format_offset_type(tz, 0);
printf("%22s (%20s) = %s\n", "", "", trans_str);
timelib_free(trans_str);
for (i = 0; i < tz->bit64.timecnt; i++) {
printf ("%016" PRIX64 " (%20" PRId64 ") = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
tz->trans[i], tz->trans[i], tz->trans_idx[i],
(long int) tz->type[tz->trans_idx[i]].offset,
tz->type[tz->trans_idx[i]].isdst,
tz->type[tz->trans_idx[i]].abbr_idx,
&tz->timezone_abbr[tz->type[tz->trans_idx[i]].abbr_idx],
tz->type[tz->trans_idx[i]].isstdcnt,
tz->type[tz->trans_idx[i]].isgmtcnt
);
date_str = format_ut_time(tz->trans[i], tz);
trans_str = format_offset_type(tz, tz->trans_idx[i]);
printf(
"%s (%20" PRId64 ") = %s\n",
date_str,
tz->trans[i],
trans_str
);
timelib_free(date_str);
timelib_free(trans_str);
}
for (i = 0; i < tz->bit64.leapcnt; i++) {
printf ("%016" PRIX64 " (%20ld) = %d\n",
tz->leap_times[i].trans,
date_str = format_ut_time(tz->trans[i], tz);
printf (
"%s (%20ld) = %d\n",
date_str,
(long) tz->leap_times[i].trans,
tz->leap_times[i].offset);
tz->leap_times[i].offset
);
timelib_free(date_str);
}
printf("\n%43sPOSIX string: %s\n", "", tz->posix_string);
if (tz->posix_info->std) {
trans_str = format_offset_type(tz, tz->posix_info->type_index_std_type);
printf("%43sstd: %s\n", "", trans_str);
timelib_free(trans_str);
if (tz->posix_info->dst) {
trans_str = format_offset_type(tz, tz->posix_info->type_index_dst_type);
printf("%43sdst: %s\n", "", trans_str);
timelib_free(trans_str);
}
}
}
@@ -469,6 +617,9 @@ static int skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
} else if (memcmp(*tzf, "TZif3", 5) == 0) {
*tzf += 20;
return 1;
} else if (memcmp(*tzf, "TZif4", 5) == 0) {
*tzf += 20;
return 1;
} else {
return 0;
}
@@ -505,11 +656,13 @@ timelib_tzinfo *timelib_parse_tzfile(const char *timezone, const timelib_tzdb *t
int transitions_result, types_result;
unsigned int type; /* TIMELIB_TZINFO_PHP or TIMELIB_TZINFO_ZONEINFO */
*error_code = TIMELIB_ERROR_NO_ERROR;
if (seek_to_tz_position(&tzf, timezone, tzdb)) {
tmp = timelib_tzinfo_ctor(timezone);
version = read_preamble(&tzf, tmp, &type);
if (version < 2 || version > 3) {
if (version < 2 || version > 4) {
*error_code = TIMELIB_ERROR_UNSUPPORTED_VERSION;
timelib_tzinfo_dtor(tmp);
return NULL;
@@ -538,7 +691,13 @@ timelib_tzinfo *timelib_parse_tzfile(const char *timezone, const timelib_tzdb *t
timelib_tzinfo_dtor(tmp);
return NULL;
}
skip_posix_string(&tzf, tmp);
read_posix_string(&tzf, tmp);
if (!integrate_posix_string(tmp)) {
*error_code = TIMELIB_ERROR_POSIX_MISSING_TTINFO;
timelib_tzinfo_dtor(tmp);
return NULL;
}
if (type == TIMELIB_TZINFO_PHP) {
read_location(&tzf, tmp);
@@ -562,6 +721,10 @@ void timelib_tzinfo_dtor(timelib_tzinfo *tz)
TIMELIB_TIME_FREE(tz->timezone_abbr);
TIMELIB_TIME_FREE(tz->leap_times);
TIMELIB_TIME_FREE(tz->location.comments);
TIMELIB_TIME_FREE(tz->posix_string);
if (tz->posix_info) {
timelib_posix_str_dtor(tz->posix_info);
}
TIMELIB_TIME_FREE(tz);
tz = NULL;
}
@@ -600,16 +763,33 @@ timelib_tzinfo *timelib_tzinfo_clone(timelib_tzinfo *tz)
memcpy(tmp->leap_times, tz->leap_times, tz->bit64.leapcnt * sizeof(tlinfo));
}
if (tz->posix_string) {
tmp->posix_string = timelib_strdup(tz->posix_string);
}
return tmp;
}
static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
/**
* Algorithm From RFC 8536, Section 3.2
* https://tools.ietf.org/html/rfc8536#section-3.2
*/
ttinfo* timelib_fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
{
uint32_t i;
uint32_t left, right;
/* If there is no transition time, we pick the first one, if that doesn't
* exist we return NULL */
/* RFC 8536: If there are no transitions, local time for all timestamps is specified
* by the TZ string in the footer if present and nonempty; otherwise, it is specified
* by time type 0.
*
* timelib: If there is also no time type 0, return NULL.
*/
if (!tz->bit64.timecnt || !tz->trans) {
if (tz->posix_info) {
*transition_time = INT64_MIN;
return timelib_fetch_posix_timezone_offset(tz, ts, NULL);
}
if (tz->bit64.typecnt == 1) {
*transition_time = INT64_MIN;
return &(tz->type[0]);
@@ -617,25 +797,45 @@ static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib
return NULL;
}
/* If the TS is lower than the first transition time, then we scan over
* all the transition times to find the first non-DST one, or the first
* one in case there are only DST entries. Not sure which smartass came up
* with this idea in the first though :) */
/* RFC 8536: Local time for timestamps before the first transition is specified by
* the first time type (time type 0). */
if (ts < tz->trans[0]) {
*transition_time = INT64_MIN;
return &(tz->type[0]);
}
/* In all other cases we loop through the available transition times to find
* the correct entry */
for (i = 0; i < tz->bit64.timecnt; i++) {
if (ts < tz->trans[i]) {
*transition_time = tz->trans[i - 1];
return &(tz->type[tz->trans_idx[i - 1]]);
/* RFC 8536: Local time for timestamps on or after the last transition is specified
* by the TZ string in the footer (Section 3.3) if present and nonempty; otherwise,
* it is unspecified.
*
* timelib: For 'unspecified', timelib assumes the last transition
*/
if (ts >= tz->trans[tz->bit64.timecnt - 1]) {
if (tz->posix_info) {
return timelib_fetch_posix_timezone_offset(tz, ts, transition_time);
}
*transition_time = tz->trans[tz->bit64.timecnt - 1];
return &(tz->type[tz->trans_idx[tz->bit64.timecnt - 1]]);
}
/* RFC 8536: The type corresponding to a transition time specifies local time for
* timestamps starting at the given transition time and continuing up to, but not
* including, the next transition time. */
left = 0;
right = tz->bit64.timecnt - 1;
while (right - left > 1) {
uint32_t mid = (left + right) >> 1;
if (ts < tz->trans[mid]) {
right = mid;
} else {
left = mid;
}
}
*transition_time = tz->trans[tz->bit64.timecnt - 1];
return &(tz->type[tz->trans_idx[tz->bit64.timecnt - 1]]);
*transition_time = tz->trans[left];
return &(tz->type[tz->trans_idx[left]]);
}
static tlinfo* fetch_leaptime_offset(timelib_tzinfo *tz, timelib_sll ts)
@@ -659,7 +859,7 @@ int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz)
ttinfo *to;
timelib_sll dummy;
if ((to = fetch_timezone_offset(tz, ts, &dummy))) {
if ((to = timelib_fetch_timezone_offset(tz, ts, &dummy))) {
return to->isdst;
}
return -1;
@@ -674,7 +874,7 @@ timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *
timelib_time_offset *tmp = timelib_time_offset_ctor();
timelib_sll transition_time;
if ((to = fetch_timezone_offset(tz, ts, &transition_time))) {
if ((to = timelib_fetch_timezone_offset(tz, ts, &transition_time))) {
offset = to->offset;
abbr = &(tz->timezone_abbr[to->abbr_idx]);
tmp->is_dst = to->isdst;
+7 -1
View File
@@ -35,7 +35,7 @@
#define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y)
const char *timelib_error_messages[8] = {
const char *timelib_error_messages[9] = {
"No error",
"Can not allocate buffer for parsing",
"Corrupt tzfile: The transitions in the file don't always increase",
@@ -43,6 +43,7 @@ const char *timelib_error_messages[8] = {
"Corrupt tzfile: No abbreviation could be found for a transition",
"The version used in this timezone identifier is unsupported",
"No timezone with this name could be found",
"A 'slim' timezone file has been detected",
};
const char *timelib_get_error_message(int error_code)
@@ -204,6 +205,11 @@ void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
}
}
timelib_sll timelib_hms_to_seconds(timelib_sll h, timelib_sll m, timelib_sll s)
{
return (h * SECS_PER_HOUR) + (m * 60) + s;
}
static const unsigned char timelib_tolower_map[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+88 -9
View File
@@ -1,8 +1,8 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2019 Derick Rethans
* Copyright (c) 2018 MongoDB, Inc.
* Copyright (c) 2015-2021 Derick Rethans
* Copyright (c) 2018,2021 MongoDB, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -144,6 +144,38 @@ typedef struct _tlocinfo
char *comments;
} tlocinfo;
#define TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29 1
#define TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29 2
#define TIMELIB_POSIX_TRANS_TYPE_MWD 3
typedef struct _timelib_posix_trans_info
{
int type; // 1=Jn, 2=n, 3=Mm.w.d
union {
int days;
struct {
int month;
int week;
int dow;
} mwd;
};
int hour;
} timelib_posix_trans_info;
typedef struct _timelib_posix_str
{
char *std;
timelib_sll std_offset;
char *dst;
timelib_sll dst_offset;
timelib_posix_trans_info *dst_begin;
timelib_posix_trans_info *dst_end;
int type_index_std_type; // index into tz->type
int type_index_dst_type; // index into tz->type
} timelib_posix_str;
typedef struct _timelib_tzinfo
{
char *name;
@@ -173,6 +205,9 @@ typedef struct _timelib_tzinfo
tlinfo *leap_times;
unsigned char bc;
tlocinfo location;
char *posix_string;
timelib_posix_str *posix_info;
} timelib_tzinfo;
typedef struct _timelib_rel_time {
@@ -277,6 +312,7 @@ typedef struct _timelib_abbr_info {
#define TIMELIB_ERR_FORMAT_LITERAL_MISMATCH 0x224
#define TIMELIB_ERR_MIX_ISO_WITH_NATURAL 0x225
#define TIMELIB_ZONETYPE_NONE 0
#define TIMELIB_ZONETYPE_OFFSET 1
#define TIMELIB_ZONETYPE_ABBR 2
#define TIMELIB_ZONETYPE_ID 3
@@ -319,12 +355,13 @@ typedef struct _timelib_tzdb {
# define timelib_realloc realloc
# define timelib_calloc calloc
# define timelib_strdup strdup
# define timelib_strndup strndup
# define timelib_free free
#endif
#define TIMELIB_VERSION 202002
#define TIMELIB_EXTENDED_VERSION 20202001
#define TIMELIB_ASCII_VERSION "2020.02"
#define TIMELIB_VERSION 202103
#define TIMELIB_EXTENDED_VERSION 20210301
#define TIMELIB_ASCII_VERSION "2021.03"
#define TIMELIB_NONE 0x00
#define TIMELIB_OVERRIDE_TIME 0x01
@@ -341,6 +378,8 @@ typedef struct _timelib_tzdb {
#define TIMELIB_ERROR_CORRUPT_NO_ABBREVIATION 0x04
#define TIMELIB_ERROR_UNSUPPORTED_VERSION 0x05
#define TIMELIB_ERROR_NO_SUCH_TIMEZONE 0x06
#define TIMELIB_ERROR_SLIM_FILE 0x07
#define TIMELIB_ERROR_POSIX_MISSING_TTINFO 0x08
#ifdef __cplusplus
extern "C" {
@@ -588,6 +627,12 @@ void timelib_strtointerval(const char *s, size_t len,
*/
void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi);
/**
* Returns the number of days from the y/m/d fields of 'time' since the Unix
* Epoch.
*/
timelib_sll timelib_epoch_days_from_time(timelib_time *time);
/**
* Takes the information from the y/m/d/h/i/s fields and makes sure their
* values are in the right range.
@@ -610,6 +655,14 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt);
/* From unixtime2tm.c */
/**
* Takes the unix timestamp in seconds from 'ts', and calculates y, m, and d,
* in the proleptic Gregorian calendar.
*
* It uses the algorithm from howardhinnant.github.io/date_algorithms.html
*/
void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d);
/**
* Takes the unix timestamp in seconds from 'ts' and populates the y/m/d/h/i/s
* fields of 'tm' without taking time zones into account
@@ -669,14 +722,26 @@ int timelib_timezone_id_is_valid(const char *timezone, const timelib_tzdb *tzdb)
/**
* Converts the binary stored time zone information from 'tzdb' for the time
* zone 'timeozne' into a structure the library can use for calculations.
* zone 'timezone' into a structure the library can use for calculations.
*
* The function can be used on both timelib_builtin_db as well as a time zone
* db as opened by timelib_zoneinfo.
*
* 'error_code' must not be a null pointer, and will always be written to. If
* the value is TIMELIB_ERROR_NO_ERROR then the file was parsed without
* problems.
*
* The function will return null upon failure, and also set an error code
* through 'error_code'. 'error_code' must not be a null pointer. The error
* code is one of the TIMELIB_ERROR_* constants as listed above. These error
* constants can be converted into a string by timelib_get_error_message.
* through 'error_code'.
*
* The error code is one of the TIMELIB_ERROR_* constants as listed above.
* These error constants can be converted into a string by
* timelib_get_error_message.
*
* If the function returns not-null, the 'error_code' might have a non-null
* value that can be used to detect incompatibilities. The only one that is
* currently detected is whether the file is a 'slim' file, in which case
* 'error_code' will be set to TIMELIB_ERROR_SLIM_FILE.
*
* This function allocates memory for the new time zone structure, which must
* be freed after use. Although it is recommended that a cache of each used
@@ -883,6 +948,12 @@ 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 values into seconds
*/
timelib_sll timelib_hms_to_seconds(timelib_sll h, timelib_sll m, timelib_sll s);
/* from astro.c */
/**
@@ -950,6 +1021,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);
* complex statements such as "next workday".
*/
timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);
timelib_time *timelib_add_wall(timelib_time *t, timelib_rel_time *interval);
/**
* Subtracts the relative time information 'interval' to the base time 't'.
@@ -959,6 +1031,13 @@ timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);
* workday".
*/
timelib_time *timelib_sub(timelib_time *t, timelib_rel_time *interval);
timelib_time *timelib_sub_wall(timelib_time *t, timelib_rel_time *interval);
/* from parse_posix.c */
void timelib_posix_str_dtor(timelib_posix_str *ps);
timelib_posix_str* timelib_parse_posix_str(const char *posix);
#ifdef __cplusplus
} /* extern "C" */
+19 -2
View File
@@ -83,11 +83,15 @@
#define SECS_PER_ERA TIMELIB_LL_CONST(12622780800)
#define SECS_PER_DAY 86400
#define SECS_PER_HOUR 3600
#define DAYS_PER_WEEK 7
#define DAYS_PER_YEAR 365
#define DAYS_PER_LYEAR 366
#define MONTHS_PER_YEAR 12
/* 400*365 days + 97 leap days */
#define DAYS_PER_LYEAR_PERIOD 146097
#define YEARS_PER_LYEAR_PERIOD 400
#define DAYS_PER_ERA 146097
#define YEARS_PER_ERA 400
#define HINNANT_EPOCH_SHIFT 719468 /* 0000-03-01 instead of 1970-01-01 */
#define TIMELIB_TZINFO_PHP 0x01
#define TIMELIB_TZINFO_ZONEINFO 0x02
@@ -127,14 +131,27 @@ struct _tlinfo
#define LONG_MIN (- LONG_MAX - 1)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* From unixtime2tm.c */
int timelib_apply_localtime(timelib_time *t, unsigned int localtime);
/* From parse_posix.c */
timelib_sll timelib_ts_at_start_of_year(timelib_sll year);
ttinfo* timelib_fetch_posix_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time);
/* From parse_tz.c */
void timelib_time_tz_abbr_update(timelib_time* tm, const char* tz_abbr);
ttinfo* timelib_fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time);
/* From timelib.c */
int timelib_strcasecmp(const char *s1, const char *s2);
int timelib_strncasecmp(const char *s1, const char *s2, size_t n);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
+49664 -27919
View File
File diff suppressed because it is too large Load Diff
+59 -127
View File
@@ -25,10 +25,6 @@
#include "timelib.h"
#include "timelib_private.h"
/* jan feb mrt apr may jun jul aug sep oct nov dec */
static int month_tab_leap[12] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
static int month_tab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
/* dec jan feb mrt apr may jun jul aug sep oct nov dec */
static int days_in_month_leap[13] = { 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
@@ -113,9 +109,9 @@ static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d)
timelib_sll days_last_month;
/* can jump an entire leap year period quickly */
if (*d >= DAYS_PER_LYEAR_PERIOD || *d <= -DAYS_PER_LYEAR_PERIOD) {
*y += YEARS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
*d -= DAYS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
if (*d >= DAYS_PER_ERA || *d <= -DAYS_PER_ERA) {
*y += YEARS_PER_ERA * (*d / DAYS_PER_ERA);
*d -= DAYS_PER_ERA * (*d / DAYS_PER_ERA);
}
do_range_limit(1, 13, 12, m, y);
@@ -192,8 +188,6 @@ void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
do_range_limit(0, 12, 12, &rt->m, &rt->y);
}
#define EPOCH_DAY 719468
static void magic_date_calc(timelib_time *time)
{
timelib_sll y, ddd, mi, mm, dd, g;
@@ -203,7 +197,7 @@ static void magic_date_calc(timelib_time *time)
return;
}
g = time->d + EPOCH_DAY - 1;
g = time->d + HINNANT_EPOCH_SHIFT - 1;
y = (10000 * g + 14780) / 3652425;
ddd = g - ((365*y) + (y/4) - (y/100) + (y/400));
@@ -360,78 +354,14 @@ static void do_adjust_special_early(timelib_time* time)
timelib_do_normalize(time);
}
static timelib_sll do_years(timelib_sll year)
{
timelib_sll i;
timelib_sll res = 0;
timelib_sll eras;
eras = (year - 1970) / 40000;
/* Hack to make sure we don't overflow. Right now, we can't easily thrown a
* warning in this case, so we'll just return some rubbish. Sucks, but at
* least it doesn't show UBSAN warnings anymore */
if (eras < -1000000 || eras > 1000000) {
return eras > 0 ? LLONG_MAX/10 : LLONG_MIN/10;
}
if (eras != 0) {
year = year - (eras * 40000);
res += (SECS_PER_ERA * eras * 100);
}
if (year >= 1970) {
for (i = year - 1; i >= 1970; i--) {
if (timelib_is_leap(i)) {
res += (DAYS_PER_LYEAR * SECS_PER_DAY);
} else {
res += (DAYS_PER_YEAR * SECS_PER_DAY);
}
}
} else {
for (i = 1969; i >= year; i--) {
if (timelib_is_leap(i)) {
res -= (DAYS_PER_LYEAR * SECS_PER_DAY);
} else {
res -= (DAYS_PER_YEAR * SECS_PER_DAY);
}
}
}
return res;
}
static timelib_sll do_months(timelib_ull month, timelib_sll year)
{
if (timelib_is_leap(year)) {
return ((month_tab_leap[month - 1] + 1) * SECS_PER_DAY);
} else {
return ((month_tab[month - 1]) * SECS_PER_DAY);
}
}
static timelib_sll do_days(timelib_ull day)
{
return ((day - 1) * SECS_PER_DAY);
}
static timelib_sll do_time(timelib_ull hour, timelib_ull minute, timelib_ull second)
{
timelib_sll res = 0;
res += hour * 3600;
res += minute * 60;
res += second;
return res;
}
static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
static void do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
{
switch (tz->zone_type) {
case TIMELIB_ZONETYPE_OFFSET:
tz->is_localtime = 1;
return -tz->z;
break;
tz->sse += -tz->z;
return;
case TIMELIB_ZONETYPE_ABBR: {
timelib_sll tmp;
@@ -439,73 +369,75 @@ static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
tz->is_localtime = 1;
tmp = -tz->z;
tmp -= tz->dst * 3600;
return tmp;
}
break;
tz->sse += (-tz->z - tz->dst * SECS_PER_HOUR);
return;
}
case TIMELIB_ZONETYPE_ID:
tzi = tz->tz_info;
/* Break intentionally missing */
default:
default: {
/* No timezone in struct, fallback to reference if possible */
if (tzi) {
timelib_time_offset *before, *after;
timelib_sll tmp;
int in_transition;
timelib_time_offset *current, *after;
timelib_sll adjustment;
int in_transition;
tz->is_localtime = 1;
before = timelib_get_time_zone_info(tz->sse, tzi);
after = timelib_get_time_zone_info(tz->sse - before->offset, tzi);
timelib_set_timezone(tz, tzi);
in_transition = (
((tz->sse - after->offset) >= (after->transition_time + (before->offset - after->offset))) &&
((tz->sse - after->offset) < after->transition_time)
);
if ((before->offset != after->offset) && !in_transition) {
tmp = -after->offset;
} else {
tmp = -tz->z;
}
timelib_time_offset_dtor(before);
timelib_time_offset_dtor(after);
{
timelib_time_offset *gmt_offset;
gmt_offset = timelib_get_time_zone_info(tz->sse + tmp, tzi);
tz->z = gmt_offset->offset;
tz->dst = gmt_offset->is_dst;
if (tz->tz_abbr) {
timelib_free(tz->tz_abbr);
}
tz->tz_abbr = timelib_strdup(gmt_offset->abbr);
timelib_time_offset_dtor(gmt_offset);
}
return tmp;
if (!tzi) {
return;
}
current = timelib_get_time_zone_info(tz->sse, tzi);
after = timelib_get_time_zone_info(tz->sse - current->offset, tzi);
tz->is_localtime = 1;
in_transition = (
((tz->sse - after->offset) >= (after->transition_time + (current->offset - after->offset))) &&
((tz->sse - after->offset) < after->transition_time)
);
if ((current->offset != after->offset) && !in_transition) {
adjustment = -after->offset;
} else {
adjustment = -current->offset;
}
timelib_time_offset_dtor(current);
timelib_time_offset_dtor(after);
tz->sse += adjustment;
timelib_set_timezone(tz, tzi);
return;
}
}
return 0;
return;
}
timelib_sll timelib_epoch_days_from_time(timelib_time *time)
{
timelib_sll y = time->y; // Make copy, as we don't want to change the original one
timelib_sll era, year_of_era, day_of_year, day_of_era;
y -= time->m <= 2;
era = (y >= 0 ? y : y - 399) / YEARS_PER_ERA;
year_of_era = y - era * YEARS_PER_ERA; // [0, 399]
day_of_year = (153 * (time->m + (time->m > 2 ? -3 : 9)) + 2)/5 + time->d - 1; // [0, 365]
day_of_era = year_of_era * DAYS_PER_YEAR + year_of_era / 4 - year_of_era / 100 + day_of_year; // [0, 146096]
return era * DAYS_PER_ERA + day_of_era - HINNANT_EPOCH_SHIFT;
}
void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
{
timelib_sll res = 0;
do_adjust_special_early(time);
do_adjust_relative(time);
do_adjust_special(time);
res += do_years(time->y);
res += do_months(time->m, time->y);
res += do_days(time->d);
res += do_time(time->h, time->i, time->s);
time->sse = res;
res += do_adjust_timezone(time, tzi);
time->sse = res;
time->sse =
(timelib_epoch_days_from_time(time) * SECS_PER_DAY) +
timelib_hms_to_seconds(time->h, time->i, time->s);
// This modifies time->sse, if needed
do_adjust_timezone(time, tzi);
time->sse_uptodate = 1;
time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = time->relative.first_last_day_of = 0;
+31 -67
View File
@@ -1,7 +1,8 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2019 Derick Rethans
* Copyright (c) 2015-2021 Derick Rethans
* Copyright (c) 2021 MongoDB
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,76 +26,42 @@
#include "timelib.h"
#include "timelib_private.h"
static int month_tab_leap[12] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
static int month_tab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d)
{
timelib_sll days, era, t;
timelib_ull day_of_era, year_of_era, day_of_year, month_portion;
/* Calculate days since algorithm's epoch (0000-03-01) */
days = ts / SECS_PER_DAY + HINNANT_EPOCH_SHIFT;
/* Adjustment for a negative time portion */
t = ts % SECS_PER_DAY;
days += (t < 0) ? -1 : 0;
/* Calculate year, month, and day. Algorithm from:
* http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
era = (days >= 0 ? days : days - DAYS_PER_ERA + 1) / DAYS_PER_ERA;
day_of_era = days - era * DAYS_PER_ERA;
year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / DAYS_PER_YEAR;
*y = year_of_era + era * YEARS_PER_ERA;
day_of_year = day_of_era - (DAYS_PER_YEAR * year_of_era + year_of_era / 4 - year_of_era / 100);
month_portion = (5 * day_of_year + 2) / 153;
*d = day_of_year - (153 * month_portion + 2) / 5 + 1;
*m = month_portion + (month_portion < 10 ? 3 : -9);
*y += (*m <= 2);
TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, *y, *m, *d););
}
/* Converts a Unix timestamp value into broken down time, in GMT */
void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts)
{
timelib_sll days, remainder, tmp_days;
timelib_sll cur_year = 1970;
timelib_sll i;
timelib_sll remainder;
timelib_sll hours, minutes, seconds;
int *months;
days = ts / SECS_PER_DAY;
remainder = ts - (days * SECS_PER_DAY);
if (ts < 0 && remainder == 0) {
days++;
remainder -= SECS_PER_DAY;
}
TIMELIB_DEBUG(printf("days=%lld, rem=%lld\n", days, remainder););
if (ts >= 0) {
tmp_days = days + 1;
} else {
tmp_days = days;
}
if (tmp_days > DAYS_PER_LYEAR_PERIOD || tmp_days <= -DAYS_PER_LYEAR_PERIOD) {
cur_year += YEARS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
tmp_days -= DAYS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
}
TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
if (ts >= 0) {
while (tmp_days >= DAYS_PER_LYEAR) {
cur_year++;
if (timelib_is_leap(cur_year)) {
tmp_days -= DAYS_PER_LYEAR;
} else {
tmp_days -= DAYS_PER_YEAR;
}
TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
}
} else {
while (tmp_days <= 0) {
cur_year--;
if (timelib_is_leap(cur_year)) {
tmp_days += DAYS_PER_LYEAR;
} else {
tmp_days += DAYS_PER_YEAR;
}
TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
}
remainder += SECS_PER_DAY;
}
TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
months = timelib_is_leap(cur_year) ? month_tab_leap : month_tab;
if (timelib_is_leap(cur_year) && cur_year < 1970) {
tmp_days--;
}
i = 11;
while (i > 0) {
TIMELIB_DEBUG(printf("month=%lld (%d)\n", i, months[i]););
if (tmp_days > months[i]) {
break;
}
i--;
}
TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, cur_year, i + 1, tmp_days - months[i]););
timelib_unixtime2date(ts, &tm->y, &tm->m, &tm->d);
remainder = ts % SECS_PER_DAY;
remainder += (remainder < 0) * SECS_PER_DAY;
/* That was the date, now we do the time */
hours = remainder / 3600;
@@ -102,9 +69,6 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts)
seconds = remainder % 60;
TIMELIB_DEBUG(printf(" hour=%lld, minute=%lld, second=%lld\n", hours, minutes, seconds););
tm->y = cur_year;
tm->m = i + 1;
tm->d = tmp_days - months[i];
tm->h = hours;
tm->i = minutes;
tm->s = seconds;
+37 -19
View File
@@ -2904,7 +2904,11 @@ static void php_date_add(zval *object, zval *interval, zval *return_value) /* {{
intobj = Z_PHPINTERVAL_P(interval);
DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
new_time = timelib_add(dateobj->time, intobj->diff);
if (intobj->civil_or_wall == PHP_DATE_WALL) {
new_time = timelib_add_wall(dateobj->time, intobj->diff);
} else {
new_time = timelib_add(dateobj->time, intobj->diff);
}
timelib_time_dtor(dateobj->time);
dateobj->time = new_time;
} /* }}} */
@@ -2957,7 +2961,11 @@ static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{
return;
}
new_time = timelib_sub(dateobj->time, intobj->diff);
if (intobj->civil_or_wall == PHP_DATE_WALL) {
new_time = timelib_sub_wall(dateobj->time, intobj->diff);
} else {
new_time = timelib_sub(dateobj->time, intobj->diff);
}
timelib_time_dtor(dateobj->time);
dateobj->time = new_time;
} /* }}} */
@@ -2996,21 +3004,22 @@ PHP_METHOD(DateTimeImmutable, sub)
static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
{
tzobj->initialized = 1;
tzobj->type = t->zone_type;
switch (t->zone_type) {
case TIMELIB_ZONETYPE_ID:
tzobj->tzi.tz = t->tz_info;
break;
case TIMELIB_ZONETYPE_OFFSET:
tzobj->tzi.utc_offset = t->z;
break;
case TIMELIB_ZONETYPE_ABBR:
tzobj->tzi.z.utc_offset = t->z;
tzobj->tzi.z.dst = t->dst;
tzobj->tzi.z.abbr = timelib_strdup(t->tz_abbr);
break;
}
tzobj->initialized = 1;
tzobj->type = t->zone_type;
switch (t->zone_type) {
case TIMELIB_ZONETYPE_ID:
tzobj->tzi.tz = t->tz_info;
break;
case TIMELIB_ZONETYPE_OFFSET:
tzobj->tzi.utc_offset = t->z;
break;
case TIMELIB_ZONETYPE_ABBR:
tzobj->tzi.z.utc_offset = t->z;
tzobj->tzi.z.dst = t->dst;
tzobj->tzi.z.abbr = timelib_strdup(t->tz_abbr);
break;
}
}
@@ -3359,8 +3368,6 @@ PHP_FUNCTION(date_diff)
dateobj2 = Z_PHPDATE_P(object2);
DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
timelib_update_ts(dateobj1->time, NULL);
timelib_update_ts(dateobj2->time, NULL);
php_date_instantiate(date_ce_interval, return_value);
interval = Z_PHPINTERVAL_P(return_value);
@@ -3369,6 +3376,7 @@ PHP_FUNCTION(date_diff)
interval->diff->invert = 0;
}
interval->initialized = 1;
interval->civil_or_wall = PHP_DATE_CIVIL;
}
/* }}} */
@@ -3848,6 +3856,7 @@ PHP_METHOD(DateInterval, __construct)
php_interval_obj *diobj = Z_PHPINTERVAL_P(ZEND_THIS);
diobj->diff = reltime;
diobj->initialized = 1;
diobj->civil_or_wall = PHP_DATE_WALL;
}
zend_restore_error_handling(&error_handling);
}
@@ -3930,6 +3939,14 @@ static int php_date_interval_initialize_from_hash(zval **return_value, php_inter
PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
{
zval *z_arg = zend_hash_str_find(myht, "civil_or_wall", sizeof("civil_or_wall") - 1);
(*intobj)->civil_or_wall = PHP_DATE_CIVIL;
if (z_arg) {
zend_long val = zval_get_long(z_arg);
(*intobj)->civil_or_wall = val;
}
}
(*intobj)->initialized = 1;
return 0;
@@ -3996,6 +4013,7 @@ PHP_FUNCTION(date_interval_create_from_date_string)
diobj = Z_PHPINTERVAL_P(return_value);
diobj->diff = timelib_rel_time_clone(&time->relative);
diobj->initialized = 1;
diobj->civil_or_wall = PHP_DATE_CIVIL;
cleanup:
timelib_time_dtor(time);
+4
View File
@@ -65,8 +65,12 @@ static inline php_timezone_obj *php_timezone_obj_from_obj(zend_object *obj) {
#define Z_PHPTIMEZONE_P(zv) php_timezone_obj_from_obj(Z_OBJ_P((zv)))
#define PHP_DATE_CIVIL 1
#define PHP_DATE_WALL 2
struct _php_interval_obj {
timelib_rel_time *diff;
int civil_or_wall;
int initialized;
zend_object std;
};
+2 -2
View File
@@ -17,8 +17,8 @@ $date->modify( "£61538461538 day" );
echo $date->format( 'r' ), "\n";
?>
--EXPECTF--
Thu, 14 Aug 168488594 16:44:23 +0000
Thu, 14 Aug 168488594 16:44:23 +0000
Thu, 14 Aug 168488594 16:44:23 +0100
Thu, 14 Aug 168488594 16:44:23 +0100
Warning: DateTime::modify(): Failed to parse time string (£61538461538 day) at position 0 (%s): Unexpected character in %sbug45866.php on line 11
Wed, 29 Jul 2009 16:44:23 +0100
-2
View File
@@ -1,7 +1,5 @@
--TEST--
Bug #52480 (Incorrect difference using DateInterval)
--XFAIL--
See https://bugs.php.net/bug.php?id=52480
--FILE--
<?php
+14
View File
@@ -0,0 +1,14 @@
--TEST--
Bug #62326 (date_diff() function returns false result)
--FILE--
<?php
date_default_timezone_set("Europe/Berlin");
$start_timestamp = date_create('2012-06-01');
$end_timestamp = date_create('2012-12-01');
$difference = date_diff($start_timestamp, $end_timestamp);
echo $difference->format('%mM / %dD %hH %iM'), "\n";
?>
--EXPECT--
6M / 0D 0H 0M
+9
View File
@@ -0,0 +1,9 @@
--TEST--
Bug #64653 (Subtraction of DateInterval yields wrong result)
--FILE--
<?php
$date = new \DateTime('2370-01-31');
echo $date->sub(new \DateInterval('P1M'))->format('Y-m-d');
?>
--EXPECT--
2369-12-31
+29
View File
@@ -0,0 +1,29 @@
--TEST--
Bug #64992 (dst not handled past 2038)
--FILE--
<?php
$firstyear = 2035;
$lastyear = 2040;
$tz = 'America/Los_Angeles';
date_default_timezone_set('America/Los_Angeles');
$dt = new DateTime((string) ($firstyear - 1) . "-07-02");
$di = new DateInterval('P6M');
for ($i = 0; $i < ($lastyear - $firstyear) * 2; $i++) {
$dt->add($di);
$gmto = $dt->getOffset();
echo "Time Zone offset for $tz for " , $dt->format('Y-m-d') , " is $gmto\n";
}
?>
--EXPECT--
Time Zone offset for America/Los_Angeles for 2035-01-02 is -28800
Time Zone offset for America/Los_Angeles for 2035-07-02 is -25200
Time Zone offset for America/Los_Angeles for 2036-01-02 is -28800
Time Zone offset for America/Los_Angeles for 2036-07-02 is -25200
Time Zone offset for America/Los_Angeles for 2037-01-02 is -28800
Time Zone offset for America/Los_Angeles for 2037-07-02 is -25200
Time Zone offset for America/Los_Angeles for 2038-01-02 is -28800
Time Zone offset for America/Los_Angeles for 2038-07-02 is -25200
Time Zone offset for America/Los_Angeles for 2039-01-02 is -28800
Time Zone offset for America/Los_Angeles for 2039-07-02 is -25200
+21
View File
@@ -0,0 +1,21 @@
--TEST--
Bug #65003 (Wrong date diff)
--FILE--
<?php
date_default_timezone_set("Europe/Moscow");
$datetime1 = new DateTime('13-03-01');
$datetime2 = new DateTime('13-04-01');
$datetime3 = new DateTime('13-03-02');
$datetime4 = new DateTime('13-04-02');
$interval = $datetime2->diff($datetime1);
echo $interval->format('%m month, %d days'), "\n"; //1 month, 3 days
$interval = $datetime4->diff($datetime3);
echo $interval->format('%m month, %d days'), "\n"; //1 month, 0 days
?>
--EXPECT--
1 month, 0 days
1 month, 0 days
+24
View File
@@ -0,0 +1,24 @@
--TEST--
Bug #66257 (strtotime doesn't use current timezone on '4 thursdays')
--XFAIL--
'4 thursdays' changes time of day, but others don't
--INI--
date.timezone=America/Chicago
--FILE--
<?php
echo date_default_timezone_get() . "\n";
date_default_timezone_set('UTC');
$timestamp = mktime(0,0,0,11,1,2014);
$four_thursdays = strtotime('4 thursdays', $timestamp) . "\n";
$first_thursday = strtotime('first thursday', $timestamp) . "\n";
$plus_three_weeks = strtotime('+ 3 weeks', $timestamp) . "\n";
$first_thursday_plus_three_weeks = strtotime('first thursday + 3 weeks', $timestamp) . "\n";
echo 'base: ', date(DateTime::ISO8601 . ' e T', $timestamp), "\n";
echo '4 thursdays: ', date(DateTime::ISO8601 . ' e T', $four_thursdays), "\n";
echo 'first thursday: ', date(DateTime::ISO8601 . ' e T', $first_thursday), "\n";
echo '+ 3 weeks: ', date(DateTime::ISO8601 . ' e T', $plus_three_weeks), "\n";
echo 'first thursday + 3 weeks: ', date(DateTime::ISO8601 . ' e T', $first_thursday_plus_three_weeks), "\n";
?>
--EXPECT--
+37
View File
@@ -0,0 +1,37 @@
--TEST--
Bug #66545 (DateTime)
--INI--
date.timezone=Europe/Paris
--FILE--
<?php
$debut = mktime(0, 0, 0, 10, 27, 2013);
$fin = mktime(23, 59, 59, 11, 10, 2013);
$d1 = new DateTime('now',new DateTimeZone('Europe/Paris'));
$d2 = new DateTime('now',new DateTimeZone('Europe/Paris'));
$d1->setTimestamp($debut);
$d2->setTimestamp($fin);
$diff = $d1->diff($d2);
print_r($diff);
?>
--EXPECT--
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 14
[h] => 23
[i] => 59
[s] => 59
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 14
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
+12
View File
@@ -0,0 +1,12 @@
--TEST--
Bug #68503 (date_diff on two dates with timezone set localised returns wrong results)
--FILE--
<?php
date_default_timezone_set('Europe/London');
echo date_diff(new DateTime("2015-02-01"), new DateTime("2015-05-01"))->format( '%yY %mM %dD' ), "\n";
date_default_timezone_set('UTC');
echo date_diff(new DateTime("2015-02-01"), new DateTime("2015-05-01"))->format( '%yY %mM %dD' ), "\n";
?>
--EXPECT--
0Y 3M 0D
0Y 3M 0D
+10
View File
@@ -0,0 +1,10 @@
--TEST--
Bug #69806
--FILE--
<?php
ini_set('date.timezone', 'America/New_York');
echo date('Y-m-d H:i:s', 2377224000)."\n";
?>
--EXPECT--
2045-05-01 00:00:00
+48
View File
@@ -0,0 +1,48 @@
--TEST--
Bug #71700 (Extra day on diff between begin and end of march 2016)
--INI--
date.timezone=Europe/Paris
--FILE--
<?php
$date1 = new \DateTime('2016-03-01');
$date2 = new \DateTime('2016-03-31');
$diff = date_diff($date1, $date2, true);
var_dump($diff);
?>
--EXPECT--
object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(30)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(30)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
+32
View File
@@ -0,0 +1,32 @@
--TEST--
Bug #71826 (DateTime::diff confuse on timezone 'Asia/Tokyo')
--FILE--
<?php
// Asia/Tokyo ...something wrong
date_default_timezone_set('Asia/Tokyo');
$a = (new DateTime('2015-2-1'))->diff(new DateTime('2015-3-1'));
echo "a(Asia/Tokyo): 2015-2-1 <--> 2015-3-1\n";
var_dump($a->m, $a->d);
$b = (new DateTime('2015-3-1'))->diff(new DateTime('2015-3-29'));
echo "\nb(Asia/Tokyo): 2015-3-1 <--> 2015-3-29\n";
var_dump($b->m, $b->d);
$c = (new DateTime('2015-4-1'))->diff(new DateTime('2015-4-29'));
echo "\nc(Asia/Tokyo): 2015-4-1 <--> 2015-4-29\n";
var_dump($c->m, $c->d);
?>
--EXPECT--
a(Asia/Tokyo): 2015-2-1 <--> 2015-3-1
int(1)
int(0)
b(Asia/Tokyo): 2015-3-1 <--> 2015-3-29
int(0)
int(28)
c(Asia/Tokyo): 2015-4-1 <--> 2015-4-29
int(0)
int(28)
+33
View File
@@ -0,0 +1,33 @@
--TEST--
Bug #73460 (Datetime add not realising it already applied DST change)
--FILE--
<?php
$date = new DateTime('2016-11-05 23:45:00', new DateTimeZone('America/New_York'));
foreach (range(1, 20) as $i) {
echo $date->format('Y/m/d H:i e T'), "\n";
$date->add(new DateInterval('PT15M'));
}
?>
--EXPECT--
2016/11/05 23:45 America/New_York EDT
2016/11/06 00:00 America/New_York EDT
2016/11/06 00:15 America/New_York EDT
2016/11/06 00:30 America/New_York EDT
2016/11/06 00:45 America/New_York EDT
2016/11/06 01:00 America/New_York EDT
2016/11/06 01:15 America/New_York EDT
2016/11/06 01:30 America/New_York EDT
2016/11/06 01:45 America/New_York EDT
2016/11/06 01:00 America/New_York EST
2016/11/06 01:15 America/New_York EST
2016/11/06 01:30 America/New_York EST
2016/11/06 01:45 America/New_York EST
2016/11/06 02:00 America/New_York EST
2016/11/06 02:15 America/New_York EST
2016/11/06 02:30 America/New_York EST
2016/11/06 02:45 America/New_York EST
2016/11/06 03:00 America/New_York EST
2016/11/06 03:15 America/New_York EST
2016/11/06 03:30 America/New_York EST
+41
View File
@@ -0,0 +1,41 @@
--TEST--
Bug #73460 (Datetime add not realising it already applied DST change)
--FILE--
<?php
date_default_timezone_set('America/New_York');
//DST starts Apr. 2nd 02:00 and moves to 03:00
$start = new \DateTime('2006-04-02T01:00:00');
$end = new \DateTime('2006-04-02T04:00:00');
while($end > $start) {
$now = clone $end;
$end->sub(new \DateInterval('PT1H'));
echo $end->format('Y-m-d H:i T') . PHP_EOL;
}
echo '-----' . \PHP_EOL;
//DST ends Oct. 29th 02:00 and moves to 01:00
$start = new \DateTime('2006-10-29T00:30:00');
$end = new \DateTime('2006-10-29T03:00:00');
$i = 0;
while($end > $start) {
$now = clone $start;
$start->add(new \DateInterval('PT30M'));
echo $start->format('Y-m-d H:i T') . PHP_EOL;
}
?>
--EXPECT--
2006-04-02 03:00 EDT
2006-04-02 01:00 EST
-----
2006-10-29 01:00 EDT
2006-10-29 01:30 EDT
2006-10-29 01:00 EST
2006-10-29 01:30 EST
2006-10-29 02:00 EST
2006-10-29 02:30 EST
2006-10-29 03:00 EST
+14
View File
@@ -0,0 +1,14 @@
--TEST--
Bug #74173 (DateTimeImmutable::getTimestamp() triggers DST switch in incorrect time)
--FILE--
<?php
$utc = new \DateTimeImmutable('2016-10-30T00:00:00+00:0');
$prg = $utc->setTimeZone(new \DateTimeZone('Europe/Prague'));
echo $prg->format('c') . "\n";
$prg->getTimestamp();
echo $prg->format('c') . "\n";
?>
--EXPECT--
2016-10-30T02:00:00+02:00
2016-10-30T02:00:00+02:00
+30
View File
@@ -0,0 +1,30 @@
--TEST--
Bug #74274 (Handling DST transitions correctly)
--FILE--
<?php
$tz = new DateTimeZone("Europe/Paris");
$startDate = new \DateTime('2018-10-28 00:00:00', $tz);
$endDateBuggy = new \DateTime('2018-10-29 23:00:00', $tz);
print_r($startDate->diff($endDateBuggy));
?>
--EXPECT--
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 1
[h] => 23
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 1
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
+32
View File
@@ -0,0 +1,32 @@
--TEST--
Bug #74524 (Date diff is bad calculated, in same time zone)
--INI--
date.timezone=Europe/Amsterdam
--FILE--
<?php
$a = new DateTime("2017-11-17 22:05:26.000000");
$b = new DateTime("2017-04-03 22:29:15.079459");
$diff = $a->diff($b);
print_r($diff);
?>
--EXPECT--
DateInterval Object
(
[y] => 0
[m] => 7
[d] => 13
[h] => 23
[i] => 36
[s] => 10
[f] => 0.920541
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 1
[days] => 227
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
+20
View File
@@ -0,0 +1,20 @@
--TEST--
Bug #75167 (DateTime::add does only care about backward DST transition, not forward)
--FILE--
<?php
$tz = new DateTimeZone('Europe/London'); // A timezone that has DST
$five_hours_interval = new DateInterval('PT5H');
$date = new DateTime("2014-3-30 00:00:00", $tz);
// Add five hours and subtract 5 hours. The $newDate should then equal the date.
$five_hours_later = (clone $date)->add($five_hours_interval);
$newDate = (clone $five_hours_later)->sub($five_hours_interval);
echo $date->format('c') . "\n";
echo $newDate->format('c');
?>
--EXPECT--
2014-03-30T00:00:00+00:00
2014-03-30T00:00:00+00:00
+22
View File
@@ -0,0 +1,22 @@
--TEST--
Bug #76032 (DateTime->diff having issues with leap days for timezones ahead of UTC)
--FILE--
<?php
date_default_timezone_set('UTC');
$d = new DateTime('2008-03-01');
$a = new DateTime('2018-03-01');
var_dump($d->diff($a)->y);
date_default_timezone_set('Europe/Amsterdam');
$d = new DateTime('2008-03-01');
$a = new DateTime('2018-03-01');
var_dump($d->diff($a)->y);
?>
--EXPECT--
int(10)
int(10)
+19
View File
@@ -0,0 +1,19 @@
--TEST--
Bug #76374 (Date difference varies according day time)
--FILE--
<?php
date_default_timezone_set('Europe/Paris');
$objDateTo = new dateTime('2017-10-01');
$objDateFrom = new dateTime('2017-01-01');
$interval = $objDateTo->diff($objDateFrom);
echo $interval->m, "\n";
$objDateTo = new dateTime('2017-10-01 12:00:00');
$objDateFrom = new dateTime('2017-01-01 12:00:00');
$interval = $objDateTo->diff($objDateFrom);
echo $interval->m, "\n";
?>
--EXPECT--
9
9
+33
View File
@@ -0,0 +1,33 @@
--TEST--
Bug #77571 (DateTime's diff DateInterval incorrect in timezones from UTC+01:00 to UTC+12:00
--FILE--
<?php
date_default_timezone_set('Europe/London');
$date3 = DateTime::createFromFormat('Y-m-d H:i:s', '2019-04-01 00:00:00'); // 2019-04-01 00:00:00.0 Europe/London (+01:00)
$date4 = clone $date3;
$date4->modify('+5 week'); // 2019-05-06 00:00:00.0 Europe/London (+01:00)
$differenceDateInterval2 = $date3->diff($date4); // interval: + 1m 4d; days 35
print_r($differenceDateInterval2);
?>
--EXPECT--
DateInterval Object
(
[y] => 0
[m] => 1
[d] => 5
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 35
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
+44
View File
@@ -0,0 +1,44 @@
--TEST--
Bug #78452 (diff makes wrong in hour for Asia/Tehran)
--FILE--
<?php
date_default_timezone_set('Asia/Tehran');
$date1 = new \DateTime('2019-09-24 11:47:24');
$date2 = new \DateTime('2019-08-21 12:47:24');
var_dump($date1->diff($date2));
?>
--EXPECT--
object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
int(1)
["d"]=>
int(2)
["h"]=>
int(23)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(1)
["days"]=>
int(33)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
+21
View File
@@ -0,0 +1,21 @@
--TEST--
Bug #79452 (DateTime::diff() generates months differently between time zones)
--FILE--
<?php
date_default_timezone_set('America/New_York');
$from = new DateTime('2019-06-01');
$to = new DateTime('2019-10-01');
var_dump($from->diff($to)->m);
date_default_timezone_set('Asia/Tokyo');
$from = new DateTime('2019-06-01');
$to = new DateTime('2019-10-01');
var_dump($from->diff($to)->m);
?>
--EXPECT--
int(4)
int(4)
+21
View File
@@ -0,0 +1,21 @@
--TEST--
Bug #79716 (Invalid date time created (with day "00"))
--FILE--
<?php
$datetime = new \DateTimeImmutable(
'2770-01-00 15:00:00.000000',
new \DateTimeZone('UTC')
);
\var_dump($datetime->format('j') === '0');
\var_dump($datetime);
?>
--EXPECTF--
bool(false)
object(DateTimeImmutable)#%d (%d) {
["date"]=>
string(26) "2769-12-31 15:00:00.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
+26
View File
@@ -0,0 +1,26 @@
--TEST--
Bug #80610 (DateTime calculate wrong with DateInterval)
--FILE--
<?php
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20800M');
$expectEaster->sub($interval);
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n" );
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20715M');
$expectEaster->sub($interval);
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n");
$expectEaster = date_create_from_format('Y-m-d H:i:s', '2020-04-12 12:00:00', new DateTimeZone('Europe/Berlin'));
$interval = new DateInterval('PT20700M');
$expectEaster->sub($interval);
$expectEaster->add($interval);
echo('easter '.$expectEaster->format('Y-m-d H:i:s')."\n");
?>
--EXPECT--
easter 2020-04-12 12:00:00
easter 2020-04-12 12:00:00
easter 2020-04-12 12:00:00
+28
View File
@@ -0,0 +1,28 @@
--TEST--
Bug #80664 (DateTime objects behave incorrectly around DST transition)
--FILE--
<?php
$dt = new DateTime('@1604215800');
$dt->setTimezone(new DateTimeZone('America/Boise'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\n";
$dt->add(new DateInterval('PT1H'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\n";
$dt->add(new DateInterval('PT1H'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\n";
$dt->add(new DateInterval('PT1M'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\n\n";
$dt = new DateTime('@1604219400');
$dt->setTimezone(new DateTimeZone('UTC'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\r\n";
$dt->setTimezone(new DateTimeZone('America/Boise'));
echo "{$dt->format('Y-m-d H:i:s T')} | {$dt->getTimestamp()}\r\n";
?>
--EXPECT--
2020-11-01 01:30:00 MDT | 1604215800
2020-11-01 01:30:00 MST | 1604219400
2020-11-01 02:30:00 MST | 1604223000
2020-11-01 02:31:00 MST | 1604223060
2020-11-01 08:30:00 UTC | 1604219400
2020-11-01 01:30:00 MST | 1604219400
+16
View File
@@ -0,0 +1,16 @@
--TEST--
Bug #80913 (DateTime(Immutable)::sub around DST yield incorrect time)
--FILE--
<?php
$date = new DateTime('2021-03-28 03:00:00', new DateTimeZone('Europe/Amsterdam'));
$_30mbefore = (clone $date)->sub(new DateInterval('PT30M'));
$_30mafter = (clone $date)->add(new DateInterval('PT30M'));
var_dump($date->format(DATE_ATOM));
var_dump($_30mbefore->format(DATE_ATOM));
var_dump($_30mafter->format(DATE_ATOM));
?>
--EXPECT--
string(25) "2021-03-28T03:00:00+02:00"
string(25) "2021-03-28T01:30:00+01:00"
string(25) "2021-03-28T03:30:00+02:00"
@@ -90,7 +90,7 @@ ba4 2010-11-06 04:30:00 EDT America/New_York + PT23H = 2010-11-07 02:30:00 EST A
ba5 2010-11-06 04:30:00 EDT America/New_York + PT22H = 2010-11-07 01:30:00 EST America/New_York
ba6 2010-11-06 04:30:00 EDT America/New_York + PT21H = 2010-11-07 01:30:00 EDT America/New_York
ba7 2010-11-06 01:30:00 EDT America/New_York + P1D = 2010-11-07 01:30:00 EDT America/New_York
ba8 2010-11-06 01:30:00 EDT America/New_York + P1DT1H = 2010-11-07 02:30:00 EST America/New_York
ba8 2010-11-06 01:30:00 EDT America/New_York + P1DT1H = 2010-11-07 01:30:00 EST America/New_York
ba9 2010-11-06 04:30:00 EDT America/New_York + PT25H = 2010-11-07 04:30:00 EST America/New_York
ba10 2010-11-06 03:30:00 EDT America/New_York + P1D = 2010-11-07 03:30:00 EST America/New_York
ba11 2010-11-06 02:30:00 EDT America/New_York + P1D = 2010-11-07 02:30:00 EST America/New_York
@@ -45,4 +45,4 @@ bd1 2010-11-07 05:30:00 EST America/New_York - 2010-11-06 04:30:00 EDT America/N
bd2 2010-11-07 04:30:00 EST America/New_York - 2010-11-06 04:30:00 EDT America/New_York = P1DT0H
bd3 2010-11-07 03:30:00 EST America/New_York - 2010-11-06 04:30:00 EDT America/New_York = P0DT24H
bd4 2010-11-07 02:30:00 EST America/New_York - 2010-11-06 04:30:00 EDT America/New_York = P0DT23H
bd7 2010-11-07 01:30:00 EDT America/New_York - 2010-11-06 01:30:00 EDT America/New_York = P1DT0H
bd7 2010-11-07 01:30:00 EDT America/New_York - 2010-11-06 01:30:00 EDT America/New_York = P0DT24H
@@ -2,8 +2,6 @@
RFC: DateTime and Daylight Saving Time Transitions (zone type 3, bd2)
--CREDITS--
Daniel Convissor <danielc@php.net>
--XFAIL--
Still not quite right
--FILE--
<?php
@@ -23,9 +21,9 @@ $tz = new DateTimeZone('America/New_York');
* Backward Transitions, diff().
*/
$end = new DateTime('2010-11-07 05:30:00');
$end = new DateTime('2010-11-07 01:00:00 EST');
$end->setTimeZone($tz);
$start = new DateTime('2010-11-06 04:30:59');
$start = new DateTime('2010-11-07 01:59:59');
echo 'bd0 ' . $end->format($date_format) . ' - ' . $start->format($date_format)
. ' = ' . $start->diff($end)->format('P%dDT%hH%iM%sS') . "\n";
@@ -41,16 +39,23 @@ $start = new DateTime('2010-11-06 04:30:00');
echo 'bd6 ' . $end->format($date_format) . ' - ' . $start->format($date_format)
. ' = ' . $start->diff($end)->format($interval_format) . "\n";
$end = new DateTime('2010-11-07 01:30:00 EST');
$end = new DateTime('2010-11-07 01:00:00 EST');
$end->setTimeZone($tz);
$start = new DateTime('2010-11-06 01:30:00');
echo 'bd8 ' . $end->format($date_format) . ' - ' . $start->format($date_format)
$start = new DateTime('2010-11-06 01:00:00');
echo 'bd8a ' . $end->format($date_format) . ' - ' . $start->format($date_format)
. ' = ' . $start->diff($end)->format($interval_format) . "\n";
$end = new DateTime('2010-11-07 01:00:00 EDT');
$end->setTimeZone($tz);
$start = new DateTime('2010-11-06 01:00:00');
echo 'bd8b ' . $end->format($date_format) . ' - ' . $start->format($date_format)
. ' = ' . $start->diff($end)->format($interval_format) . "\n";
echo "\n";
?>
--EXPECT--
bd0 2010-11-07 01:00:00 EST America/New_York - 2010-11-07 01:59:59 EDT America/New_York = PT0H0M1S
bd0 2010-11-07 01:00:00 EST America/New_York - 2010-11-07 01:59:59 EDT America/New_York = P0DT0H0M1S
bd5 2010-11-07 01:30:00 EST America/New_York - 2010-11-06 04:30:00 EDT America/New_York = P0DT22H
bd6 2010-11-07 01:30:00 EDT America/New_York - 2010-11-06 04:30:00 EDT America/New_York = P0DT21H
bd8 2010-11-07 01:30:00 EST America/New_York - 2010-11-06 01:30:00 EDT America/New_York = P1DT1H
bd8a 2010-11-07 01:00:00 EST America/New_York - 2010-11-06 01:00:00 EDT America/New_York = P1DT0H
bd8b 2010-11-07 01:00:00 EDT America/New_York - 2010-11-06 01:00:00 EDT America/New_York = P0DT24H
@@ -2,8 +2,6 @@
RFC: DateTime and Daylight Saving Time Transitions (zone type 3, fs)
--CREDITS--
Daniel Convissor <danielc@php.net>
--XFAIL--
Still not quite right
--FILE--
<?php