Bug with Zend_Date calculating subtraction two date - php

I write this function:
public function calcDifferentDate($dateStart, $dateEnd = false, $output = Zend_Date::DAY)
{
$dateEnd = $dateEnd ? $dateEnd : Zend_Date::now()->toString('YYYY-MM-dd');
$dateStartZD = new Zend_Date($dateStart, 'YYYY-MM-dd');
$dateEndZD = new Zend_Date($dateEnd, 'YYYY-MM-dd');
return $dateEndZD->sub($dateStartZD)->toString($output);
}
If call this:
echo calcDifferentDate('2011-11-10');
and today is: '2011-11-14'
the output returned is 05 and not 04
why? where am I doing wrong?
P.S. I use ZF 1.11.11 version
I found the solution
this work right! :D
public function calcDaysDiffDate($dateStart, $dateEnd = '')
{
$dateEnd = !empty($dateEnd) ? $dateEnd : Zend_Date::now()->toString('YYYY-MM-dd');
$dateStartZD = new Zend_Date($dateStart, 'YYYY-MM-dd');
$dateEndZD = new Zend_Date($dateEnd, 'YYYY-MM-dd');
$dateStartZD->sub($dateEndZD);
return $dateStartZD->getTimestamp() / (60 * 60 * 24);
}

Try returning this instead:
$newDate = new Zend_Date($dateEndZD->sub($dateStartZD), 'YYYY-MM-dd');
return $newDate->get($output);
The calculations are incorrect, I will try to get to that later. But for now, you'll need your logic to be similar to that, because like I said in my comment, your method was resulting in a fatal error due to the fact that your date subtraction was returning an integer instead of a Zend_Date object from which to call toString().
Edit
Sorry about my presumptuous, not well-thought-out previous answer. After more careful testing I believe I found your issue. The sub() function accepts an optional second param $part which is the part of the date will be returned from the resulting date subtraction. No need to call a toString() now even if you could.
So without further adieu, here it is with the fixed return statement:
public function calcDifferentDate($dateStart, $dateEnd = false, $output = Zend_Date::DAY)
{
$dateEnd = $dateEnd ? $dateEnd : Zend_Date::now()->toString('YYYY-MM-dd');
$dateStartZD = new Zend_Date($dateStart, 'YYYY-MM-dd');
$dateEndZD = new Zend_Date($dateEnd, 'YYYY-MM-dd');
return $dateEndZD->sub($dateStartZD, $output); // <-- fixed
}
Second Edit
After chatting with OP, it appears that my solution will not work for ZF 1.11.x due to the differences in the Zend_Date::sub() method.

The accepted answer for this question: How to compare the date parts of two Zend_Date objects? recommends using DateTime instead of Zend_Date in the following way (I've modified the code a bit to suit your needs):
$date1 = new DateTime('2011-11-14');
$date2 = new DateTime('2011-11-10');
$diffDays = $date1->diff($date2)->days;
I've tried it and it seems to return the correct result. It could be a good alternative to Zend_Date, if you are not absolutely required to use it.
Hope that helps,

I find solution:
public function calcDaysDiffDate($dateStart, $dateEnd = '')
{
$dateEnd = !empty($dateEnd) ? $dateEnd : Zend_Date::now()->toString('YYYY-MM-dd');
$dateStartZD = new Zend_Date($dateStart, 'YYYY-MM-dd');
$dateEndZD = new Zend_Date($dateEnd, 'YYYY-MM-dd');
$dateStartZD->sub($dateEndZD);
return $dateStartZD->getTimestamp() / (60 * 60 * 24);
}

Related

How to insert in a wordpress page a widget that shows days since a specific order was made?

I have to output this information but I am not used to work with PHP, don´t know what I am doing wrong.
I query woocommerce order date like this:
$order = wc_get_order(456);
$orderdate = $order->date_created;
That seems to be working ok, it returns a date in this format:
2020-10-15T17:38:37-03:00
For current date, I create a variable like this:
$date = date('d-m-y h:i:s');
But when I try to output the difference in days between order date and current, it always give me 0
This is how I tried to calculate the difference:
function dateDiff($date1, $date2)
{
$date1_ts = strtotime($date);
$date2_ts = strtotime($orderdate);
$diff = $date2_ts - $date1_ts;
return round($diff / 86400);
}
$dateDiff = dateDiff($date1, $date2);
echo ("day difference is: " . $dateDiff );
Thanks a lot for reading, hope you can help me.
Short explanation of what is wrong with your code:
$dateDiff = dateDiff($date1, $date2);
In your case dateDiff is a function which expects two parameters $date1 and $date2. But you are passing non existing variables to the function. They do not exist out of the function scope, because you didn't declare them.
Then you are trying to get timestamps from dates which are in parent scope and probably getting NULL as a result from both cases :)
$date1_ts = strtotime($date);
$date2_ts = strtotime($orderdate);
Small improvements:
Its better to use DateTime class when you are working with dates. DateTime class has powerful method called format that allows you to output date in suitable format for you.
If we combine what you did into one piece of code with some changes, then we will get this:
$order = wc_get_order(456);
$order_date = new DateTime($order->date_created);
$current_date = new DateTime();
function dateDiff($date1, $date2)
{
// check if diff is not equal to zero in order to avoid division be zero exception
if ($diff = abs(strtotime($date2) - strtotime($date1))) {
return round($diff / 86400);
}
return 0;
}
echo ("day difference is: " . dateDiff(
$current_date->format('d-m-Y h:i:s'),
$order_date->format('d-m-Y h:i:s')
) . " days");

