This is the function I have written, it works fine except for the fact that it has the potential to be called hundreds of times, causing some speed bottlenecks. I want to know if there is a way to optimize this code to be more efficient in regards to execution time.
/**
* Takes in the airing values, and then converts them to user local time, giving back the day, dayname, and a formatted timestring.
* The Day is an ISO calendar day of the week, Hour is a 24-hour format hour, and Minutes is the minutes
* #param int $airing_day The airing day (1-7)
* #param int $airing_hour The airing hour (0-23)
* #param int $airing_minutes The airing minutes (0-59)
* #return array The Array of values with keys ['day', 'dayname', 'timestring']
*/
public static function airingTimeToUserTimezone($airing_day, $airing_hour, $airing_minutes)
{
// February 1st the 2016 is a monday, perfect for conversion, since we can correlate 1 to Monday and 7 to Sunday
$AirDateTime = new DateTime('2016-2-' . $airing_day . ' ' . $airing_hour . ':' . $airing_minutes . ':00');
$AirDateTime->setTimezone(self::$user->DateTimeZone);
$toret = array();
$toret['day'] = $AirDateTime->format('N');
$toret['dayname'] = $AirDateTime->format('l');
$toret['hour'] = $AirDateTime->format('G');
$toret['minutes'] = $AirDateTime->format('i');
$toret['timestring'] = $AirDateTime->format("g:i A");
return $toret;
}
Unless this is going to executed thousands of times a second, you probably aren't going to see much of a performance hit from this function. However, one optimization I see is to call DateTime::format() only once:
public static function airingTimeToUserTimezone($airing_day, $airing_hour, $airing_minutes)
{
$AirDateTime = new DateTime("2016-02-$airing_day $airing_hour:$airing_minutes:00");
$AirDateTime->setTimezone(self::$user->DateTimeZone);
$toret = array();
list (
$toret['day'],
$toret['dayname'],
$toret['hour'],
$toret['minutes'],
$toret['timestring']
) = explode("/", $AirDateTime->format("N/l/G/i/g:i A"));
return $toret;
}
Related
This is a two part problem which should be trivial but date and time handling in PHP seems to be anything but and everything I've tried so far has either given incorrect results or crashed my program
I'm trying to replicate the following two SQL Server commands in PHP
Count the days since the start of the millennium
select (cast (DATEDIFF(day,'2000-01-01',getdate()) as int)
Count the number of seconds since midnight
datediff(second,convert(date,getdate()),getdate())
I've tried all combinations of date_diff, getdate, strotime and more but nothing seems to give me a properly ISO formatted datetime or a workable method of calculating days and seconds elapsed.
I'm using PHP7 so should have all built-in functions up to date.
What am I missing?
edit: sample input data.
today's date in format '2020-11-22 16:57:10.112'
a given date in format '2000-01-01 00:00:00.000'
expected output data : 7631 days
today's date in format '2020-11-22 16:57:10.112'
previous midnight in format '2020-11-22 00:00:00.000'
expected output data : 61215 seconds
It's rather easy to do if you know your way around DateTime:
function daysSinceStartOfMillennium(DateTimeImmutable $date): int
{
$millenniumStart = new DateTimeImmutable('2000-01-01');
return $date->diff($millenniumStart)->days;
}
function secondsSinceMidnightOfDate(DateTimeImmutable $date): int
{
$midnightToday = new DateTimeImmutable('today');
$diff = $date->diff($midnightToday);
return $diff->s // seconds
+ $diff->i * 60 // minutes to seconds
+ $diff->h * 60 * 60 // hours to seconds
;
}
You could also modify the functions to take date strings as arguments and create a DateTime object inside them.
I opted to create a descriptive variable inside the millennium function to better convey the solution. The creation of this variable can be omitted if you wish and the argument passed directly into the return statement:
return $date->diff(new DateTimeImmutable('2000-01-01'))->days;
Note that if you only need to use these function for the current date, they can be simplified to take no arguments:
function daysSinceStartOfMillennium(): int
{
$millenniumStart = new DateTimeImmutable('2000-01-01');
return (new DateTimeImmutable())->diff($millenniumStart)->days;
}
function secondsSinceMidnight(): int
{
$midnightToday = new DateTimeImmutable('today');
$diff = (new DateTimeImmutable())->diff($midnightToday);
return $diff->s // seconds
+ $diff->i * 60 // minutes to seconds
+ $diff->h * 60 * 60 // hours to seconds
;
}
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
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;
I want to increment a value in MySQL everyday. Starting with 0 and adding +1 for each day.
I've looked at several methods but it usually involves parsing it out of the calendar and starts getting messy. Does anyone have a nice solution for how I could do this in php/mysql?
Reason:
I have a table of data and want to email 1 row each day. So row 0 will send on day one, then row 2 on day 2.
You can use difference between days to get id , so every day you got a new id
class Helper
{
public static $start=1523664000; //2018-04-13
public static function getRowOfToday(){
$now = time(); // or your date as well
$datediff = $now - Helper::$start;
$id = round($datediff / (60 * 60 * 24));
return $id;
}
}
then you can call Helper::getRowOfToday() to get the current row
I need to change the shiftdate variable after 05:30 AM. Since i need to generate data from past 24 hrs starting 05:31 AM to Next day 05:30 AM. I tried like this, but its giving previous day every time. Please help.
I want $shiftdate to use in my sql query;
Code:
<?php
if(date('H:i')>="00:00" || date('H:i')<"05:30"){
$shiftdate= date('Y-m-d',strtotime(date('Y-m-d'))-24*60*60);
}
else if(date('H:i')>"05:30" || date('H:i')<"00:00")
{
$shiftdate=date('Y-m-d');
}
echo $shiftdate;
?>
You can't just compare string like "05:30" as a number and hope for the best. You need to compare numerical value of the hour and then numerical value of the minute.
You have a race in between the first if and the else if
Also the else if doesn't cover it completely, so if it hit's the sweetspot, you can end up with $shiftdate being NULL.
Make it a function with protoype shiftdate_type_whatever_it_is fn_name(int hour, int minute);. This way you can simply unit test the function for different (think boundary) values of the date("H:i");
You can use the DateTime classes for this and encapsulate your check into a function:-
/**
* #param DateTime $checkTime
* #return string
*/
function getShiftDate(DateTime $checkTime)
{
$shiftDate = (new DateTime())->setTimestamp($checkTime->getTimestamp());
$hours = (int)$checkTime->format('H');
$minutes = (int)$checkTime->format('i');
$totalMins = $hours * 60 + $minutes;
if($totalMins < 330){
$shiftDate->modify('yesterday');
}
return $shiftDate->format('Y-m-d');
}
var_dump(getShiftDate(new DateTime()));
Obviously the input to the function may need to be modified as I don't know how you get your date/time, but that won't be a problem. Post a comment if you need help with that.