I need to retrieve all items of a particular type with Doctrine, where the 'date' field is stored as DateTime. Which means I'm working with the PHP DateTime object as well.
I need to do two things overall: if given a month and year, retrieve all records for that month and year; and if not given a month and year, retrieve all records for this month.
I've been looking at using BETWEEN statements in the Doctrine Query Builder, but can't figure out how to cleanly extract the month and year from PHP DateTime objects created 'now' so that the time portion of DateTime doesn't affect the query. I also can't see a way of just setting the day portion of the Date in DateTime to be 01 either, without affecting the month and year.
I thought this would be a fairly standard task but can't find a solution. Any help appreciated.
EDIT:
Found a hacky way to get the dates for between, but Doctrine is returning an empty array. Using the following code
/**
* Returns feed for month and year
*/
public function getMonthYearFeed($month, $year)
{
// Create two times at the start of this month and next month
$startDate = \DateTime::createFromFormat('d-n-Y', "01-".$month."-".$year);
$startDate->setTime(0, 0 ,0);
$endDate = \DateTime::createFromFormat('d-n-Y', "01-".($month+1)."-".$year);
$endDate->setTime(0, 0, 0);
$notes = $this->em->getRepository('AppBundle:Note')->createQueryBuilder('n')->where('n BETWEEN :start AND :end')->setParameter('start', $startDate->format('Y-m-d H:i:s'))->setParameter('end', $endDate->format('Y-m-d H:i:s'))->getQuery()->getResult();
return $notes;
}
Putting something like this in your Repository should get you started. I haven't tested it aside from the 'last day of this month' bit which seems to work fine.
/**
* #param int $month
* #param int $year
*
* #return object[]
*/
public function findByDate($year = null, $month = null)
{
if ($month === null) {
$month = (int) date('m');
}
if ($year === null) {
$year = (int) date('Y');
}
$startDate = new \DateTimeImmutable("$year-$month-01T00:00:00");
$endDate = $startDate->modify('last day of this month')->setTime(23, 59, 59);
$qb = $this->createQueryBuilder('object');
$qb->where('object.date BETWEEN :start AND :end');
$qb->setParameter('start', $startDate->format('Y-m-d H:i:s'));
$qb->setParameter('end', $endDate->format('Y-m-d H:i:s'));
return $qb->getQuery()->getResult();
}
Very importand moment. Use data type of setParameter.
Like this for symfony.
use Doctrine\DBAL\Types\Type;
$query->setParameter('start', $startDate, Type::DATETIME);
This appears to be working, although I will definitely need to modify the way I construct the dates (and move the function into the repo). I had originally forgotten the '.date' bit of DQL, and there was no need to output the DateTime object as a format.
// Create two times at the start of this month and next month
$startDate = \DateTime::createFromFormat('d-n-Y', "01-".$month."-".$year);
$startDate->setTime(0, 0 ,0);
$endDate = \DateTime::createFromFormat('d-n-Y', "01-".($month+1)."-".$year);
$endDate->setTime(0, 0, 0);
$notes = $this->em->getRepository('AppBundle:Note')->createQueryBuilder('n')->where('n.date BETWEEN :start AND :end')->setParameter('start', $startDate)->setParameter('end', $endDate)->getQuery()->getResult();
// $notes = $this->em->getRepository('MrshllSiteBundle:Note')->findByDate();
return $notes;
Related
I'm trying to build a scheduler in which an incremental day addition and subtraction method is required.
Here, I am simply trying to add a day to this variable (which is displayed to the user elsewhere) each time this function is executed via a button I set up that routes to a certain location. But, I keep getting this error
Call to a member function addDay() on integer
whenever I try to execute this. I am new to using the Carbon interface and looked through the documents, which led me to try parsing the function (worked when I had the same error with a string) but to no avail obviously. Any help is appreciated and/or a possible explanation of how this error is working really.
function addDay(){
$day = (int) Carbon::now()->format('j');
$day = $day->addDay();
}
Thanks in advance. If there is a better way to do this (adding days incrementally with the button/link), I would love to hear it. My logic seems flawed after working on the application the entire day.
You're casting the Carbon date object into an integer by using the (int) in the first $day variable. Therefor when you're trying to access the function addDay() it's failing, because $day is no longer a Carbon object but an integer.
$day = Carbon::now();
$day = $day->addDay()->format('j');
This should work, and if you need to cast it to an integer for some reason, then do it like this.
$day = Carbon::now();
$day = (int) $day->addDay()->format('j');
This way you cast the integer after you've added the day.
There is also a much cleaner approach to this syntax, which uses method chaining like so
$day = (int) Carbon::now()->addDay()->format('j');
As #Classified said but a cleaner approach would be to work with Carbon object first and then apply format on that.
Like this:
$dateObj = Carbon::now()->addDay();
$day = (int) $dateObj->format('j');
Cleaner approach and better readability.
What is the desired returned value ?
$day = Carbon::now()->addDay();
return $day->dayOfWeek; //day of the week, 03/08/18 (now) returns 6 (INT)
return $day->format('j'); //day of the month, 03/08/18 (now) returns "4" (STRING)
return $day->day; //day of the month, 03/08/18 (now) returns 4 (INT)
return $day //Carbon object (at now() + 24h) that you can manipulate
You have to addDay to Carbon instance not to the integer (the day) :
$dt = Carbon::create(2012, 1, 31, 0); // 2012-01-31 00:00:00
echo $dt->addDay(); // 2012-03-04 00:00:00
I have a field in my DB which has a time stamp using moment js. The result is like so.
["2018-02-11 11:30:17","2018-02-11 11:20:17","2018-02-11 11:10:17"]
But when i return created_at colum from db the array is given like so:
[{"date":"2018-02-11 11:40:17.000000","timezone_type":3,"timezone":"Asia\/Karachi"},{"date":"2018-02-11 11:40:31.000000","timezone_type":3,"timezone":"Asia\/Karachi"},{"date":"2018-02-11 11:40:40.000000","timezone_type":3,"timezone":"Asia\/Karachi"}]
So how can i take two column dates in a format where carbon can understand? I want the "starttime" column to compare with "created_at". Is this achievable? Here is my code so far:
$cleanStart = Clean::pluck('starttime')->toArray();
$cleanFinish = Clean::pluck('created_at')->toArray();
$from = Carbon::parse($cleanStart);
$to = Carbon::parse($cleanFinish);
$diff_in_hours = $to->diffInHours($from);
return $diff_in_hours;
But it gives me an error:
Type error: DateTime::__construct() expects parameter 1 to be string, array given
Also how can i give the array to carbon.
So finally here is the thing i tried:
$cleanCollection = Clean::get(['starttime','created_at']);
foreach($cleanCollection as $cleanObj){
$startTime = Carbon::parse($cleanObj->starttime);
$diff = $cleanObj->created_at->diffInseconds($startTime);
}
echo $diff;
But when ever i refresh the page, the value changes in seconds. and if another record is added, it adds up again.
Pluck will give you an array of all of the start times from your result set which is why you're passing an array into parse. You're actually getting all of the start times and all of the created ats then trying to compare all to all, effectively.
You either need to get a single result,
Like
$clean = Clean::first();
$from = Carbon::parse($clean->starttime);
$to = Carbon::parse($clean->created_at);
$diff_in_hours = $to->diffInHours($from);
Or if you wanted it for each row you'd have to iterate over them and do much the same
Clean::all()->each(function ($clean) {
$from = Carbon::parse($clean->starttime);
$to = Carbon::parse($clean->created_at);
$diff_in_hours = $to->diffInHours($from); // for this row
});
The other thing you could do is put an accessor on your Clean model to help you out with this
public function getDiffInHoursAttribute()
{
$from = Carbon::parse($this->starttime);
$to = Carbon::parse($this->created_at);
return $to->diffInHours($from);
}
Then
echo Clean::first()->diffInHours;
Or
foreach(Clean::all() as $clean) {
echo $clean->diffInHours;
}
Also, if you add the following to your model, Eloquent will automatically parse the strings into Carbon objects so you can skip the need for Carbon::parse() in your code
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'starttime'
];
Try adding protected $dates to your Clean model, like this:
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'created_at',
'updated_at'
];
As you can read from the comments inside the code, put all of the columns that should be converted to dates inside, this will help you achieve date manipulations easier.
EDIT 1:
$start = new Carbon($cleanStart['date'], $cleanStart['timezone']);
$finish = new Carbon($cleanFinish['date'], $cleanFinish['timezone']);
Then you can compare like this:
var_dump($start->eq($finish)); //Is start date same as finish date
var_dump($start->ne($finish)); //Is start date not same as finish date
var_dump($start->gt($finish)); //Is start date greater than finish date
var_dump($start->gte($finish)); //Is start date greater than or equal to finish date
var_dump($start->lt($finish)); //Is start date less than finish date
var_dump($start->lte($finish)); //Is start date less than or equal to finish date
EDIT 2:
In order for the code underneath to work, you must initialize $start and $finish dates as in EDIT 1
$ago = $start->diffForHumans($finish, true); //2 days OR 3 weeks OR 1 hour
Say, I have 4 dates and the date today is 1/7/16:
1/7/16 1:06:02
1/7/16 8:01:24
1/8/16 7:02:23
1/6/16 3:12:34
How can I only pick 1/7/16 1:06:02 and 1/7/16 8:01:24.
What date function from PHP can I use to only get the date of today?
Thanks!
UPDATE:
This is being used as a MYSQL selector.
Example: $db->query("DELETE * FROM entry WHERE date='$today'");
How would this work? How can I get my MYSQL query to select only the dates for today?
UPDATE 2:
I've tried using curdate(), but the code is not working...
This is what I'm doing:
$db->query("DELETE * FROM entry WHERE date=curdate()");
What am I doing wrong?
LAST UPDATE: curdate() worked properly...
UPDATE: As MySQL query, you can do this as follows:
$db->query("DELETE * FROM entry WHERE DATE(date) = CURDATE()");
CURDATE() returns today's date.
If you want to do with php, see below to use the DateTime class.
$now = new DateTime;
$otherDate = new DateTime('2016-01-01'); // or e.g. 2016-01-01 21:00:02
// Setting the time to 0 will ensure the difference is measured only in days
$now->setTime( 0, 0, 0 );
$otherDate->setTime( 0, 0, 0 );
var_dump($now->diff($otherDate)->days === 0); // Today
var_dump($now->diff($otherDate)->days === -1); // Yesterday
var_dump($now->diff($otherDate)->days === 1); // Tomorrow
The answer by schellingerht is totally correct, but
$now = new DateTime;
should be this instead:
$now = new DateTime('Today');
That will ensure that the date from today with 0 hours and 0 minutes is selected. Otherwise the output could be Today even if the date is tomorrow.
Hope that helps!
Convert to timestamp and then format for only the date. Then compare to the same format of todays date:
var_dump(
date('Ymd', strtotime('1/7/16 1:06:02')) === date('Ymd')
);
Try using DATE() (http://www.w3schools.com/sql/func_date.asp). You don't need to convert your dates to a string - this is not efficient.
DELETE FROM yourTable WHERE DATE(yourDateField) = DATE(NOW)
You could compare today's date with Y-m-d format, with your date parsed with the same format.
$today = date('Y-m-d');
$otherdate = date('Y-m-d', strtotime('2022-02-02 16:00:26'));
if($today === $otherdate){
\\ your code for Today
} else {
\\ your code for not Today
}
Can someone help me please, I need a function or a way in PHP, to exclude the day from a given date regardless of its format.
Thank you.
try this
$newdate = date("m-Y",strtotime($date));
I ran into this as well, and made preg match to check for 2 date formats with year notation yyyy. Not the solution I'd prefer, but it works for me. Downside is that you have to define all the date formats and it depends on yyyy notation.
// check form to be yyyy-mm-dd
if (preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/D', $date)){
$date = date("Y-m", $date);
}
// check form to be mm-dd-yyyy
elseif (preg_match('/^[0-9]{2}-[0-9]{2}-[0-9]{4}$/D', $date)){
$date = date("m-Y", $date);
}
I am answering this for people who have encountered the same problem
if (!function_exists('remove_days_in_date')) {
/**
* This function allows you to delete some number of days in a date
* #param \DateTime $date The date in which to delete the days
* #param int $n_day The number of days to delete
* #return string
*/
function remove_days_in_date(\DateTime $date, int $n_day) {
date_sub($date,date_interval_create_from_date_string("$n_day days"));
date_format($date,"Y-m-d h:i:s");
return date_format($date,"Y-m-d h:i:s");
}
}
echo remove_days_in_date(new \DateTime, 12);
I have a problem where I need to handle dates where the month and day parts are optional. For example, the year will always be known but sometimes the day or month and day will be unknown.
In MySQL I can create a table with a date field and while I can't find any reference in the MySQL Manual it will accept the following as valid:
(YYYY-MM-DD format):
2011-02-10 // Current date
2011-02-00 // Day unknown so replaced with 00
2011-00-00 // Day and month unkown so replaced with 00-00
Test calculations from within the database work fine so I can still sort results easily. In the manual it says that month needs to be between 01 and 12, and day between 01 and 31 - but it does accept 00.
First question: Am I going to run into trouble using 00 in the month or day parts or is this perfectly acceptable?
Next question: Is there a PHP function (or MySQL format command) that will automatically format the following dates into the required format string?
2011 becomes 2011-00-00
2011-02 becomes 2011-02-00
Or do I need write a special function to handle this?
The following doesn't work:
<?php
$date = date_create_from_format('Y-m-d', '2011-00-00');
echo date_format($date, 'Y-m-d');
// Returns 2010-11-30
$date = date_create_from_format('Y-m-d', '2011-02-00');
echo date_format($date, 'Y-m-d');
// Returns 2011-01-31
?>
Third question: Is there a PHP function (or MySQL command) to format the dates for use in PHP?
Finally, is this the best approach? Or is there a 'best practise' method?
EDIT:
Here is what I'm currently doing:
A date field can accept a date in the format YYYY, YYYY-MM, or YYYY-MM-DD and before sending to the database it is processed in this function:
/**
* Takes a date string in the form:
* YYYY or
* YYYY-MM or
* YYYY-MM-DD
* and validates it
*
* Use date_format($date, $format); to reverse.
*
* #param string $phpDate Date format [YYYY | YYYY-MM | YYYY-MM-DD]
*
* #return array 'date' as YYYY-MM-DD, 'format' as ['Y' | 'Y-m' | 'Y-m-d'] or returns false if invalid
*/
function date_php2mysql($phpDate) {
$dateArr = false;
// Pattern match
if (preg_match('%^(?P<year>\d{4})[- _/.]?(?P<month>\d{0,2})[- _/.]?(?P<day>\d{0,2})%im', trim($phpDate), $parts)) {
if (empty($parts['month'])) {
// Only year valid
$date = $parts['year']."-01-01";
$format = "Y";
} elseif (empty($parts['day'])) {
// Year and month valid
$date = $parts['year']."-".$parts['month']."-01";
$format = "Y-m";
} else {
// Year month and day valid
$date = $parts['year']."-".$parts['month']."-".$parts['day'];
$format = "Y-m-d";
}
// Double check that it is a valid date
if (strtotime($date)) {
// Valid date and format
$dateArr = array('date' => $date, 'format' => $format);
}
} else {
// Didn't match
// Maybe it is still a valid date
if (($timestamp = strtotime($phpDate)) !== false) {
$dateArr = array('date' => date('Y-m-d', $timestamp), 'format' => "Y-m-d");
}
}
// Return result
return $dateArr;
}
So it pattern matches the input $phpDate where it must begin with 4 digits, then optionally pairs of digits for the month and the day. These are stored in an array called $parts.
It then checks if months or days exist, specifying the format string and creating the date.
Finally, if everything checks out, it returns a valid date as well as a format string. Otherwise it returns FALSE.
I end up with a valid date format for my database and I have a way of using it again when it comes back out.
Anyone think of a better way to do this?
I have a problem where I need to handle dates where the month and day parts are optional.
For example, the year will always be known but sometimes the day or month and day will be
unknown.
In many occasions, we do need such 'more or less precise' dates, and I use such dates as 2011-04-01 (precise), as well as 2011-04 (= April 2011) and 2011 (year-only date) in archives metadata. As you mention it, MySQL date field tolerates '2011-00-00' though no FAQs tell about it, and it's fine.
But then, I had to interface the MySQL database via ODBC and the date fields
are correctly translated, except the 'tolerated' dates (Ex: '2011-04-00' results empty in the resulting MySQL-ODBC-connected ACCESS database.
For that reason, I came to the conclusion that the MySQL date field could be converted in a plain VARCHAR(10) field : As long as we don't need specific MySQL date functions, it works fine, and of course, we can still use php date functions and your fine date_php2mysql() function.
I would say that the only case when a MySQL date field is needed
is when one needs complex SQL queries, using MySQL date functions in the query itself.
(But such queries would not work anymore on 'more or less precise' dates!...)
Conclusion : For 'more or less precise' dates,
I presently discard MySQL date field and use plain VARCHAR(10) field
with aaaa-mm-jj formated data. Simple is beautiful.
Since the data parts are all optional, would it be tedious to store the month, day, and year portions in separate integer fields? Or in a VARCHAR field? 2011-02-00 is not a valid date, and I wouldnt't think mysql or PHP would be excited about it. Test it out with str_to_time and see what kind of results you get, also, did you verify that the sorting worked right in MySQL? If the docs say that 1 through 31 is required, and it is taking 00, you might be relying on what is, in essence, a bug.
Since 2011-02-00 is not a valid date, none of PHP's formatting functions will give you this result. If it handled it at all, I wouldn't be surprised if you got 2001-01-31 if you tried. All the more reason to either store it as a string in the database, or put the month, day, and year in separate integer fields. If you went with the latter route, you could still do sorting on those columns.
I have also encountered this problem. I ended up using the PEAR Date package. Most date classes won't work with optional months or optional days, but the PEAR Date package does. This also means you don't need custom formatting functions and can use the fancy formatting methods provided by the Date package.
I have found this link in a textbook. This states that month and day values can be zero to allow for the possiblity of storing incomplete or unknown data
http://books.google.co.uk/books?id=s_87mv-Eo4AC&pg=PA145&lpg=PA145&dq=mysql+date+of+death+when+month+unknown&source=bl&ots=tcRGz3UDtg&sig=YkwpkAlDtBP1KKTDtqSyZCl63hs&hl=en&ei=Btf5TbL1NIexhAfkveyTAw&sa=X&oi=book_result&ct=result&resnum=8&ved=0CFMQ6AEwBw#v=onepage&q&f=false
If you pull your date in pieces from the database you can get it as if it's 3 fields.
YEAR(dateField) as Year, MONTH(dateField) as Month, DAY(dateField) as DAY
Then pushing those into the corresponding fields in the next bit of PHP will give you the result you're looking for.
$day = 0;
$month = 0;
$year = 2013;
echo $datestring;
$format = "Y";
if($month)
{
$format .= "-m";
if($day)
$format .="-d";
else
$day = 1;
}
else
{
$month = 1;
$day = 1;
}
$datestring = strval($year)."-".strval($month)."-".strval($day);
$date = date($format, strtotime($datestring));
echo $date; // "2013", if $month = 1, "2013-01", if $day and $month = 1, "2013-01-01"