php detect and convert dates from a string - php

I was wondering if I could somehow to detect a date in a string and convert it into a standard date format.
Let's consider the input strings below:
Company registered on 16 March 2003
or
Activity between 10 May 2006 an 10 July 2008 - no changes.
Now I would like a PHP function to apply over the strings and get the dates as YYYY-mm-dd
Example:
$date = DateExtract($sting1); // output: 2003-03-16
$date = DateExtract($sting2); // output: ['2006-05-10','2008-07-10']

For finding first two digit number Date Regexp would be - (?<![0-9])[0-9]{2}(?![0-9]) This can also be apply to four digit for Year also and for Month you can use hard-coded string search code.
$string = "Activity between 10 May 2006 an 10 July 2008 - no changes.";
preg_match_all('/(\d{1,2}) (\w+) (\d{4})/', $string, $matches);
print_r($matches);
Output :
Array
(
[0] => Array
(
[0] => 10 May 2006
[1] => 10 July 2008
)
[1] => Array
(
[0] => 10
[1] => 10
)
[2] => Array
(
[0] => May
[1] => July
)
[3] => Array
(
[0] => 2006
[1] => 2008
)
)
Updates
For find Complete Date in string you can use this -
It works for short code for Month like Jan and complete name like January also.
Code:
$string = "Activity between 10 May 2006 an 10 July 2008 - no changes.";
preg_match_all('/(\b\d{1,2}\D{0,3})?\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|(Nov|Dec)(?:ember)?)\D?(\d{1,2}\D?)?\D?((19[7-9]\d|20\d{2})|\d{2})/', $string, $complete);
print_r($complete);
Result:
Array
(
[0] => Array
(
[0] => 10 May 2006
[1] => 10 July 2008
)
[1] => Array
(
[0] => 10
[1] => 10
)
[2] => Array
(
[0] =>
[1] =>
)
[3] => Array
(
[0] => 20
[1] => 20
)
[4] => Array
(
[0] => 06
[1] => 08
)
[5] => Array
(
[0] =>
[1] =>
)
)
So you can fetch complete date form here and convert it into standard date format.
Demo Here

Tricky. One approach might be to reason that dates always appear after certain grammatical words, as shown in your examples, e.g. "between", "on" etc. Using such words as a beginning anchor, we would then match until we find what we can reasonably assume to be the end of the date string. Here's what I hacked together:
//some strings
$strs = [
"Company was in business between 14 March 2008 and 21 November 2012 inclusive",
"I was born on 29 May 1980, 17:37 - it was a Thursday",
"The big bang did not occur at 2pm, 14 Jun 1971, that's for sure."
];
//container to store possible date matches from strings
$possible_dates = array();
//prep months - long and short forms, to be used in matching
$date_prefix_words = array('between', 'on', 'at', 'during', 'and');
$months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
$months_short = array_map(function($month) { return substr($month, 0, 3); }, $months);
//iterate over and search strings - convert times like 2pm to 14:00, as, if they appear before the date, e.g. string 3, it doesn't get parsed
foreach($strs as $str) {
$str = preg_replace_callback('/\b\d{1,2}[ap]m\b/', function($time) { return date('H:i', strtotime($time[0])); }, $str);
preg_match_all('/(?<=\b'.implode('\b( |:)|\b', $date_prefix_words).'\b( |:))(\d|am|pm| |,|\'|:|'.implode('|', $months).'|'.implode('|', $months_short).')+/i', $str, $matches);
if (count($matches)) $possible_dates = array_merge($possible_dates, $matches[0]);
}
//output before and after results
foreach($possible_dates as &$pd) {
$pd = preg_replace('/, ?$/', '', $pd);
echo '<p>Before: '.$pd.'<br />After: '.date('Y-m-d', strtotime($pd)).'</p>';
}
Clearly I'm making certain assumptions about your date formats, and you may need to tweak the REGEX, but it sort of works.

