The reason is that this happens:
$ a=20171231; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Dec 1 00:00:00 PST 2017>
The command date goes to the start of the month in december.
When you add 1 day, it goes to the day 2
.
Instead, when the day is 1 of january it goes back a full month
$ a=20180101; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Dec 1 00:00:00 PST 2017>
That is an internal characteristic of date.
date
goes back to the previous month with the same day number as given:
$ a=20171110; b=$(date -d "$a -1 month"); echo "<$b>"
<Tue Oct 10 00:00:00 PST 2017>
That is the given date in november goes back to october. But if the day is 31 it gets into trouble. For example from 31 of october there is no 31 of september, so it goes back to october 1:
$ a=20171031; b=$(date -d "$a -1 month"); echo "<$b>"
<Sun Oct 1 00:00:00 AST 2017>
Month shifts are exact only when the day is 01, February is a particularly odd month:
$ a=20170331; b=$(date -d "$a -1 month"); echo "<$b>"
<Fri Mar 3 00:00:00 AST 2017>
Because there is no 31 of February.