Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Date shown in the widget is always showing month + 1 #6

Open
lukBB opened this issue Jan 28, 2018 · 4 comments
Open

Date shown in the widget is always showing month + 1 #6

lukBB opened this issue Jan 28, 2018 · 4 comments

Comments

@lukBB
Copy link

lukBB commented Jan 28, 2018

Thank you for this extension. I'm trying to integrate it into my yii2 website. I'm using active data provider like in the code below but each calendar item is showing wrong month. eg item from 19th of May is showing 19th of June in the widget (please see the picture attached). Could you advised please what might be the issue ?

echo ActiveCalendar::widget([
'language' => 'en',

'dataProvider' => new ActiveDataProvider([
    'query' => Event::find()->andWhere(['calendar_id' => $calendar->id])
]),
'options' => [
    // HTML attributes for the container.
    // the `tag` option is specially handled as the HTML tag name
],
'clientOptions' => [
	'enableContextMenu' => true,
	'enableRangeSelection' => true,
	'displayWeekNumber' => false,
	'alwaysHalfDay' =>true,
	'disabledDays'=> [],
	'startYear'=> '2018',
	'minDate'=> new JsExpression('new Date("2018-01-01")'),
	'maxDate'=> new JsExpression('new Date("2018-12-31")'),
	
    // JS Options to be passed to the `calendar()` plugin.
    // see http://bootstrap-year-calendar.com/#Documentation/Options
	// The `dataSource` property will be overwritten by the dataProvider.
],
'clientEvents' => [
	'mouseOnDay' => '',
    // JS Events for the `calendar()` plugin.
    // see http://bootstrap-year-calendar.com/#Documentation/Events
]
]);

$i = 1;
foreach($calendar->events as $event) {
echo 'Event ' . $i . ' start date: ' . $event->startDate . '- ';
echo 'end date: ' . $event->endDate . '<br/>';
$i++;
}

snip_year_calendar

@buttflattery
Copy link

There looks to be a logical fault in the Helper JsExpressionHelper and that needs a fix in the method parsePhpDate.

#What's The problem#

What it is doing is that when you call the method

return JsExpressionHelper::parse($this->event_date);

in your model passing it the date string or object it returns back the Javascript expression new Date(2018, 05, 19) now look closely there is a difference when you call the Date function of javascript by passing the date as string and date as parameters(year,month,date).

As in javascript Date() function starting month January is 0 rather than 1.

try example in your console

    console.log("new Date(2015,10,10) outputs : ", new Date(2015, 10, 10));
    console.log("new Date('2015-10-10') outputs : ", new Date('2015-10-10'));

So in this case the helper is passing the month date and year by using php date function like JsExpression('new Date(' . $date->format('Y, m, d') . ')');
and that too without decrementing by one, means it is using php date function and it has month representation starting from 1-12 or 01-12.
So the datepicker is loading the May date into June which is technically correct at its end.

#What to do#

You need to change the javascript expression returned by the function either return the expression with string or provide all the parameters manually.

But since the helper has all static functions and you cant extend them i would suggest

  1. You copy that helper to the common/components folder and rename it as ExperssionHelper.
  2. In your model change the calls to JsExpressionHelper to ExpressionHelper.
  3. Then change the code to the following you can have 2 approaches to it first try this one by using string format if it works
     /**
         * Parses a DateTime object to a `JsExpression` containing a javascript date
         * object.
         *
         * @param DateTime $date
         * @return JsExpression
         */
        public static function parsePhpDate(DateTime $date)
        {
            return new JsExpression('new Date("' . $date->format('Y-m-d') . '")');
        }

If for some reason the calendar does not work with that format change it to the following and it will work

/**
  * Parses a DateTime object to a `JsExpression` containing a javascript date
  * object.
  *
  * @param DateTime $date
  * @return JsExpression
  */
 public static function parsePhpDate(DateTime $date)
 {
     $month=$date->format('m')-1;
     $day=$date->format('d');
     $year=$date->format('Y');
     
     return new JsExpression('new Date('.$year.','.$month.','.$day.')');

 }