First of all You have to extract all part of date from the string separately.
RegEx Demo
First approach:
<?php
function standard_date_format($str) {
preg_match_all('/(\d{1,2}) (\w+) (\d{4})/', $str, $matches);
foreach ( $matches[1] as $day ) { $days [] = $day; }
foreach ( $matches[2] as $month ) { $months[] = $month; }
foreach ( $matches[3] as $year ) { $years [] = $year; }
$all_months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
for ($i = sizeof ($days) - 1; $i >= 0; $i--) {
$month = array_search ($months[$i], $all_months) + 1;
$month = strlen ($month) < 2 ? '0'.$month : $month;
$results[] = $years[$i] . '-' . $month . '-' . $days[$i];
}
return $results;
}
$str1 = "Company registered on 16 March 2003";
$str2 = "Activity between 10 May 2006 an 10 July 2008 - no changes.";
print_r(standard_date_format($str1)); // output: 2003-03-16
print_r(standard_date_format($str2)); // output: ['2006-05-10','2008-07-10']
PHP Demo
Second approach:
<?php
function standard_date_format($str) {
preg_match_all('/(\d{1,2}) (\w+) (\d{4})/', $str, $matches);
$dates = array_map("strtotime", $matches[0]);
$result = array_map(function($v) {return date("Y-m-d", $v); }, $dates);
return $result;
}
$str1 = "Company registered on 16 March 2003";
$str2 = "Activity between 10 May 2006 an 10 July 2008 - no changes.";
print_r(standard_date_format($str1)); // output: 2003-03-16
print_r(standard_date_format($str2)); // output: ['2006-05-10','2008-07-10']
PHP Demo

Related

array_merge seems not to be recognised key. I would like to insert numeric key but it always starts from 0

