Myself I have such a string
citation
2d6h8y4m
d - days
min - min
h - h
y - years
m - months
s - seconds
I would like to add today's date as saved time , ie
2 days , 6 hours, 8 years and 4 months
I know how to do it in a simple way - the loop on all the text and read in sequence numbers, but my guess is that it can be done more simply - a regex . Sorry sag on this if someone gave me such a function or somehow me clues (eg . Given pattern on one character) enough , I can Narratives
#INFO
Not understood still to drive not want to do today's date to add eg 2 days , 4 months , etc.
Ie today is 2014-11-22 5:43:45 p.m.
After the addition of I 2 days 6 hours 8 years and 4 months I have
2022-04-24 11:43:45 p.m.
Separate int values & char/word by preg_match_all(). Create and array() that contains the full meaning of character (ie, h = hours). Then just foreach(). Example:
$avr = array('d'=>'days', 'min'=>'min','h'=>'hours', 'y'=>'years', 'm'=>'months', 's'=>'seconds');
$str = '2d6h8y4m';
preg_match_all('/\d+/', $str, $int);
preg_match_all('/[a-z]+/', $str, $word);
$len = count($int[0]) - 1;
$result = '';
foreach($int[0] as $k=>$v){
if($len == $k){
$result .= ' and ' . $v . ' ' . $avr[$word[0][$k]];
}else{
$suf = ($k == 0) ? '' : ', ';
$result .= $suf . $v . ' ' . $avr[$word[0][$k]];
}
}
echo $result;
Output:
2 days, 6 hours, 8 years and 4 months
The regex you're looking for is
(\d+)d(\d+)h(\d+)y(\d+)m
\d matches any digit. From this you can extract what was matched between the brackets. Here is the full code:
preg_match("(\d+)d(\d+)h(\d+)y(\d+)m", "2d6h8y4m", $matches);
Now $matches will be an array containing what is matched between the brackets. $matches[0] will be the first bracket (the day), $matches[1] will be the first bracket (the hour), etc.
Why are you reinventing the wheel?
Why don't you store intervals in ISO-8601 duration format, like for example: P8Y5M2DT6H ?
$date = new DateTime();
$interval = new DateInterval('P8Y5M2DT6H');
$date->add($interval);
demo
Related
I get POST request date strings in the format of mY (no leading zeros on the month), so there's no delimiting character to split on.
Examples: 122019 or 62019
I need to separate the five or six digit string into a one or two digit month and a four digit year.
Eg1 : 122019
$a[0] = 12;
$a[1] = 2019
Eg2 : 62021
$a[0] = 6;
$a[1] = 2021
I don't know about this date format, especially about how you use an integer as a date. But let's consider that it's on purpose, the year is always gonna be 4 character, so you can just get the year by taking the last 4 char and use the rest as the month.
Using substr() see PHP.NET Substr
Return part of a string
And you can specify the start and length of the part you want to get, or using - to get character starting from the end of the string.
$weirdDate = 122019;
//takes the last 4 character
$year = substr($weirdDate , -4);
//takes the string from the beginning to 4 char before the end
$month = substr($weirdDate , 0,strlen($weirdDate)-4);
echo $year;
echo $month;
Again, it seems like a weird way to get a month/year date, but i'm answering based on the assumption that a year is gonna be 4 char long.
If it's not, you can't really split the number since the month part can be 1 or 2 char long.
09-2019 would be 92019
11-2019 would be 112019
A simple use of the substr() function will do this nicely
$in = '122019';
$year = substr($in,-4);
$month = substr($in,0, strlen($in)-4);
echo $year . ' month ' . $month;
$a[] = $month;
$a[] = $year;
RESULT
2019 month 12
array (
[0] => 12
[1] => 2019
)
Or if we use $in = '62019';
The RESULT would be
2019 month 6
array (
[0] => 6
[1] => 2019
)
Reference 'substr()`
You could also use substr with strpos. With substr(), you could first get the year by providing a negative offset to start capturing from back of the string as the year is going to be 4 digits. Then, you could use strpos() to find the index of the year and use this as the ending index to get the month.
That being said, best way to deal with this data is to either have a proper date format or better to have a JSON string with proper keys for days, month and year along with date.
Code:
<?php
$str = '122019';
$year = substr($str,-4);
$month = substr($str,0,strpos($str,$year));
echo $month," ",$year;
I have one another solution to use str_replace() with substr() like:
<?php
$string = "122019";
$year = substr($string, -4);
$date = str_replace($year, "", $string);
$myArray = array($date,$year); // convert into an array
echo "<pre>";
print_r($myArray);
?>
Desired Output:
Array
(
[0] => 12
[1] => 2019
)
Side Note: This will only work, if your year based on last 4 characters and other then these 4 characters must be date, as you mentioned in your question.
To accomplish this feat with two function calls, use negative parameters with substr() calls.
There is no reason to call strlen(), strpos(), or str_replace().
For a single call technique, use preg_split() with a lookahead pattern to ensure that no characters are consumed while exploding.
Codes: (Demo)
$mY = '62021';
var_export([
substr($mY, 0, -4), // month
substr($mY, -4) // year
]);
echo "\n---\n";
var_export(
preg_split('/(?=\d{4}$)/', $mY)
);
I need to find the first and last number with length n and starting with digit d.
For example.
i need to find the first and last number with length 6 and starting in 2
The result should be first number=200000 and last number=299999 .
I there any functions available in php to help me to get a logic to solve this.??
Hope Someone can help me..
You could try relying on the fact that the smallest number will end in all 0's and the largest in all 9's, and then use str_repeat to generate the appropriate tailing digits:
echo $d . str_repeat('0', $n-1);
echo $d . str_repeat('9', $n-1);
For $d = 2 and $n = 6, this will give you 200000 and 299999.
If you need them as integers, just do
$start = (int)($d . str_repeat('0', $n-1));
$finish = (int)($d . str_repeat('9', $n-1));
var_dump($start);
var_dump($finish);
Output:
int(200000)
int(299999)
Here is an option which uses algebra to get the numbers you want. Based on the width of the number, we can compute the smallest number with that width. Then, the starting number is simply this value times the starting first digit. And the ending number can also be teased out.
$length = 6;
$first = 2;
$begin = pow(10, $length-1);
$start = $first * $begin;
$end = (($first + 1) * $begin) - 1;
echo $start . "\n";
echo $end;
200000
299999
Demo
This should outperform the accepted answer because generating the numbers requires only a few arithmetic operations, rather than complex string manipulation operations.
Despite some help earlier on I am still floundering in regex problems and now in array problems.
I am trying to allow users to put time in as 205pm 1405 14:05 2.05 pm and so on.
Previously I had times stored as 14:05 (standard mySQL TIME format) but users were not liking that but if I convert to 2:05 pm then, when the updated values are entered (in similar format), that obviously breaks the database.
I have NO TROUBLE going 14:05 to 2:05 pm but I am having a nightmare going in the opposite direction.
I have fudged things a bit with a cascading IF statement to get the string length but I have spent literally hours trying to get at the output.
IE if I get 2-05 pm, to start off with I just want to get 205.
Here is my atrocious code:
if ($_POST['xxx']='yyy')
{
$stuff=$_POST['stuff'];
$regex='/^\d\D*\d\D*\d\D*\d\D*\d\D*$/';
if (preg_match($regex, $stuff, $matches)) {echo " More than 4 digits. This cannot be a time."; }
else{
$regex='/^\d\D*\d\D*\d\D*\d\D*$/';
if (preg_match($regex, $stuff, $matches)) {echo " >>4 digits";}
else{
$regex='/^\d\D*\d\D*\d\D*$/';
if (preg_match($regex, $stuff, $matches)) {echo " >>3 digits";}
else{
$regex='/^\d\D*\d\D*$/';
if (preg_match($regex, $stuff, $matches)) {echo " Less than 3 digits. This cannot be a time.";}
}
}
}
}
debug ($matches,"mat1");
$NEWmatches = implode($matches);
debug ($matches,"matN1");
preg_match_all('!\d+!', $NEWmatches, $matches);
debug ($matches,"mat2");
$matches = implode($matches);
debug ($matches,"mat3");
echo "<br> Matches $matches"; /// I hoped to get the digits only here
?>
Thanks for any help.
$times = array(
'205pm', '1405', '4:05', '2.05 pm'
);
foreach($times as $time)
{
// parsing string into array with 'h' - hour, 'm' - minutes and 'ap' keys
preg_match('/(?P<h>\d{1,2})\D?(?P<m>\d{2})\s*(?P<ap>(a|p)m)?/i', $time, $matches);
// construction below is not necessary, it just removes extra values from array
$matches = array_intersect_key($matches,
array_flip(array_filter(array_keys($matches), 'is_string')));
// output the result
var_dump($matches);
}
If you are using that string at strtotime then it is easier just to reformat it to the correct format, like this
$times = array(
'205pm', '1405', '4:05', '2.05 pm'
);
var_dump(preg_replace('/(\d{1,2})\D?(\d{2})(\s*(a|p)m)?/i', '$1:$2$3', $times));
ps: for more complex possible situations I would suggest to reformat the time and do something like this, otherwise regexp can be a nightmare..
$times = array(
'9 pm', '205pm', '1405', '4:05', '2.05 pm'
);
$times = preg_replace('/(\d{1,2})\D?(\d{2})(\s*(a|p)m)?/i', '$1:$2$3', $times);
foreach($times as $time)
{
$date = strtotime($time);
if ($date === false) { echo 'Unable to parse the time ' . $time . "\n"; continue; }
$hour = date('G', $date);
$minutes = date('i', $date);
echo $hour . " : " . $minutes . "\n";
}
For your given example "2-05 or 14:05" you can use this RegEx:
^(?<HOUR>[0-9]{1,2})\s{0,}((-|:|\.)\s{0,})?(?<MIN>[0-9]{2})\s{0,}(?<MODE>(a|p)m)?$
"Hour" will hold the the first 2 numbers of the string, "MIN" will always hold the last 2 numbers of the string. "MODE" will hold (am or pm)
So you can combine them at the end to an single string. Also you can just run an simple Replace("-","").
I have a need for a function that will do the following thing:
If I have a string like this "2 1 3 6 5 4 8 7" I have to insert dashes between pairs of numbers following some rules.
The rules are simple.
Put a dash between two numbers if the first one of the pair is smaller then the one that follows it. Do all possible combinations of this and if a pair already has a dash then the space next to it can't have a dash.
Basically my results for above string would be
2 1-3 6 5 4 8 7
2 1-3 6 5 4-8 7
2 1 3-6 5 4 8 7
2 1 3-6 5 4-8 7
2 1 3 6 5 4-8 7
I did create a function that does this but I am thinking it is pretty sluggish and I don't want to taint your ideas with it. If possible I would like to know how you guys are thinking about this and even some pseudo code or code would be great.
EDIT 1:
here is the code I have so far
$string = "2 1 3 6 5 4 8 7";
function dasher($string){
global $dasherarray;
$lockcodes = explode(' ', $string);
for($i = 0; $i < count($lockcodes) - 1; $i++){
if(strlen($string) > 2){
$left = $lockcodes[$i];
$right = $lockcodes[$i+1];
$x = $left . ' ' . $right;
$y = $left . '-' . $right;
if (strlen($left) == 1 && strlen($right) == 1 && (int)$left < (int)$right) {
$dashercombination = str_replace($x, $y, $string);
$dasherarray[] = $dashercombination;
dasher($dashercombination);
}
}
}
return array_unique($dasherarray);
}
foreach(dasher($string) as $combination) {
echo $combination. '<br>';
}
Perhaps this will be helpful in terms of offering different methods to parse the string.
$str="2 1 3 6 5 4 8 7";
$sar=explode(' ',$str);
for($i=1;$i<count($sar);$i++)
if($sar[$i-1]<$sar[$i])
print substr_replace($str,'-',2*($i-1)+1,1) . "\n";
Note that the code expects only single digits numbers in the string.
Note that the code expects that the string is formatted as per your example. It would be good to add some sanity checks (collapse multiple spaces, strip/trim blanks at the beginning/end).
We can improve upon this by finding all the spaces in the string and using them to index substrings for comparison, still assuming that only a single spaces separates adjacent numbers.
<?php
$str="21 11 31 61 51 41 81 71";
$letter=' ';
#This finds the locations of all the spaces in the strings
$spaces = array_keys(array_intersect(str_split($str),array($letter)));
#This function takes a start-space and an end-space and finds the number between them.
#It also takes into account the special cases that we are considering the first or
#last space in the string
function ssubstr($str,$spaces,$start,$end){
if($start<0)
return substr($str,0,$spaces[$end]);
if($end==count($spaces))
return substr($str,$spaces[$start],strlen($str)-$spaces[$start]);
return substr($str,$spaces[$start],$spaces[$end]-$spaces[$start]);
}
#This loops through all the spaces in the string, extracting the numbers on either side for comparison
for($i=0;$i<count($spaces);$i++){
$firstnum=ssubstr($str,$spaces,$i-1,$i);
$secondnum=ssubstr($str,$spaces,$i,$i+1) . "\n";
if(intval($firstnum)<intval($secondnum))
print substr_replace($str,'-',$spaces[$i],1) . "\n";
}
?>
Note the explicit conversion to integers in order to avoid lexicographic comparison.
Let's say in PHP I have a string-variable : "This takes between 5 and 7 days"
I need to store some sensible information about the time it takes in an integer.
I'm satisfied if the result would be 5.
I tried stripping non-numeric characters, but end up with 57 then.
How can this be done in a better way?
Use preg_match to match the first digit group using a regex:
$subject = 'This takes between 5 and 7 days';
if (preg_match('/\d+/', $subject, $matches)) {
echo 'First number is: ' . $matches[0];
} else {
echo 'No number found';
}
Using preg_match_all you could match all digit groups (5 and 7 in this example):
$subject = 'This takes between 5 and 7 days';
if (preg_match_all('/\d+/', $subject, $matches)) {
echo 'Matches found:<br />';
print_r($matches);
} else {
echo 'No number found';
}
If you want to quantify the numbers appropriately, I would suggest the following:
<?php
$subject = "This takes between 5 and 7 days";
$dayspattern = '/\d+(\.\d+)? ?days?/';
$hourspattern = '/\d+(\.\d+)? ?hours?/'
$hours = -1;
if (preg_match($dayspattern , $subject, $matches) > 0)
{
preg_match($dayspattern, $matches[0], $days);
$hours = $days * 24;
} elseif (preg_match($dayspattern , $subject, $matches) > 0) {
preg_match($hourspattern, $matches[0], $hours);
$hours = $hours;
}
?>
You would need to consider:
what happens when no numbers are found, or numbers are given as text instead.
what happens when someone says '1 day and 5 hours'
Hopefully this gives you enough information to do the rest yourself.
If you would like to extract the price, ie €7.50 OR $50 etc
here is the regular expression comes to the solution for me.
preg_match('/\d+\.?\d*/',$price_str,$matches);
echo $matches[0];
results 7.50 OR 50
Seperate the subject into words by splitting the string into an array. Then, somehow I don't know, take the words out of the array. Maybe by looping through all children of the array inside a try-catch block, and try to change each element into an int type:
for($i=0;$i<$words->length;$i++){
try{
$tempNum = (int)$words[$i];
}catch($ex){
$words->remove($i);
}
}
Or something like that. I don't know any of the array methods, but you get what I mean. Anyway, the $words array now only contains numbers.