<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------


/**
 * 日期时间操作类
 * @category   ORG
 * @package  ORG
 * @subpackage  Date
 * @author    liu21st <liu21st@gmail.com>
 * @version   $Id: Date.class.php 2662 2012-01-26 06:32:50Z liu21st $
 */
class Date {

    /**
     * 日期的时间戳
     * @var integer
     * @access protected
     */
     protected $date;

    /**
     * 时区
     * @var integer
     * @access protected
     */
     protected $timezone;

    /**
     * 年
     * @var integer
     * @access protected
     */
     protected $year;

    /**
     * 月
     * @var integer
     * @access protected
     */
     protected $month;

    /**
     * 日
     * @var integer
     * @access protected
     */
     protected $day;

    /**
     * 时
     * @var integer
     * @access protected
     */
     protected $hour;

    /**
     * 分
     * @var integer
     * @access protected
     */
     protected $minute;

    /**
     * 秒
     * @var integer
     * @access protected
     */
     protected $second;

    /**
     * 星期的数字表示
     * @var integer
     * @access protected
     */
     protected $weekday;

    /**
     * 星期的完整表示
     * @var string
     * @access protected
     */
     protected $cWeekday;

    /**
     * 一年中的天数 0－365
     * @var integer
     * @access protected
     */
     protected $yDay;

    /**
     * 月份的完整表示
     * @var string
     * @access protected
     */
     protected $cMonth;

    /**
     * 日期CDATE表示
     * @var string
     * @access protected
     */
     protected $CDATE;

    /**
     * 日期的YMD表示
     * @var string
     * @access protected
     */
     protected $YMD;

    /**
     * 时间的输出表示
     * @var string
     * @access protected
     */
     protected $CTIME;

     // 星期的输出
     protected $Week = array("日","一","二","三","四","五","六");

    /**
     * 架构函数
     * 创建一个Date对象
     * @param mixed $date  日期
     * @static
     * @access public
     */
    public function __construct($date='') {
        //分析日期
        $this->date =   $this->parse($date);
        $this->setDate($this->date);
    }

    /**
     * 日期分析
     * 返回时间戳
     * @static
     * @access public
     * @param mixed $date 日期
     * @return string
     */
    public function parse($date) {
        if (is_string($date)) {
            if (($date == "") || strtotime($date) == -1) {
                //为空默认取得当前时间戳
                $tmpdate = time();
            } else {
                //把字符串转换成UNIX时间戳
                $tmpdate = strtotime($date);
            }
        } elseif (is_null($date))  {
            //为空默认取得当前时间戳
            $tmpdate = time();

        } elseif (is_numeric($date)) {
            //数字格式直接转换为时间戳
            $tmpdate = $date;

        } else {
            if (get_class($date) == "Date") {
                //如果是Date对象
                $tmpdate = $date->date;
            } else {
                //默认取当前时间戳
                $tmpdate = time();
            }
        }
        return $tmpdate;
    }

    /**
     * 验证日期数据是否有效
     * @access public
     * @param mixed $date 日期数据
     * @return string
     */
    public function valid($date) {

    }

    /**
     * 日期参数设置
     * @static
     * @access public
     * @param integer $date  日期时间戳
     * @return void
     */
    public function setDate($date) {
        $dateArray  =   getdate($date);
        $this->date         =   $dateArray[0];            //时间戳
        $this->second       =   $dateArray["seconds"];    //秒
        $this->minute       =   $dateArray["minutes"];    //分
        $this->hour         =   $dateArray["hours"];      //时
        $this->day          =   $dateArray["mday"];       //日
        $this->month        =   $dateArray["mon"];        //月
        $this->year         =   $dateArray["year"];       //年

        $this->weekday      =   $dateArray["wday"];       //星期 0～6
        $this->cWeekday     =   '星期'.$this->Week[$this->weekday];//$dateArray["weekday"];    //星期完整表示
        $this->yDay         =   $dateArray["yday"];       //一年中的天数 0－365
        $this->cMonth       =   $dateArray["month"];      //月份的完整表示

        $this->CDATE        =   $this->format("%Y-%m-%d");//日期表示
        $this->YMD          =   $this->format("%Y%m%d");  //简单日期
        $this->CTIME        =   $this->format("%H:%M:%S");//时间表示

        return ;
    }

    /**
     * 日期格式化
     * 默认返回 1970-01-01 11:30:45 格式
     * @access public
     * @param string $format  格式化参数
     * @return string
     */
    public function format($format = "%Y-%m-%d %H:%M:%S") {
        return strftime($format, $this->date);
    }

