Matching html with a regex is not advisable, but if there can be no angle brackets in between, you might get away with a single pattern using the \G anchor:
(?:<p>(?=[^<>]*</p>)|\G(?!^))[^\d<>]*\d+\K\h
Explanation
(?: Non capture group
<p>(?=[^<>]*</p>) Match <p> and assert a closing </p> without angle brackets in between
| Or
\G(?!^) Assert the current position at the end of the previous match, but not at the start of the string
) Close the non capture group
[^\d<>]* Match optional chars other than < or > or a digit
\d+ Match 1+ digits
\K Forget what is matched so far
\h Match a single horizontal whitespace char
See a regex demo and a PHP demo.
$re = '~(?:<p>(?=[^<>]*</p>)|\G(?!^))[^\d<>]*\d+\K\h~';
$str = '<p>My 90 days 123 work.</p>';
echo preg_replace($re, " ", $str);
Output
<p>My 90 days 123 work.</p>
Or using DOMDocument to find the paragraphs, and do the replacement after matching a digit and a horizontal whitespace char:
$str = '<p>My 90 days 123 work.</p>';
$domDoc = new DOMDocument;
$domDoc->loadHTML($str, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
foreach ($domDoc->getElementsByTagName('p') as $p) {
echo preg_replace("/\d\K\h/", " ", $p->nodeValue);
}
Output
My 90 days 123 work.