Here's how I'm going about it:
- Pretending against better knowledge the 
tm structure holds local time (non-DST if anyone asks; it doesn't matter, but must be consistent with step 3), convert it to time_t. 
- Convert the date back into a 
tm structure, but this time in UTC representation. 
- Pretending against better knowledge that 
tm structure to also hold local (non-DST if anyone asks, but more importantly consistent with step 1), and convert it to time_t once more. 
- From the two 
time_t results I can now compute the difference between local time (non-DST if anyone asks) and UTC in time_t units. 
- Adding that difference to the first 
time_t result gives me the proper time in UTC. 
Convoluted, but I'm confident it should work. Unless the computer uses a TAI-based clock, in which case on rare occasions the result might be off by a second. The consistent DST setting (as opposed to having mktime auto-detect it) is necessary so that I don't get thrown off by an entire hour when trying to compute a date/time close to the start or end of the DST period.
tm tt;
// populate tt here
tt.tm_isdst = 0;
time_t tLoc = mktime(&tt);
tt = *gmtime(&tLoc);
tt.tm_isdst = 0;
time_t tRev = mktime(&tt);
time_t tDiff = tLoc - tRev;
time_t tUTC = tLoc + tDiff;
However, this approach comes with the caveat that gmtime() is not thread-safe. So if anyone has any better solution, I'd love to hear it.