c++datedatetime

How to get broken time from sys_seconds received by get_info() using sys_info member begin


I am trying to find out if given date is equal to dst transition date or not (either dst is starting or ending on that day). My program is as below

#include "date/tz.h"
#include <chrono>
#include <iostream>

template <class Duration>
bool
is_dst(date::zoned_time<Duration> const& zt)
{
    using namespace date;
    using namespace std;
    using namespace chrono;
    auto info = zt.get_info();
    std::cout << "TZ Range start: " << info.begin + info.offset << " range end " << info.end + info.offset << std::endl;
    auto dst_hours = std::chrono::duration_cast<std::chrono::hours>(info.save);
    auto dst_minutes = std::chrono::duration_cast<std::chrono::minutes>(info.save - dst_hours);
    return info.save != minutes{0};
}

int
main(int argc, char *argv[])
{
    using namespace date;
    using namespace std;
    using namespace chrono;

    std::string tzname = argv[1];
    int yy = std::atoi(argv[2]);
    int mm = std::atoi(argv[3]);
    int dd = std::atoi(argv[4]);
    int h = std::atoi(argv[5]);
    int m = std::atoi(argv[6]);

    // Construct a local_time type from the broken-down time
    local_seconds localTime = date::local_days{date::year{yy}/mm/dd} + std::chrono::hours{h} + std::chrono::minutes{m};

    auto tz = locate_zone(tzname);
    date::zoned_time<std::chrono::seconds> zt{tz, localTime, date::choose::latest}; 
    auto dst_on_off = is_dst(zoned_time<std::chrono::seconds>{tz, zt, date::choose::latest});
    cout << "input time :" << localTime << " latest=" << dst_on_off << '\n';
}

Now I have following questions if I execute in following way ./hh_get_utc_dst_offset "Australia/Sydney" 2025 10 05 01 00 TZ Range start: 2025-04-06 02:00:00 range end 2025-10-05 02:00:00 input time :2025-10-05 01:00:00 latest=0

./hh_get_utc_dst_offset "Australia/Sydney" 2025 10 05 03 00 TZ Range start: 2025-10-05 03:00:00 range end 2026-04-05 03:00:00 input time :2025-10-05 03:00:00 latest=1

So as per documentation of sys_info "The begin and end fields indicate that for the associated time_zone and time_point, the offset and abbrev are in effect in the range [begin, end)." What does this exactly mean ?

In first execution where dst is off its printing current range In second execution where dst is on its printing future range

Also if I want to convert info.begin into broken time how can I do it ? I want to compare date given on command line with date received from info.being and info.end.


Solution

  • So as per documentation of sys_info "The begin and end fields indicate that for the associated time_zone and time_point, the offset and abbrev are in effect in the range [begin, end)." What does this exactly mean ?

    I believe this is the crux of the question.

    The expression [begin, end) refers to a "half-open" range. The range begins on and includes begin. But the range does not include end. The range stops the instant prior to end.

    And this means that the .offset is not valid at end and therefore this expression:

    info.end + info.offset
    

    is not valid.

    A less error-prone way to convert [begin, end) to local time would be:

    auto tz = zt.get_time_zone();
    std::cout << "TZ Range start: " << tz->to_local(info.begin)
              << " range end "      << tz->to_local(info.end) << std::endl;
    

    Also if I want to convert info.begin into broken time how can I do it ? I want to compare date given on command line with date received from info.being and info.end.

    You can break a serial time_point into fields in either UTC fields, or local fields. To decide which, first obtain the serial time_point in either sys_time or local_time respectively.

    Assuming you want local fields, and showing just for info.begin:

    // First convert the `sys_time` to `local_time`:
    auto local_begin = tz->to_local(info.begin);
    // local_begin is a datetime
    // truncate it to just a date
    auto local_begin_date = floor<days>(local_begin);
    // Extract the local time of day from the datetime and date
    auto local_tod = local_begin - local_begin_date;
    // Convert local_begin_date to a {y, m, d} data structure
    year_month_day local_ymd{local_begin_date};
    // Convert local_tod to a {h, m, s} data structure
    hh_mm_ss local_hms{local_tod};
    

    year_month_day has .year(), .month() and .day() getters. These return strongly typed representations year, month and day. These in turn can be explicitly cast to int, unsigned and unsigned respectively.

    hh_mm_ss has .hours(), .minutes() and .seconds() getters returning strongly typed representations hours, minutes and seconds. Each of these can be converted to integral types with the .count() member function.

    At each stage, from local_begin, all the way down to seconds, each type can be streamed out like so:

    cout << local_begin << '\n';
    

    Finally, all of this was voted into C++20 and now lives in namespace std::chrono and the header <chrono>. It has been fully implemented in the latest versions of gcc and MSVC. LLVM implementation is underway.

    I encourage porting to C++20 whenever possible.