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:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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" */
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+59
-127
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
--TEST--
|
||||
Bug #52480 (Incorrect difference using DateInterval)
|
||||
--XFAIL--
|
||||
See https://bugs.php.net/bug.php?id=52480
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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--
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user