How to mock date in php unit test?

I'm new to php unit testing. How do I mock the date in the function below. Currently it is getting the current date. But I want to change the date in the mock to the first day of a month.
function changeStartEndDate() {
if (date('j', strtotime("now")) === '1') {
$this->startDate = date("Y-n-j", strtotime("first day of previous month"));
$this->endDate = date("Y-n-j", strtotime("last day of previous month")) . ')';
} else {
$this->startDate = date("Y-n-j", strtotime(date("Y-m-01")));
$this->endDate = date("Y-n-j", strtotime("yesterday"));
}
}
I've tried doing this but its not working.
public function testServicesChangeStartEndDate() {
$mock = $this->getMockBuilder('CoreFunctions')
->setMethods(array('changeStartEndDate'))
->getMock();
$mock->method('changeStartEndDate')
->with(date("Y-n-j", strtotime(date("Y-m-01"))));
$this->assertSame(
'1',
$this->core->changeStartEndDate()
);
}
Unit testing works best by avoiding side effects. Both date and strtotime are depending on an external state defined on your host system, namely the current time.
One way to deal with that is to make current time an injectable property allowing you to "freeze" it or to set it to a specific value.
If you look at at the definition to strtotime it allows setting current time:
strtotime ( string $time [, int $now = time() ] ) : int
Same with date:
date ( string $format [, int $timestamp = time() ] ) : string
So always inject that value from your function to decouple the results of your code from your host's state.
function changeStartEndDate($now) {
if (date('j', strtotime("now", $now), $now) === '1') {
...
$this->startDate = date("Y-n-j", strtotime(date("Y-m-01", $now), $now));
$this->endDate = date("Y-n-j", strtotime("yesterday", $now), $now);
}
Is your function part of a class? I would then make the $now part of the constructor, and have it default to time(). In your test cases, you can always inject a fixed number, and it will always return the same output.
class MyClassDealingWithTime {
private $now;
public function __construct($now = time()) {
$this->now = $now;
}
private customDate($format) {
return date($format, $this->now);
}
private customStringToTime($timeSring) {
return strtotime($timeStrimg, $this->now);
}
}
In your test cases you then set $now to the value you need, e.g. via
$firstDayOfAMonth = (new DateTime('2017-06-01'))->getTimestamp();
$testInstance = new MyClassDealingWithTime(firstDayOfAMonth);
$actual = $testInstance->publicMethodYouWantTotest();
...
Disclaimer: I wrote the library mentioned on this answer.
I'm adding an answer to provide an alternative way that works with zero modifications to your code, and no need to inject the current time.
If you can afford to install the php uopz extension, then you can use https://github.com/slope-it/clock-mock.
You can then use ClockMock::freeze and ClockMock::reset to "move" the internal php clock to a specific point in time during your tests.

Retrieving the last 7 days with RethinkDB during()

I have been trying for some hours now to try and return all results in the last 7 days, using RethinkDB and PHP.
Surveying the documentation at https://www.rethinkdb.com/docs/dates-and-times/javascript/ I have found the most appropriate RethinkDB function would be during().
Below is my code that I have come to the realization that this task is far harder than it appears. I have attempted to use the DateTime from PHP directly into the during, but this was also not successful.
$sevenago = new DateTime('7 days ago');
$sevenago->setTimeZone(new DateTimeZone('Asia/Kolkata'));
$sevenago = date_format($sevenago, 'U');
$now = new DateTime();
$now->setTimeZone(new DateTimeZone('Asia/Kolkata'));
$now = date_format($now, 'U');
$ordersLastWeek = r\table("orders")->filter(function($doc) {
return $doc('status')->eq('shipped')
->rAnd($doc('time')->during(r\epochTime($sevenago), r\epochTime($now)));
})->run($conn);
The time field is stored as follows (standard RethinkDB DateTime type):
{"$reql_type$":"TIME","epoch_time":1509557927.661,"timezone":"+00:00"}
Any help would be appreciated.
Try this:
$ordersLastWeek = r\table("orders")->filter(function($doc) use ($sevenago, $now) {
return $doc('status')->eq('shipped')->rAnd($doc('time')->during(r\epochTime($sevenago), r\epochTime($now)));
})->run($conn);
In your code you forgot to use use ($sevenago, $now) for the filter function.
$ordersLastWeek = r\table("orders")->filter(function($doc) {
return $doc('status')->eq('shipped')
->rAnd(($doc('time') > r\epoch_time($sevenago)));
})->run($conn);
Have you tried this. Hope it works.

PHP: DateTime '-1 day'

I want to get the date of yesterday of the current date in a specifc time zone.
I tried like this, but it is not working:
$date = new DateTime(NULL, new DateTimeZone('Pacific/Wake'));
$yesterday = $date->modify( '-1 day' );
$yesterday = $yesterday->format('Y-m-d');
I am still getting today's date.
This problem, according to the documentation for the modify() method, seems to entirely depend on which version of php you're using. In this case, method chaining(which is what you're attempting to do is called), is only available on php version 5.3.0 and up, according to the changelog on the previously linked docs.
That in mind, it explains why your code didn't work, and #Deryck's did. If you ever do upgrade your php version, or get your host to upgrade it, you could likely reduce those three lines to two:
$date = new DateTime(NULL, new DateTimeZone('Pacific/Wake'));
$date = $date->modify( '-1 day' )->format('Y-m-d');
Not much of an improvement, I realize, but there's your reason for why it failed to work.
Below are two of the methods I see of getting around this; one is creation of a class.. which seems like overkill to me unless this is apart of something grander... the other is a creation of a function. Both shove the extra lines into something that takes up less space, in a sense.
class DT {
private $now; //can be null
private $timezone; //DateTimeZone object
public function __construct($tz_str, $now = NULL) {
$this->now = $now;
$this->timezone = new DateTimeZone($tz_str);;
}
public function mod($mod,$format) {
$dt = new DateTime($this->now, $this->timezone);
$dt->modify($mod);
return $dt->format($format);
}
}
function mod_formatted_zone($tz_str, $mod, $format, $now = NULL) {
$timezone = new DateTimeZone($tz_str);
$dt = new DateTime($now,$timezone);
$dt->modify($mod);
return $dt->format($format);
}
The use of either is simple; in the case of the class, it'd be something like..
$dt = new DT('Pacific/Wake');
echo $dt->mod('-1 day', 'Y-m-d');
While in the case of the function, it'd simply be..
echo mod_formatted_zone('Pacific/Wake', '-1 day', 'Y-m-d');
Seems to work once you don't re-assign the $date variable unnecessarily. See below:
<?php
$date = new DateTime(NULL, new DateTimeZone('Pacific/Wake'));
$date->modify("-1 day");
$date = $date->format("Y-m-d");
// echo $date; // just in case you wanna echo - ya dig
?>
View demo
FYI:
Wake Island Time Zone (UTC+12:00)
Which means 1 day before is actually today (for me at least, on the western hemisphere).
I want to get the date of yesterday of the current date in a specifc time zone.
You can specify relative dates in the DateTime constructor. This will work for you:-
$yesterday = new DateTime('- 1 day', new DateTimeZone('Pacific/Wake'));
var_dump($yesterday);
Proof!.

