Get elements of Timestamp array where Timestamp is in a specific Month - php

I have an Array of Timestamps (as key) and Strings (as value).
I would like to get all the elements of the Array, where the Timestamp is in a specific Month.
E.g: 2015-April: from 0:00 1th of April 2015, until 23:59 30th of April 2015.
What is the best method for that? Shall i sort my array first with ksort, or that makes it slower?
EDITED: Dump of my array:
array(327) { [1428226200]=> string(95) "foo" [1428231600]=> string(95) "bar" ... }
I also can get an array like this easily, if it's better:
array(327) { ["2014/03/05 09:30"]=> string(95) "foo" ["2015/04/07 11:00"]=> string(95) "bar" ... }

Convert the timestamps to a date and compare against the formatted month:
$dates = /* your array */
$aprilDates = [];
foreach ($dates as $timestamp => $value) {
$date = new \DateTime("#$timestamp");
if ($date instanceof \DateTime && $date->format('m') == 4) {
$aprilDates[$timestamp] = $value;
}
}
Online Demo
Instead of \DateTime, you can also use the date function:
foreach ($dates as $timestamp => $value) {
if (date('m', $timestamp) == 2) {
$aprilDates[$timestamp] = $value;
}
}
Yet another option:
$startDate = strtotime('first day of April');
$endDate = strtotime('last day of April 23:59:59');
foreach ($dates as $timestamp => $value) {
if ($timestamp >= $startDate && $timestamp <= $endDate) {
echo $value, PHP_EOL;
}
}
Regarding your question
Shall i sort my array first with ksort, or that makes it slower?
Sorting the array first will do one additional iteration over the array. That means yes, it will be slower. With only a few hundreds items in there, it will likely make no noticeable difference though.

Related

Display the item nearest to current time

I have a list of dates/times items in PHP array that are formatted like this:
2019-03-19 00:00:00
2019-03-19 02:30:00
2019-03-19 05:00:00
2019-03-19 14:30:00
2019-03-19 23:59:59
etc.
I'm sure this is easy, I just can't wrap my head around it. What equation do I use to display the item that is closest to the current time without going over.
So if current time is 22:00:00, I would want to display item 14:30:00, rather than 23:59:59.
Since your times are in Y-m-d H:i:s you can just compare them as strings and use a simple foreach loop to get your result:
$dates = array('2019-03-19 00:00:00',
'2019-03-19 02:30:00',
'2019-03-19 05:00:00',
'2019-03-19 14:30:00',
'2019-03-19 23:59:59');
$now = date('Y-m-d H:i:s');
$latest = '';
// add sort($dates) here if they are not already sorted.
foreach ($dates as $date) {
if ($date > $now) break;
$latest = $date;
}
echo $latest;
Demo on 3v4l.org
Note this code assumes your dates are already sorted, if not, add sort($dates) before the foreach loop.
First of all convert all the Dates to this format
$changed_date_1 = date('YmdHis', strtotime($orignaldate_1));
$changed_date_2 = date('YmdHis', strtotime($orignaldate_2));
$changed_date_3 = date('YmdHis', strtotime($orignaldate)_3);
so 2019-03-19 00:00:00 will become 20190319000000, and so on, now they can be compared easily.
than run a foreach loop in which iterate through all these date
$closestdate= date('Y-m-d H:i:s');//intaily set it to current date
$closest_difference= 99999999999999999;//intaily set a big value, more than 15 digits
foreach($datesArray as $item){
$difference = $item - date('YmdHis');
if($difference < $closest_difference){
$closes_difference = $difference;
$closestdate = $item;//this item is closest one. in next iteration this may change
}
}
echo $Closesdate;
/**
* Gets the nearest moment of the same day as $today.
*
* #param string[] $dates - A list of dates (needed format: "Y-m-d H:i:s")
* #param string|null $today - The date used for comparaison. (default is current date)
* #return string|bool - Returns the nearest date, or false.
*/
function getNearestDate(array $dates, ?string $today = null) {
if (!$today) {
$today = date('Y-m-d H:i:s');
}
$fnDT = function($d) {
return DateTime::createFromFormat('Y-m-d H:i:s', $d);
};
usort($dates, function($a, $b) use ($fnDT, $today) {
$da = abs($fnDT($today)->getTimestamp() - $fnDT($a)->getTimestamp());
$db = abs($fnDT($today)->getTimestamp() - $fnDT($b)->getTimestamp());
return $da - $db;
});
$nearest = $dates[0];
if ($fnDT($nearest)->format('Y-m-d') !== $fnDT($today)->format('Y-m-d')) {
print_r('No date of the same day.');
return false;
}
return $nearest;
}
Test it on 3v4l.org