You can copy the following to your folder common/components

    <?php 
    namespace common\components;
    
    use DateTime;
    use yii\base\InvalidParamException;
    use yii\web\JsExpression;
    
    /**
     * Helper to parse data into
     * [JsExpression](http://www.yiiframework.com/doc-2.0/yii-web-jsexpression.html) * which is what the widgets expect when dealing with dates for the JS plugin.
     *
     * Its main usage is in classes implementing the [[DataItem]] interface
     *
     * ```php
     * public function getStartDate()
     * {
     *     return JsExpressionHelper::parse($this->start_date);
     * }
     * ```
     *
     * @author Angel (Faryshta) Guevara <angeldelcaos@gmail.com>
     */
    class ExpressionHelper
    {
        /**
         * Parses a date to a `JsExpression` containing a javascript date object.
         *
         * @param DateTime|string|integer $date
         * @param string $format only used when the ``$date` param is an string
         * @return JsExpression
         */
        public static function parse($date, $format = 'Y-m-d')
        {
            if (is_string($date)) {
                echo "string<br />";
                return self::parseString($date, $format);
            }
            if (is_integer($date)) {
                return self::parseTimestamp($date);
            }
            if (is_object($date) && $date instanceof DateTime) {
                return self::parsePhpDate($date);
            }
    
            throw new InvalidParamException('The parameter `$date` must be a '
                . 'formatted string, a timestamp or a `DateTime` object'
            );
        }
    
        /**
         * Parses a DateTime object to a `JsExpression` containing a javascript date
         * object.
         *
         * @param DateTime $date
         * @return JsExpression
         */
        public static function parsePhpDate(DateTime $date)
        {
            $month=$date->format('m')-1;
            $day=$date->format('d');
            $year=$date->format('Y');
            
            return new JsExpression('new Date('.$year.','.$month.','.$day.')');
        }
    
        /**
         * Parses an string to a `JsExpression` containing a javascript date object.
         *
         * @param string $date
         * @param string $format used to create a temporal `DateTime` object
         * @return JsExpression
         * @see http://php.net/manual/es/datetime.format.php
         */
        public static function parseString($date, $format = 'Y-m-d')
        {
            return self::parsePhpDate(DateTime::createFromFormat(
                $format,
                $date
            ));
        }
    
        /**
         * Parses a timestamp integer to a `JsExpression` containing a javascript
         * date object.
         *
         * @param integer $date
         * @return JsExpression
         * @see http://php.net/manual/es/datetime.settimestamp.php
         */
        public static function parseTimestamp($date)
        {
            $PhpDate = new DateTime();
            $PhpDate->setTimestamp($date);
            return self::parsePhpDate($PhpDate);
        }
    }

You Event Model should look like

    use common\components\ExpressionHelper;
    class Event extends \yii\db\ActiveRecord implements DataItem
    {
    
    public function getName()
    {
        return $this->event_title;
    }
    
    public function getStartDate()
    {
        return ExpressionHelper::parse($this->event_date);
    }
    
    public function getEndDate()
    {
        return ExpressionHelper::parse($this->event_date_end);
    }

@s1lver
Copy link

s1lver commented Apr 2, 2018

I think the problem is not even this. I use the following expression:

       /**
	 * @return \yii\web\JsExpression
	 */
	public function getStartDate() {
		return JsExpressionHelper::parse($this->date, 'Y-n-j');
	}

Date format

I get the correct JS line :

"startDate":new Date(2018, 3, 29),
"endDate":new Date(2018, 3, 29)

But, the date still shifts for a month ahead. The problem is that in JS months begin with 0. Therefore, even if you pass immediately the correct date format. The final date will be shifted to +1.

MDN
@lukBB
Proceeding from the foregoing, the problem is solved by a simple preparation of the date:

	/**
	 * @return \yii\web\JsExpression
	 */
	public function getStartDate() {
		$datetime = new \DateTime($this->date);
		$date = $datetime->sub(new \DateInterval('P1M'));

		return JsExpressionHelper::parse($date->format('Y-n-j'));
	}

@Faryshta
Copy link
Member

@s1lver with your approach how does january gets converted? wouldnt it switch to december previous year?

and what happens with dates which are not existent previous year, for example converting march 31 to february 31?

@s1lver
Copy link

s1lver commented Jan 9, 2019

You're right. Perhaps this solution is better.

/**
 * @return JsExpression
 * @throws \Exception
 */
public function getStartDate() {
	$datetime = new \DateTime($this->date);
	$month = $datetime->format('m') - 1;
	$day = $datetime->format('d');
	$year = $datetime->format('Y');

	return new JsExpression('new Date('.$year.','.$month.','.$day.')');
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants