I have integrated bootstrap year calendar yii2 into my website. I'm using active data provider to load the calendar items. I have encountered one issue so far. The month of each calendar item is not correct. One month is added to each calendar item. Eg item from 19th of May is shown as 19th of June etc. Has anyone faced a similar issue in the past?
echo ActiveCalendar::widget([
'language' => 'es',
'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->startDate . '<br/>';
$i++;
}
Edit: model as in the plugin documentation
class Event extends \yii\db\ActiveRecord implements DataItem
{
public function getName()
{
return $this->event_title;
}
public function getStartDate()
{
return JsExpressionHelper::parse($this->event_date);
}
public function getEndDate()
{
return JsExpressionHelper::parse($this->event_date_end);
}
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 Date(string)
and date as Date(year,month,date)
.
As in javascript Date()
function starting month January
is 0
rather than 1
.
See example
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.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 <[email protected]>
*/
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);
}
}
Your 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);
}