PHP days difference calculation error

I have some PHP code to calculate the number of days between two specific dates. The difference should not count Sundays and Saturdays. Also, I have an array of dates, which includes holidays, which also need to be skipped.
I gave the starting date as 01-05-2015 and ending date as 01-06-2015. I gave the entire days in the month of may as array. Thus the difference should be 1 day. But I am getting the output as 7. What is the problem? Here is the code.
function dateRange($first, $last) {
$dates = array();
$current = strtotime($first);
$now = $current;
$last = strtotime($last);
while( $current <= $last ) {
if (date('w', $current) != 0){
$dates[] = date('d-m-Y', $current);
}
$current = strtotime('+1 day', $current);
}
unset($dates[0]);
return $dates;
}
$datea = "01-05-2015";
$date = "01-06-2015";
$hdsarray = array("1-05-2015","2-05-2015","4-05-2015","5-05-2015","7-05-2015","8-05-2015","9-05-2015","11-05-2015","12-05-2015","14-05-2015","15-05-2015","16-05-2015","18-05-2015","19-05-2015","21-05-2015","22-05-2015","23-05-2015","25-05-2015","26-05-2015","28-05-2015","29-05-2015","30-05-2015");
$datesarray = dateRange($datea, $date);
$result = array_diff($hdsarray,$datesarray);
$date_diff = sizeof($result);
echo $date_diff;
The only problem I can see is in the usage of array_diff, It actually includes the sat and sun which is excluded by dateRange function, if not found in holidays list.
Instead, you can pass your holiday dates in dateRange function, and filter over there.
function dateRange($first, $last, $excludeDates) {
$dates = array();
$current = strtotime($first);
$now = $current;
$last = strtotime($last);
while( $current <= $last ) {
if (date('w', $current) != 0 && date('w', $current) != 6 && !in_array(date('j-m-Y', $current), $excludeDates)){
$dates[] = date('d-m-Y', $current);
}
$current = strtotime('+1 day', $current);
}
return $dates;
}
$datea = "01-05-2015";
$date = "01-06-2015";
$hdsarray = array("1-05-2015","2-05-2015","4-05-2015","5-05-2015","7-05-2015","8-05-2015","9-05-2015","11-05-2015","12-05-2015","14-05-2015","15-05-2015","16-05-2015","18-05-2015","19-05-2015","21-05-2015","22-05-2015","23-05-2015","25-05-2015","26-05-2015","28-05-2015","29-05-2015","30-05-2015");
$datesarray = dateRange($datea, $date, $hdsarray);print_r($datesarray);
Result:
Array
(
[0] => 06-05-2015
[1] => 13-05-2015
[2] => 20-05-2015
[3] => 27-05-2015
[4] => 01-06-2015
)
All the 5 dates come in the result, are not sat, sun, and also not there in holidays list.
It seems that there are several problems here. First, as pointed out by others the condition:
if (date('w', $current) != 0){
only checks for Sundays, if it should also include Saturday's it should be:
if (date('w', $current) != 0 && date('w', $current) != 6){
Secondly, it seems that the $hdsarray array does not contain all of the days in May. It seems that all of the Wednesdays are missing.
The third issue is that you are using array_diff on two arrays, one containing Dates and the other ones containing Strings. From the documentation:
Two elements are considered equal if and only if (string) $elem1 ===
(string) $elem2. In words: when the string representation is the same.
In your $hdsarray you are using "1-05-2015" to denote the first day of the month, while:
echo date('d-m-Y', strtotime("1-05-2015"));
results in "01-05-2015". You will need to add an additional 0 in $hdsarray for these dates or work with dates as well.
Last but not least, the current algorithm will not work correctly if the $hdsarray contains dates for a Saturday or Sunday, the result of array_diff will still contain these dates. Since you want to filter the result of daterange the array_filter function might be more suitable.
Despite an answer has already been provided, here is a little snippet with a class handling everything for you:
<?php
class dateRange {
protected $start, $end, $daysToExclude, $datesToExclude;
function __construct($dateStart, $dateEnd, $daysToExclude, $datesToExclude) {
$this->start = $dateStart;
$this->end = $dateEnd;
$this->daysToExclude = $daysToExclude;
$this->datesToExclude = $this->fixFormat($datesToExclude);
}
public function getRangeLength ($callback = null) {
$tmp = array();
$now = strtotime($this->start);
$to = strtotime($this->end);
while ( $now <= $to ) {
if (!in_array(date("w", $now), $this->daysToExclude)) {
$tmp[] = date('d-m-Y', $now);
}
$now = strtotime('+1 day', $now);
}
is_callable($callback) && call_user_func($callback, array_diff($tmp,$this->datesToExclude));
return count(array_diff($tmp,$this->datesToExclude));
}
private function fixFormat($el) {
if (!is_array($el)) {
return false;
}
else {
foreach ($el as &$value) {
$value = date("d-m-Y",strtotime($value));
}
return $el;
}
}
}
?>
I decided to keep your current logic (using date_diff), but I thought that, in the future, you may have your boss telling you "You know what? I don't want to have mondays aswell there" so, with the current system, you will have to edit your function manually and, perhaps, you won't remember anymore what you did.
The class above expects four parameters:
dateStart (d-m-Y format)
dateEnd (d-m-Y format)
daysToExclude (array with IDs of the days to exclude -> example array(0,6) to exclude saturdays and sundays).
datesToExclude (array with the dates to exclude, every format supported).
The class will automatically fix the datesToExclude array format in order to allow you to use date_diff.
Here is an example to use it, following your case:
<?php
$dateStart = "01-05-2015";
$dateEnd = "01-06-2015";
$daysToExclude = array(0,6);
$exclusions = array(
"1-05-2015",
"2-05-2015",
"4-05-2015",
"5-05-2015",
"7-05-2015",
"8-05-2015",
"9-05-2015",
"11-05-2015",
"12-05-2015",
"14-05-2015",
"15-05-2015",
"16-05-2015",
"18-05-2015",
"19-05-2015",
"21-05-2015",
"22-05-2015",
"23-05-2015",
"25-05-2015",
"26-05-2015",
"28-05-2015",
"29-05-2015",
"30-05-2015"
);
$dateRange = new dateRange($dateStart, $dateEnd, $daysToExclude, $exclusions);
echo $dateRange->getRangeLength();
?>
The code above outputs 5.
The function getRangeLength also accepts a callback and will return the array resulting of the date_diff operation, so you can also:
$dateRange->getRangeLength(function($res) {
echo "Literal output: <br />";
print_r($res);
echo "<br />count is: " . count($res);
});
The above outputs:
Literal output:
Array ( [3] => 06-05-2015 [8] => 13-05-2015 [13] => 20-05-2015 [18] => 27-05-2015 [21] => 01-06-2015 )
count is: 5
So if you later will need to remove mondays too, you will be able to easily do that by changing daysToExclude to array(0,1,6);
Hope this will be helpful to anyone else who will need this, despite a valid answer has already been posted.
Your original problem, in any case, was pretty much related to the array_diff function, which was NOT doing its job because of the fact that the date strings were not compatible, because "1-01-2015" is different from "01-01-2015", unless you first convert BOTH of them to times and then back to dates.
The code is fine (except that $nowis not used at all). The problem is the $hdsarray is wrong:
It should $hdsarray = array("01-05-2015", "02-05-2015", "04-05-2015", "05-05-2015", "07-05-2015", "08-05-2015", "09-05-2015",...);
date('d-m-Y', $current);will always return a leading 0 for all days between 1 and 9.
That's where the difference comes from.

How to find date difference excluding specific dates in array - PHP

I know how to calculate date difference using PHP like;
$newdate = "01-03-2013";
$olddate = "01-06-2013";
$date_diff = abs(strtotime($olddate)-strtotime($newdate)) / 86400;
echo $date_diff;
But suppose, if I have some dates in an array like;
$datesarray = array(10-05-2013, 20-05-2013, 12-08-2013);
etc., holding some specific dates, is it possible to calculate date difference excluding the dates in array along with the Sundays, if they lie in between the start and end dates?
just loop through the $datesarray and check for each one if it's between the $olddate and $newdate. If so, increase a $counter variable (which starts at 0, obviously).
Then $date_diff - $counter will give you the expected result.
I would use the DateTime class in a custom function like this:
function dates_between(DateTime $start, DateTime $end, $format = 'm-d-Y') {
$date = $start;
$dates = array();
$oneDay = new DateInterval('P1D');
// push all dates between start and end to the result
while(($date = $date->add($oneDay)) < $end) {
$dates []= $date->format($format);
}
return $dates;
}
Example usage:
$now = new DateTime();
$nextWeek = new DateTime('+1 week');
var_dump(dates_between($now, $nextWeek));
Output:
array(6) {
[0] =>
string(10) "07-12-2013"
[1] =>
string(10) "07-13-2013"
[2] =>
string(10) "07-14-2013"
[3] =>
string(10) "07-15-2013"
[4] =>
string(10) "07-16-2013"
[5] =>
string(10) "07-17-2013"
}
The following script creates and array of timestamps from your array of UK dates and then calculates the max and min timestamps to calculate the days difference.
If the timestamp defaults to 0, it is not added to the timestamp array, avoiding huge results for one bad date defaulting to the epoch
I.e. When date is invalid or pre epoch 1/1/1970
<?php
$datesarray = array('10-05-2013', '20-05-2013', '12-08-2013');
$date_diff=0; // default for 0 or 1 dates
if( (is_array($datesarray)) && (sizeof($datesarray)>1) )
{
$timestampsarray=array();
reset($datesarray);
while(list($key,$value)=each($datesarray))
{
$timestamp=timestamp_from_UK($value);
if($timestamp!=0) $timestampsarray[$key]=$timestamp;
}
$date_diff = abs(max($timestampsarray)-min($timestampsarray)) / 86400;
}
echo $date_diff;
function timestamp_from_UK($ukdatetime)
{
// where PHP is processing UK dates d-m-y correctly
$ukdatetime=str_replace('/', '-', $ukdatetime);
if(date("d", strtotime("1-2-1970"))==1) return strtotime($ukdatetime);
// Fallback script for when PHP is NOT processing UK dates
$success=false;
if(!$success) $success=preg_match("/([0-9]{1,2})[^0-9]([0-9]{1,2})[^0-9]([0-9]{2,4})[^0-9]([0-9]{1,2})[^0-9]([0-9]{1,2})[^0-9]([0-9]{1,2})/", $ukdatetime, $matches);
if(!$success) $success=preg_match("/([0-9]{1,2})[^0-9]([0-9]{1,2})[^0-9]([0-9]{2,4})[^0-9]([0-9]{1,2})[^0-9]([0-9]{1,2})/", $ukdatetime, $matches);
if(!$success) $success=preg_match("/([0-9]{1,2})[^0-9]([0-9]{1,2})[^0-9]([0-9]{2,4})/", $ukdatetime, $matches);
if(!$success) return 0;
// ensure all values are set - to avoid invalid offset
for($i=4;$i<=6;$i++)
{
if(!isset($matches[$i])) $matches[$i]=0;
}
// $matches[0] is the full matched string
return mktime($matches[4], $matches[5], $matches[6], $matches[2], $matches[1], $matches[3]);
}
?>

My variable is not being filled, remains empty

I have a problem with comparing dates. I pull one date from a database through an API. These dates are stored in an array because one column contains multiple dates and I have to cycle through them to find the next upcoming date. The dates are in the format: 'dd/mm/yy'
$rawDate = $e->calendarsummary;
$filter = preg_replace("/[a-z]/","", $rawDate);
$createArray = explode(',', $filter);
$dates = array_filter(array_map('trim', $createArray));
foreach($dates as $d)
{
$dateTime = DateTime::createFromFormat('dmy', $d);
if($dateTime >= $now)
{
$finalDate = $dateTime;
$total = $finalDate->format('l j/m/y');
break;
}
}
If I place a var_export of $dateTime after $dateTime = DateTime::createFromFormat('dmy', $d); it returns 'false'. So I'm guessing $dateTime is empty although my array $dates is filled correctly.
a var_export of $dates returns:
array ( 0 => '15/06/13', 1 => '16/06/13', )
and a var export of $now returns todays date: '16/06/13'
So I'm a bit stuck why my variable $DateTime remains empty?
EDIT: Apparantly the return of 'false' means it's an error, so something went wrong when formatting my dates from the array?
If the $dates array contains elements such 15/06/13 so use just use wrong date format. E.g.:
$dateTime = DateTime::createFromFormat('d/m/y', $d);
$dateTime = DateTime::createFromFormat('d/m/y', $d);
not
$dateTime = DateTime::createFromFormat('dmy', $d);
Your dates are in format d/m/y, not dmy. Try the following instead:
$dateTime = DateTime::createFromFormat('d/m/y', $d);
When I do that, I get:
class DateTime#1 (3) { public $date => string(19) "2013-06-15 17:17:12" public $timezone_type => int(3) public $timezone => string(3) "UTC" }
... instead of bool(false).
you could use strtotime as a different method.
$now = date('d/m/y');
foreach($dates as $d)
{
if(strtotime($d) >= strtotime($now))
{
$finalDate = $dateTime;
$total = date('l j/m/y', strtotime($d));
break;
}
}

Add missing dates to an array

I have the following array:
Array ( [2010-10-30] => 1 [2010-11-11] => 1 [2010-11-13] => 11 )
I am trying to fill in the array with all the missing dates between the first and last elements. I was attempting using the following but got nowhere:
foreach($users_by_date as $key => $value){
$real_next_day = date($key, time()+86400);
$array_next_day = key(next($users_by_date));
if($real_next_day != $array_next_day){
$users_by_date[$real_next_day] = $value;
}
}
The DateTime, DateInterval and DatePeriod classes can really help out here.
$begin=date_create('2010-10-30');
$end=date_create('2010-11-13');
$i = new DateInterval('P1D');
$period=new DatePeriod($begin,$i,$end);
foreach ($period as $d){
$day=$d->format('Y-m-d');
$usercount= isset($users_by_date[$day]) ? $users_by_date[$day] :0;
echo "$day $usercount";
}
I have been waiting for a chance to try out DateTime and DateInterval objects in PHP 5.3, your question was the perfect opportunity to do just that. Note that this code will not work with PHP versions earlier than 5.3
<?php
$dates = array('2010-10-30' => 1, '2010-11-01' => 1, '2010-11-13' => 1);
// get start and end out of array
reset($dates);
$start = new DateTime(key($dates));
end($dates);
$end = new DateTime(key($dates));
foreach (new DatePeriod($start, new DateInterval('P1D'), $end) as $date) {
$dateKey = $date->format('Y-m-d'); // get properly formatted date out of DateTime object
if (!isset($dates[$dateKey])) {
$dates[$dateKey] = 1;
}
}
print_r($dates);
The functions you are looking for (but not using in your example) are strtotime & diff
You would get the day range between your two dates $numdiff, and simply do something in a loop that would do:
for ($i=1; $i<=$numdiff; $i++) {
echo date("Y-m-d", strtotime("2010-10-30 +".$i." day"));
}
Result should be something like:
2010-10-31
2010-11-01
2010-11-02...
You could then pop that into your array as needed. Hope that gets you started in the right direction.
For indexes using timestamps, you can generate your array easily using the range function.
$startStamp = ...
$endStamp = ...
$oneDay = 60*60*24;
$timeIndexes = range($startStamp, $endStamp, $oneDay);
$filler = array_fill(0, count($timeIndexes), null);
$timeArray = array_combine($timeIndexes, $filler);
I am not sure what values you want in the array, but hopefully it is relatively straight-forward from here.
If you are going to be converting every timestamp to a formatted date string anyhow and would just prefer to use date strings in the first place, consider this modification.
$dateStringIndexes = array_map(
function ($t) {
return date('Y-m-d', $t);
},
$timeIndexes
);
Of course, since you are on PHP 5.2, you will likely have to compromise with a foreach loop instead of the closure.
This is the PHP 5.2 capable function I came up with that works.
Thanks guys
reset($users_by_date);
$date = key($users_by_date);
end($users_by_date);
$end = key($users_by_date);
while(strtotime($date) <= strtotime($end)){
$datearray[] = date("Y-m-d", strtotime($date));
$date = date("Y-m-d", strtotime("+1 day", strtotime($date)));
}
foreach($datearray as $key => $value){
if(!isset($users_by_date[$value])){
$users_by_date[$value] = 0;
}
}
ksort($users_by_date);

Categories