Is it possible to change data type from String to Date

I'm getting in trouble where I was coding for connection using OpenX API with XML-RPC2. I get the problem that the data type is required by the fire function is the dateTime.iso8601.
This is my code:
$sdatetime = new DateTime('2013-01-01 00:00:00');
$edatetime = new DateTime('2013-06-01 00:00:00');
$startDate = $sdatetime->format(DateTime::ISO8601);
$endDate = $edatetime->format(DateTime::ISO8601);
try {
$result = $aClient->agencyPublisherStatistics($sessionId, 1, $startDate, $endDate);
print_r($result);
} catch (XML_RPC2_FaultException $e) {
die('Exception #' . $e->getFaultCode() . ' : ' . $e->getFaultString());
}
This is result error, when I run script above:
Exception #3 : Incorrect
parameters passed to method: Wanted dateTime.iso8601, got string at
param 3
If I run print_r(gettype($startDate)); I get the type data is string not date.
My question, for variables $startDate and $endDate how to make their data type to be dateTime.iso8601 or date rather than string.
Thanks.
it looks like your agencyPublisherStatistics requires specific XML_RPC2_Value date object. You cancreate this by using.
$startDate = XML_RPC2_Value::createFromNative($startDate, ‘datetime’);
same for the end date.. let me know if this works..
Try this,
$sdatetime = date(DATE_ISO8601, strtotime('2013-01-01 00:00:00'));
$edatetime = date(DATE_ISO8601, strtotime('2013-06-01 00:00:00'));
OR
Check below links,
http://pear.php.net/manual/en/package.webservices.xml-rpc2.client.php
https://bugs.php.net/bug.php?id=51950
may this help you.
use DateTime::setISODate
$sdatetime = new DateTime('2013-01-01 00:00:00');
$edatetime = new DateTime('2013-06-01 00:00:00');
$startDate = $sdatetime->setISODate(2013);
$endDate = $edatetime->setISODate(2013);

Categories