php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #8828 mktime using mday<=0
Submitted: 2001-01-21 08:06 UTC Modified: 2002-06-12 11:37 UTC
From: dieter at fiebelkorn dot net Assigned:
Status: Closed Package: Date/time related
PHP Version: 4.0.4pl1 OS: MacOS X (Darwin 1.2)
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: dieter at fiebelkorn dot net
New email:
PHP Version: OS:

 

 [2001-01-21 08:06 UTC] dieter at fiebelkorn dot net
On PHP documantation:
mktime(hour,min,sec, year,0,mon) refers the last day of 
month 'mon-1'. On MacOS X this failed, there it refers 
the first day of month 'mon' and mktime(hour,min,sec, 
year,-1,mon) refers the last day of month 'mon-1'.

See the test:

[aragorn:~/Downloads] dieter% cat A.c
#include <stdio.h>
#include <time.h>

main()
{
        struct tm mytm;
        int i;

        mytm.tm_sec = 0; mytm.tm_min = 0;
        mytm.tm_hour = 0; mytm.tm_year = 101;
        mytm.tm_wday = 0; mytm.tm_yday = 0;
        mytm.tm_isdst = 0; mytm.tm_gmtoff = 0;
        mytm.tm_zone = 0;

        for (i = 2; i >= -2; i--)
        {
                mytm.tm_mday = i;
                mytm.tm_mon = 2;
                printf ("%02d.%02d ",
                           mytm.tm_mday, mytm.tm_mon+1);
                printf (" --> %ld", mktime(&mytm));
                printf (" --> %02d.%02d\n",
                           mytm.tm_mday, mytm.tm_mon+1);
        }
}
[aragorn:~/Downloads] dieter% cc -o A A.c
[aragorn:~/Downloads] dieter% ./A
02.03  --> 983487600 --> 02.03
01.03  --> 983401200 --> 01.03
00.03  --> 983401200 --> 01.03
-1.03  --> 983314800 --> 28.02
-2.03  --> 983228400 --> 27.02
[aragorn:~/Downloads] dieter% 

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2001-01-22 22:32 UTC] jimw@php.net
macos x is behaving in a non-standard way. we just use the underlying mktime() function.

you might try reporting it to the darwin developers.

http://www.opensource.apple.com/projects/bugs.html
 [2001-01-27 07:26 UTC] dieter at fiebelkorn dot net
I have send a bug report to darwin-developers and 
apple, too, but

i have check some UNIX-systems and i haven't found 
any man-page, where is describe what should be 
happend on "tm_mday" <= 0.
IMHO this is a undocumented feature, which is used by 
PHP.

man mktime (Linux 2.2.13)
------------------------------------
       tm_mday
              The day of the month, in the range 1 to 31.

Sun Release 4.1
----------------------
       int tm_mday;     /* day of month (1 - 31) */


SunOS 5.5
---------------
       int  tm_mday;       /* day of the month - [1, 31] */

HP-UX Release 10.20
------------------------------
       int tm_mday;     /* day of month - [1,31] */


HP-UX Release 11.00
-------------------------------
       int tm_mday;     /* day of month - [1,31] */

AIX 4.3.2
------------

       int tm_mday;    /* Day of month (1 - 31) */


Darwin 1.0.2
-----------------
       int tm_mday;     /* day of month (1 - 31) */

I think i should by much better to check mktime() on 
configure and set a #define for compilation. Only on 
"datetime.c" must be made a patch to support MacOS 
X's / Darwin's mktime()-systemcall, i think!

 [2001-03-14 13:34 UTC] jason@php.net
This actually is part of the Ansi C standard.

Solaris 8 manpage -----------------------

The original values of the components may be either greater than or less than the specified range. For example, a tm_hour of -1 means 1 hour before midnight, tm_mday of 0 means the day preceding the current month, and tm_mon of -2 means 2 months before January of tm_year


---------------------------------------

Alg from c standard
----------------------------------------

#define QUOT(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b)))
#define REM(a,b) ((a)-(b)*QUOT(a,b))

SS = tm_hour*3600 + tm_min*60 + tm_sec +
  (tm_leapsecs == _NO_LEAP_SECONDS ? X1 :
                 tm_leapsecs) -
  (tm_zone == _LOCALTIME ? X2 : tm_zone) * 60;

// X1 is the appropriate number of leap seconds, determined by
// the implementation, or 0 if it cannot be determined. 
// X2 is the appropriate offset from local time to UTC,
// determined by the implementation, or

// (tm_isdst >= 0 ? tm_isdst : 0)
// if the offset cannot be determined

         M = REM(tm_mon, 12);
         Y = tm_year + 1900 + QUOT(tm_mon, 12);
         Z = Y - (M < 2 ? 1 : 0);
         D = Y*365 + (Z/400)*97 + (Z%400)/4 +
         M[(int []){0,31,59,90,120,151,181,212,243,273,
                               304,335}] +
        tm_mday + QUOT(SS, 86400);
               S = REM(SS, 86400);
------------------------------------------------------
Mac OS X is not following this standard

-Jason

 [2001-03-14 15:56 UTC] jason@php.net
I was incorrect, This is not a part of C99(ANSI/ISO/IEC9899-1999) this is just implemented in Solaris, Linux, Irix, and possibly a few others.

-Jason
 [2001-04-15 10:59 UTC] dieter at fiebelkorn dot net
FIRST patch to make php_mktime() more compatible [i think it is not complete for the whole PHP4-project]. It includes a time correction for values less then 1 on "mday", "hour", "min", "sec". NOT on "mon"!!


