Source for file jDateTime.class.php

Documentation is available at jDateTime.class.php

  1. <?php
  2. /**
  3. @package     jelix
  4. @subpackage  utils
  5. @author      Gérald Croes, Laurent Jouanneau
  6. @contributor Laurent Jouanneau, Julien Issler
  7. @contributor Loic Mathaud
  8. @contributor Florian Hatat
  9. @contributor Emmanuel Hesry, Brice G.
  10. @contributor Hadrien Lanneau <hadrien@over-blog.com>
  11. @copyright   2005-2011 Laurent Jouanneau
  12. @copyright   2007 Loic Mathaud
  13. @copyright   2007-2008 Florian Hatat
  14. @copyright   2001-2005 CopixTeam, GeraldCroes, Laurent Jouanneau
  15. @copyright   2008-2011 Julien Issler
  16. @copyright   2009 Emmanuel Hesry
  17. @copyright   2010 Hadrien Lanneau, 2011 Brice G.
  18. *
  19. *  This class was get originally from the Copix project (CopixDate.lib.php, Copix 2.3dev20050901, http://www.copix.org)
  20. *  Only few lines of code are still copyrighted 2001-2005 CopixTeam (LGPL licence).
  21. *  Initial authors of this Copix classes are Gerald Croes and Laurent Jouanneau,
  22. *  and this class was adapted/improved for Jelix by Laurent Jouanneau
  23. *
  24. @link        http://www.jelix.org
  25. @licence     http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public Licence, see LICENCE file
  26. */
  27.  
  28. // on windows plateform, this function doesn't exist.
  29. if(!function_exists('strptime')){
  30.     /**
  31.      * @ignore
  32.      */
  33.     function strptime $strdate$format ){
  34.         // It's not a full compatibility with strptime of PHP5.1, but it is
  35.         // enough for our needs
  36.         $plop array'S'=>'tm_sec''M'=>'tm_min''H'=>'tm_hour',
  37.             'd'=>'tm_mday''m'=>'tm_mon''Y'=>'tm_year');
  38.  
  39.         $regexp preg_quote($format'/');
  40.         $regexp str_replace(
  41.                 array('%d','%m','%Y','%H','%M','%S'),
  42.                 array('(?P<tm_mday>\d{2})','(?P<tm_mon>\d{2})',
  43.                       '(?P<tm_year>\d{4})','(?P<tm_hour>\d{2})',
  44.                       '(?P<tm_min>\d{2})','(?P<tm_sec>\d{2})'),
  45.                 $regexp);
  46.         if(preg_match('/^'.$regexp.'$/'$strdate$m)){
  47.             $result=array('tm_sec'=>0,'tm_min'=>0,'tm_hour'=>0,'tm_mday'=>0,'tm_mon'=>0,'tm_year'=>0,'tm_wday'=>0,'tm_yday'=>0,'unparsed'=>'');
  48.             foreach($m as $key => $value){
  49.                 if(!isset($result[$key])){
  50.                     continue;
  51.                 }
  52.                 $result[$keyintval($value);
  53.                 switch($key){
  54.                 case 'tm_mon':
  55.                     $result[$key]--;
  56.                     break;
  57.                 case 'tm_year':
  58.                     $result[$key-= 1900;
  59.                     break;
  60.                 default:
  61.                     break;
  62.                 }
  63.             }
  64.             return $result;
  65.         }
  66.         return false;
  67.     }
  68. }
  69.  
  70.  
  71. /**
  72.  * Utility to manipulate dates and convert date format
  73.  * @package     jelix
  74.  * @subpackage  utils
  75.  * @todo PHP53 : replace the use of strptime by date_parse_from_format
  76.  */
  77. class jDateTime {
  78.     public $day;
  79.     public $month;
  80.     public $year;
  81.     public $hour;
  82.     public $minute;
  83.     public $second;
  84.  
  85.     public $defaultFormat = 11;
  86.  
  87.     const LANG_DFORMAT=10;
  88.     const LANG_DTFORMAT=11;
  89.     const LANG_TFORMAT=12;
  90.     const LANG_SHORT_DTFORMAT=13;
  91.     const DB_DFORMAT=20;
  92.     const DB_DTFORMAT=21;
  93.     const DB_TFORMAT=22;
  94.     const ISO8601_FORMAT=40;
  95.     const TIMESTAMP_FORMAT=50;
  96.     const RFC822_FORMAT=60;
  97.     const RFC2822_FORMAT=61;
  98.     const FULL_LANG_DATE=62;
  99.  
  100.     /**
  101.      *
  102.      */
  103.     function __construct($year=0$month=0$day=0$hour=0$minute=0$second=0){
  104.         $this->year = $year;
  105.         $this->month = $month;
  106.         $this->day = $day;
  107.         $this->hour = $hour;
  108.         $this->minute = $minute;
  109.         $this->second = $second;
  110.  
  111.         if(!$this->_check())
  112.         {
  113.           throw new jException('jelix~errors.datetime.invalid',
  114.               array($this->year$this->month$this->day,
  115.                 $this->hour$this->minute$this->second));
  116.         }
  117.     }
  118.  
  119.     /**
  120.      * checks if the current jDateTime object is a valid gregorian date/time
  121.      * @return bool true if the date/time are valid.
  122.      */
  123.     private function _check({
  124.         // Only check the date if it is defined (eg. day, month and year are
  125.         // strictly positive).
  126.         if($this->day > && $this->month > && $this->year > 0
  127.             && !checkdate($this->month$this->day$this->year))
  128.         {
  129.             return false;
  130.         }
  131.         if(!(($this->second >= 0&& ($this->second < 60)
  132.             && ($this->minute >= 0&& ($this->minute < 60)
  133.             && ($this->hour >= 0&& ($this->hour < 24)))
  134.         {
  135.             return false;
  136.         }
  137.         return true;
  138.     }
  139.  
  140.      /**
  141.      * Check if jDateTime is "null" (all values egals to 0)
  142.      *
  143.      * @return boolean 
  144.      * @author Hadrien Lanneau (hadrien at over-blog dot com)
  145.      ***/
  146.     public function isNull({
  147.         return ($this->year === && $this->month === && $this->day === && $this->hour == && $this->minute == && $this->second == 0);
  148.     }
  149.  
  150.     /**
  151.      * convert the date to a string format
  152.      * @param int $format one of the class constant xxx_FORMAT, or -1 if it should use the default format
  153.      * @return string the string date
  154.      * @see jDateTime:$defaultFormat
  155.      */
  156.     function toString($format=-1){
  157.         if($format==-1)
  158.             $format $this->defaultFormat;
  159.  
  160.         $str='';
  161.         switch($format){
  162.            case self::LANG_DFORMAT:
  163.                $t mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year );
  164.                $lf jLocale::get('jelix~format.date');
  165.                $str date($lf$t);
  166.                break;
  167.            case self::LANG_DTFORMAT:
  168.                $t mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year );
  169.                $lf jLocale::get('jelix~format.datetime');
  170.                $str date($lf$t);
  171.                break;
  172.            case self::LANG_TFORMAT:
  173.                $t mktime $this->hour$this->minute,$this->second 0);
  174.                $lf jLocale::get('jelix~format.time');
  175.                $str date($lf$t);
  176.                break;
  177.            case self::LANG_SHORT_DTFORMAT:
  178.                $t mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year );
  179.                $lf jLocale::get('jelix~format.short_datetime');
  180.                $str date($lf$t);
  181.                break;
  182.            case self::DB_DFORMAT:
  183.                $str sprintf('%04d-%02d-%02d'$this->year$this->month$this->day);
  184.                break;
  185.            case self::DB_DTFORMAT:
  186.                $str sprintf('%04d-%02d-%02d %02d:%02d:%02d'$this->year$this->month$this->day$this->hour$this->minute$this->second);
  187.                break;
  188.            case self::DB_TFORMAT:
  189.                $str sprintf('%02d:%02d:%02d'$this->hour$this->minute$this->second);
  190.                break;
  191.            case self::ISO8601_FORMAT:
  192.                $str sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ'$this->year$this->month$this->day$this->hour$this->minute$this->second);
  193.                break;
  194.            case self::TIMESTAMP_FORMAT:
  195.                $str =(string) mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year );
  196.                break;
  197.            case self::RFC822_FORMAT:
  198.            case self::RFC2822_FORMAT:
  199.                $str date('r'mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year ));
  200.                break;
  201.            case self::FULL_LANG_DATE:
  202.                $t mktime $this->hour$this->minute,$this->second $this->month$this->day$this->year );
  203.                // traduction du mois
  204.                $month jLocale::get('jelix~date_time.month.'.date('m',$t).'.label');
  205.                // traduction du jour
  206.                $day jLocale::get('jelix~date_time.day.'.date('w',$t).'.label');
  207.                // récupération du formatage de la date
  208.                $lf jLocale::get('jelix~format.date_full');
  209.                // récupération du format ordinal du jour dans le mois surtout pour le format en anglais (1st, 2nd, 3rd et th pour les autres
  210.                $ordinal jLocale::get('jelix~date_time.day.'.$this->day.'.ordinal');
  211.                // on mets le tout dans le bon ordre à l'aide de la chaine de formatage
  212.                $str sprintf($lf$day$this->day$ordinal$month$this->year);
  213.                break;
  214.         }
  215.        return $str;
  216.     }
  217.  
  218.     /**
  219.      * read a string to extract date values
  220.      * @param string $str the string date
  221.      * @param int $format one of the class constant xxx_FORMAT, or -1 if it should use the default format
  222.      * @see jDateTime:$defaultFormat
  223.      */
  224.     function setFromString($str,$format=-1){
  225.         if($format==-1){
  226.             $format $this->defaultFormat;
  227.         }
  228.         $this->year = 0;
  229.         $this->month = 0;
  230.         $this->day = 0;
  231.         $this->hour = 0;
  232.         $this->minute = 0;
  233.         $this->second = 0;
  234.         $ok=false;
  235.  
  236.         switch($format){
  237.            case self::LANG_DFORMAT:
  238.                $lf jLocale::get('jelix~format.date_st');
  239.                if($res strptime $str$lf )){
  240.                    $ok=true;
  241.                    $this->year = $res['tm_year']+1900;
  242.                    $this->month = $res['tm_mon'+1;
  243.                    $this->day = $res['tm_mday'];
  244.                }
  245.                break;
  246.            case self::LANG_DTFORMAT:
  247.                $lf jLocale::get('jelix~format.datetime_st');
  248.                if($res strptime $str$lf )){
  249.                    $ok=true;
  250.                    $this->year = $res['tm_year'1900;
  251.                    $this->month = $res['tm_mon'1;
  252.                    $this->day = $res['tm_mday'];
  253.                    $this->hour = $res['tm_hour'];
  254.                    $this->minute = $res['tm_min'];
  255.                    $this->second = $res['tm_sec'];
  256.                }
  257.                break;
  258.            case self::LANG_TFORMAT:
  259.                $lf jLocale::get('jelix~format.time_st');
  260.                if($res strptime $str$lf )){
  261.                    $ok=true;
  262.                    $this->hour = $res['tm_hour'];
  263.                    $this->minute = $res['tm_min'];
  264.                    $this->second = $res['tm_sec'];
  265.                }
  266.                break;
  267.            case self::LANG_SHORT_DTFORMAT:
  268.                $lf jLocale::get('jelix~format.short_datetime_st');
  269.                if($res strptime $str$lf )){
  270.                    $ok=true;
  271.                    $this->year = $res['tm_year'1900;
  272.                    $this->month = $res['tm_mon'1;
  273.                    $this->day = $res['tm_mday'];
  274.                    $this->hour = $res['tm_hour'];
  275.                    $this->minute = $res['tm_min'];
  276.                }
  277.                break;
  278.            case self::DB_DFORMAT:
  279.                if($res strptime$str"%Y-%m-%d" )){
  280.                    $ok=true;
  281.                    $this->year = $res['tm_year'1900;
  282.                    $this->month = $res['tm_mon'1;
  283.                    $this->day = $res['tm_mday'];
  284.                }
  285.                break;
  286.            case self::DB_DTFORMAT:
  287.                if($res strptime$str"%Y-%m-%d %H:%M:%S" )){
  288.                    $ok=true;
  289.                    $this->year = $res['tm_year'1900;
  290.                    $this->month = $res['tm_mon'1;
  291.                    $this->day = $res['tm_mday'];
  292.                    $this->hour = $res['tm_hour'];
  293.                    $this->minute = $res['tm_min'];
  294.                    $this->second = $res['tm_sec'];
  295.                }
  296.                break;
  297.            case self::DB_TFORMAT:
  298.                if($res strptime$str"%H:%M:%S" )){
  299.                    $ok=true;
  300.                    $this->hour = $res['tm_hour'];
  301.                    $this->minute = $res['tm_min'];
  302.                    $this->second = $res['tm_sec'];
  303.                }
  304.                break;
  305.            case self::ISO8601_FORMAT:
  306.                if($ok=preg_match('/^(\d{4})(?:\-(\d{2})(?:\-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{2,3}))?)?(Z|([+\-])(\d{2}):(\d{2})))?)?)?$/'$str$match)){
  307.                     $c count($match)-1;
  308.                     $this->year = intval($match[1]);
  309.                     if($c<2break;
  310.                     $this->month = intval($match[2]);
  311.                     if($c<3break;
  312.                     $this->day = intval($match[3]);
  313.                     if($c<4break;
  314.                     $this->hour = intval($match[4]);
  315.                     $this->minute = intval($match[5]);
  316.                     if($match[6!= ''$this->second = intval($match[6]);
  317.                     if($match[8!= 'Z'){
  318.                         $d new jDuration(array('hour'=>$match[10],'minute'=>$match[11]));
  319.                         if($match[9== '+')
  320.                             $this->add($d);
  321.                         else
  322.                             $this->sub($d);
  323.                     }
  324.                }
  325.                break;
  326.            case self::TIMESTAMP_FORMAT:
  327.                $ok=true;
  328.                $t getdate intval($str) );
  329.                $this->year = $t['year'];
  330.                $this->month = $t['mon'];
  331.                $this->day = $t['mday'];
  332.                $this->hour = $t['hours'];
  333.                $this->minute = $t['minutes'];
  334.                $this->second = $t['seconds'];
  335.                break;
  336.            case self::RFC822_FORMAT:
  337.            case self::RFC2822_FORMAT:
  338.                // Note the "x" modifier, otherwise the pattern would look like
  339.                // obfuscated code.
  340.                $regexp "/^
  341.                      (?: (?P<nday> Mon | Tue | Wed | Thu | Fri | Sat | Sun) , )? \s+
  342.                      (?P<day>\d{1,2}) \s+
  343.                      (?P<nmonth> Jan | Feb | Mar | Apr | May | Jun |
  344.                                Jul | Aug | Sep | Oct | Nov | Dec) \s+
  345.                      (?P<year>\d{4}) \s+
  346.                      (?P<hour>\d{2}) : (?P<minute>\d{2}) (?: : (?P<second>\d{2}))? \s+
  347.                      (?P<tzsign>[+-]) (?P<tzhour>\d{2}) (?P<tzminute>\d{2})$/x";
  348.  
  349.                $english_months array("Jan""Feb""Mar""Apr""May""Jun",
  350.                    "Jul""Aug""Sep""Oct""Nov""Dec");
  351.  
  352.                $match array("year" => 0"month" => 0"day" => 0,
  353.                    "hour" => 0"minute" => 0"second" => 0"tzsign" => "+",
  354.                    "tzhour" => 0"tzminute" => 0);
  355.  
  356.                if($ok preg_match($regexp$str$match)){
  357.                    $this->year = intval($match['year']);
  358.                    $this->month = array_search($match['nmonth']$english_months1;
  359.                    $this->day = intval($match['day']);
  360.                    $this->hour = intval($match['hour']);
  361.                    $this->minute = intval($match['minute']);
  362.                    $this->second = intval($match['second']);
  363.  
  364.                    # Adjust according to the timezone, so that the stored time
  365.                    # corresponds to UTC.
  366.                    $tz new jDuration(array('hour'=>intval($match['tzhour']),
  367.                        'minute'=>intval($match['tzminute'])));
  368.                    if($match['tzsign'== '+'){
  369.                        $this->sub($tz);
  370.                    }
  371.                    else{
  372.                        $this->add($tz);
  373.                    }
  374.                }
  375.                break;
  376.         }
  377.  
  378.         return $ok && $this->_check();
  379.     }
  380.  
  381.     /**
  382.      * Add a duration to the date.
  383.      * You can specify the duration in a jDuration object or give each value of
  384.      * the duration.
  385.      * @param jDuration/int $year the duration value or a year with 4 digits
  386.      * @param int $month month with 2 digits
  387.      * @param int $day day with 2 digits
  388.      * @param int $hour hour with 2 digits
  389.      * @param int $minute minute with 2 digits
  390.      * @param int $second second with 2 digits
  391.      */
  392.     public function add($year$month=0$day=0$hour=0$minute=0$second=0{
  393.         if ($year instanceof jDuration{
  394.             $dt $year;
  395.         else {
  396.             $dt new jDuration(array("year" => $year"month" => $month,
  397.                 "day" => $day"hour" => $hour"minute" => $minute,
  398.                 "second" => $second));
  399.         }
  400.         $t mktime($this->hour$this->minute$this->second + $dt->seconds,
  401.              $this->month + $dt->months$this->day + $dt->days$this->year);
  402.  
  403.         $t getdate ($t);
  404.         $this->year = $t['year'];
  405.         $this->month = $t['mon'];
  406.         $this->day = $t['mday'];
  407.         $this->hour = $t['hours'];
  408.         $this->minute = $t['minutes'];
  409.         $this->second = $t['seconds'];
  410.     }
  411.  
  412.     /**
  413.      * substract a <b>duration</b> to the date
  414.      * You can specify the duration in a jDuration object or give each value of
  415.      * the duration.
  416.      * @param jDuration/int $year the duration value or a year with 4 digits
  417.      * @param int $month month with 2 digits
  418.      * @param int $day day with 2 digits
  419.      * @param int $hour hour with 2 digits
  420.      * @param int $minute minute with 2 digits
  421.      * @param int $second second with 2 digits
  422.      */
  423.     public function sub($year$month=0$day=0$hour=0$minute=0$second=0{
  424.         if ($year instanceof jDuration{
  425.             $dt $year;
  426.         else {
  427.             $dt new jDuration(array("year" => $year"month" => $month,
  428.                 "day" => $day"hour" => $hour"minute" => $minute,
  429.                 "second" => $second));
  430.         }
  431.         $dt->mult(-1);
  432.         $this->add($dt);
  433.     }
  434.  
  435.     /**
  436.      * to know the duration between two dates.
  437.      * @param jDateTime $dt  the date on which a sub will be made with the date on the current object
  438.      * @param bool $absolute 
  439.      * @return jDuration a jDuration object
  440.      */
  441.     public function durationTo($dt$absolute=true){
  442.         if($absolute){
  443.             $t gmmktime($dt->hour$dt->minute$dt->second,
  444.                 $dt->month$dt->day$dt->year)
  445.                 - gmmktime($this->hour$this->minute$this->second,
  446.                     $this->month$this->day$this->year);
  447.             return new jDuration($t);
  448.         }
  449.         else{
  450.             return new jDuration(array(
  451.                 "year" => $dt->year $this->year,
  452.                 "month" => $dt->month $this->month,
  453.                 "day" => $dt->day $this-> day,
  454.                 "hour" => $dt->hour $this->hour,
  455.                 "minute" => $dt->minute $this->minute,
  456.                 "second" => $dt->second $this->second
  457.             ));
  458.         }
  459.     }
  460.  
  461.     /**
  462.      * compare two date
  463.      * @param jDateTime $dt the date to compare
  464.      * @return integer -1 if $dt > $this, 0 if $dt = $this, 1 if $dt < $this
  465.      */
  466.     public function compareTo($dt){
  467.         $fields=array('year','month','day','hour','minute','second');
  468.         foreach($fields as $field){
  469.             if($dt->$field $this->$field)
  470.                 return -1;
  471.             if($dt->$field $this->$field)
  472.                 return 1;
  473.         }
  474.         return 0;
  475.     }
  476.  
  477.     /**
  478.     * set date to current datetime
  479.     */
  480.     public function now({
  481.         $this->year = intval(date('Y'));
  482.         $this->month = intval(date('m'));
  483.         $this->day = intval(date('d'));
  484.         $this->hour = intval(date('H'));
  485.         $this->minute = intval(date('i'));
  486.         $this->second = intval(date('s'));
  487.     }
  488.  
  489.  
  490.     /**
  491.     * Substract a date with another
  492.     * @param jDateTime $date 
  493.     * @return jDateTime 
  494.     * @author Hadrien Lanneau <hadrien@over-blog.com>
  495.     * @since 1.2
  496.     */
  497.     public function substract($date null{
  498.         if (!$date{
  499.             $date new jDateTime();
  500.             $date->now();
  501.         }
  502.  
  503.         $newDate new jDateTime();
  504.  
  505.         $items array(
  506.                 'second',
  507.                 'minute',
  508.                 'hour',
  509.                 'day',
  510.                 'month',
  511.                 'year'
  512.             );
  513.  
  514.         foreach ($items as $k => $i{
  515.             $newDate->{$i$date->{$i$this->{$i};
  516.             if ($newDate->{$i0{
  517.                 switch ($i{
  518.                     case 'second':
  519.                     case 'minute':
  520.                         $sub 60;
  521.                         break;
  522.                     case 'hour':
  523.                         $sub 24;
  524.                         break;
  525.                     case 'day':
  526.                         switch ($this->month{
  527.                             // Month with 31 days
  528.                             case 1:
  529.                             case 3:
  530.                             case 5:
  531.                             case 7:
  532.                             case 8:
  533.                             case 10:
  534.                             case 12:
  535.                                 $sub 31;
  536.                                 break;
  537.                             // Month with 30 days
  538.                             case 4:
  539.                             case 6:
  540.                             case 9:
  541.                             case 11:
  542.                                 $sub 30;
  543.                                 break;
  544.                             // February
  545.                             case 2:
  546.                                 if ($this->year == and
  547.                                         !(
  548.                                                 $this->year 100 == and
  549.                                                 $this->year 400 != 0
  550.                                         )) {
  551.                                     // Bissextile
  552.                                     $sub 29;
  553.                                 }
  554.                                 else {
  555.                                    $sub 28;
  556.                                 }
  557.                                 break;
  558.                         }
  559.                         break;
  560.                     case 'month':
  561.                         $sub 12;
  562.                         break;
  563.                     default:
  564.                         $sub 0;
  565.                 }
  566.                 $newDate->{$iabs($sub $newDate->{$i});
  567.                 if (isset($items[$k+1])) {
  568.                     $newDate->{$items[$k+1]}--;
  569.                 }
  570.             }
  571.         }
  572.         return $newDate;
  573.     }
  574. }

Documentation generated on Mon, 19 Sep 2011 14:12:24 +0200 by phpDocumentor 1.4.3