We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 32963
    • 1,732 Posts
    It’s just very strange to me! Why would the developers of PHP only limit the date or timestamp to 68 years? huh

    PHP’s native date functions can only handle dates between 1970 and 1/1/2038!!!!

      xWisdom
      www.xwisdomhtml.com
      The fear of the Lord is the beginning of wisdom:
      MODx Co-Founder - Create and do more with less.
      • 28042 ☆ A M B ☆
      • 24,524 Posts
      That has nothing to do with PHP. That is a limitation of the UNIX timestamp. The UNIX timestamp is the count of the seconds since the beginning of the "UNIX epoch" in 1970. It will be in big trouble on 32-bit systems in a few more years when its size exceeds the 32-bit limit.

      It really isn’t supposed to be used for date calculations, but for keeping track of file creation and changes. That’s why it’s called a UNIX timestamp. There most certainly would not be any files created on a UNIX system before 1970!

      So any date problems, like the age business here on SMF, is not the fault of PHP or of the UNIX timestamp. but of the coders who didn’t use the appropriate date/time functions. It would be appropriate for storing the creation date of an account, but not for a date of birth.
        Studying MODX in the desert - http://sottwell.com
        Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
        Join the Slack Community - http://modx.org
        • 32241
        • 1,495 Posts
        I just know that, thanks Susan for clarifying it and Raymond for mention it.
          Wendy Novianto
          [font=Verdana]PT DJAMOER Technology Media
          [font=Verdana]Xituz Media
          • 32963
          • 1,732 Posts
          Thanks for sharing Susan. I did not know that at all. From what I see most of the PHP date format functions only accept timestamps which means that I can’t format dates that are outside the range.

          I think there’re ways around this but does anyone have other solutions that they would like to share or see added to the MODx API? Such additions will be called extensions to the api.


            xWisdom
            www.xwisdomhtml.com
            The fear of the Lord is the beginning of wisdom:
            MODx Co-Founder - Create and do more with less.
            • 22303 MODX Staff
            • 10,725 Posts
            Yes, this is one facility in PHP that is a bit convoluted, but...there are ways of dealing with it.

            In fact, I have already adopted and included some custom strtotime() and strftime() functions that handle this in the XPDO layer; am using this for a project right now to ensure proper date of birth handling (and other historical dates); and seems to work pretty well, even on Windows and even in PHP 4, I think). There are still some bugs in what I’m working on when using with MySQL DATE fields (rather than TIMESTAMP or DATETIME), but feel free to take a look at the functions and make use of them to handle dates outside this limited range. I’ll be making some updates that should resolve at least some of these bugs this week.

            FYI, these functions are solutions I have adopted from external sources, and thus I want to give proper credit for the code. For the xPDOObject::strtotime() implementation, I adopted a function referred to as safestrtotime() by Ed Lecky-Thompson (as presented at http://nosheep.net/story/php-strtotime-limitation/). For xPDOObject::strftime() I adopted some additional code that will handle custom mappings for years outside the range handled on 32-bit systems, by some simple addition/subtraction techniques.

            I’ll go ahead and publish these here in case someone wants to make these functions more robust or has time to debug them in various situations. I plan on putting a custom version of mktime in here as well, so you can also construct proper timestamps outside the ranges.

            PLEASE NOTE: if you’re not going to use them on a class, you’ll need to name the functions something other than strtotime() or strftime().

            <?php
                function strtotime($strInput) {
                    $iVal= -1;
                    for ($i= 1900; $i <= 1969; $i++) {
                        // Check for this year string in date
                        $strYear= (string) $i;
                        if (!(strpos($strInput, $strYear) === false)) {
                            $replYear= $strYear;
                            $yearSkew= 1970 - $i;
                            $strInput= str_replace($strYear, '1970', $strInput);
                        }
                    }
                    $iVal= strtotime($strInput);
                    if ($yearSkew > 0) {
                        $numSecs= (60 * 60 * 24 * 365 * $yearSkew);
                        $iVal= $iVal - $numSecs;
                        $numLeapYears= 0; // determine number of leap years in period
                        for ($j= $replYear; $j <= 1969; $j++) {
                            $thisYear= $j;
                            $isLeapYear= false;
                            // Is div by 4?
                            if (($thisYear % 4) == 0) {
                                $isLeapYear= true;
                            }
                            // Is div by 100?
                            if (($thisYear % 100) == 0) {
                                $isLeapYear= false;
                            }
                            // Is div by 1000?
                            if (($thisYear % 1000) == 0) {
                                $isLeapYear= true;
                            }
                            if ($isLeapYear == true) {
                                $numLeapYears++;
                            }
                        }
                        $iVal= $iVal - (60 * 60 * 24 * $numLeapYears);
                    }
                    return $iVal;
                }
            ?>


            <?php
                function strftime($format= "", $timestamp= false) {
                    if ($timestamp === false) {
                        $timestamp= time();
                    }
                    if ($timestamp >= 0 && $timestamp <= 2147480047) { // between 1 Jan 1970 00:00:00 and 19 Jan 2038 03:14:07 GMT
                        return strftime($format, $timestamp);
                    }
                    $mappings= array (
                        array (
                            "start" => 1902,
                            "end" => 1951,
                            "map" => 1986
                            ), // 1902-1951 = 1986-2035
                        array (
                            "start" => 1952,
                            "end" => 1969,
                            "map" => 1980
                            ), // 1952-1969 = 1980-1997
                
                    );
                    if ($timestamp < 0) {
                        $year= 1970;
                        while ($timestamp < 0) {
                            $days= ($year % 4 == 0 && ($year % 100 > 0 || $year % 400 == 0)) ? 366 : 365;
                            $timestamp += $days * 86400;
                            $year--;
                        }
                    } else // if ($timestamp > 2147480047)
                        {
                        $year= 2038;
                        while ($timestamp > 0) {
                            $days= ($year % 4 == 0 && ($year % 100 > 0 || $year % 400 == 0)) ? 366 : 365;
                            $timestamp -= $days * 86400;
                            $year++;
                        }
                    }
                    foreach ($mappings as $mapping)
                        if ($year >= $mapping["start"] && $year <= $mapping["end"]) {
                            $find= array (
                                "%y",
                                "%Y",
                                "%D"
                            );
                            $replace= array (
                                substr($year,
                                -2
                            ), $year, "%m/%d/" . substr($year, -2));
                            $format= & str_replace($find, $replace, $format);
                            $map_from_1970= mktime(0, 0, 0, 1, 1, $mapping["map"]);
                            return strftime($format, $timestamp + $map_from_1970);
                        }
            
                    return "strftime (): Year not mapped yet: " . $year;
                }
            ?>


            IMPORTANT EDIT: I unwittingly published this with a bug in the strftime implementation here (as Victor would say, "damn cargo cult programmers, copying and pasting code") -- the line that read
            $year= 1969;
            should have read
            $year= 1970;
            as it does now. I also believe their to still be a bug here somewhere, but I’m still trying to track it down in conjunction with manipulating the various date and time types from MySQL (and other database platforms)
              • 32963
              • 1,732 Posts
              Many thanks for sharing Jason! This is absolutely perfect.

              I’ve also stumbled accross ADODB Date library (I think it doesn’t require ADODB) which when combined with Jason’s strtotime functions should be enough to replace PHP’s date functions. ADODB Date Library also supports:

              getdate() with adodb_getdate()
              date() with adodb_date()
              gmdate() with adodb_gmdate()
              mktime() with adodb_mktime()
              gmmktime() with adodb_gmmktime()
              gmstrftime() with adodb_gmstrftime()

              See http://phplens.com/phpeverywhere/adodb_date_library for more info on ADODB.

              The nice thing is to just have it all wrapped into a Date_Extension for modx:

              echo $modx->date->strtotime("1950-1-1");






                xWisdom
                www.xwisdomhtml.com
                The fear of the Lord is the beginning of wisdom:
                MODx Co-Founder - Create and do more with less.
                • 28042 ☆ A M B ☆
                • 24,524 Posts
                Quote from: xwisdom at Jun 19, 2006, 08:53 AM

                From what I see most of the PHP date format functions only accept timestamps which means that I can’t format dates that are outside the range.

                That’s because that clunky "number of seconds since 1/1/1970" needs all the help it can get to be of any use to anybody!
                  Studying MODX in the desert - http://sottwell.com
                  Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                  Join the Slack Community - http://modx.org
                  • 28042 ☆ A M B ☆
                  • 24,524 Posts
                  And it’s also useful to examine the native MySQL date and time functions; it is far more efficient to use these in the process of a query than to get the raw value and then use an external (such as PHP) function to manipulate it.
                    Studying MODX in the desert - http://sottwell.com
                    Tips and Tricks from the MODX Forums and Slack Channels - http://modxcookbook.com
                    Join the Slack Community - http://modx.org
                    • 31337
                    • 258 Posts
                    Quote from: sottwell at Jun 19, 2006, 08:49 PM

                    That’s because that clunky "number of seconds since 1/1/1970" needs all the help it can get to be of any use to anybody!

                    I disagree. The unix timestamp was never meant to represent historical dates. It’s height of idiocy (or rather a brilliant sign of immature and inexperienced code developers) to base all of your time functions around the unix timestamp. Square peg ... meet round hole smiley

                    By contrast, take a look at CPAN (Perl’s version of PEAR) and see how many modules there are that handle dates and how they do it:
                    http://search.cpan.org/search?query=date&mode=all

                    Not to start a Perl vs. PHP advocacy war here, but one of them was clearly designed by someone with serious software experience grin
                      • 22303 MODX Staff
                      • 10,725 Posts
                      FYI -- I corrected a bug in the strftime alternative posted in this thread; just letting everyone know in case you are using it...