php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #17123 mktime(), localtime(), strtotime(), etc. are broken for dates <1970 in Win32
Submitted: 2002-05-09 12:04 UTC Modified: 2002-11-24 13:13 UTC
Votes:29
Avg. Score:4.7 ± 0.6
Reproduced:27 of 27 (100.0%)
Same Version:8 (29.6%)
Same OS:14 (51.9%)
From: jasonh at trurocollege dot ac dot uk Assigned:
Status: Wont fix Package: Date/time related
PHP Version: 4.2.0 OS: WinNT5.0
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2002-05-09 12:04 UTC] jasonh at trurocollege dot ac dot uk
We've struggled with the fact that mktime(), strtotime(), localtime(), etc. are broken for dates <1970 under Win32.  Below is some PHP code that shows how this might be worked around to generate (and use) negative Unix timestamps under Windows.  It would be nice if this idea could be incorporated into all the timestamp functions (since we don't want to have to rewrite something like strtotime() which is quite essential for us in deciphering MSSQL datetime fields).

The concept is to fiddle dates before 1970 to be dates 56 years later when supplying them to the broken Win32 native functions, and then fiddle the results back to the correct values.  This will ensure that leap year counts are still correct and give dates on the correct days of the week.  The code below shows fiddling the generation of a full timestamp (mktime()) and fiddling the interpretation of those full timestamps (localtime()).

---------

$epoch2 = mktime(0,0,0,1,1,2026);

function mymktime($hr,$mi,$sc,$m,$d,$y) {
	global $epoch2;
	$fiddle = 0;
	if($y <= 1970) { // Should be just < 1970, but this will help cope with 0 month or 0 day
		$fiddle = $epoch2;
		$y = $y + 56;
	}
	return mktime($hr,$mi,$sc,$m,$d,$y) - $fiddle;
}

function mylocaltime($ts,$assoc) {
	global $epoch2;
	$fiddle = false;
	if($ts < 0 && $ts != -1) {
		$fiddle = true;
		$ts += $epoch2;
	}
	$res = localtime($ts,$assoc);
	if($fiddle)
		$res[$assoc?"tm_year":5] -= 56;
	return $res;
}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-05-24 20:31 UTC] derick@php.net
Fixed by documenting this.
 [2002-06-02 19:06 UTC] k.schroeder@php.net
Reopened in reaction of new comment under #17472.

Regards, Kai
 [2002-06-22 14:39 UTC] ldprice at attbi dot com
mktime error with the following code:

mktime returns -1 for years less than 1970

Windows XP Pro, PHP 4.1.1, Microsoft-IIS/5.1
Windows 2000 Server, PHP 4.2.1, Apache/2.0.39 (Win32)

Date of Birth:   
Warning: unexpected error in date() in C:\catalog\includes\functions\general.php on line 531

  function tep_date_short($raw_date) {
    if ( ($raw_date == '0000-00-00 00:00:00') || ($raw_date == '') ) return false;

// remove the first digit if it is 0 - as php treats these as Octals
    $year = substr($raw_date, 0, 4);
    $month = substr($raw_date, 5, 2); if (substr($month, 0, 1) == '0') $month = substr($month, 1);
    $day = substr($raw_date, 8, 2); if (substr($day, 0, 1) == '0') $day =  substr($day, 1);
    $hour = substr($raw_date, 11, 2); if (substr($hour, 0, 1) == '0') $hour = substr($hour, 1);
    $minute = substr($raw_date, 14, 2); if (substr($minute, 0, 1) == '0') $minute = substr($minute, 1);
    $second = substr($raw_date, 17, 2); if (substr($second, 0, 1) == '0') $second = substr($second, 1);

    return date(DATE_FORMAT, mktime($hour, $minute, $second, $month, $day, $year));
 [2002-07-04 16:08 UTC] eru@php.net
Verified during the Bughunt.
 [2002-08-01 10:56 UTC] percy_H_ at _AT_majiclab_DOHT_ dot _KOM
I have 4.0.6 on several machines (3 I think), and I have the same problem on all of them (I have Windows 2000 on all of them).  As much as I want to convert my server to a linux box, it still leaves me two computers with Windows 2000 and this bug.  I had one idea, but since I don't have 4.2.0 I can't work on it yet.  With the new W32 API capabilities of 4.2.0, would it not be possible to use some Windows API functions to parse date/time information rather than the included PHP functions?  Obviously, Windows can handle the dates.  I use JavaScript a lot and it has a range of approximately 275,000 years before and after 1970.  I tested my valid range yesterday using the PHP functions, I could go from 1970 to about 2037.  Wow.  What about all the people that were born before 1970?  Older than 32 years...

Anyway, when I get home I will download 4.2.0 and then start hacking away at using the PROPER Windows API functions for date/time since PHP seems to be wanting to use the ones that don't work.

Hope something gets figured out, even if I have to program my own DLL to make it work... :)

-Percy
 [2002-08-04 04:47 UTC] yohgaki@php.net
Since the time stamp is started from 1970, I don't think it supposed to work in anyways.

Or is there anything wrong is returned when too small/large date is supplied? It should fail, but it should not crash PHP for example. Please reopen if PHP behaves badly.

BTW, there should be feature requests for more generic date functions so no need for feature request also.


 [2002-11-04 04:53 UTC] Andreas at ap-stbr dot de
The problem still occurs with 4.2.3: mktime(0,0,0,09,23,1967) returns -1 instead of a valid timestamp.
 [2002-11-24 13:11 UTC] asiop at yahoo dot com
The problem is that all of the PHP date functions are based on that miserable timestamp.

At least if there were a consisent set of alternative functions that handle dates and bypass this problem it would be nice.

The bottom line of having no official result for this is unbearable, there's no way to calculate the age diffrence of people who are older than 33..

This is quite basic and the lack of it makes this otherwise very strong language - very poor and unprofessional.

too bad.
 [2002-11-24 13:13 UTC] rasmus@php.net
Just use Julian dates as supported by the calendar extension.
 [2002-11-24 14:47 UTC] asiop at yahoo dot com
Sadly, I had to rebuild date function to be compatible with mylocal..

here's the code, extensions are welcome:

    function mydate($format, $timestamp = null) {
        if ($timestamp == null) {
            return @date($format);
        }
        if ($timestamp > -1) {
            return @date($format, $timestamp);
        }

        $dateArr = $this->mylocaltime($timestamp, true);

        $search = array("s","i","H","d","m","Y","y", "z","I");
        $replace = array(sprintf("%02d",$dateArr["tm_sec"]), sprintf("%02d",$dateArr["tm_min"]), sprintf("%02d",$dateArr["tm_hour"]),
                        sprintf("%02d",$dateArr["tm_mday"]), sprintf("%02d",$dateArr["tm_mon"]+1), sprintf("%02d",$dateArr["tm_year"]+1900), sprintf("%02d",$dateArr["tm_year"] - (floor($dateArr["tm_year"] / 100) * 100)),
                        $dateArr["tm_yday"], $dateArr["tm_isdst"]);
        return str_replace($search, $replace, $format);


    }
// enjoy
 [2002-12-10 08:15 UTC] f dot vulto at re-base dot com
PEAR offers a Date() and a Date_Calc() class which is capable of handling dates pre 1970 and post 2038.  PEAR::Date is part of the standard PHP distribution.

Example:

   require_once 'Date/Date.php';
   $oDate =& new Date('1967-09-23 00:00:00');
   print $oDate->format('%d/%B/%Y %H.%M.%S');

See also:
   
   http://pear.php.net
   http://docs.akbkhome.com/pearcvs/Date.html
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 16 05:01:29 2024 UTC