    /**
     * 是否为闰年
     * @static
     * @access public
     * @return string
     */
    public function isLeapYear($year='') {
        if(empty($year)) {
            $year = $this->year;
        }
        return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
    }

    /**
     * 计算日期差
     *
     *  w - weeks
     *  d - days
     *  h - hours
     *  m - minutes
     *  s - seconds
     * @static
     * @access public
     * @param mixed $date 要比较的日期
     * @param string $elaps  比较跨度
     * @return integer
     */
    public function dateDiff($date, $elaps = "d") {
        $__DAYS_PER_WEEK__       = (7);
        $__DAYS_PER_MONTH__       = (30);
        $__DAYS_PER_YEAR__       = (365);
        $__HOURS_IN_A_DAY__      = (24);
        $__MINUTES_IN_A_DAY__    = (1440);
        $__SECONDS_IN_A_DAY__    = (86400);
        //计算天数差
        $__DAYSELAPS = ($this->parse($date) - $this->date) / $__SECONDS_IN_A_DAY__ ;
        switch ($elaps) {
            case "y"://转换成年
                $__DAYSELAPS =  $__DAYSELAPS / $__DAYS_PER_YEAR__;
                break;
            case "M"://转换成月
                $__DAYSELAPS =  $__DAYSELAPS / $__DAYS_PER_MONTH__;
                break;
            case "w"://转换成星期
                $__DAYSELAPS =  $__DAYSELAPS / $__DAYS_PER_WEEK__;
                break;
            case "h"://转换成小时
                $__DAYSELAPS =  $__DAYSELAPS * $__HOURS_IN_A_DAY__;
                break;
            case "m"://转换成分钟
                $__DAYSELAPS =  $__DAYSELAPS * $__MINUTES_IN_A_DAY__;
                break;
            case "s"://转换成秒
                $__DAYSELAPS =  $__DAYSELAPS * $__SECONDS_IN_A_DAY__;
                break;
        }
        return $__DAYSELAPS;
    }

    /**
     * 人性化的计算日期差
     * @static
     * @access public
     * @param mixed $time 要比较的时间
     * @param mixed $precision 返回的精度
     * @return string
     */
    public function timeDiff( $time ,$precision=false) {
        if(!is_numeric($precision) && !is_bool($precision)) {
            static $_diff = array('y'=>'年','M'=>'个月','d'=>'天','w'=>'周','s'=>'秒','h'=>'小时','m'=>'分钟');
            return ceil($this->dateDiff($time,$precision)).$_diff[$precision].'前';
        }
        $diff = abs($this->parse($time) - $this->date);
        static $chunks = array(array(31536000,'年'),array(2592000,'个月'),array(604800,'周'),array(86400,'天'),array(3600 ,'小时'),array(60,'分钟'),array(1,'秒'));
        $count =0;
        $since = '';
        for($i=0;$i<count($chunks);$i++) {
            if($diff>=$chunks[$i][0]) {
                $num   =  floor($diff/$chunks[$i][0]);
                $since .= sprintf('%d'.$chunks[$i][1],$num);
                $diff =  (int)($diff-$chunks[$i][0]*$num);
                $count++;
                if(!$precision || $count>=$precision) {
                    break;
                }
            }
       }
        return $since.'前';
    }

    /**
     * 返回周的某一天 返回Date对象
     * @access public
     * @return Date
     */
    public function getDayOfWeek($n){
        $week = array(0=>'sunday',1=>'monday',2=>'tuesday',3=>'wednesday',4=>'thursday',5=>'friday',6=>'saturday');
        return (new Date($week[$n]));
    }

    /**
     * 计算周的第一天 返回Date对象
     * @access public
     * @return Date
     */
    public function firstDayOfWeek() {
        return $this->getDayOfWeek(1);
    }

    /**
     * 计算月份的第一天 返回Date对象
     * @access public
     * @return Date
     */
    public function firstDayOfMonth() {
        return (new Date(mktime(0, 0, 0,$this->month,1,$this->year )));
    }

    /**
     * 计算年份的第一天 返回Date对象
     * @access public
     * @return Date
     */
    public function firstDayOfYear() {
        return (new Date(mktime(0, 0, 0, 1, 1, $this->year)));
    }

    /**
     * 计算周的最后一天 返回Date对象
     * @access public
     * @return Date
     */
    public function lastDayOfWeek() {
        return $this->getDayOfWeek(0);
    }

    /**
     * 计算月份的最后一天 返回Date对象
     * @access public
     * @return Date
     */
    public function lastDayOfMonth() {
        return (new Date(mktime(0, 0, 0, $this->month + 1, 0, $this->year )));
    }

