Open lukBB opened 6 years ago
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
common/components
folder and rename it as ExperssionHelper
. JsExpressionHelper
to ExpressionHelper
. /**
* 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);
}
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');
}
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'));
}
@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?
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.')');
}
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 ?