This is not necessarily true. Take a high dividend paying stock (10%) for example. If deep ITM, you would have a loss higher than the initial cost for european options, or face an early exercise for amercian options.
I will follow an example from Fidelity.
A long calendar spread with calls is created by buying one
“longer-term” call and selling one “shorter-term” call with the same
strike price. In the example a two-month (56 days to expiration) 100
Call is purchased and a one-month (28 days to expiration) 100 Call is
sold. This strategy is established for a net debit (net cost)...
Fidelity also shows the following figure:

Let's define Black Scholes in Julia:
using Distributions, DataFrames
N(x) = cdf(Normal(0,1),x)
function BSM(S,K,t,r,d,σ, cp) # (cp = 1 for call, -1 for put)
d1 = ( log(S/K) + (r - d + 1/2*σ^2)*t ) / (σ*sqrt(t))
d2 = d1 - σ*sqrt(t)
opt = cp*exp(-d*t)S*N(cp*d1) - cp*exp(-r*t)*K*N(cp*d2)
return opt
end
Fidelity states the following further below:
*Profit or loss of the long call is based on its estimated value on the expiration date of the short call. This value was calculated using
a standard Black-Scholes options pricing formula with the following
assumptions: 28 days to expiration, volatility of 30%, interest rate
of 1% and no dividend.
So assume S=K=100, r = 0.01, d=0 and σ = 0.3, which in Julia gives us the following, which is essentially identical to Fidelity's numbers (note that I use negative values as cash outflows; usually one would show the cost/premium of the option as a positive value):

Fidelity shows a profit and loss table below:

Let's focus on s = 105. The near dated that we sold expiries in the money, which results in a loss of 5 (105-100), which is net -1.65 because we received 3.35 from selling the option. The long dated option still has 28 days left now. Black Scholes states it is worth ~6.52. Therefore, we have a total of ~1.76 (-4.75 + 6.51). This gives a profit of ~0.11.
Let's look at Julia again and make it interactive:
using Plots, PlotThemes
theme(:juno)
gui = @manipulate for k = 80:1:120, σ_init = 0.01:0.01:0.75, days_short = 1:1:100, days_long = 1:1:200,
s = 80:1:120, r = -0.2:0.01:0.2, d = -0.2:0.01:0.2, σ_end = 0.01:0.01:0.75;
t1 = days_short/365
t2 = days_long/365
spot = ["At Initiation", 150, 125,120,115,110,105,100,95,90,85,80,75, 50]
short_28_day = append!([-BSM.(s,k,t1,r,d,σ_init,1)[1]],-max.(spot[2:end] .-k,0).+BSM.(s,k,t1,r,d,σ_init,1)[1])
long = append!([BSM.(s,k,t2,r,d,σ_init,1)[1]] ,-BSM.(s,k,t2,r,d,σ_init,1)[1] .+ [x[1] for x in BSM.(spot[2:end],k,t2-t1,r,d,σ_end,1)])
net = short_28_day .+ long
df = DataFrame(Spot = spot, short = short_28_day, long = long, net = net )
#plot(df.Spot[2:end], df.net[2:end], label = "Long Calendar Spread with Calls at expiration of Short Call")
#hline!([-df.net[1]], label = "Max Loss", ylimit = [minimum(df.net)*1.2,maximum(df.net)*1.7])
#hline!([0], label = false, linewidth = 0.5)
#vline!([110], label = "Negative time value (see below)")
end
@layout! gui vbox(vbox(vbox(vbox(vbox(hbox(s, k),hbox( σ_init, σ_end)),hbox(r,d),hbox(days_short,days_long)))), observe(_))

This is again very similar to Fidelities example (the first line with (1.05) net loss is computed wrongly by Fidelity).
Looks good and the intuition is explained nicely at Fidelity's page:
- If the stock price falls sharply, then the price of both calls approach zero (you simply end up with your initial cost).
- If the stock price rallies sharply so that both calls are deep in the money, then the prices of both calls approach parity (time value will be zero).
Fidelity also shows a chart, which can be replicated with the Julia code from above by uncommenting (get rid of #) the plot section in the code. The result looks like this:

One of the provided links also states that
Calendar spreads are long vega (volatility), meaning they perform best
when volatility moves higher.
This is easy to show in the chart. I added a second $\sigma$ to allow for a different IV at expiry of the short dated.

Now the interesting part
High dividends (or negative interest rates), will look like this.

The point where the strategy loses more money than the upfront cost is where time value of a european option turns negative. See for example this answer. If you plot this for the exact option at hand, you will see the following figure. Once time value turns negative, you will end up with a loss that exceeds the initial expense.
spot = 80:1:130
plot(spot,[x[1] for x in BSM.(spot,100,28/365,0.0,0.1,0.3,1)].- BSM.(100,100,28/365,0.0,0.1,0.3,1)[1], label = "Option Premium", ylabel = "Option Value", xlabel = "Spot")
plot!(spot, max.(spot.-100,0).- BSM.(100,100,28/365,0.0,0.1,0.3,1)[1], label = "Intrinsic Value", legend = :topleft)
hline!([0],linewidth= 0.3, label = false)
