<?php
$tests = [
'bar 3 m foo', # -
'bass drop 2 d bare', # -
'2 3 m foo foo', # -
'1 222 4 d baz', # -
'5d 4h 3m 2s', # -
'5 days 4 minutes', # -
'9w8d7h6m5s', # -
'2 d 3 m baz', # +
'5d 4h 3m 2s foo', # +
'2 s foo', # +
'222 h baz bar', # +
'1 mon foo', # + mon for month?
'1w bar', # +
'1 month baz', # +
'2 months foobar', # +
'4 months 2 months foo', # + repeating same string is allowed by strtotime, though it sums up https://3v4l.org/FIgNC
'5m3s bar', # +
'7mon6w5d4h3m2s bazinga!', # +
'3 seconds 5 hours 5 minutes jeeey hoe :D damn I should sleep...' # +
];
/*
* Replaces (s|m|h|d|w|mon) with strtotime compatible abbreviations
*
* It *should* allow these: https://gist.github.com/DaveRandom/625fb4bf73112474fb5768a0a300e4e5
*
* NOTE: Below regex...uh, specially the (?&str) bit probably sucks...
*
* @param array $tests
* @return array
*/
function normalizeTimeSpec($tests) {
$reg1 = <<<'REGEX'
~
\s? (?<int> \d{1,5}) \s?
(?<time>
(?: s (?=(?:ec(?:ond)?s?)?(?:\b|\d))
| m (?=(?:in(?:ute)?s?)?(?:\b|\d))
| h (?=(?:(?:ou)?rs?)?(?:\b|\d))
| d (?=(?:ays?)?(?:\b|\d))
| w (?=(?:eeks?)?(?:\b|\d))
| mon (?=(?:(?:th)?s?)?(?:\b|\d))
)
)
[^\d]*?(?=\b|\d)
# do only extract start of string
| .+ (*SKIP)
~uix
REGEX;
$array = [];
foreach($tests as $case){
$output = preg_replace_callback ($reg1,
function($matches){
if (!isset($matches['int'])) {
return '';
}
switch($matches['time']){
case 's':
$t = ' sec ';
break;
case 'm':
$t = ' min ';
break;
case 'h':
$t = ' hour ';
break;
case 'd':
$t = ' day ';
break;
case 'w':
$t = ' week ';
break;
case 'mon':
$t = ' month ';
break;
}
return $matches['int'].$t;
}, $case);
$array[] = $output;
}
return $array;
}
/*
* Test whether given time string passes regex rules (and show the matches array)
* This is mainly for Reminders plugin where we allow trailing (but not leading) strings
*
* @param array $tests
* @return array
*/
function testTimeSpec($tests){
$finalArray = [];
# I saw another solution given by @bwoebi - https://3v4l.org/6BoW1
# However that doesn't fail any longer than "5 months 4 weeks" see ^
# pretty sure these can be improved (but it's late and I can't regex any more now...)
$regex = <<<'REGEX'
/(?(DEFINE)
(?<int>
(\s*\b)?
(\d{1,5})?
(\s*)?
)
(?<timepart>
(?&int)
( s(ec(ond)?s?)?
| m(in(ute)?s?|onths?)?
| h(rs?|ours?)?
| d(ays?)?
| w(eeks?)?
)
\b
)
)
^(?<time>
(?:(?&timepart)(*SKIP).)+
)
(?<string>.+)$
/uix
REGEX;
foreach($tests as $case){
if(preg_match($regex, $case, $matches)){
$finalArray[] = $matches['time'].$matches['string'];
}
}
return $finalArray;
}
$normalized = normalizeTimeSpec($tests);
$output = testTimeSpec(normalizeTimeSpec($tests));
print_r($normalized);
print_r($output);
- Output for 5.6.0 - 5.6.40, 7.0.0 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33, 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
- Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] => 5 day 4 hour 3 min 2 sec
[5] => 5 day 4 min
[6] => 9 week 8 day 7 hour 6 min 5 sec
[7] => 2 day 3 min
[8] => 5 day 4 hour 3 min 2 sec
[9] => 2 sec
[10] => 222 hour
[11] => 1 month
[12] => 1 week
[13] => 1 month
[14] => 2 month
[15] => 4 month 2 month
[16] => 5 min 3 sec
[17] => 7 month 6 week 5 day 4 hour 3 min 2 sec
[18] => 3 sec 5 hour 5 min
)
Array
(
)
- Output for 8.3.5
- Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] => 5 day 4 hour 3 min 2 sec
[5] => 5 day 4 min
[6] => 9 week 8 day 7 hour 6 min 5 sec
[7] => 2 day 3 min
[8] => 5 day 4 hour 3 min 2 sec
[9] => 2 sec
[10] => 222 hour
[11] => 1 month
[12] => 1 week
[13] => 1 month
[14] => 2 month
[15] => 4 month 2 month
[16] => 5 min 3 sec
[17] => 7 month 6 week 5 day 4 hour 3 min 2 sec
[18] => 3 sec 5 hour 5 min
)
Array
(
)
preferences:
222.74 ms | 403 KiB | 291 Q