<?php

class DateTimeEx implements ISortable
{

  /**
   *
   * @var integer
   */
  public $year;

  /**
   *
   * @var integer
   */
  public $month;

  /**
   *
   * @var integer
   */
  public $day;

  /**
   *
   * @var integer
   */
  public $hour;

  /**
   *
   * @var integer
   */
  public $minute;

  /**
   *
   * @var integer
   */
  public $second;

  /**
   *
   * @param mixed $dateTime          
   */
  public function __construct($dateTime = null)
  {
    $obj = DateHelper::ensureObject ( $dateTime );
    $this->year = intval ( $obj->format ( 'Y' ) );
    $this->month = intval ( $obj->format ( 'n' ) );
    $this->day = intval ( $obj->format ( 'j' ) );
    $this->hour = intval ( $obj->format ( 'G' ) );
    $this->minute = intval ( $obj->format ( 'i' ) );
    $this->second = intval ( $obj->format ( 's' ) );
  }

  /**
   *
   * @param string $format          
   * @return string
   */
  public function format($format)
  {
    return $this->toDateTime ()
      ->format ( $format );
  }

  /**
   *
   * @return DateTime
   */
  public function toDateTime()
  {
    $obj = new DateTime ();
    $obj->setDate ( $this->year, $this->month, $this->day );
    $obj->setTime ( $this->hour, $this->minute, $this->second );
    return $obj;
  }

  /**
   *
   * @return string
   */
  public function toDateTimeString()
  {
    return DateHelper::formatISO9075DateTime ( $this->toDateTime () );
  }

  /**
   *
   * @return string
   */
  public function toSmartString()
  {
    if ($this->toTimeString () == '00:00:00')
      return $this->toDateString ();
    else
      return $this->toString ();
  }

  /**
   *
   * @return string
   */
  public function toFilesafeDateTimeString()
  {
    return $this->format ( 'Ymd_His' );
  }

  /**
   *
   * @return string
   */
  public function toMigrationDateTimeString()
  {
    return $this->format ( 'ymd_His' );
  }

  /**
   *
   * @return string
   */
  public function toDateString()
  {
    return DateHelper::formatISO9075Date ( $this->toDateTime () );
  }

    /**
   *
   * @return string
   */
  public function toDateStringWithoutYear()
  {
    return $this->format("m-d");
  }

/**
   *
   * @param
   *          boolean correct default false
   * @return integer correct==true ? base1monday : base0sunday
   */
  public function getDayOfWeek($correct = false)
  {
    $dow = $this->toDateTime ()
      ->format ( 'w' );
    if ($correct)
      if ($dow == 0)
        $dow = 7;
    return $dow;
  }

  /**
   *
   * @return string
   */
  public function getDayOfWeekName()
  {
    $dow = $this->getDayOfWeek ( true );
    return DateHelper::getDayName ( $dow );
  }

  /**
   *
   * @return string
   */
  public function getMonthName()
  {
    return DateHelper::getMonthName ( $this->month );
  }

  /**
   *
   * @return string
   */
  public function toTimeString()
  {
    return DateHelper::formatISO9075Time ( $this->toDateTime () );
  }

  /**
   */
  public function incMonth()
  {
    if ($this->month == 12)
    {
      $this->month = 1;
      $this->year ++;
    }
    else
      $this->month ++;
    $days = $this->daysInMonth ();
    if ($this->day > $days)
      $this->day = $days;
  }

  /**
   */
  public function decMonth()
  {
    if ($this->month == 1)
    {
      $this->month = 12;
      $this->year --;
    }
    else
      $this->month --;
    $days = $this->daysInMonth ();
    if ($this->day > $days)
      $this->day = $days;
  }

  /**
   *
   * @return number
   */
  public function daysInMonth()
  {
    return cal_days_in_month ( CAL_GREGORIAN, $this->month, $this->year );
  }

  /**
   *
   * @return string
   */
  public function __toString()
  {
    return $this->toDateTimeString ();
  }

  /**
   *
   * @return string
   */
  public function toString()
  {
    return $this->__toString ();
  }

  /**
   *
   * @param string $str          
   * @return DateTimeEx the $this object
   */
  public function addFromString($str)
  {
    $dt = $this->toDateTime ();
    $dt->add ( date_interval_create_from_date_string ( $str ) );
    $this->set ( $dt );
    return $this;
  }

  /**
   *
   * @param string $str          
   * @return DateTimeEx the $this object
   */
  public function subFromString($str)
  {
    $dt = $this->toDateTime ();
    $dt->sub ( date_interval_create_from_date_string ( $str ) );
    $this->set ( $dt );
    return $this;
  }

  /**
   *
   * @param unknown $other          
   * @return DateInterval
   */
  public function subDate($other)
  {
    $other = new DateTimeEx ( $other );
    $diff = $this->toDateTime ()
      ->diff ( $other->toDateTime () );
    return $diff;
  }

  /**
   *
   * @param mixed $dateTime          
   * @return DateTimeEx
   */
  public function set($dateTime)
  {
    $obj = DateHelper::ensureObject ( $dateTime );
    $this->year = intval ( $obj->format ( 'Y' ) );
    $this->month = intval ( $obj->format ( 'n' ) );
    $this->day = intval ( $obj->format ( 'j' ) );
    $this->hour = intval ( $obj->format ( 'G' ) );
    $this->minute = intval ( $obj->format ( 'i' ) );
    $this->second = intval ( $obj->format ( 's' ) );
    return $this;
  }

