No, in general, casting from double to decimal is not always safe:
[TestCase(double.MinValue)]
[TestCase(double.MaxValue)]
[TestCase(double.NaN)]
[TestCase(double.NegativeInfinity)]
[TestCase(double.PositiveInfinity)]
public void WillFail(double input)
{
decimal result = (decimal)input; // Throws OverflowException!
}
As OP clarified in a comment to the question, "safe" being "doesn't cause run time exceptions", the above shows that exceptions can occur when casting a double to a decimal.
The above is the generic answer many Googlers might've come here for. However, to also answer the specific question by OP, here's a strong indication that the code will not throw exceptions, even on edge cases:
[Test]
public void SpecificCodeFromOP_WillNotFail_NotEvenOnEdgeCases()
{
int downtimeMinutes = 90;
foreach (TimeSpan duration in new[] {
TimeSpan.FromHours(2d), // From OP
TimeSpan.MinValue,
TimeSpan.Zero,
TimeSpan.MaxValue })
{
decimal calculatedDowntimePercent = duration.TotalMinutes > 0 ?
(downtimeMinutes / (decimal)duration.TotalMinutes) * 100.0m : 0.0m;
}
}