First off, I suggest using named timers.
Note that your timers don't stay "in order" because you're setting a different delay (interval) for each of the three infinite timers.
If this is intended you can use something like:
on 150:TEXT:*:?: {
if ($1 == !startspam) {
.timerannounce1 0 1200 if ( $!me ison #somechan ) msg #somechan <your message 1>
.timerannounce2 0 2100 if ( $!me ison #somechan ) msg #somechan <your message 2>
.timerannounce3 0 3000 if ( $!me ison #somechan ) msg #somechan <your message 3>
msg $nick Spam started
}
elseif ($1 == !stopspam) {
.timerannounce* off
msg $nick Spam stopped
}
}
That's close to the code you use already. And with this method, the first timer will trigger after 1200s and trigger again each 1200s. The third timer will trigger after 3000s and trigger again each 3000s. Therefore the first timer has triggered a second time (1200s + 1200s) before the third timer triggered the first time etc... It's like three planets having a different time of circulation. They lap each other.
If you want to keep the three messages "in order" instead, you have to use the samy delay (interval) for all three timers. To start them with some "offset" none the less, use timers again

on 150:TEXT:*:?: {
if ($1 == !startspam) {
.timerannounce.init1 1 0 .timerannounce1 0 1200 if ( $!me ison #somechan ) msg #somechan <your message 1>
.timerannounce.init2 1 30 .timerannounce2 0 1200 if ( $!me ison #somechan ) msg #somechan <your message 2>
.timerannounce.init3 1 60 .timerannounce3 0 1200 if ( $!me ison #somechan ) msg #somechan <your message 3>
msg $nick Spam started
}
elseif ($1 == !stopspam) {
.timerannounce* off
msg $nick Spam stopped
}
}
This code will
start the first infinite timer immediately, the second after 30 seconds and the third after 60 seconds (this is your offset for the timers).
The infinite timers themself all use the same delay/interval (1200s) and therefore keep their relative order.
Maybe you need to restart mIRC to get rid of timers already started/still running.