    /**
     * 计算年份的最后一天 返回Date对象
     * @access public
     * @return Date
     */
    public function lastDayOfYear() {
        return (new Date(mktime(0, 0, 0, 1, 0, $this->year + 1)));
    }

    /**
     * 计算月份的最大天数
     * @access public
     * @return integer
     */
    public function maxDayOfMonth() {
        $result = $this->dateDiff(strtotime($this->dateAdd(1,'m')),'d');
        return $result;
    }

    /**
     * 取得指定间隔日期
     *
     *    yyyy - 年
     *    q    - 季度
     *    m    - 月
     *    y    - day of year
     *    d    - 日
     *    w    - 周
     *    ww   - week of year
     *    h    - 小时
     *    n    - 分钟
     *    s    - 秒
     * @access public
     * @param integer $number 间隔数目
     * @param string $interval  比较类型
     * @return Date
     */
    public function dateAdd($number = 0, $interval = "d") {
        $hours =  $this->hour;
        $minutes =  $this->minute;
        $seconds =  $this->second;
        $month =  $this->month;
        $day =  $this->day;
        $year =  $this->year;

        switch ($interval) {
            case "yyyy":
                //---Add $number to year
                $year += $number;
                break;

            case "q":
                //---Add $number to quarter
                $month += ($number*3);
                break;

            case "m":
                //---Add $number to month
                $month += $number;
                break;

            case "y":
            case "d":
            case "w":
                //---Add $number to day of year, day, day of week
                $day += $number;
                break;

            case "ww":
                //---Add $number to week
                $day += ($number*7);
                break;

            case "h":
                //---Add $number to hours
                $hours += $number;
                break;

            case "n":
                //---Add $number to minutes
                $minutes += $number;
                break;

            case "s":
                //---Add $number to seconds
                $seconds += $number;
                break;
        }

        return (new Date(mktime($hours,
                                $minutes,
                                $seconds,
                                $month,
                                $day,
                                $year)));

    }

    /**
     * 日期数字转中文
     * 用于日和月、周
     * @static
     * @access public
     * @param integer $number 日期数字
     * @return string
     */
    public function  numberToCh($number) {
        $number = intval($number);
        $array  = array('一','二','三','四','五','六','七','八','九','十');
        $str = '';
        if($number  ==0)  { $str .= "十" ;}
        if($number  <  10){
           $str .= $array[$number-1] ;
        }
        elseif($number  <  20  ){
           $str .= "十".$array[$number-11];
        }
        elseif($number  <  30  ){
           $str .= "二十".$array[$number-21];
        }
        else{
           $str .= "三十".$array[$number-31];
        }
        return $str;
    }

    /**
     * 年份数字转中文
     * @static
     * @access public
     * @param integer $yearStr 年份数字
     * @param boolean $flag 是否显示公元
     * @return string
     */
    public function  yearToCh( $yearStr ,$flag=false ) {
        $array = array('零','一','二','三','四','五','六','七','八','九');
        $str = $flag? '公元' : '';
        for($i=0;$i<4;$i++){
            $str .= $array[substr($yearStr,$i,1)];
        }
        return $str;
    }

    /**
     *  判断日期 所属 干支 生肖 星座
     *  type 参数：XZ 星座 GZ 干支 SX 生肖
     *
     * @static
     * @access public
     * @param string $type  获取信息类型
     * @return string
     */
    public function magicInfo($type) {
        $result = '';
        $m      =   $this->month;
        $y      =   $this->year;
        $d      =   $this->day;

        switch ($type) {
        case 'XZ'://星座
            $XZDict = array('摩羯','宝瓶','双鱼','白羊','金牛','双子','巨蟹','狮子','处女','天秤','天蝎','射手');
            $Zone   = array(1222,122,222,321,421,522,622,722,822,922,1022,1122,1222);
            if((100*$m+$d)>=$Zone[0]||(100*$m+$d)<$Zone[1])
                $i=0;
            else
                for($i=1;$i<12;$i++){
                if((100*$m+$d)>=$Zone[$i]&&(100*$m+$d)<$Zone[$i+1])
                  break;
                }
            $result = $XZDict[$i].'座';
            break;

        case 'GZ'://干支
            $GZDict = array(
                        array('甲','乙','丙','丁','戊','己','庚','辛','壬','癸'),
                        array('子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥')
                        );
            $i= $y -1900+36 ;
            $result = $GZDict[0][$i%10].$GZDict[1][$i%12];
            break;

        case 'SX'://生肖
            $SXDict = array('鼠','牛','虎','兔','龙','蛇','马','羊','猴','鸡','狗','猪');
            $result = $SXDict[($y-4)%12];
            break;

        }
        return $result;
    }

    public function __toString() {
        return $this->format();
    }
}