C++解析带时区的日期字符串:%Z无法正确处理时区怎么办?
%Z Isn't Handling Your Timezone Offset (and How to Fix It) Hey there, let's break down why your code is giving an unexpected timestamp and how to get the correct Central Time conversion.
The Root Problem: %Z Doesn't Parse Offset Values
The %Z format specifier for std::get_time is designed to match timezone abbreviations like EST, EDT, or UTC—not numeric offset formats like -04:00. When your code tries to parse -04:00 with %Z, it simply ignores that part of the string entirely.
After parsing, mktime treats the tm struct as your program's local timezone (not the -04:00 offset from your input), which leads to the 2-hour discrepancy you're seeing.
Solutions to Fix the Timezone Handling
Option 1: Use C++20's chrono Library (Recommended)
C++20 introduced full timezone support in the <chrono> library, which natively handles numeric offsets and timezone conversions (including daylight saving time). This is the cleanest, most maintainable approach if your compiler supports it.
#include <iostream> #include <chrono> #include <format> #include <string> using namespace std; using namespace chrono; int main() { string sToParse = "2020-04-17T09:30:00-04:00"; // Parse the input string with its timezone offset sys_seconds utc_timestamp = parse("%Y-%m-%dT%H:%M:%S%Ez", sToParse); // Convert to US Central Time (handles DST automatically) const auto central_tz = locate_zone("America/Chicago"); zoned_time central_time(central_tz, utc_timestamp); // Output the formatted Central Time string cout << format("{:%A, %B %d, %Y %H:%M:%S}", central_time) << endl; // Output the UTC timestamp (matches your expected 1587130200) cout << utc_timestamp.time_since_epoch().count() << endl; return 0; }
This code will correctly parse the -04:00 offset, convert to Central Daylight Time (CDT, UTC-5), and output your expected results.
Option 2: Manual Offset Parsing (For Older C++ Standards)
If you can't use C++20, you'll need to manually split the input string, parse the offset, and adjust the timestamp yourself. Here's a working example:
#include <iostream> #include <sstream> #include <iomanip> #include <ctime> #include <string> #include <cstdlib> using namespace std; // Helper function to convert a UTC tm struct to a timestamp (replaces non-standard timegm) time_t utc_tm_to_timestamp(tm* dt) { const int days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; int year = dt->tm_year + 1900; int month = dt->tm_mon; int day = dt->tm_mday; int hour = dt->tm_hour; int min = dt->tm_min; int sec = dt->tm_sec; auto is_leap = [](int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }; long total_days = 0; for (int y = 1970; y < year; ++y) { total_days += is_leap(y) ? 366 : 365; } for (int m = 0; m < month; ++m) { total_days += days_in_month[m]; if (m == 1 && is_leap(year)) total_days += 1; } total_days += day - 1; return total_days * 86400L + hour * 3600L + min * 60L + sec; } int main() { string sToParse = "2020-04-17T09:30:00-04:00"; // Split input into datetime and offset parts size_t offset_pos = sToParse.find_last_of('+', '-'); string dt_str = sToParse.substr(0, offset_pos); string offset_str = sToParse.substr(offset_pos); // Parse datetime into tm struct tm dt = {}; istringstream ss(dt_str); ss >> get_time(&dt, "%Y-%m-%dT%H:%M:%S"); if (ss.fail()) { cerr << "Failed to parse datetime part" << endl; return 1; } // Parse timezone offset into seconds int offset_hours = atoi(offset_str.substr(0, 3).c_str()); int offset_mins = atoi(offset_str.substr(4).c_str()); long offset_sec = offset_hours * 3600L + offset_mins * 60L; // Calculate UTC timestamp: input time = UTC + offset → UTC = input time - offset time_t input_as_utc_ts = utc_tm_to_timestamp(&dt); time_t utc_ts = input_as_utc_ts - offset_sec; // Convert UTC timestamp to Central Time (CDT = UTC-5) tm central_tm = *gmtime(&utc_ts); central_tm.tm_hour -= 5; mktime(¢ral_tm); // Auto-adjust for date overflow // Output formatted Central Time char buf[100]; strftime(buf, sizeof(buf), "%A, %B %d, %Y %H:%M:%S", ¢ral_tm); cout << buf << endl; // Output UTC timestamp cout << utc_ts << endl; return 0; }
Key Takeaways
%Zis for timezone abbreviations, not numeric offsets—this was the core issue in your original code.- C++20's
chronolibrary is the best tool for timezone-aware date handling. - For older C++ versions, manual offset parsing works but requires careful handling of UTC conversions and daylight saving time.
内容的提问来源于stack exchange,提问作者flashc5