I would like to make an array like below.
The key should be day. The value should be string weekday name.
$array = [
1 =>['mon'],
2 =>['tue'],
3 =>['wed']
];
However $day (key) can't seem to be recognised.
CODE is
//$i is the first day of a specific month. this sample is 2022-11-01
for($j = $i ; (int)date_create($j)->format('w') < 6 ; $j++){
print_r("くj");
print_r($j);
$day = date_create($j)->format('j');
print_r("day");
print_r($day);
$dailyArray = array_merge($dailyArray ,array( $day => mb_strtolower(date_create($j)->format('D'))));//
print_r("THE RESULT!!");
print_r($dailyArray);
result(November/2022) is
THE RESULT!! day4
Array
(
[0] => Tue
[1] => Wed
[2] => Thu
[3] => Fri
)
It's not the question, but it can't set (int)date_create($j)->format('w') <= 6 . How can I do the loop until sat?
The following script was based on your code basically and it will print out the result as expected, till Saturday
$dailyArray = array();
$d = 1;
while($d < 6 ){
$j = implode("-", array(2022, 11, str_pad($d, 2, '0', STR_PAD_LEFT)));
print_r("くj");
print_r($j);
$day = date_create($j)->format('j');
print_r("day");
print_r($day);
$dailyArray = array_merge($dailyArray , array( $day => strtolower(date_create($j)->format('D'))));
$d++;
}
print_r("<p>THE RESULT!!");
print_r($dailyArray);
It should print out
THE RESULT!!Array ( [0] => tue [1] => wed [2] => thu [3] => fri [4] => sat )
Actually, if (int)date_create($j)->format('w') < 6 is preferred, a shorter version could be
$d = 1;
$j = implode("-", array(2022, 11, str_pad($d, 2, '0', STR_PAD_LEFT)));
while((int)date_create($j)->format('w') < 6 ){
$j = implode("-", array(2022, 11, str_pad($d, 2, '0', STR_PAD_LEFT)));
$dailyArray[$d] = strtolower(date_create($j)->format('D'));
$d++;
}
It printed out:
THE RESULT!!Array ( [1] => tue [2] => wed [3] => thu [4] => fri [5] => sat )

Count months from a date to December then count months from January the following year to December that same year, then from January to end date

I have a start_date of 1/10/2018, and an end_date of 1/8/2020, the difference between the two dates in months is 22, that is 1 year 10 months, now, I want to create tables that terminate at the end of each year as follows:
table 1
column_heading will be "1/10/2018 - 31/12/2018"
and the row will be "2 months"
table 2
column_heading will be "1/1/2019 - 31/12/2019"
and the row will be "12 months"
table 3
column_heading will be "1/1/2020 - 1/8/2020"
and the row will be "8 months"
I would like to loop something, maybe the difference between the dates to create the number of tables necessary, if the two dates exist within the same year it will only create 1 table, or 2 if it enters the next year, I am using laravel and carbon to manipulate the dates.
Thank you in anticipation of your help.
Something like this
Here's one way. Note that I had to convert the format of your dates to YYYY-mm-dd in order to use PHP date functions. In the end you'll get an array and it's easy for you to transform the final dates into the format you desire. You can test it here: https://www.tehplayground.com/lvuTdWl91TeItEQC
The code:
<?php
// example code
$s = "1/10/2018";
$e = "1/08/2020";
// reassemble so we can use the date functions YYYY-mm-dd
$s = implode("-", array_reverse(explode("/", $s)) );
$e = implode("-", array_reverse(explode("/", $e)) );
// get the parts separated
$start = explode("-",$s);
$end = explode("-",$e) ;
$iterations = ((intVal($end[0]) - intVal($start[0])) * 12) - (intVal($start[1]) - intVal($end[1])) ;
$sets=[$start[0] => array("start" => $s, "end" => "", "months" => 0)];
$curdstart= $curd = $s;
$curyear = date("Y", strtotime($s));
for($x=1; $x<=$iterations; $x++) {
$curdend = date("Y-m-d", strtotime($curd . " +{$x} months"));
$curyear = date("Y", strtotime($curdend));
if (!isset($sets[$curyear])) {
$sets[$curyear]= array("start" => $curdend, "end" => "", "months" => 0);
}
$sets[$curyear]['months']++;
$sets[$curyear]['end'] = date("Y-m-", strtotime($curdend)) . "31";
}
die(print_r($sets,1));
$mctr = 0 ;
The output:
Array
(
[2018] => Array
(
[start] => 2018-10-1
[end] => 2018-12-31
[months] => 2
)
[2019] => Array
(
[start] => 2019-01-01
[end] => 2019-12-31
[months] => 12
)
[2020] => Array
(
[start] => 2020-01-01
[end] => 2020-08-31
[months] => 8
)
)

Use RegEx to get (parsed) times from string

I need some direction on how to alter my current RegEx line.
Example string:
9:00 AM - 9:30 AM
Desired output:
900 930 (each can be an index in an array or whatever)..
Current approach:
preg_match('/^([0-9]{1,2}):([0-9]{1,2}) ?([ap]m)/i', trim($presentationtime), $matches);
however this only seems to get me the FIRST (stripped/parsed) time.
results:
$presentationtime = $matches[1] . $matches[2];
echo 'Matches check: '. $matches[0] . '<br>';
echo 'Matches check: '. $matches[1] . '<br>';
returns:
Matches check: 9
Matches check: 00
How can I alter my regex to get BOTH times (stripped/parsed the same way)..
I'm expecting a 6 index array..but can only get a 3 index/count array
as #anubhava says use preg_match_all
$presentationtime = '9:00 AM - 9:30 AM';
preg_match_all('/([0-9]{1,2}):([0-9]{1,2}) /', trim($presentationtime), $matches);
print_r($matches);
results:
Array
(
[0] => Array
(
[0] => 9:00
[1] => 9:30
)
[1] => Array
(
[0] => 9
[1] => 9
)
[2] => Array
(
[0] => 00
[1] => 30
)
)
Edit to answer the comment:
very lazy workaround to get one dimensional array, banal regex
$presentationtime = '9:00 AM - 9:30 PM';
preg_match('/([0-9]{1,2}):([0-9]{1,2})\s([apm]+)\s-\s([0-9]{1,2}):([0-9]{1,2})\s([apm]+)/i', trim($presentationtime), $matches);
result
Array
(
[0] => 9:00 AM - 9:30 PM
[1] => 9
[2] => 00
[3] => AM
[4] => 9
[5] => 30
[6] => PM
)
You have to simply use preg_match_all (as stated by anubhava in the comment) and remove the ^ at the start of the regex:
preg_match_all('/([0-9]{1,2}):([0-9]{1,2}) ?([ap]m)/i', trim($presentationtime), $matches);
Then $matches will be like:
Array
(
[0] => Array
(
[0] => 9:00 AM
[1] => 9:30 AM
)
[1] => Array
(
[0] => 9
[1] => 9
)
[2] => Array
(
[0] => 00
[1] => 30
)
[3] => Array
(
[0] => AM
[1] => AM
)
)
If you want to refine the regex you may use:
preg_match('/([0-9]{1,2}):([0-5]\d)\s*([ap]m)/i', trim($presentationtime), $matches);
The minute section is will match 2 digits from 00 to 59, the space section \s* is optional and matches more than one whitespace char (space, tab, CR, FF...)
One way of doing that:
$data = '9:00 AM - 9:30 AM';
print str_replace('-', ' ', preg_replace("/[^0-9-]/","", $data));
Result:
900 930
keep digits and dash
replace dash with space
In the previous example, I was using limited regex with preg_replace. To keep things simple and using time with AM/PM, I'd just explode (as you already thought about),
$data = '9:00 AM - 9:30 PM';
$timeArray = array_map('trim', explode('-', $data)); // trim each array item
$timeArray = array_map('Time24Hr', $timeArray); // clean up time
print_r($timeArray);
function Time24Hr($time) {
$time_ampm = explode(' ', $time);
if (strtolower(trim($time_ampm[1])) === 'am') {
// A
return $time_ampm[0];
// B
//return str_replace(':', '', $time_ampm[0]);
// C
//return $time;
}
$hr_min = explode(':', trim($time_ampm[0]));
// A
return ($hr_min[0] + 12) . ':' . $hr_min[1];
// B
//return ($hr_min[0] + 12) . $hr_min[1];
// C
//return ($hr_min[0] + 12) . ':' . $hr_min[1]. ' ' . $time_ampm[1];
}
Results:
Result A:
Array
(
[0] => 9:00
[1] => 21:30
)
Result B:
Array
(
[0] => 900
[1] => 2130
)
Result C: -- obviously this is insane, but you can easily make it sane. This is just an example
Array
(
[0] => 9:00 AM
[1] => 21:30 PM
)
Of course, this simplistic method can be streamlined much further. This method doesn't need regex.

match a string with an array of values and return the key

so i have an array with months:
$arr_month = array(
1=>'january',
2=>'february',
3=>'march',
4=>'april',
5=>'may',
6=>'june',
7=>'july',
8=>'august',
9=>'september',
10=>'october',
11=>'november',
12=>'december'
);
i also have strings (i know, they look weird...) that look like:
23 de january del 2003
12 de may de 1976
6 de february de 1987
What i want is to find and match the month in the string with the array and return the array key.
so:
23 de january del 2003 returns 1
12 de may de 1976 returns 5
6 de february de 1987 returns 2
and so on...
Any idea how?
This should work for you.
$dateString = "23 de january del 2003"; // as an example
$exploded = explode (" ", $dateString); // separate the string into an array of words
$month = $exploded[2]; // we want the 3rd word in the array;
$key = array_search ($month, $arr_month); // lets find the needle in the haystack
print $key;
yields 1.
See http://php.net/manual/en/function.array-search.php for more.
$index = -1;
foreach ($arr_month as $k => $v) {
if (strstr($mystring, $v)) {
$index = $k;
break;
}
}
// exists with $index set
You should be able to do like this:
<?php
function getMonth($time){
$matches = array();
preg_match("/[0-9] de ([a-z]+) de/", $time, $matches);
return date('n',strtotime($matches[1]));
}
?>
Then you don't even need your month array :D
EDIT if you for some reason want the array in there:
<?php
function getMonth($time){
$matches = array();
$pattern = "/[0-9] de ([a-z]+) de/";
preg_match($pattern, $time, $matches);
$month = $matches[1];
$arr_month (
1=>'january',
2=>'february',
3=>'march',
4=>'april',
5=>'may',
6=>'june',
7=>'july',
8=>'august',
9=>'september',
10=>'october',
11=>'november',
12=>'december'
);
return in_array($month, $arr_month) ? array_search($month, $arr_month) : false;
}
?>
You can do this with the optional search param with array keys:
$matchedKeys = array_keys($arr_month, "november");
var_dump($matchedKeys);
// array(1) { [0]=> int(11) }
As you can see this will return an array of all matching keys.
If you're trying to parse a date, bear in mind that PHP can do that for you (though it depends on how arbitrary your input data is - this is obviously more reliable if you know the date format in advance).
eg:
print_r(date_parse("6 de february de 1987"));
gives:
Array
(
[year] => 1987
[month] => 2
[day] =>
[hour] =>
[minute] =>
[second] =>
[fraction] =>
[warning_count] => 2
[warnings] => Array
(
[14] => Double timezone specification
[22] => The parsed date was invalid
)
[error_count] => 2
[errors] => Array
(
[0] => Unexpected character
[2] => The timezone could not be found in the database
)
[is_localtime] => 1
[zone_type] => 0
)
So it's given up on the day, but it did correctly identify the month and year.
Using the more modern DateTime api fails on your input (is that a mixture of French and English?)
But it does work for the following:
$d = new DateTime("6th february 1987", new DateTimeZone("UTC"));
print $d->format("Y-m-d H:i:s") . "\n";'
Gives:
1987-02-06 00:00:00

PHP, multidimensional arrays: to intersected events with days of the month

I want to build a movie timetable. I have $array1 containing the movie titles and airing dates and times:
array =
0: array =
Title: string = American Beauty
Date: string = 25/09/2012
Time: string = 15:00 - 16:20
1: array =
Title: string = The Godfather
Date: string = 25/09/2012
Time: string = 16:20 - 18:20
2: array =
Title: string = Pulp Fiction
Date: string = 26/09/2012
Time: string = 15:00 - 16:20
And I have $array2 containing the days of the month grouped by Mondays, Tuesday s, Wednesday s, Thursday s and Fridays (no movies during the weekend)
Array
(
[1] => Array
(
[0] => 3
[1] => 10
[2] => 17
[3] => 24
[4] =>
)
[2] => Array
(
[0] => 4
[1] => 11
[2] => 18
[3] => 25
[4] =>
)
[3] => Array
(
[0] => 5
[1] => 12
[2] => 19
[3] => 26
[4] =>
)
[4] => Array
(
[0] => 6
[1] => 13
[2] => 20
[3] => 27
[4] =>
)
[5] => Array
(
[0] => 7
[1] => 14
[2] => 21
[3] => 28
[4] =>
)
)
I need to intersect these two arrays so I can print under day 25 the movie “American Beauty” also under day 25 “The Godfather” and under day 26 “Pulp Fiction”.
Meaning I need to print:
SEPTEMBER 2012
Monday Tuesday Wednesday ....
3 4 5
10 11 12
17 18 19
24 25 26
15:00-16:20 American Beauty 15:00-16:20 Pulp Fiction
16:20-18:20 The Godfather
My tries so far:
foreach( $array1 as $key => $value )
{
$theTime = $value['Time'];
$theTime = explode("/", $theTime );
$days[] = $theTime [0];
$months[] = $theTime [1];
}
So I have all the airing days in array $days but from here I don’t know how to follow or even if this approach is the correct one.
Here's how I get $array2:
$month = 9;
$year = 2012;
for ($i = 1; $i <= 31; $i++)
{
$timestamp = mktime(0, 0, 0, $month , $i, $year);
if (date("n", $timestamp) == $month )
{
$day = date("N", $timestamp);
// Monday 1 to Friday 5
if ($day == 1 OR $day <= 5) {
$array2[$day][] = date("j", $timestamp);
}
}
}
Please help, I’m stuck.
Thanks very much
Ok you will iterate over $array1 and parse the date of the movie.
Then, you will look inside array2 if your day is there
foreach($array1 as $movie){
$movieDate = new DateTime($movie);
//as you indexed $array2 with monday = 1 -> friday = 5 you can use the "w" format
if(isset($array2[$movieDate->format("w"))){
$array[$movieDate->format("j")][] = $movie;
}
}
I made some mutation in your output array, it becomes :
SEPTEMBER 2012
Monday Tuesday Wednesday ....
3=>[] 4 =>[] 5=>[]
10=>[] 11 =>[] 12=>[]
17=>[] 18 =>[] 19=>[]
24 =>[] 25=>[ 26 =>[
15:00-16:20 American Beauty, 15:00-16:20 Pulp Fiction]
16:20-18:20 The Godfather]

Categories