Repository: gabime/spdlog
File: tests/test_timezone.cpp (C++)

Code before:
        // restore original TZ
#ifdef _WIN32
        if (has_original_) {
            _putenv_s("TZ", original_tz_.c_str());
        } else {
            _putenv_s("TZ", "");
        }
        _tzset();
#else
        if (has_original_) {
            setenv("TZ", original_tz_.c_str(), 1);
        } else {
            unsetenv("TZ");
        }
        tzset();
#endif
    }
};

using spdlog::details::os::utc_minutes_offset;

#ifdef _WIN32
#define EST5EDT "EST5EDT"
#define ISTN2IDT "IST-2IDT"
#else
#define EST5EDT  "EST5EDT,M3.2.0,M11.1.0"
#define ISTN2IDT "IST-2IDT,M3.4.4/26,M10.5.0"
#endif

TEST_CASE("UTC Offset - Western Hemisphere (USA - Standard Time)", "[timezone][west]") {
    // EST5EDT: Eastern Standard Time (UTC-5)
    ScopedTZ tz(EST5EDT);

    // Jan 15th (Winter)
    auto tm = make_tm(2023, 1, 15, 12, 0);
    REQUIRE(utc_minutes_offset(tm) == -300);
}

TEST_CASE("UTC Offset - Eastern Hemisphere (Europe/Israel - Standard Time)", "[timezone][east]") {
    // IST-2IDT: Israel Standard Time (UTC+2)
    ScopedTZ tz(ISTN2IDT);

    // Jan 15th (Winter)
    auto tm = make_tm(2023, 1, 15, 12, 0);
    REQUIRE(utc_minutes_offset(tm) == 120);
}

TEST_CASE("UTC Offset - Zero Offset (UTC/GMT)", "[timezone][utc]") {
    ScopedTZ tz("GMT0");

    // Check Winter

Review comment (suggestion):
The POSIX TZ rule strings here (especially `IST-2IDT,M3.4.4/26,M10.5.0`) are non-obvious. Adding a short comment explaining what these start/end rules represent (and that they’re chosen to make winter/summer offsets deterministic across libc implementations) would make future maintenance safer.
```suggestion
#else
// POSIX TZ strings with explicit DST rules so winter/summer offsets are
// deterministic across libc implementations (independent of system tzdata).
//
// EST5EDT  -> US Eastern: standard time is UTC-5 ("EST"), daylight time is
//              UTC-4 ("EDT"), DST from second Sunday in March (M3.2.0)
//              to first Sunday in November (M11.1.0).
#define EST5EDT  "EST5EDT,M3.2.0,M11.1.0"
//
// IST-2IDT -> Israel time used here for testing: standard time is UTC+2
//              ("IST"), daylight time is UTC+3 ("IDT"). DST starts on the
//              4th Thursday in March at 26:00 local wall clock
//              (M3.4.4/26 == 02:00 the following day) and ends on the
//              last Sunday in October at 00:00 (M10.5.0).
```

Code after:
        // restore original TZ
#ifdef _WIN32
        if (has_original_) {
            _putenv_s("TZ", original_tz_.c_str());
        } else {
            _putenv_s("TZ", "");
        }
        _tzset();
#else
        if (has_original_) {
            setenv("TZ", original_tz_.c_str(), 1);
        } else {
            unsetenv("TZ");
        }
        tzset();
#endif
    }
};

using spdlog::details::os::utc_minutes_offset;

/*
 * POSIX 2024 defines three formats for the TZ environment variable,
 *
 * 1. Implementation defined format which always starts with a colon:
 *    ":characters".
 * 2. A specifier which fully describes the timezone rule in format
 *    "stdoffset[dst[offset][,start[/time],end[/time]]]". Note the
 *    offset and start/end part could be omitted, in which case one hour
 *    is implied, or it's considered implementation-defined when changing
 *    to and from Daylight Saving Time occurs.
 * 3. Geographical or special timezone from an implementation-defined
 *    timezone database.
 *
 * On POSIX-compilant systems, we prefer format 2, and explicitly specify the
 * DST rules to avoid implementation-defined behavior.
 *
 * See also IEEE 1003.1-2024 8.3 Other Environment Variables.
 */
#ifndef _WIN32
/*
 * Standard time is UTC-5 ("EST"), DST time is UTC-4 ("EDT"). DST is active
 * from 2:00 on the 2nd Sunday in March, to 2:00 on 1st Sunday in November.
 */
#define EST5EDT  "EST5EDT,M3.2.0,M11.1.0"
/*
 * Standard time is UTC+2 ("IST"), DST time is UTC+3 ("IDT"). DST is active
 * from 2:00 on following day of the 4th Thursday in March, to 2:00 on the
 * last Sunday in October.
 */
#define IST_MINUS2_IDT "IST-2IDT,M3.4.4/26,M10.5.0"