php-src/ext/standard/datetime.c

594 lines
16 KiB
C
Raw Normal View History

/*
+----------------------------------------------------------------------+
1999-07-16 13:13:16 +00:00
| PHP version 4.0 |
+----------------------------------------------------------------------+
1999-07-16 13:13:16 +00:00
| Copyright (c) 1997, 1998, 1999 The PHP Group |
+----------------------------------------------------------------------+
1999-07-16 13:13:16 +00:00
| This source file is subject to version 2.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_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$ */
#include "php.h"
#include "zend_operators.h"
#include "datetime.h"
#include "snprintf.h"
#include "php_globals.h"
#include <time.h>
1999-07-24 02:43:22 +00:00
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <stdio.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) && !(WIN32||WINNT)
extern time_t timezone;
#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)
#if !(WIN32|WINNT)
1999-07-23 19:51:27 +00:00
extern PHPAPI time_t parsedate(char *p, struct timeval *now);
#endif
1999-07-23 19:51:27 +00:00
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(time)
{
return_value->value.lval = (long) time(NULL);
return_value->type = IS_LONG;
}
void _php3_mktime(INTERNAL_FUNCTION_PARAMETERS, int gm)
{
1999-06-27 21:45:06 +00:00
pval *arguments[7];
struct tm ta, *tn;
time_t t;
int i, gmadjust=0,arg_count = ARG_COUNT(ht);
1999-06-27 21:45:06 +00:00
if (arg_count > 7 || getParametersArray(ht, arg_count, arguments) == FAILURE) {
WRONG_PARAM_COUNT;
}
/* convert supplied arguments to longs */
for (i = 0; i < arg_count; i++) {
convert_to_long(arguments[i]);
}
t=time(NULL);
1999-06-27 21:45:06 +00:00
#ifdef HAVE_TZSET
1999-04-24 20:48:12 +00:00
tzset();
#endif
tn = localtime(&t);
memcpy(&ta,tn,sizeof(struct tm));
1999-06-27 21:45:06 +00:00
ta.tm_isdst = arg_count > 6 ? arguments[6]->value.lval : gm ? 0 : -1;
switch(arg_count) {
1999-06-27 21:45:06 +00:00
case 7:
case 6:
ta.tm_year = arguments[5]->value.lval - ((arguments[5]->value.lval > 1000) ? 1900 : 0);
/* fall-through */
case 5:
ta.tm_mday = arguments[4]->value.lval;
/* fall-through */
case 4:
ta.tm_mon = arguments[3]->value.lval - 1;
/* fall-through */
case 3:
ta.tm_sec = arguments[2]->value.lval;
/* fall-through */
case 2:
ta.tm_min = arguments[1]->value.lval;
/* fall-through */
case 1:
1999-06-15 12:31:29 +00:00
ta.tm_hour = arguments[0]->value.lval;
case 0:
break;
}
1999-06-15 12:31:29 +00:00
t=mktime(&ta); /* Need to do this because of Daylight savings */
tn = localtime(&t);
if (gm) {
#if HAVE_TM_GMTOFF
1999-06-27 21:45:06 +00:00
gmadjust=tn->tm_gmtoff;
1999-06-15 12:31:29 +00:00
#else
1999-06-27 21:45:06 +00:00
gmadjust=timezone;
1999-06-15 12:31:29 +00:00
#endif
1999-06-27 21:45:06 +00:00
ta.tm_hour += gmadjust / 3600;
ta.tm_min += gmadjust % 3600;
1999-06-15 12:31:29 +00:00
}
1999-06-27 21:45:06 +00:00
RETURN_LONG(mktime(&ta));
}
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(mktime)
{
_php3_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(gmmktime)
{
_php3_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
static void
_php3_date(INTERNAL_FUNCTION_PARAMETERS, int gm)
{
pval *format, *timestamp;
time_t the_time;
struct tm *ta;
int i, size = 0, length, h, beat;
char tmp_buff[16];
switch(ARG_COUNT(ht)) {
case 1:
if (getParameters(ht, 1, &format) == FAILURE) {
WRONG_PARAM_COUNT;
}
the_time = time(NULL);
break;
case 2:
if (getParameters(ht, 2, &format, &timestamp) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long(timestamp);
the_time = timestamp->value.lval;
break;
default:
WRONG_PARAM_COUNT;
}
convert_to_string(format);
if (gm) {
ta = gmtime(&the_time);
} else {
ta = localtime(&the_time);
}
if (!ta) { /* that really shouldn't happen... */
php3_error(E_WARNING, "unexpected error in date()");
RETURN_FALSE;
}
for (i = 0; i < format->value.str.len; i++) {
switch (format->value.str.val[i]) {
case 'U': /* seconds since the epoch */
size += 10;
break;
case 'F': /* month, textual, full */
case 'l': /* day (of the week), textual */
case 'T': /* timezone name */
size += 9;
break;
case 'Z': /* timezone offset in seconds */
size += 6;
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 */
size += 3;
break;
case 'y': /* year, numeric, 2 digits */
case 'm': /* month, numeric */
1999-07-20 19:11:32 +00:00
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 */
1999-07-20 16:59:30 +00:00
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 */
size += 2;
break;
case '\\':
if(i < format->value.str.len-1) {
i++;
}
case 'L': /* boolean for leap year */
case 'B': /* Swatch Beat, 3 digits */
size += 3;
break;
case 'w': /* day of the week, numeric */
default:
size++;
break;
}
}
return_value->value.str.val = (char *) emalloc(size + 1);
return_value->value.str.val[0] = '\0';
for (i = 0; i < format->value.str.len; i++) {
switch (format->value.str.val[i]) {
case '\\':
if(i < format->value.str.len-1) {
char ch[2];
ch[0]=format->value.str.val[i+1];
ch[1]='\0';
strcat(return_value->value.str.val, ch);
i++;
}
break;
case 'U': /* seconds since the epoch */
sprintf(tmp_buff, "%ld", (long)the_time); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'F': /* month, textual, full */
strcat(return_value->value.str.val, mon_full_names[ta->tm_mon]);
break;
case 'l': /* day (of the week), textual, full */
strcat(return_value->value.str.val, day_full_names[ta->tm_wday]);
break;
case 'Y': /* year, numeric, 4 digits */
sprintf(tmp_buff, "%d", ta->tm_year + 1900); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'M': /* month, textual, 3 letters */
strcat(return_value->value.str.val, mon_short_names[ta->tm_mon]);
break;
case 'D': /* day (of the week), textual, 3 letters */
strcat(return_value->value.str.val, day_short_names[ta->tm_wday]);
break;
case 'z': /* day (of the year) */
sprintf(tmp_buff, "%d", ta->tm_yday); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'y': /* year, numeric, 2 digits */
sprintf(tmp_buff, "%02d", ((ta->tm_year)%100)); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'm': /* month, numeric */
sprintf(tmp_buff, "%02d", ta->tm_mon + 1); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
1999-07-20 19:11:32 +00:00
case 'n': /* month, numeric, no leading zeros */
sprintf(tmp_buff, "%d", ta->tm_mon + 1); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'd': /* day of the month, numeric */
sprintf(tmp_buff, "%02d", ta->tm_mday); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'j':
sprintf(tmp_buff, "%d", ta->tm_mday); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'H': /* hour, numeric, 24 hour format */
sprintf(tmp_buff, "%02d", ta->tm_hour); /* SAFE */
strcat(return_value->value.str.val, 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(return_value->value.str.val, tmp_buff);
break;
1999-07-20 16:59:30 +00:00
case 'G': /* hour, numeric, 24 hour format, no leading zeros */
sprintf(tmp_buff, "%d", ta->tm_hour); /* SAFE */
strcat(return_value->value.str.val, 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(return_value->value.str.val, tmp_buff);
break;
case 'i': /* minutes, numeric */
sprintf(tmp_buff, "%02d", ta->tm_min); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 's': /* seconds, numeric */
sprintf(tmp_buff, "%02d", ta->tm_sec); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
case 'A': /* AM/PM */
strcat(return_value->value.str.val, (ta->tm_hour >= 12 ? "PM" : "AM"));
break;
case 'a': /* am/pm */
strcat(return_value->value.str.val, (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(return_value->value.str.val, "th");
} else {
switch (ta->tm_mday % 10) {
case 1:
strcat(return_value->value.str.val, "st");
break;
case 2:
strcat(return_value->value.str.val, "nd");
break;
case 3:
strcat(return_value->value.str.val, "rd");
break;
default:
strcat(return_value->value.str.val, "th");
break;
}
}
break;
case 't': /* days in current month */
sprintf(tmp_buff, "%2d", phpday_tab[isleap((ta->tm_year+1900))][ta->tm_mon] );
strcat(return_value->value.str.val, tmp_buff);
break;
case 'w': /* day of the week, numeric EXTENSION */
sprintf(tmp_buff, "%01d", ta->tm_wday); /* SAFE */
strcat(return_value->value.str.val, 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", timezone);
#endif
strcat(return_value->value.str.val, tmp_buff);
break;
case 'L': /* boolean for leapyear */
sprintf(tmp_buff, "%d", (isleap((ta->tm_year+1900)) ? 1 : 0 ) );
strcat(return_value->value.str.val, tmp_buff);
break;
case 'T': /* timezone name */
#if HAVE_TM_ZONE
strcat(return_value->value.str.val, ta->tm_zone);
#else
strcat(return_value->value.str.val, tzname[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);
if (beat > 999) beat = 0;
sprintf(tmp_buff, "%03d", beat); /* SAFE */
strcat(return_value->value.str.val, tmp_buff);
break;
default:
length = strlen(return_value->value.str.val);
return_value->value.str.val[length] = format->value.str.val[i];
return_value->value.str.val[length + 1] = '\0';
break;
}
}
return_value->value.str.len = strlen(return_value->value.str.val);
return_value->type = IS_STRING;
}
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(date)
{
_php3_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(gmdate)
{
_php3_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(getdate)
{
pval *timestamp_arg;
struct tm *ta;
time_t timestamp;
if (ARG_COUNT(ht) == 0) {
timestamp = time(NULL);
} else if (ARG_COUNT(ht) != 1 || getParameters(ht, 1, &timestamp_arg) == FAILURE) {
WRONG_PARAM_COUNT;
} else {
convert_to_long(timestamp_arg);
timestamp = timestamp_arg->value.lval;
}
ta = localtime(&timestamp);
if (!ta) {
php3_error(E_WARNING, "Cannot perform date calculation");
return;
}
if (array_init(return_value) == FAILURE) {
php3_error(E_ERROR, "Unable to initialize array");
return;
}
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);
}
/* Return date string in standard format for http headers */
char *php3_std_date(time_t t)
{
struct tm *tm1;
char *str;
PLS_FETCH();
tm1 = gmtime(&t);
str = emalloc(81);
if (PG(y2k_compliance)) {
snprintf(str, 80, "%s, %02d-%s-%04d %02d:%02d:%02d GMT",
day_full_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_full_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);
}
/*
* CheckDate(month, day, year);
* returns True(1) if it is valid date
*
*/
#define isleap(year) (((year%4) == 0 && (year%100)!=0) || (year%400)==0)
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(checkdate)
{
pval *month, *day, *year;
int m, d, y;
if (ARG_COUNT(ht) != 3 ||
getParameters(ht, 3, &month, &day, &year) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_long(day);
convert_to_long(month);
convert_to_long(year);
y = year->value.lval;
m = month->value.lval;
d = day->value.lval;
if (y < 100)
y += 1900;
if (y < 0 || y > 32767) {
RETURN_FALSE;
}
if (m < 1 || m > 12) {
RETURN_FALSE;
}
if (d < 1 || d > phpday_tab[isleap(y)][m - 1]) {
RETURN_FALSE;
}
RETURN_TRUE; /* True : This month,day,year arguments are valid */
}
#if HAVE_STRFTIME
1999-05-16 11:19:26 +00:00
PHP_FUNCTION(strftime)
{
pval *format_arg, *timestamp_arg;
char *format,*buf;
time_t timestamp;
struct tm *ta;
1999-04-21 17:11:01 +00:00
int max_reallocs = 5;
size_t buf_len=64, real_len;
switch (ARG_COUNT(ht)) {
case 1:
if (getParameters(ht, 1, &format_arg)==FAILURE) {
RETURN_FALSE;
}
time(&timestamp);
break;
case 2:
if (getParameters(ht, 2, &format_arg, &timestamp_arg)==FAILURE) {
RETURN_FALSE;
}
convert_to_long(timestamp_arg);
timestamp = timestamp_arg->value.lval;
break;
default:
WRONG_PARAM_COUNT;
break;
}
convert_to_string(format_arg);
if (format_arg->value.str.len==0) {
RETURN_FALSE;
}
format = format_arg->value.str.val;
ta = localtime(&timestamp);
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);
1999-04-21 17:11:01 +00:00
if(!--max_reallocs) break;
}
1999-04-21 17:11:01 +00:00
if(real_len && real_len != buf_len) {
buf = (char *) erealloc(buf,real_len+1);
RETURN_STRINGL(buf, real_len, 0);
}
efree(buf);
RETURN_FALSE;
}
#endif
1999-07-23 19:51:27 +00:00
#if !(WIN32|WINNT)
1999-07-23 19:51:27 +00:00
/* {{{ proto int strtotime(string time, int now) */
PHP_FUNCTION(strtotime)
{
pval *timep, *nowp;
int ac;
struct timeval tv;
ac = ARG_COUNT(ht);
if (ac < 1 || ac > 2 || getParameters(ht, ac, &timep, &nowp)==FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string(timep);
if (ac == 2) {
convert_to_long(nowp);
tv.tv_sec = nowp->value.lval;
tv.tv_usec = 0;
RETURN_LONG(parsedate(timep->value.str.val, &tv));
} else {
RETURN_LONG(parsedate(timep->value.str.val, NULL));
}
}
#endif
1999-07-23 19:51:27 +00:00
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/