Creating a QTimeZone("UTC") appears to be a valid action. It's not clear whether "UTC" is a valid IANA ID, but it does appear in the list of availableTimeZoneIds(). tst_QTimeZone::dataStreamTest() also does this.
Its internal representation, as created by QTimeZone::QTimeZone(const QByteArray &ianaId) is a QUtcTimeZonePrivate which isValid().
When QTimeZone is serialised into a QDataStream, it calls tz.d->serialize(ds); which is QUtcTimeZonePrivate::serialize. This writes QStringLiteral("OffsetFromUtc") followed by the IANA ID and the offset (etc.) to the datastream.
When QTimeZone is deserialised it looks for this marker string, and if present, passes all of the parameters to the QTimeZone constructor (not just the name):
This appears to be designed to support non-standard zones, but instead it fails to support standard ones. The docstring of this constructor states:
The \a ianaId must not be one of the available system IDs returned by availableTimeZoneIds().
And the code also checks this, leaving d unset for standard zones, which results in the QTimeZone being invalid:
So the valid timezone which was serialised and then deserialised has become invalid. This also affects serialisation of QDateTimes with timezones.
My guess is that we should check on deserialisation whether the stored timezone is supported by our system, and if so, call the single-argument constructor instead, like this: