diff --git a/NEWS b/NEWS index 59ad61f7236..68af2eee925 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ PHP NEWS ?? ??? 2005, PHP 5.1 Beta 3 - Upgraded bundled SQLite library for PDO:SQLite to 3.2.2 (Ilia) - Added PDO_MYSQL_ATTR_USE_BUFFERED_QUERY parameter for pdo_mysql. (Ilia) +- Implemented feature request #33452 (Year belonging to ISO week). (Derick) - Fixed bug #33491 (crash after extending MySQLi internal class). (Tony) - Fixed bug #33475 (cURL handle is not closed on curl_close(). (Ilia) - Fixed bug #33469 (Compile error undefined reference to ifx_checkAPI). (Jani) diff --git a/ext/date/lib/dow.c b/ext/date/lib/dow.c index eaa3165cd5b..e0df26677dd 100644 --- a/ext/date/lib/dow.c +++ b/ext/date/lib/dow.c @@ -41,10 +41,74 @@ timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d) } c1 = century_value(y / 100); y1 = (y % 100); - m1 = is_leap(y) ? m_table_leap[m] : m_table_common[m]; + m1 = timelib_is_leap(y) ? m_table_leap[m] : m_table_common[m]; return (c1 + y1 + m1 + (y1 / 4) + d) % 7; } + /* jan feb mar apr may jun jul aug sep oct nov dec */ +static int d_table_common[13] = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; +static int d_table_leap[13] = { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; +static int ml_table_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d) +{ + return (timelib_is_leap(y) ? d_table_leap[m] : d_table_common[m]) + d - 1; +} + +timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m) +{ + return timelib_is_leap(y) ? ml_table_leap[m] : ml_table_common[m]; +} + +void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy) +{ + int y_leap, prev_y_leap, doy, jan1weekday, weekday; + + y_leap = timelib_is_leap(y); + prev_y_leap = timelib_is_leap(y-1); + doy = timelib_day_of_year(y, m, d) + 1; + if (y_leap && m > 2) { + doy++; + } + jan1weekday = timelib_day_of_week(y, 1, 1); + weekday = timelib_day_of_week(y, m, d); + if (weekday == 0) weekday = 7; + if (jan1weekday == 0) jan1weekday = 7; + /* Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */ + if (doy <= (8 - jan1weekday) && jan1weekday > 4) { + *iy = y - 1; + if (jan1weekday == 5 || (jan1weekday == 6 && prev_y_leap)) { + *iw = 53; + } else { + *iw = 52; + } + } else { + *iy = y; + } + /* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */ + if (*iy == y) { + int i; + + i = y_leap ? 366 : 365; + if ((i - doy) < (4 - weekday)) { + *iy = y + 1; + *iw = 1; + return; + } + } + /* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */ + if (*iy == y) { + int j; + + j = doy + (7 - weekday) + (jan1weekday - 1); + *iw = j / 7; + if (jan1weekday > 4) { + *iw -= 1; + } + } +} + timelib_sll timelib_daynr_from_weeknr(timelib_sll y, timelib_sll w, timelib_sll d) { timelib_sll dow, day; diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c index 79e8f621a9c..fa06fa21e66 100644 --- a/ext/date/lib/parse_tz.c +++ b/ext/date/lib/parse_tz.c @@ -193,7 +193,7 @@ static int tz_search(char *timezone, int left, int right) mid = (left + right) / 2; - cmp = strcmp(timezone, timezonedb_idx[mid].id); + cmp = strcasecmp(timezone, timezonedb_idx[mid].id); if (cmp < 0) { return tz_search(timezone, left, mid - 1); } else if (cmp > 0) { diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index 4827449ca16..79061b36f30 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -36,7 +36,10 @@ /* From dow.c */ timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d); +timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d); timelib_sll timelib_daynr_from_weeknr(timelib_sll y, timelib_sll w, timelib_sll d); +timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m); +void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy); /* From parse_date.re */ timelib_time *timelib_strtotime(char *s); diff --git a/ext/date/lib/timelib_structs.h b/ext/date/lib/timelib_structs.h index 8590e47f692..5eb7e1fdb44 100644 --- a/ext/date/lib/timelib_structs.h +++ b/ext/date/lib/timelib_structs.h @@ -154,7 +154,7 @@ typedef struct timelib_time { #define DAYS_PER_YEAR 365 #define DAYS_PER_LYEAR 366 -#define is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) +#define timelib_is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) #define DEBUG(s) if (0) { s } diff --git a/ext/date/lib/timezonedb.h b/ext/date/lib/timezonedb.h index 5e0235e2401..f4ac15b526b 100644 --- a/ext/date/lib/timezonedb.h +++ b/ext/date/lib/timezonedb.h @@ -101,8 +101,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "America/Eirunepe" , 0x006E07 }, { "America/El_Salvador" , 0x006EF3 }, { "America/Ensenada" , 0x006F5C }, - { "America/Fortaleza" , 0x0072A8 }, { "America/Fort_Wayne" , 0x0073BC }, + { "America/Fortaleza" , 0x0072A8 }, { "America/Glace_Bay" , 0x0074DA }, { "America/Godthab" , 0x0077FE }, { "America/Goose_Bay" , 0x007AA8 }, @@ -118,8 +118,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "America/Indiana/Indianapolis" , 0x008C96 }, { "America/Indiana/Knox" , 0x008DB4 }, { "America/Indiana/Marengo" , 0x008FD9 }, - { "America/Indianapolis" , 0x00910B }, { "America/Indiana/Vevay" , 0x009229 }, + { "America/Indianapolis" , 0x00910B }, { "America/Inuvik" , 0x0092ED }, { "America/Iqaluit" , 0x0095E4 }, { "America/Jamaica" , 0x0098D6 }, @@ -157,8 +157,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "America/Paramaribo" , 0x00E0B3 }, { "America/Phoenix" , 0x00E139 }, { "America/Port-au-Prince" , 0x00E1BB }, - { "America/Porto_Acre" , 0x00E2C1 }, { "America/Port_of_Spain" , 0x00E3A3 }, + { "America/Porto_Acre" , 0x00E2C1 }, { "America/Porto_Velho" , 0x00E3EC }, { "America/Puerto_Rico" , 0x00E4CE }, { "America/Rainy_River" , 0x00E51C }, @@ -294,8 +294,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "Atlantic/Madeira" , 0x01BB39 }, { "Atlantic/Reykjavik" , 0x01C027 }, { "Atlantic/South_Georgia" , 0x01C1D4 }, - { "Atlantic/Stanley" , 0x01C20C }, { "Atlantic/St_Helena" , 0x01C4D4 }, + { "Atlantic/Stanley" , 0x01C20C }, { "Australia/ACT" , 0x01C51D }, { "Australia/Adelaide" , 0x01C82E }, { "Australia/Brisbane" , 0x01CB3F }, @@ -323,8 +323,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "Brazil/West" , 0x01F809 }, { "Canada/Atlantic" , 0x01F8F5 }, { "Canada/Central" , 0x01FDD1 }, - { "Canada/Eastern" , 0x0201D2 }, { "Canada/East-Saskatchewan" , 0x0206B6 }, + { "Canada/Eastern" , 0x0201D2 }, { "Canada/Mountain" , 0x020833 }, { "Canada/Newfoundland" , 0x020B9D }, { "Canada/Pacific" , 0x0210BC }, @@ -341,35 +341,35 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "EST" , 0x0236C9 }, { "EST5EDT" , 0x0237E7 }, { "Etc/GMT" , 0x023CDA }, - { "Etc/GMT0" , 0x023D12 }, - { "Etc/GMT-0" , 0x023D4A }, { "Etc/GMT+0" , 0x023D82 }, - { "Etc/GMT-1" , 0x023DBA }, { "Etc/GMT+1" , 0x023DF4 }, - { "Etc/GMT-10" , 0x023E2E }, { "Etc/GMT+10" , 0x023E69 }, - { "Etc/GMT-11" , 0x023EA4 }, { "Etc/GMT+11" , 0x023EDF }, - { "Etc/GMT-12" , 0x023F1A }, { "Etc/GMT+12" , 0x023F55 }, + { "Etc/GMT+2" , 0x024040 }, + { "Etc/GMT+3" , 0x0240B4 }, + { "Etc/GMT+4" , 0x024128 }, + { "Etc/GMT+5" , 0x02419C }, + { "Etc/GMT+6" , 0x024210 }, + { "Etc/GMT+7" , 0x024284 }, + { "Etc/GMT+8" , 0x0242F8 }, + { "Etc/GMT+9" , 0x02436C }, + { "Etc/GMT-0" , 0x023D4A }, + { "Etc/GMT-1" , 0x023DBA }, + { "Etc/GMT-10" , 0x023E2E }, + { "Etc/GMT-11" , 0x023EA4 }, + { "Etc/GMT-12" , 0x023F1A }, { "Etc/GMT-13" , 0x023F90 }, { "Etc/GMT-14" , 0x023FCB }, { "Etc/GMT-2" , 0x024006 }, - { "Etc/GMT+2" , 0x024040 }, { "Etc/GMT-3" , 0x02407A }, - { "Etc/GMT+3" , 0x0240B4 }, { "Etc/GMT-4" , 0x0240EE }, - { "Etc/GMT+4" , 0x024128 }, { "Etc/GMT-5" , 0x024162 }, - { "Etc/GMT+5" , 0x02419C }, { "Etc/GMT-6" , 0x0241D6 }, - { "Etc/GMT+6" , 0x024210 }, { "Etc/GMT-7" , 0x02424A }, - { "Etc/GMT+7" , 0x024284 }, { "Etc/GMT-8" , 0x0242BE }, - { "Etc/GMT+8" , 0x0242F8 }, { "Etc/GMT-9" , 0x024332 }, - { "Etc/GMT+9" , 0x02436C }, + { "Etc/GMT0" , 0x023D12 }, { "Etc/Greenwich" , 0x0243A6 }, { "Etc/UCT" , 0x0243DE }, { "Etc/Universal" , 0x024416 }, @@ -432,9 +432,9 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "GB" , 0x02F8AF }, { "GB-Eire" , 0x02FDDA }, { "GMT" , 0x030305 }, - { "GMT0" , 0x03033D }, - { "GMT-0" , 0x030375 }, { "GMT+0" , 0x0303AD }, + { "GMT-0" , 0x030375 }, + { "GMT0" , 0x03033D }, { "Greenwich" , 0x0303E5 }, { "Hongkong" , 0x03041D }, { "HST" , 0x0305C7 }, @@ -519,8 +519,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "US/Aleutian" , 0x03590D }, { "US/Arizona" , 0x035C67 }, { "US/Central" , 0x035CE9 }, - { "US/Eastern" , 0x0361E8 }, { "US/East-Indiana" , 0x0366DB }, + { "US/Eastern" , 0x0361E8 }, { "US/Hawaii" , 0x0367F9 }, { "US/Indiana-Starke" , 0x03687B }, { "US/Michigan" , 0x036AA0 }, @@ -529,8 +529,8 @@ typedef struct { char *id; unsigned int pos; } tzdb_idx; tzdb_idx timezonedb_idx { "US/Pacific-New" , 0x037531 }, { "US/Samoa" , 0x03792A }, { "UTC" , 0x0379A7 }, - { "WET" , 0x0379DF }, { "W-SU" , 0x037C86 }, + { "WET" , 0x0379DF }, { "Zulu" , 0x037FB5 }, }; /* This is a generated file, do not modify */ diff --git a/ext/date/lib/tm2unixtime.c b/ext/date/lib/tm2unixtime.c index e67df46f2ab..f708526f87a 100644 --- a/ext/date/lib/tm2unixtime.c +++ b/ext/date/lib/tm2unixtime.c @@ -58,7 +58,7 @@ static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d) do_range_limit(1, 13, 12, m, y); - leapyear = is_leap(*y); + leapyear = timelib_is_leap(*y); days_this_month = leapyear ? days_in_month_leap[*m] : days_in_month[*m]; last_month = (*m) - 1; @@ -68,7 +68,7 @@ static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d) } else { last_year = (*y); } - leapyear = is_leap(last_year); + leapyear = timelib_is_leap(last_year); days_last_month = leapyear ? days_in_month_leap[last_month] : days_in_month[last_month]; if (*d <= 0) { @@ -138,7 +138,7 @@ static timelib_sll do_years(timelib_sll year) if (year >= 1970) { for (i = year - 1; i >= 1970; i--) { - if (is_leap(i)) { + if (timelib_is_leap(i)) { res += (DAYS_PER_LYEAR * SECS_PER_DAY); } else { res += (DAYS_PER_YEAR * SECS_PER_DAY); @@ -146,7 +146,7 @@ static timelib_sll do_years(timelib_sll year) } } else { for (i = 1969; i >= year; i--) { - if (is_leap(i)) { + if (timelib_is_leap(i)) { res -= (DAYS_PER_LYEAR * SECS_PER_DAY); } else { res -= (DAYS_PER_YEAR * SECS_PER_DAY); @@ -158,7 +158,7 @@ static timelib_sll do_years(timelib_sll year) static timelib_sll do_months(timelib_ull month, timelib_ull year) { - if (is_leap(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); diff --git a/ext/date/lib/unixtime2tm.c b/ext/date/lib/unixtime2tm.c index 169cce0c934..59ac9a8774b 100644 --- a/ext/date/lib/unixtime2tm.c +++ b/ext/date/lib/unixtime2tm.c @@ -63,7 +63,7 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) tmp_days = days + 1; while (tmp_days >= DAYS_PER_LYEAR) { cur_year++; - if (is_leap(cur_year)) { + if (timelib_is_leap(cur_year)) { tmp_days -= DAYS_PER_LYEAR; } else { tmp_days -= DAYS_PER_YEAR; @@ -82,7 +82,7 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) while (tmp_days <= 0) { cur_year--; DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year);); - if (is_leap(cur_year)) { + if (timelib_is_leap(cur_year)) { tmp_days += DAYS_PER_LYEAR; } else { tmp_days += DAYS_PER_YEAR; @@ -92,7 +92,7 @@ void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) } DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year);); - months = is_leap(cur_year) ? month_tab_leap : month_tab; + months = timelib_is_leap(cur_year) ? month_tab_leap : month_tab; i = 11; while (i > 0) { DEBUG(printf("month=%lld (%d)\n", i, months[i]);); diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 2b28bed9a55..aa1d77d1fac 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -29,6 +29,8 @@ #include function_entry date_functions[] = { + PHP_FE(date, NULL) + PHP_FE(gmdate, NULL) PHP_FE(strtotime, NULL) {NULL, NULL, NULL} }; @@ -85,6 +87,198 @@ PHP_MINFO_FUNCTION(date) php_info_print_table_end(); } +/* =[ Helper functions ] ================================================== */ + +static char* guess_timezone(TSRMLS_D) +{ + char *env; + + env = getenv("TZ"); + if (env) { + return env; + } + /* Check config setting */ + if (DATEG(default_timezone)) { + return DATEG(default_timezone); + } + return "GMT"; +} + +/* =[ date() and gmdate() ]================================================ */ +#include "ext/standard/php_smart_str.h" + +static char *mon_full_names[] = { + "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" +}; + +static char *mon_short_names[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static char *day_full_names[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static char *day_short_names[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static char *english_suffix(int number) +{ + if (number >= 10 && number <= 19) { + return "th"; + } else { + switch (number % 10) { + case 1: return "st"; + case 2: return "nd"; + case 3: return "rd"; + } + } + return "th"; +} + +static char *php_format_date(char *format, int format_len, timelib_time *t, int localtime) +{ + smart_str string = {0}; + int i; + char buffer[33]; + timelib_time_offset *offset; + timelib_sll isoweek, isoyear; + + if (localtime) { + offset = timelib_get_time_zone_info(t->sse, t->tz_info); + } + buffer[32] = '\0'; + timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); + + for (i = 0; i < format_len; i++) { + switch (format[i]) { + /* day */ + case 'd': snprintf(&buffer, 32, "%02d", (int) t->d); break; + case 'D': snprintf(&buffer, 32, "%s", day_short_names[timelib_day_of_week(t->y, t->m, t->d)]); break; + case 'j': snprintf(&buffer, 32, "%d", (int) t->d); break; + case 'l': snprintf(&buffer, 32, "%s", day_full_names[timelib_day_of_week(t->y, t->m, t->d)]); break; + case 'S': snprintf(&buffer, 32, "%s", english_suffix(t->d)); break; + case 'w': snprintf(&buffer, 32, "%d", (int) timelib_day_of_week(t->y, t->m, t->d)); break; + case 'z': snprintf(&buffer, 32, "%d", (int) timelib_day_of_year(t->y, t->m, t->d)); break; + + /* week */ + case 'W': snprintf(&buffer, 32, "%d", (int) isoweek); break; /* iso weeknr */ + case 'o': snprintf(&buffer, 32, "%d", (int) isoyear); break; /* iso year */ + + /* month */ + case 'F': snprintf(&buffer, 32, "%s", mon_full_names[t->m - 1]); break; + case 'm': snprintf(&buffer, 32, "%02d", (int) t->m); break; + case 'M': snprintf(&buffer, 32, "%s", mon_short_names[t->m - 1]); break; + case 'n': snprintf(&buffer, 32, "%d", (int) t->m); break; + case 't': snprintf(&buffer, 32, "%d", (int) timelib_days_in_month(t->y, t->m)); break; + + /* year */ + case 'L': snprintf(&buffer, 32, "%d", timelib_is_leap((int) t->y)); break; + case 'y': snprintf(&buffer, 32, "%02d", (int) t->y % 100); break; + case 'Y': snprintf(&buffer, 32, "%04d", (int) t->y); break; + + /* time */ + case 'a': snprintf(&buffer, 32, "%s", t->h >= 12 ? "pm" : "am"); break; + case 'A': snprintf(&buffer, 32, "%s", t->h >= 12 ? "PM" : "AM"); break; + case 'B': snprintf(&buffer, 32, "[B unimplemented]"); break; + case 'g': snprintf(&buffer, 32, "%d", (t->h % 12) ? (int) t->h % 12 : 12); break; + case 'G': snprintf(&buffer, 32, "%d", (int) t->h); break; + case 'h': snprintf(&buffer, 32, "%02d", (t->h % 12) ? (int) t->h % 12 : 12); break; + case 'H': snprintf(&buffer, 32, "%02d", (int) t->h); break; + case 'i': snprintf(&buffer, 32, "%02d", (int) t->i); break; + case 's': snprintf(&buffer, 32, "%02d", (int) t->s); break; + + /* timezone */ + case 'I': snprintf(&buffer, 32, "%d", localtime ? offset->is_dst : 0); break; + case 'O': snprintf(&buffer, 32, "%c%02d%02d", + localtime ? ((offset->offset < 0) ? '-' : '+') : '+', + localtime ? abs(offset->offset / 3600) : 0, + localtime ? abs((offset->offset % 3600) / 60) : 0 + ); + break; + case 'T': snprintf(&buffer, 32, "%s", localtime ? offset->abbr : "GMT"); break; + case 'e': snprintf(&buffer, 32, "%s", localtime ? t->tz_info->name : "UTC"); break; + case 'Z': snprintf(&buffer, 32, "%d", localtime ? offset->offset : 0); break; + + /* full date/time */ + case 'c': snprintf(&buffer, 32, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + (int) t->y, (int) t->m, (int) t->d, + (int) t->h, (int) t->i, (int) t->s, + localtime ? ((offset->offset < 0) ? '-' : '+') : '+', + localtime ? abs(offset->offset / 3600) : 0, + localtime ? abs((offset->offset % 3600) / 60) : 0 + ); + break; + case 'r': snprintf(&buffer, 32, "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d", + day_short_names[timelib_day_of_week(t->y, t->m, t->d)], + (int) t->d, mon_short_names[t->m - 1], + (int) t->y, (int) t->h, (int) t->i, (int) t->s, + localtime ? ((offset->offset < 0) ? '-' : '+') : '+', + localtime ? abs(offset->offset / 3600) : 0, + localtime ? abs((offset->offset % 3600) / 60) : 0 + ); + break; + case 'U': snprintf(&buffer, 32, "%lld", (long long) t->sse); break; + + case '\\': if (i < format_len) i++; buffer[0] = format[i]; buffer[1] = '\0'; break; + + default: buffer[0] = format[i]; buffer[1] = '\0'; + } + smart_str_appends(&string, buffer); + buffer[0] = '\0'; + } + + smart_str_0(&string); + + return string.c; +} + +static void php_date(INTERNAL_FUNCTION_PARAMETERS, int localtime) +{ + char *format; + int format_len; + long ts = time(NULL); + timelib_time *t; + char *string; + timelib_tzinfo *tzi; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &format, &format_len, &ts) == FAILURE) { + RETURN_FALSE; + } + + t = timelib_time_ctor(); + + if (localtime) { + tzi = timelib_parse_tzfile(guess_timezone()); + if (! tzi) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find any timezone setting"); + RETURN_FALSE; + } + timelib_unixtime2local(t, ts, tzi); + } else { + tzi = NULL; + timelib_unixtime2gmt(t, ts); + } + string = php_format_date(format, format_len, t, localtime); + + RETVAL_STRING(string, 0); + timelib_time_dtor(t); +} + +PHP_FUNCTION(date) +{ + php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} + +PHP_FUNCTION(gmdate) +{ + php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} + +/* Backwards compability function */ signed long php_parse_date(char *string, signed long *now) { timelib_time *parsed_time; @@ -101,23 +295,6 @@ signed long php_parse_date(char *string, signed long *now) return retval; } -static char* guess_timezone(TSRMLS_D) -{ - char *env; - - env = getenv("TZ"); - if (env) { - return env; - } - if (DATEG(default_timezone)) { - return DATEG(default_timezone); - } - /* Check config setting */ - /* - */ - return "GMT"; -} - /* {{{ proto int strtotime(string time, int now) Convert string representation of date and time to a timestamp */ diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 75eeb1e47e8..b8f5902e34c 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -25,6 +25,8 @@ extern zend_module_entry date_module_entry; #define phpext_date_ptr &date_module_entry PHP_FUNCTION(strtotime); +PHP_FUNCTION(date); +PHP_FUNCTION(gmdate); PHP_MINIT_FUNCTION(date); PHP_MSHUTDOWN_FUNCTION(date); diff --git a/ext/date/tests/bug33452.phpt b/ext/date/tests/bug33452.phpt new file mode 100644 index 00000000000..75287a2f278 --- /dev/null +++ b/ext/date/tests/bug33452.phpt @@ -0,0 +1,10 @@ +--TEST-- +Bug #33452 (Support for year accompanying ISO week nr) +--FILE-- + +--EXPECT-- +2005-53 +2004-53 diff --git a/ext/standard/tests/time/date.phpt b/ext/date/tests/date.phpt similarity index 100% rename from ext/standard/tests/time/date.phpt rename to ext/date/tests/date.phpt diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 9ad9f9d7055..856a06c62ac 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -179,9 +179,7 @@ function_entry basic_functions[] = { PHP_FE(gmstrftime, NULL) #endif - PHP_FE(date, NULL) PHP_FE(idate, NULL) - PHP_FE(gmdate, NULL) PHP_FE(getdate, NULL) PHP_FE(localtime, NULL) PHP_FE(checkdate, NULL) diff --git a/ext/standard/datetime.c b/ext/standard/datetime.c index 9280e336620..9f1a9ce5438 100644 --- a/ext/standard/datetime.c +++ b/ext/standard/datetime.c @@ -281,416 +281,6 @@ PHP_FUNCTION(gmmktime) } /* }}} */ -/* {{{ php_date - */ -static void php_date(INTERNAL_FUNCTION_PARAMETERS, int gm) -{ - pval **format, **timestamp; - time_t the_time; - struct tm *ta, tmbuf; - int i, size = 0, length, h, beat, fd, wd, yd, wk; - char tmp_buff[32]; -#if !HAVE_TM_GMTOFF - long tzone; - char *tname[2]= {"GMT Standard Time", "BST"}; -#endif - - switch(ZEND_NUM_ARGS()) { - case 1: - if (zend_get_parameters_ex(1, &format) == FAILURE) { - WRONG_PARAM_COUNT; - } - the_time = time(NULL); - break; - case 2: - if (zend_get_parameters_ex(2, &format, ×tamp) == FAILURE) { - WRONG_PARAM_COUNT; - } - convert_to_long_ex(timestamp); - the_time = Z_LVAL_PP(timestamp); -#ifdef PHP_WIN32 - if (the_time < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Windows does not support dates prior to midnight (00:00:00), January 1, 1970"); - RETURN_FALSE; - } -#endif - break; - default: - WRONG_PARAM_COUNT; - } - convert_to_string_ex(format); - - if (gm) { - ta = php_gmtime_r(&the_time, &tmbuf); -#if !HAVE_TM_GMTOFF - tzone = 0; -#endif - } else { - ta = php_localtime_r(&the_time, &tmbuf); -#if !HAVE_TM_GMTOFF -#ifdef __CYGWIN__ - tzone = _timezone; -#else - tzone = timezone; -#endif - if (tzname[0] != NULL) { - tname[0] = tzname[0]; - } else { - tname[0] = "???"; - } - - if (tzname[1] != NULL) { - tname[1] = tzname[1]; - } -#endif - } - - if (!ta) { /* that really shouldn't happen... */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unexpected error"); - RETURN_FALSE; - } - for (i = 0; i < Z_STRLEN_PP(format); i++) { - switch (Z_STRVAL_PP(format)[i]) { - case 'r': /* rfc822 format */ - size += 31; - break; - case 'c': /* iso8601 date (Dublin Core Date) */ - size += 25; - break; - case 'U': /* seconds since the epoch */ - size += 10; - break; - case 'F': /* month, textual, full */ - case 'l': /* day (of the week), textual */ - size += 28; - break; - case 'T': /* timezone name */ -#if HAVE_TM_ZONE - size += strlen(ta->tm_zone); -#elif HAVE_TZNAME - if (ta->tm_isdst > 0 ) { - size += strlen(tname[1]); - } else { - size += strlen(tname[0]); - } -#endif - break; - case 'Z': /* timezone offset in seconds */ - size += 6; - break; - case 'O': /* GMT offset in [+-]HHMM format */ - size += 5; - break; - case 'Y': /* year, numeric, 4 digits */ - size += 4; - break; - case 'M': /* month, textual, 3 letters */ - case 'D': /* day, textual, 3 letters */ - case 'z': /* day of the year, 1 to 366 */ - case 'B': /* Swatch Beat, 3 digits */ - size += 3; - break; - case 'y': /* year, numeric, 2 digits */ - case 'm': /* month, numeric */ - case 'n': /* month, numeric, no leading zeroes */ - case 'd': /* day of the month, numeric */ - case 'j': /* day of the month, numeric, no leading zeros */ - case 'H': /* hour, numeric, 24 hour format */ - case 'h': /* hour, numeric, 12 hour format */ - case 'G': /* hour, numeric, 24 hour format, no leading zeroes */ - case 'g': /* hour, numeric, 12 hour format, no leading zeroes */ - case 'i': /* minutes, numeric */ - case 's': /* seconds, numeric */ - case 'A': /* AM/PM */ - case 'a': /* am/pm */ - case 'S': /* standard english suffix for the day of the month (e.g. 3rd, 2nd, etc) */ - case 't': /* days in current month */ - case 'W': /* ISO-8601 week number of year, weeks starting on Monday */ - size += 2; - break; - case '\\': - if (i < Z_STRLEN_PP(format) - 1) { - i++; - } - size ++; - break; - case 'L': /* boolean for leap year */ - case 'w': /* day of the week, numeric */ - case 'I': /* DST? */ - default: - size++; - break; - } - } - - Z_STRVAL_P(return_value) = (char *) emalloc(size + 1); - Z_STRVAL_P(return_value)[0] = '\0'; - - for (i = 0; i < Z_STRLEN_PP(format); i++) { - switch (Z_STRVAL_PP(format)[i]) { - case '\\': - if (i < Z_STRLEN_PP(format) - 1) { - char ch[2]; - ch[0]=Z_STRVAL_PP(format)[i + 1]; - ch[1]='\0'; - strcat(Z_STRVAL_P(return_value), ch); - i++; - } - break; - case 'U': /* seconds since the epoch */ - sprintf(tmp_buff, "%ld", (long)the_time); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'F': /* month, textual, full */ - strcat(Z_STRVAL_P(return_value), mon_full_names[ta->tm_mon]); - break; - case 'l': /* day (of the week), textual, full */ - strcat(Z_STRVAL_P(return_value), day_full_names[ta->tm_wday]); - break; - case 'Y': /* year, numeric, 4 digits */ - sprintf(tmp_buff, "%d", ta->tm_year + YEAR_BASE); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'M': /* month, textual, 3 letters */ - strcat(Z_STRVAL_P(return_value), mon_short_names[ta->tm_mon]); - break; - case 'D': /* day (of the week), textual, 3 letters */ - strcat(Z_STRVAL_P(return_value), day_short_names[ta->tm_wday]); - break; - case 'z': /* day (of the year) */ - sprintf(tmp_buff, "%d", ta->tm_yday); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'y': /* year, numeric, 2 digits */ - sprintf(tmp_buff, "%02d", ((ta->tm_year)%100)); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'm': /* month, numeric */ - sprintf(tmp_buff, "%02d", ta->tm_mon + 1); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'n': /* month, numeric, no leading zeros */ - sprintf(tmp_buff, "%d", ta->tm_mon + 1); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'd': /* day of the month, numeric */ - sprintf(tmp_buff, "%02d", ta->tm_mday); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'j': - sprintf(tmp_buff, "%d", ta->tm_mday); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'H': /* hour, numeric, 24 hour format */ - sprintf(tmp_buff, "%02d", ta->tm_hour); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'h': /* hour, numeric, 12 hour format */ - h = ta->tm_hour % 12; if (h==0) h = 12; - sprintf(tmp_buff, "%02d", h); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'G': /* hour, numeric, 24 hour format, no leading zeros */ - sprintf(tmp_buff, "%d", ta->tm_hour); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'g': /* hour, numeric, 12 hour format, no leading zeros */ - h = ta->tm_hour % 12; if (h==0) h = 12; - sprintf(tmp_buff, "%d", h); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'i': /* minutes, numeric */ - sprintf(tmp_buff, "%02d", ta->tm_min); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 's': /* seconds, numeric */ - sprintf(tmp_buff, "%02d", ta->tm_sec); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'A': /* AM/PM */ - strcat(Z_STRVAL_P(return_value), (ta->tm_hour >= 12 ? "PM" : "AM")); - break; - case 'a': /* am/pm */ - strcat(Z_STRVAL_P(return_value), (ta->tm_hour >= 12 ? "pm" : "am")); - break; - case 'S': /* standard english suffix, e.g. 2nd/3rd for the day of the month */ - if (ta->tm_mday >= 10 && ta->tm_mday <= 19) { - strcat(Z_STRVAL_P(return_value), "th"); - } else { - switch (ta->tm_mday % 10) { - case 1: - strcat(Z_STRVAL_P(return_value), "st"); - break; - case 2: - strcat(Z_STRVAL_P(return_value), "nd"); - break; - case 3: - strcat(Z_STRVAL_P(return_value), "rd"); - break; - default: - strcat(Z_STRVAL_P(return_value), "th"); - break; - } - } - break; - case 't': /* days in current month */ - sprintf(tmp_buff, "%2d", phpday_tab[isleap((ta->tm_year+YEAR_BASE))][ta->tm_mon] ); - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'w': /* day of the week, numeric EXTENSION */ - sprintf(tmp_buff, "%01d", ta->tm_wday); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'O': /* GMT offset in [+-]HHMM format */ -#if HAVE_TM_GMTOFF - sprintf(tmp_buff, "%c%02d%02d", (ta->tm_gmtoff < 0) ? '-' : '+', abs(ta->tm_gmtoff / 3600), abs( (ta->tm_gmtoff % 3600) / 60 )); -#else - sprintf(tmp_buff, "%c%02d%02d", ((ta->tm_isdst ? tzone - 3600:tzone)>0)?'-':'+', abs((ta->tm_isdst ? tzone - 3600 : tzone) / 3600), abs(((ta->tm_isdst ? tzone - 3600 : tzone) % 3600) / 60)); -#endif - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'Z': /* timezone offset in seconds */ -#if HAVE_TM_GMTOFF - sprintf(tmp_buff, "%ld", ta->tm_gmtoff); -#else - sprintf(tmp_buff, "%ld", ta->tm_isdst ? -(tzone- 3600) : -tzone); -#endif - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'L': /* boolean for leapyear */ - sprintf(tmp_buff, "%d", (isleap((ta->tm_year+YEAR_BASE)) ? 1 : 0 ) ); - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'T': /* timezone name */ -#if HAVE_TM_ZONE - strcat(Z_STRVAL_P(return_value), ta->tm_zone); -#elif HAVE_TZNAME - strcat(Z_STRVAL_P(return_value), ta->tm_isdst ? tname[1] : tname[0]); -#endif - break; - case 'B': /* Swatch Beat a.k.a. Internet Time */ - beat = (((((long)the_time)-(((long)the_time) - - ((((long)the_time) % 86400) + 3600))) * 10) / 864); - while (beat < 0) { - beat += 1000; - } - beat = beat % 1000; - sprintf(tmp_buff, "%03d", beat); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'I': - sprintf(tmp_buff, "%d", ta->tm_isdst); - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'r': -#if HAVE_TM_GMTOFF - sprintf(tmp_buff, "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d", - day_short_names[ta->tm_wday], - ta->tm_mday, - mon_short_names[ta->tm_mon], - ta->tm_year + YEAR_BASE, - ta->tm_hour, - ta->tm_min, - ta->tm_sec, - (ta->tm_gmtoff < 0) ? '-' : '+', - abs(ta->tm_gmtoff / 3600), - abs( (ta->tm_gmtoff % 3600) / 60 ) - ); -#else - sprintf(tmp_buff, "%3s, %02d %3s %04d %02d:%02d:%02d %c%02d%02d", - day_short_names[ta->tm_wday], - ta->tm_mday, - mon_short_names[ta->tm_mon], - ta->tm_year + YEAR_BASE, - ta->tm_hour, - ta->tm_min, - ta->tm_sec, - ((ta->tm_isdst ? tzone - 3600 : tzone) > 0) ? '-' : '+', - abs((ta->tm_isdst ? tzone - 3600 : tzone) / 3600), - abs( ((ta->tm_isdst ? tzone - 3600 : tzone) % 3600) / 60 ) - ); -#endif - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'c': -#if HAVE_TM_GMTOFF - sprintf(tmp_buff, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", - ta->tm_year + YEAR_BASE, - ta->tm_mon + 1, - ta->tm_mday, - ta->tm_hour, - ta->tm_min, - ta->tm_sec, - (ta->tm_gmtoff < 0) ? '-' : '+', - abs(ta->tm_gmtoff / 3600), - abs( (ta->tm_gmtoff % 3600) / 60 ) - ); -#else - sprintf(tmp_buff, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", - ta->tm_year + YEAR_BASE, - ta->tm_mon + 1, - ta->tm_mday, - ta->tm_hour, - ta->tm_min, - ta->tm_sec, - ((ta->tm_isdst ? tzone - 3600 : tzone) > 0) ? '-' : '+', - abs((ta->tm_isdst ? tzone - 3600 : tzone) / 3600), - abs( ((ta->tm_isdst ? tzone - 3600 : tzone) % 3600) / 60 ) - ); -#endif - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - case 'W': /* ISO-8601 week number of year, weeks starting on Monday */ - wd = ta->tm_wday == 0 ? 6 : ta->tm_wday - 1; /* weekday */ - yd = ta->tm_yday + 1; /* days since January 1st */ - - fd = (7 + wd - yd % 7+ 1) % 7; /* weekday (1st January) */ - - /* week is a last year week (52 or 53) */ - if ((yd <= 7 - fd) && fd > 3){ - wk = (fd == 4 || (fd == 5 && isleap((ta->tm_year + YEAR_BASE - 1)))) ? 53 : 52; - } - /* week is a next year week (1) */ - else if (isleap((ta->tm_year+YEAR_BASE)) + 365 - yd < 3 - wd){ - wk = 1; - } - /* normal week */ - else { - wk = (yd + 6 - wd + fd) / 7 - (fd > 3); - } - - sprintf(tmp_buff, "%d", wk); /* SAFE */ - strcat(Z_STRVAL_P(return_value), tmp_buff); - break; - - default: - length = strlen(Z_STRVAL_P(return_value)); - Z_STRVAL_P(return_value)[length] = Z_STRVAL_PP(format)[i]; - Z_STRVAL_P(return_value)[length + 1] = '\0'; - break; - } - } - Z_STRLEN_P(return_value) = strlen(Z_STRVAL_P(return_value)); - Z_TYPE_P(return_value) = IS_STRING; -} -/* }}} */ - -/* {{{ proto string date(string format [, int timestamp]) - Format a local time/date */ -PHP_FUNCTION(date) -{ - php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto string gmdate(string format [, int timestamp]) - Format a GMT/UTC date/time */ -PHP_FUNCTION(gmdate) -{ - php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - /* {{{ php_idate */ PHPAPI int php_idate(char format, int timestamp, int gm) diff --git a/ext/standard/datetime.h b/ext/standard/datetime.h index f0d3d4ecd72..d1d3c04fac7 100644 --- a/ext/standard/datetime.h +++ b/ext/standard/datetime.h @@ -25,9 +25,7 @@ PHP_FUNCTION(time); PHP_FUNCTION(mktime); PHP_FUNCTION(gmmktime); -PHP_FUNCTION(date); PHP_FUNCTION(idate); -PHP_FUNCTION(gmdate); PHP_FUNCTION(localtime); PHP_FUNCTION(getdate); PHP_FUNCTION(checkdate); diff --git a/ext/standard/tests/time/bug27719.phpt b/ext/standard/tests/time/bug27719.phpt index 2484356aaa7..c8a51bea1e8 100644 --- a/ext/standard/tests/time/bug27719.phpt +++ b/ext/standard/tests/time/bug27719.phpt @@ -10,7 +10,7 @@ Bug #27719: mktime returns incorrect timestamp for dst days echo "$b ".date("m/d/y h:i:s\n",$b); echo "$c ".date("m/d/y h:i:s\n",$c); echo "\n"; - putenv("TZ=EST5DST"); // DST not in effect + putenv("TZ=EST5EDT"); // DST not in effect $a = mktime(0, 0, 0, 2, 4, 2004, 0); $b = mktime(0, 0, 0, 2, 4, 2004, 1); $c = mktime(0, 0, 0, 2, 4, 2004, -1); @@ -18,7 +18,7 @@ Bug #27719: mktime returns incorrect timestamp for dst days echo "$b ".date("m/d/y h:i:s\n",$b); echo "$c ".date("m/d/y h:i:s\n",$c); echo "\n"; - putenv("TZ=EST5DST"); // Just before DST changeover + putenv("TZ=EST5EDT"); // Just before DST changeover $a = mktime(0, 0, 0, 4, 4, 2004, 0); $b = mktime(0, 0, 0, 4, 4, 2004, 1); $c = mktime(0, 0, 0, 4, 4, 2004, -1); @@ -26,7 +26,7 @@ Bug #27719: mktime returns incorrect timestamp for dst days echo "$b ".date("m/d/y h:i:s\n",$b); echo "$c ".date("m/d/y h:i:s\n",$c); echo "\n"; - putenv("TZ=EST5DST"); // Just after DST changeover + putenv("TZ=EST5EDT"); // Just after DST changeover $a = mktime(3, 0, 0, 4, 4, 2004, 0); $b = mktime(3, 0, 0, 4, 4, 2004, 1); $c = mktime(3, 0, 0, 4, 4, 2004, -1); @@ -34,7 +34,7 @@ Bug #27719: mktime returns incorrect timestamp for dst days echo "$b ".date("m/d/y h:i:s\n",$b); echo "$c ".date("m/d/y h:i:s\n",$c); echo "\n"; - putenv("TZ=EST5DST"); // DST in effect + putenv("TZ=EST5EDT"); // DST in effect $a = mktime(0, 0, 0, 6, 4, 2004, 0); $b = mktime(0, 0, 0, 6, 4, 2004, 1); $c = mktime(0, 0, 0, 6, 4, 2004, -1);