php-src/ext/standard/datetime.c

1145 lines
32 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Rasmus Lerdorf <rasmus@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#if HAVE_STRPTIME
#define _XOPEN_SOURCE
#endif
#include "php.h"
#include "zend_operators.h"
#include "datetime.h"
#include "php_globals.h"
#include <time.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <stdio.h>
#include "php_parsedate.h"
char *mon_full_names[] = {
"January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
};
char *mon_short_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
char *day_full_names[] = {
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
char *day_short_names[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
#if !defined(HAVE_TM_ZONE) && !defined(_TIMEZONE) && !defined(HAVE_DECLARED_TIMEZONE)
#ifdef NETWARE
#define timezone _timezone /* timezone is called '_timezone' in new version of LibC */
#endif
extern time_t timezone;
extern int daylight;
#endif
static int phpday_tab[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
#define isleap(year) ((((year) % 4) == 0 && ((year) % 100) != 0) || ((year) % 400)==0)
#define YEAR_BASE 1900
/* {{{ proto int time(void)
Return current UNIX timestamp */
PHP_FUNCTION(time)
{
RETURN_LONG((long)time(NULL));
}
/* }}} */
/* {{{ php_mktime
*/
void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm)
{
pval **arguments[7];
struct tm *ta, tmbuf;
time_t t, seconds;
int i, gmadjust, arg_count = ZEND_NUM_ARGS();
int is_dst = -1, chgsecs = 0;
long val;
if (arg_count > 7 || zend_get_parameters_array_ex(arg_count, arguments) == FAILURE) {
WRONG_PARAM_COUNT;
}
/* convert supplied arguments to longs */
for (i = 0; i < arg_count; i++) {
convert_to_long_ex(arguments[i]);
}
t = time(NULL);
#ifdef HAVE_TZSET
tzset();
#endif
/*
** Set default time parameters with local time values,
** EVEN when some GMT time parameters are specified!
** This may give strange result, with PHP gmmktime(0, 0, 0),
** which is assumed to return GMT midnight time
** for today (in localtime), so that the result time may be
** AFTER or BEFORE the current time.
** May be we should initialize tn using gmtime(), so that
** default parameters for PHP gmmktime would be the current
** GMT time values...
*/
ta = php_localtime_r(&t, &tmbuf);
/* Let DST be unknown. mktime() should compute the right value
** and behave correctly. Unless the user overrides this.
*/
ta->tm_isdst = -1;
/*
** Now change date values with supplied parameters.
*/
switch(arg_count) {
case 7: /* daylight saving time flag */
#ifdef PHP_WIN32
if (daylight > 0) {
ta->tm_isdst = is_dst = Z_LVAL_PP(arguments[6]);
} else {
ta->tm_isdst = is_dst = 0;
}
#else
ta->tm_isdst = is_dst = Z_LVAL_PP(arguments[6]);
#endif
/* fall-through */
case 6: /* year */
/* special case:
a zero in year, month and day is considered illegal
as it would be interpreted as 30.11.1999 otherwise
*/
if ( ( Z_LVAL_PP(arguments[5])==0)
&&(Z_LVAL_PP(arguments[4])==0)
&&(Z_LVAL_PP(arguments[3])==0)
) {
RETURN_LONG(-1);
}
/*
** Accept parameter in range 0..1000 interpreted as 1900..2900
** (if 100 is given, it means year 2000)
** or in range 1001..9999 interpreted as is (this will store
** negative tm_year for years in range 1001..1899)
** This function is then Y2K ready, and accepts a wide range of
** dates including the whole gregorian calendar.
** But it cannot represent ancestral dates prior to year 1001.
** Additionally, input parameters of 0..70 are mapped to 100..170
*/
if (Z_LVAL_PP(arguments[5]) < 70)
ta->tm_year = Z_LVAL_PP(arguments[5]) + 100;
else
ta->tm_year = Z_LVAL_PP(arguments[5])
- ((Z_LVAL_PP(arguments[5]) > 1000) ? 1900 : 0);
/* fall-through */
case 5: /* day in month (1-based) */
val = (*arguments[4])->value.lval;
if (val < 1) {
chgsecs += (1-val) * 60*60*24;
val = 1;
}
ta->tm_mday = val;
/* fall-through */
case 4: /* month (zero-based) */
val = (*arguments[3])->value.lval - 1;
while (val < 0) {
val += 12; ta->tm_year--;
}
ta->tm_mon = val;
/* fall-through */
case 3: /* second */
val = (*arguments[2])->value.lval;
if (val < 1) {
chgsecs += (1-val); val = 1;
}
ta->tm_sec = val;
/* fall-through */
case 2: /* minute */
val = (*arguments[1])->value.lval;
if (val < 1) {
chgsecs += (1-val) * 60; val = 1;
}
ta->tm_min = val;
/* fall-through */
case 1: /* hour */
val = (*arguments[0])->value.lval;
/*
We avoid midnight and a couple of hours after midnight here to work around
various OS-level bugs in mktime and specifically daylight savings time issues
in many mktime implementation.
See bugs #27533 and #27719 for more info.
*/
if (val < 4) {
chgsecs += (4-val) * 60*60; val = 4;
}
ta->tm_hour = val;
/* fall-through */
case 0:
break;
}
t = mktime(ta);
#ifdef PHP_WIN32
if (t - chgsecs < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Windows does not support negative values for this function");
RETURN_LONG(-1);
}
#endif
seconds = t - chgsecs;
/*
Here we check to see if the chgsecs fuzz factor we applied caused us to
move from dst to non-dst or vice-versa. If so we adjust accordingly to
avoid being off by an hour on the dst changeover date.
*/
if (is_dst == -1) {
struct tm t1, t2;
t1 = *localtime(&t);
t2 = *localtime(&seconds);
if (t1.tm_isdst != t2.tm_isdst) {
seconds += (t1.tm_isdst == 1) ? 3600 : -3600;
ta = localtime(&seconds);
}
/*
If the user didn't specify whether the timestamp passed in was dst or not
then we fill it in based on the dst setting at the evaluated timestamp
at the current TZ
*/
is_dst = ta->tm_isdst;
}
if (gm) {
#if HAVE_TM_GMTOFF
/*
** mktime(ta) very nicely just filled ta->tm_gmtoff with
** the exactly right value for adjustment if we want GMT.
*/
gmadjust = ta->tm_gmtoff;
#else
/*
** If correcting for daylight savings time, we set the adjustment to
** the value of timezone - 3600 seconds.
*/
#ifdef __CYGWIN__
gmadjust = -(is_dst ? _timezone - 3600 : _timezone);
#else
gmadjust = -(is_dst ? timezone - 3600 : timezone);
#endif
#endif
seconds += gmadjust;
}
RETURN_LONG(seconds);
}
/* }}} */
/* {{{ proto int mktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a date */
PHP_FUNCTION(mktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto int gmmktime(int hour, int min, int sec, int mon, int day, int year)
Get UNIX timestamp for a GMT date */
PHP_FUNCTION(gmmktime)
{
php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
/* {{{ 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, &timestamp) == 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
*/
int php_idate(char format, int timestamp, int gm)
{
time_t the_time;
struct tm *ta, tmbuf;
int h, beat, fd, wd, yd, wk;
#if !HAVE_TM_GMTOFF
long tzone;
char *tname[2]= {"GMT Standard Time", "BST"};
#endif
the_time = timestamp;
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
tname[0] = tzname[0];
#endif
}
switch (format) {
case 'U': /* seconds since the epoch */
return (long)the_time;
case 'Y': /* year, numeric, 4 digits */
return ta->tm_year + YEAR_BASE;
case 'z': /* day (of the year) */
return ta->tm_yday;
case 'y': /* year, numeric, 2 digits */
return (ta->tm_year) % 100;
case 'm': /* month, numeric */
case 'n':
return ta->tm_mon + 1;
case 'd': /* day of the month, numeric */
case 'j':
return ta->tm_mday;
case 'H': /* hour, numeric, 24 hour format */
case 'G':
return ta->tm_hour;
case 'h': /* hour, numeric, 12 hour format */
case 'g':
h = ta->tm_hour % 12;
if (h == 0) {
h = 12;
}
return h;
case 'i': /* minutes, numeric */
return ta->tm_min;
case 's': /* seconds, numeric */
return ta->tm_sec;
case 't': /* days in current month */
return phpday_tab[isleap((ta->tm_year + YEAR_BASE))][ta->tm_mon];
case 'w': /* day of the week, numeric EXTENSION */
return ta->tm_wday;
case 'Z': /* timezone offset in seconds */
#if HAVE_TM_GMTOFF
return ta->tm_gmtoff;
#else
return ta->tm_isdst ? -(tzone - 3600) : -tzone;
#endif
case 'L': /* boolean for leapyear */
return isleap(ta->tm_year + YEAR_BASE) ? 1 : 0;
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;
return beat;
case 'I':
return ta->tm_isdst;
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) */
if ((yd <= 7 - fd) && fd > 3) { /* week is a last year week (52 or 53) */
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);
}
return wk;
break;
default:
return 0;
}
}
/* }}} */
/* {{{ proto int idate(string format [, int timestamp])
Format a local time/date as integer */
PHP_FUNCTION(idate)
{
zval **format, **timestamp;
int t, ret;
switch (ZEND_NUM_ARGS()) {
case 1:
if (zend_get_parameters_ex(1, &format) == FAILURE) {
WRONG_PARAM_COUNT;
}
t = time(NULL);
break;
case 2:
if (zend_get_parameters_ex(2, &format, &timestamp) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long_ex(timestamp);
t = Z_LVAL_PP(timestamp);
break;
default:
WRONG_PARAM_COUNT;
}
convert_to_string_ex(format);
if (Z_STRLEN_PP(format) != 1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "idate format is one char");
RETURN_FALSE;
}
ret = php_idate(Z_STRVAL_PP(format)[0], t, 0);
RETURN_LONG(ret);
}
/* }}} */
/* {{{ proto array localtime([int timestamp [, bool associative_array]])
Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array */
PHP_FUNCTION(localtime)
{
zval **timestamp_arg, **assoc_array_arg;
struct tm *ta, tmbuf;
time_t timestamp;
int assoc_array = 0;
int arg_count = ZEND_NUM_ARGS();
if (arg_count < 0 || arg_count > 2 ||
zend_get_parameters_ex(arg_count, &timestamp_arg, &assoc_array_arg) == FAILURE) {
WRONG_PARAM_COUNT;
}
switch (arg_count) {
case 0:
timestamp = (long)time(NULL);
break;
case 1:
convert_to_long_ex(timestamp_arg);
timestamp = Z_LVAL_PP(timestamp_arg);
break;
case 2:
convert_to_long_ex(timestamp_arg);
convert_to_long_ex(assoc_array_arg);
timestamp = Z_LVAL_PP(timestamp_arg);
assoc_array = Z_LVAL_PP(assoc_array_arg);
break;
}
#ifdef PHP_WIN32
if (timestamp < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Windows does not support negative values for this function");
RETURN_FALSE
}
#endif
if (NULL == (ta = php_localtime_r(&timestamp, &tmbuf))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid local time");
RETURN_FALSE;
}
array_init(return_value);
if (assoc_array) {
add_assoc_long(return_value, "tm_sec", ta->tm_sec);
add_assoc_long(return_value, "tm_min", ta->tm_min);
add_assoc_long(return_value, "tm_hour", ta->tm_hour);
add_assoc_long(return_value, "tm_mday", ta->tm_mday);
add_assoc_long(return_value, "tm_mon", ta->tm_mon);
add_assoc_long(return_value, "tm_year", ta->tm_year);
add_assoc_long(return_value, "tm_wday", ta->tm_wday);
add_assoc_long(return_value, "tm_yday", ta->tm_yday);
add_assoc_long(return_value, "tm_isdst", ta->tm_isdst);
} else {
add_next_index_long(return_value, ta->tm_sec);
add_next_index_long(return_value, ta->tm_min);
add_next_index_long(return_value, ta->tm_hour);
add_next_index_long(return_value, ta->tm_mday);
add_next_index_long(return_value, ta->tm_mon);
add_next_index_long(return_value, ta->tm_year);
add_next_index_long(return_value, ta->tm_wday);
add_next_index_long(return_value, ta->tm_yday);
add_next_index_long(return_value, ta->tm_isdst);
}
}
/* }}} */
/* {{{ proto array getdate([int timestamp])
Get date/time information */
PHP_FUNCTION(getdate)
{
pval **timestamp_arg;
struct tm *ta, tmbuf;
time_t timestamp;
if (ZEND_NUM_ARGS() == 0) {
timestamp = time(NULL);
} else if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &timestamp_arg) == FAILURE) {
WRONG_PARAM_COUNT;
} else {
convert_to_long_ex(timestamp_arg);
timestamp = Z_LVAL_PP(timestamp_arg);
}
ta = php_localtime_r(&timestamp, &tmbuf);
if (!ta) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot perform date calculation");
return;
}
array_init(return_value);
add_assoc_long(return_value, "seconds", ta->tm_sec);
add_assoc_long(return_value, "minutes", ta->tm_min);
add_assoc_long(return_value, "hours", ta->tm_hour);
add_assoc_long(return_value, "mday", ta->tm_mday);
add_assoc_long(return_value, "wday", ta->tm_wday);
add_assoc_long(return_value, "mon", ta->tm_mon + 1);
add_assoc_long(return_value, "year", ta->tm_year + 1900);
add_assoc_long(return_value, "yday", ta->tm_yday);
add_assoc_string(return_value, "weekday", day_full_names[ta->tm_wday], 1);
add_assoc_string(return_value, "month", mon_full_names[ta->tm_mon], 1);
add_index_long(return_value, 0, timestamp);
}
/* }}} */
/* {{{ php_std_date
Return date string in standard format for http headers */
char *php_std_date(time_t t TSRMLS_DC)
{
struct tm *tm1, tmbuf;
char *str;
tm1 = php_gmtime_r(&t, &tmbuf);
str = emalloc(81);
if (PG(y2k_compliance)) {
snprintf(str, 80, "%s, %02d %s %04d %02d:%02d:%02d GMT",
day_short_names[tm1->tm_wday],
tm1->tm_mday,
mon_short_names[tm1->tm_mon],
tm1->tm_year + 1900,
tm1->tm_hour, tm1->tm_min, tm1->tm_sec);
} else {
snprintf(str, 80, "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
day_short_names[tm1->tm_wday],
tm1->tm_mday,
mon_short_names[tm1->tm_mon],
((tm1->tm_year) % 100),
tm1->tm_hour, tm1->tm_min, tm1->tm_sec);
}
str[79] = 0;
return (str);
}
/* }}} */
/* {{{ proto bool checkdate(int month, int day, int year)
Returns true(1) if it is a valid date in gregorian calendar */
PHP_FUNCTION(checkdate)
{
long m, d, y;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &m, &d, &y) == FAILURE) {
RETURN_FALSE;
}
if (y < 1 || y > 32767 || m < 1 || m > 12 || d < 1 || d > phpday_tab[isleap(y)][m - 1]) {
RETURN_FALSE;
}
RETURN_TRUE; /* True : This month, day, year arguments are valid */
}
/* }}} */
#if HAVE_STRFTIME
/* {{{ _php_strftime
*/
void _php_strftime(INTERNAL_FUNCTION_PARAMETERS, int gm)
{
pval **format_arg, **timestamp_arg;
char *format, *buf;
time_t timestamp;
struct tm *ta, tmbuf;
int max_reallocs = 5;
size_t buf_len=64, real_len;
switch (ZEND_NUM_ARGS()) {
case 1:
if (zend_get_parameters_ex(1, &format_arg)==FAILURE) {
RETURN_FALSE;
}
time(&timestamp);
break;
case 2:
if (zend_get_parameters_ex(2, &format_arg, &timestamp_arg)==FAILURE) {
RETURN_FALSE;
}
convert_to_long_ex(timestamp_arg);
timestamp = Z_LVAL_PP(timestamp_arg);
break;
default:
WRONG_PARAM_COUNT;
break;
}
convert_to_string_ex(format_arg);
if (Z_STRLEN_PP(format_arg)==0) {
RETURN_FALSE;
}
#ifdef PHP_WIN32
if (timestamp < 0) {
RETURN_FALSE;
}
#endif
format = Z_STRVAL_PP(format_arg);
if (gm) {
ta = php_gmtime_r(&timestamp, &tmbuf);
} else {
ta = php_localtime_r(&timestamp, &tmbuf);
}
buf = (char *) emalloc(buf_len);
while ((real_len=strftime(buf, buf_len, format, ta))==buf_len || real_len==0) {
buf_len *= 2;
buf = (char *) erealloc(buf, buf_len);
if (!--max_reallocs) {
break;
}
}
if (real_len && real_len != buf_len) {
buf = (char *) erealloc(buf, real_len + 1);
RETURN_STRINGL(buf, real_len, 0);
}
efree(buf);
RETURN_FALSE;
}
/* }}} */
/* {{{ proto string strftime(string format [, int timestamp])
Format a local time/date according to locale settings */
PHP_FUNCTION(strftime)
{
_php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto string gmstrftime(string format [, int timestamp])
Format a GMT/UCT time/date according to locale settings */
PHP_FUNCTION(gmstrftime)
{
_php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
#endif
/* {{{ proto int strtotime(string time, int now)
Convert string representation of date and time to a timestamp */
PHP_FUNCTION(strtotime)
{
zval **z_time, **z_now;
int argc;
time_t now;
argc = ZEND_NUM_ARGS();
if (argc < 1 || argc > 2 || zend_get_parameters_ex(argc, &z_time, &z_now)==FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string_ex(z_time);
if (!Z_STRLEN_PP(z_time)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Called with an empty time parameter.");
RETURN_LONG(-1);
}
if (argc == 2) {
convert_to_long_ex(z_now);
now = Z_LVAL_PP(z_now);
RETURN_LONG(php_parse_date(Z_STRVAL_PP(z_time), &now));
} else {
RETURN_LONG(php_parse_date(Z_STRVAL_PP(z_time), NULL));
}
}
/* }}} */
#if HAVE_STRPTIME
/* {{{ proto string strptime(string timestamp, string format)
Parse a time/date generated with strftime() */
PHP_FUNCTION(strptime)
{
char *ts;
int ts_length;
char *format;
int format_length;
struct tm parsed_time;
char *unparsed_part;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
&ts, &ts_length, &format, &format_length) == FAILURE) {
return;
}
unparsed_part = strptime(ts, format, &parsed_time);
if (unparsed_part == NULL) {
RETURN_FALSE;
}
array_init(return_value);
add_assoc_long(return_value, "tm_sec", parsed_time.tm_sec);
add_assoc_long(return_value, "tm_min", parsed_time.tm_min);
add_assoc_long(return_value, "tm_hour", parsed_time.tm_hour);
add_assoc_long(return_value, "tm_mday", parsed_time.tm_mday);
add_assoc_long(return_value, "tm_mon", parsed_time.tm_mon);
add_assoc_long(return_value, "tm_year", parsed_time.tm_year);
add_assoc_long(return_value, "tm_wday", parsed_time.tm_wday);
add_assoc_long(return_value, "tm_yday", parsed_time.tm_yday);
add_assoc_string(return_value, "unparsed", unparsed_part, 1);
}
/* }}} */
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 fdm=marker
* vim<600: sw=4 ts=4
*/