Page 1 of 1

mktime pre 1970 confusion (linux)

Posted: Tue Aug 31, 2004 10:20 am
by mchaggis
Hi,

I have read a few post on this, but they all seem to be windows, so appologies for repeat post, but not answers as far as I can see.

Now, I'm sure that this never used to do this, as I have had scripts running for a couple of years now that display date of birth infomation, without any complaints (but maybe it's just never been spotted)

But it would appear that I have discovered the pre 1970 bug... Now the PHP manual states that time_t must be 32bit, but I'm using Mandrake 9.2 and Redhat 8, both 32bit platforms as far as I am aware(?) , so surely they shouldn't have this problem.

My advantage is that I have 100's of scripts that reference the following funtion (which is now the one failing) so I only have to replace this code:

Code: Select all

function formatdate ($format, $dt)
{
   /*
    * Formats MySQL datetime format to normal format
    */
   list($mydate, $mytime) = split(" ", $dt);

   if ($mydate == "0000-00-00" || $mydate == "") return "";

   list($y, $m, $d) = split("-",$mydate);
   list($h, $i, $s) = split(":",$mytime);
   return date($format,
               mktime(intval($h),
                      intval($i),
                      intval($s),
                      intval($m),
                      intval($d),
                      intval($y)
                     )
              );
}
It work appear that I can infact pass the date function a valid timestamp that pre date 1970, it is just mktime that is failling.

Is there any solution?

Posted: Tue Aug 31, 2004 11:25 am
by feyd
looks like mktime will only accept a year range from the epoch.

Posted: Tue Aug 31, 2004 12:03 pm
by markl999
The user comments for mktime offer a few pre 1970 solutions. PEAR's DATE package can also do the same thing.

Posted: Tue Aug 31, 2004 12:41 pm
by mchaggis
They do give examples, but they all seem to say that Unix systems should have the range 1901 - 2038.

Anyway, I have used one of the functions in the examples, but still would like to know why linux does this and how i can fix the system?

Ah well :?

Posted: Tue Aug 31, 2004 12:44 pm
by markl999
but still would like to know why linux does this and how i can fix the system?
You can't really fix it as it isn't broken, it just the way it is :o
http://www.nationmaster.com/encyclopedia/Unix-epoch

Posted: Tue Aug 31, 2004 12:52 pm
by mchaggis
I'm not saying that it's broken technically... But the docs suggest that unix systems should have a range from 1901 and it mainly affects windose servers.

It is all to do with the time system var being 32-bit, and as far as I was aware this was true on my systems, but obviously not :D

Posted: Tue Aug 31, 2004 12:58 pm
by mchaggis
Oh, just for reference, the function I have used is:

Code: Select all

function &mktime ($hour = false, $minute = false, $second = false, $month = false, $date = false, $year = false)
   {
       // For centuries, the Egyptians used a (12 * 30 + 5)-day calendar
       // The Greek began using leap-years in around 400 BC
       // Ceasar adjusted the Roman calendar to start with Januari rather than March
       // All knowledge was passed on by the Arabians, who showed an error in leaping
       // In 1232 Sacrobosco (Eng.) calculated the error at 1 day per 288 years
       //    In 1582, Pope Gregory XIII removed 10 days (Oct 15-24) to partially undo the
       // error, and he instituted the 400-year-exception in the 100-year-exception,
       // (notice 400 rather than 288 years) to undo the rest of the error
       // From about 2044, spring will again coincide with the tropic of Cancer
       // Around 4100, the calendar will need some adjusting again
  
       if ($hour === false)  $hour  = Date ("G");
       if ($minute === false) $minute = Date ("i");
       if ($second === false) $second = Date ("s");
       if ($month === false)  $month  = Date ("n");
       if ($date === false)  $date  = Date ("j");
       if ($year === false)  $year  = Date ("Y");
  
       if ($year >= 1970) return mktime ($hour, $minute, $second, $month, $date, $year);
  
       //    date before 1-1-1970 (Win32 Fix)
       $m_days = Array (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
       if ($year % 4 == 0 && ($year % 100 > 0 || $year % 400 == 0))
       {
           $m_days[1] = 29; // non leap-years can be: 1700, 1800, 1900, 2100, etc.
       }
  
       //    go backward (-), based on $year
       $d_year = 1970 - $year;
       $days = 0 - $d_year * 365;
       $days -= floor ($d_year / 4);          // compensate for leap-years
       $days += floor (($d_year - 70) / 100);  // compensate for non-leap-years
       $days -= floor (($d_year - 370) / 400); // compensate again for giant leap-years
          
       //    go forward (+), based on $month and $date
       for ($i = 1; $i < $month; $i++)
       {
           $days += $m_days [$i - 1];
       }
       $days += $date - 1;
  
       //    go forward (+) based on $hour, $minute and $second
       $stamp = $days * 86400;
       $stamp += $hour * 3600;
       $stamp += $minute * 60;
       $stamp += $second;
  
       return $stamp;
   }
I renamed it to maketime tho as of course it conflicts with the built in one and I've not figured out a way of over riding the build in function mktime yet.

Posted: Tue Aug 31, 2004 12:58 pm
by feyd
it is a signed 32-bit integer.. the same on Windows machines. They just handle negative values differently.