  /**
   *
   * @param string $h          
   * @param string $m          
   * @param string $s          
   * @return DateTimeEx
   */
  public function setTime($h = null, $m = null, $s = null)
  {
    if ($h !== null)
    {
      $h = intval ( $h );
      if ($h >= 0 && $h < 24)
        $this->hour = intval ( $h );
    }
    if ($m !== null)
    {
      $m = intval ( $m );
      if ($m >= 0 && $m < 60)
        $this->minute = intval ( $m );
    }
    if ($s !== null)
    {
      $s = intval ( $s );
      if ($s >= 0 && $s < 60)
        $this->second = intval ( $s );
    }
    return $this;
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getToday()
  {
    $now = clone $this;
    $now->setTime ( 0, 0, 0 );
    return $now;
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getFirstDayOfWeek()
  {
    $now = clone $this;
    $dow = $now->getDayOfWeek ( true );
    $days = $dow - 1;
    if ($days > 0)
      $now->subFromString ( "$days day" );
    $now->setTime ( 0, 0, 0 );
    return $now;
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getLastDayOfWeek()
  {
    $now = clone $this;
    $dow = $now->getDayOfWeek ( true );
    $days = 7 - $dow;
    if ($days > 0)
      $now->addFromString ( "$days day" );
    $now->setTime ( 0, 0, 0 );
    return $now;
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getFirstDayOfMonth()
  {
    $now = clone $this;
    $now->day = 1;
    $now->setTime ( 0, 0, 0 );
    return $now;
  }

  /**
   *
   * @return DateTimeEx
   */
  public function getLastDayOfMonth()
  {
    $now = clone $this;
    $now->day = $now->daysInMonth ();
    $now->setTime ( 0, 0, 0 );
    return $now;
  }

  /**
   *
   * @param $other DateTimeEx
   *          (non-PHPdoc)
   * @see ISortable::compare()
   */
  public function compare($other)
  {
    if ($this->year == $other->year)
      if ($this->month == $other->month)
        if ($this->day == $other->day)
          if ($this->hour == $other->hour)
            if ($this->minute == $other->minute)
              if ($this->second == $other->second)
                return 0;
              else
                return $this->second < $other->second ? - 1 : 1;
            else
              return $this->minute < $other->minute ? - 1 : 1;
          else
            return $this->hour < $other->hour ? - 1 : 1;
        else
          return $this->day < $other->day ? - 1 : 1;
      else
        return $this->month < $other->month ? - 1 : 1;
    else
      return $this->year < $other->year ? - 1 : 1;
  }

  /**
   *
   * @return boolean
   */
  public function isBeforeNow($now = null)
  {
    $now = new DateTimeEx ( $now );
    return $this->compare ( $now ) < 0;
  }

  /**
   *
   * @return boolean
   */
  public function isAfterNow($now = null)
  {
    $now = new DateTimeEx ( $now );
    return $this->compare ( $now ) > 0;
  }

  /**
   *
   * @return boolean
   */
  public function isToday()
  {
    $a = clone $this;
    $a->setTime ( 0, 0, 0 );
    $b = new DateTimeEx ();
    $b->setTime ( 0, 0, 0 );
    return $a->compare ( $b ) == 0;
  }

  /**
   *
   * @return boolean
   */
  public function isValid()
  {
    if (in_range ( $this->month, 1, 12 ))
      if (in_range ( $this->day, 1, $this->daysInMonth () ))
        if (in_range ( $this->hour, 0, 23 ))
          if (in_range ( $this->minute, 0, 59 ))
            if (in_range ( $this->second, 0, 59 ))
              return true;
    return false;
  }

  /**
   *
   * @return DateTimeEx
   */
  public static function now()
  {
    return new DateTimeEx ();
  }

  /**
   *
   * @param string $format
   *          default null
   * @return string
   */
  public static function nows($format = null, $interval = null)
  {
    if (isEmpty ( $format ))
      $format = DateHelper::ISO9075_DATETIME;
    
    if (!isEmpty ( $interval ))
    {
    	return self::now ()->addFromString($interval)->format ( $format );
    }
      
    return self::now ()->format ( $format );
  }
  


  /**
   *
   * @param mixed $x          
   * @param mixed $y          
   * @return number
   */
  public static function compareStatic($x, $y)
  {
    $x = new DateTimeEx ( $x );
    $y = new DateTimeEx ( $y );
    return $x->compare ( $y );
  }

  /**
   * parse a date time string with the specified format
   *
   * @param string $string          
   * @param string $format
   *          optional (default is DateHelper::ISO9075_DATETIME='Y-m-d H:i:s')
   * @param DateTimeEx $default
   *          optional (default is NULL)
   * @return DateTimeEx or null on fail
   */
  public static function parse($string, $format = DateHelper::ISO9075_DATETIME, DateTimeEx $default = null)
  {
    $dte = DateTime::createFromFormat ( $format, $string );
    if ($dte == null)
      return $default;
    else
      return new DateTimeEx ( $dte );
  }

  public static function parseDate($string, $time = "00:00:00", $format = DateHelper::ISO9075_DATETIME, DateTimeEx $default = null)
  {
    return self::parse ( "{$string} {$time}", $format, $default );
  }

  /**
   *
   * @return DateTimeEx
   */
  public function __clone()
  {
    return new DateTimeEx ( $this );
  }
}