*** ext/standard/datetime.c.orig        Fri Dec  8 12:38:02 2000
--- ext/standard/datetime.c     Sun Apr 15 16:42:37 2001
***************
*** 81,87 ****
        struct tm *ta, tmbuf;
        time_t t;
        int i, gmadjust, seconds, arg_count = ZEND_NUM_ARGS();
!       int is_dst = -1;
  
        if (arg_count > 7 || zend_get_parameters_array_ex(arg_count,arguments) == FAILURE) {
                WRONG_PARAM_COUNT;
--- 81,87 ----
        struct tm *ta, tmbuf;
        time_t t;
        int i, gmadjust, seconds, arg_count = ZEND_NUM_ARGS();
!       int is_dst = -1, val, chgsecs = 0;
  
        if (arg_count > 7 || zend_get_parameters_array_ex(arg_count,arguments) == FAILURE) {
                WRONG_PARAM_COUNT;
***************
*** 148,172 ****
                          - (((*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:
!               ta->tm_hour = (*arguments[0])->value.lval;
                /* fall-through */
        case 0:
                break;
        }
  
!       seconds = mktime(ta);
        if (is_dst == -1)
                is_dst = ta->tm_isdst;
  
--- 148,180 ----
                          - (((*arguments[5])->value.lval > 1000) ? 1900 : 0);
                /* fall-through */
        case 5:
!               val = (*arguments[4])->value.lval;
!               if (val < 1) { chgsecs += (1-val) * 60*60*24; val = 1; }
!               ta->tm_mday = val;
                /* fall-through */
        case 4:
                ta->tm_mon = (*arguments[3])->value.lval - 1;
                /* fall-through */
        case 3:
!               val = (*arguments[2])->value.lval;
!               if (val < 1) { chgsecs += (1-val); val = 1; }
!               ta->tm_sec = val;
                /* fall-through */
        case 2:
!               val = (*arguments[1])->value.lval;
!               if (val < 1) { chgsecs += (1-val) * 60; val = 1; }
!               ta->tm_min = val;
                /* fall-through */
        case 1:
!               val = (*arguments[0])->value.lval;
!               if (val < 1) { chgsecs += (1-val) * 60*60; val = 1; }
!               ta->tm_hour = val;
                /* fall-through */
        case 0:
                break;
        }
  
!       seconds = mktime(ta) - chgsecs;
        if (is_dst == -1)
                is_dst = ta->tm_isdst;

 [2001-04-16 03:20 UTC] dieter at fiebelkorn dot net
Now for tm_mon < 0, too:

*** ext/standard/datetime.c.orig	Fri Dec  8 12:38:02 2000
--- ext/standard/datetime.c	Sun Apr 15 17:28:46 2001
***************
*** 81,87 ****
  	struct tm *ta, tmbuf;
  	time_t t;
  	int i, gmadjust, seconds, arg_count = ZEND_NUM_ARGS();
! 	int is_dst = -1;
  
  	if (arg_count > 7 || 
zend_get_parameters_array_ex(arg_count,arguments) == 
FAILURE) {
  		WRONG_PARAM_COUNT;
--- 81,87 ----
  	struct tm *ta, tmbuf;
  	time_t t;
  	int i, gmadjust, seconds, arg_count = ZEND_NUM_ARGS();
! 	int is_dst = -1, val, chgsecs = 0;
  
  	if (arg_count > 7 || 
zend_get_parameters_array_ex(arg_count,arguments) == 
FAILURE) {
  		WRONG_PARAM_COUNT;
***************
*** 148,172 ****
  			  - (((*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:
! 		ta->tm_hour = (*arguments[0])->value.lval;
  		/* fall-through */
  	case 0:
  		break;
  	}
  
! 	seconds = mktime(ta);
  	if (is_dst == -1)
  		is_dst = ta->tm_isdst;
  
--- 148,182 ----
  			  - (((*arguments[5])->value.lval > 1000) ? 1900 : 0);
  		/* fall-through */
  	case 5:
! 		val = (*arguments[4])->value.lval;
! 		if (val < 1) { chgsecs += (1-val) * 60*60*24; val = 1; 
}
! 		ta->tm_mday = val;
  		/* fall-through */
  	case 4:
! 		val = (*arguments[3])->value.lval - 1;
! 		while (val < 0) { val += 12; ta->tm_year--; }
! 		ta->tm_mon = val;
  		/* fall-through */
  	case 3:
! 		val = (*arguments[2])->value.lval;
! 		if (val < 1) { chgsecs += (1-val); val = 1; }
! 		ta->tm_sec = val;
  		/* fall-through */
  	case 2:
! 		val = (*arguments[1])->value.lval;
! 		if (val < 1) { chgsecs += (1-val) * 60; val = 1; }
! 		ta->tm_min = val;
  		/* fall-through */
  	case 1:
! 		val = (*arguments[0])->value.lval;
! 		if (val < 1) { chgsecs += (1-val) * 60*60; val = 1; }
! 		ta->tm_hour = val;
  		/* fall-through */
  	case 0:
  		break;
  	}
  
! 	seconds = mktime(ta) - chgsecs;
  	if (is_dst == -1)
  		is_dst = ta->tm_isdst;
  

 [2001-05-07 12:54 UTC] derick@php.net
Dup of #10686
 [2002-06-12 11:37 UTC] hholzgra@php.net
fixed in latest CVS, should work on all OS/libc versions now
as negative offsets are now taken care of internaly so libc mktime() won't receive any negative (or 0 for tm_mday) values anymore (see also #10686)
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Dec 27 00:01:30 2024 UTC