HowardHinnant / date

A date and time library based on the C++11/14/17 <chrono> header
Other
3.14k stars 677 forks source link

Possible error to changing dst over the year? #709

Closed chrisgermain closed 2 years ago

chrisgermain commented 2 years ago

Hi Howard! I am working to an astronomy project that calculates some date/time data. I find your library very useful and I use it mostly for finding the time zone of a specific location. The problem I have encountered is that the timezone for a location where one can observe the DST seems to change one day later than normal, i.e. instead of the night between Saturday to Sunday it occures on the next night. This is the function I use:

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

using namespace std
using namespace std::chrono;
using namespace date;

float tzone (int yr, unsigned int mo, unsigned int dy, string location) {
    float tmz;
    auto local_date = make_zoned (location, date::local_days{month{mo}/day{dy}/year{yr}});
    auto offset = local_date.get_info().offset;
    tmz = offset.count () / 3600.0;
    return tmz;
}

int main()
{   int yy (2021);
    unsigned int mm (10), dd(31);
    string location = "Europe/Bucharest";
    auto dst = tzone (yy, mm, dd, location);
    cout << "timezone = " << dst << endl;
    return 0;
}

The output for me is: timezone = 3 As far as I am aware the offset should change on 31st of october this year and it should be 2 instead of 3 for this location.

Thank you and please have my honest admiration for your work. Chris

HowardHinnant commented 2 years ago

Hi Chris,

Using this program:

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

int
main()
{
    using namespace date;
    using namespace std;

    string location = "Europe/Bucharest";
    auto info = locate_zone(location)->get_info(sys_days{2021_y/10/1});
    zoned_time end{location, info.end};
    cout << "DST ends at " <<  end
         << " / " << format("%F %T %Z", end.get_sys_time()) << '\n';
}

It looks like DST ends at 2021-10-31 03:00:00 EET / 2021-10-31 01:00:00 UTC. Your program is querying the database at 2021-10-31 00:00:00 EEST, 4 hours prior to the transition. The 0h time is implicit in this line:

    auto local_date = make_zoned (location, date::local_days{month{mo}/day{dy}/year{yr}});

If you change it to:

    auto local_date = make_zoned (location, date::local_days{month{mo}/day{dy}/year{yr}}+3h, choose::latest);

then your program will output 2.

The reason you need the choose::latest is because 3am occurs twice on this morning, and without the choose::latest you get an ambiguity error. If you (for example), added 4h instead, you don't need the choose::latest, and it will still output 2.

You could also input the time as UTC using sys_days instead of local_days, which eliminates any possibility of ambiguity:

    auto local_date = make_zoned (location, date::sys_days{month{mo}/day{dy}/year{yr}}+1h);

Output:

2
chrisgermain commented 2 years ago

Thank you very much for the solution, Howard!

Sent with ProtonMail Secure Email.

‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Saturday, October 16th, 2021 at 02:29, Howard Hinnant @.***> wrote:

Hi Chris,

Using this program:

#

include

<

iostream

#

include

<

chrono

#

include

"

date/tz.h

"

int

main

() {

using

namespace

date

;

using

namespace

std

;

string location =

"

Europe/Bucharest

"

;

auto

info =

locate_zone

(location)->

get_info

(sys_days{2021_y/

10

/

1

}); zoned_time end{location, info.

end

}; cout <<

"

DST ends at

"

<< end <<

"

/

"

<<

format

(

"

%F %T %Z

"

, end.

get_sys_time

()) <<

'

\n

'

; }

It looks like DST ends at 2021-10-31 03:00:00 EET / 2021-10-31 01:00:00 UTC. Your program is querying the database at 2021-10-31 00:00:00 EET, 3 hours prior to the transition. The 0h time is implicit in this line:

auto

local_date = make_zoned (location, date::local_days{month{mo}/day{dy}/year{yr}});

If you change it to:

auto

local_date = make_zoned (location, date::local_days{month{mo}/day{dy}/year{yr}}+3h, choose::latest);

then your program will output 2.

The reason you need the choose::latest is because 3am occurs twice on this morning, and without the choose::latest you get an ambiguity error. If you (for example), added 4h instead, you don't need the choose::latest, and it will still output 2.

You could also input the time as UTC using sys_days instead of local_days, which eliminates any possibility of ambiguity:

auto

local_date = make_zoned (location, date::sys_days{month{mo}/day{dy}/year{yr}}+1h);

Output:

2

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

chrisgermain commented 11 months ago

Hi Howard,

Maybe it seems peculiar that I come back with a question that looks very much the same with the previous one, but it arised recently. You gave me a good soluitionm that I have incorporated into my project. Still please tell me how to solve this problem of an event that occurs very close to the moment of changing to the winter time say 29-th of october 2023. For instance. in Bucharest, on 29-th at 2 o'clock in the morning DST is still observed, while 3 hors later it would be not. How shall I implement this? I have this piece of code:

float tzone(int an, int luna, int zi, const string &location) { float tmz; unsigned int ui_zi = static_cast(zi); unsigned int ui_luna = static_cast(luna); unsigned int ui_an = static_cast(an); auto local_date = make_zoned(location, date::sys_days{month{ui_luna} / day{ui_zi} / year{static_cast(ui_an)}} + 1h); auto offset = local_date.get_info().offset; tmz = offset.count() / 3600.0; return tmz; }

int main() { int year= 2023, mth= 10, day = 28, hour, minute, sec; string location = "Europe/Bucharest"; float offset_value = tzone(an, luna, zi, location); //using hour, minute, sec would be good as variables cout << offset_value <<endl; return 0;

Thank you!

Sent with Proton Mail secure email.

------- Original Message ------- On Wednesday, November 3rd, 2021 at 03:47, Howard Hinnant @.***> wrote:

Closed #709.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

HowardHinnant commented 11 months ago

Hi Chris,

I think your code would benefit from moving fully into the chrono type system, as opposed to converting into chrono just long enough to get something like the UTC offset. For example, how is the UTC offset used? I presume it is used to convert local time to UTC and vice-versa. If that is the case it is much easier to just stay within the chrono library and let the library do that for you.

For example:

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

int main()
{
    using namespace date;
    using namespace std;
    using namespace chrono;

    auto tp_local = zoned_time{"Europe/Bucharest", local_days{2023_y/10/29} + 2h};
    cout << "At " << tp_local << " the UTC time is " << tp_local.get_sys_time() << '\n';
    tp_local = tp_local.get_sys_time() + 3h;
    cout << "Three hours later the local time is " << tp_local
         << " and the UTC time is " << tp_local.get_sys_time() << '\n';
}

Output:

At 2023-10-29 02:00:00 EEST the UTC time is 2023-10-28 23:00:00
Three hours later the local time is 2023-10-29 04:00:00 EET and the UTC time is 2023-10-29 02:00:00

tp_local is a pairing of a time zone and time_point. The time point can be specified as either local time (as done above), or as UTC by changing local_days to sys_days. Hours, minutes, seconds and even sub seconds (e.g. milliseconds, nanoseconds, whatever) can be added on. In this example 2 hours is added on to indicate 2:00 in the morning local time.

Now tp_local contains both the local time and the UTC time. And when you print it out it just prints out the local time. To get the local time as a chrono::time_point, it is tp_local.get_local_time(). To get the UTC time it is tp_local.get_sys_time().

One can also set the zoned_time to a new time by doing arithmetic in either the local time system, or in UTC. Above I do the arithmetic in UTC by getting the UTC time out, adding 3 hours to that, and assigning that back into the zoned_time.

Note that the UTC offset is never mentioned in this code, and changes from 3h to 2h between the two print statements.

Also note that the UTC time is bumped by 3h whereas the local time is bumped only by 2h in this example. If you wanted to bump the local time by 3h instead, then you would change this line:

    tp_local = tp_local.get_local_time() + 3h;

And the output changes to:

At 2023-10-29 02:00:00 EEST the UTC time is 2023-10-28 23:00:00
Three hours later the local time is 2023-10-29 05:00:00 EET and the UTC time is 2023-10-29 03:00:00

In your code it is ambiguous if the specified dates are intended to be local time or UTC. You make them UTC by the use of sys_days. However it is not clear to me that this is intended because of the manual addition of 1h within the tzone function.

chrisgermain commented 11 months ago

As I have told you in my previous mail that I worked on an astronomy project and I needed to automatically turn Julian Days into local time (I use my own library for this operation) with a good enough precission (seconds) taking account of DST changing. I use function tzone to get the offset which would be passed as argument in function jdtoloc:

loctime tme = jdtoloc(long double julian_days, float offset)

The problem is that the location is also a variable (it could be any location on Earth... That is why I cannot add manually +3h as I do not know it at compile time). I do not want to print out the result, but passing it as a struct argument to another function:

struct loctime { int yyyy; int mm; int dd; int hh; int mm; float ss; };

Thank you gratefully for your help!

Sent with Proton Mail secure email.

------- Original Message ------- On Monday, November 6th, 2023 at 17:04, Howard Hinnant @.***> wrote:

Hi Chris,

I think your code would benefit from moving fully into the chrono type system, as opposed to converting into chrono just long enough to get something like the UTC offset. For example, how is the UTC offset used? I presume it is used to convert local time to UTC and vice-versa. If that is the case it is much easier to just stay within the chrono library and let the library do that for you.

For example:

#

include

"

date/tz.h

"

#

include

<

iostream

int

main

() {

using

namespace

date

;

using

namespace

std

;

using

namespace

chrono

;

auto

tp_local = zoned_time{

"

Europe/Bucharest

"

, local_days{2023_y/

10

/

29

} + 2h}; cout <<

"

At

"

<< tp_local <<

"

the UTC time is

"

<< tp_local.

get_sys_time

() <<

'

\n

'

; tp_local = tp_local.

get_sys_time

() + 3h; cout <<

"

Three hours later the local time is

"

<< tp_local <<

"

and the UTC time is

"

<< tp_local.

get_sys_time

() <<

'

\n

'

; }

Output:

At 2023-10-29 02:00:00 EEST the UTC time is 2023-10-28 23:00:00 Three hours later the local time is 2023-10-29 04:00:00 EET and the UTC time is 2023-10-29 02:00:00

tp_local is a pairing of a time zone and time_point. The time point can be specified as either local time (as done above), or as UTC by changing local_days to sys_days. Hours, minutes, seconds and even sub seconds (e.g. milliseconds, nanoseconds, whatever) can be added on. In this example 2 hours is added on to indicate 2:00 in the morning local time.

Now tp_local contains both the local time and the UTC time. And when you print it out it just prints out the local time. To get the local time as a chrono::time_point, it is tp_local.get_local_time(). To get the UTC time it is tp_local.get_sys_time().

One can also set the zoned_time to a new time by doing arithmetic in either the local time system, or in UTC. Above I do the arithmetic in UTC by getting the UTC time out, adding 3 hours to that, and assigning that back into the zoned_time.

Note that the UTC offset is never mentioned in this code, and changes from 3h to 2h between the two print statements.

Also note that the UTC time is bumped by 3h whereas the local time is bumped only by 2h in this example. If you wanted to bump the local time by 3h instead, then you would change this line:

tp_local = tp_local.get_local_time() + 3h;

And the output changes to:

At 2023-10-29 02:00:00 EEST the UTC time is 2023-10-28 23:00:00 Three hours later the local time is 2023-10-29 05:00:00 EET and the UTC time is 2023-10-29 03:00:00

In your code it is ambiguous if the specified dates are intended to be local time or UTC. You make them UTC by the use of sys_days. However it is not clear to me that this is intended because of the manual addition of 1h within the tzone function.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

HowardHinnant commented 11 months ago

Thanks for the details. Below is a solution for creating this function:

loctime
to_loctime(std::string const& location, long double julian_days)

To start off with I reuse a Julian date clock that I created years ago, and is documented here: https://stackoverflow.com/questions/33964461/handling-julian-days-in-c11-14/33964462#33964462. I've tweaked the code to be compatible with C++17. It is a chrono clock with long double as the representation and days (using long double) as the period. The epoch is coded in the proleptic Gregorian calendar as November/24/-4713+12h.

The main advantage of this clock is to be able to convert a long double into a type safe chrono time_point, and then convert that time_point to that of system_clock which has an epoch of January/1/1970 00:00:00.

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

struct jdate_clock;

template <class Duration>
    using jdate_time = std::chrono::time_point<jdate_clock, Duration>;

struct jdate_clock
{
    using rep        = long double;
    using period     = date::days::period;
    using duration   = std::chrono::duration<rep, period>;
    using time_point = std::chrono::time_point<jdate_clock>;

    static constexpr bool is_steady = false;

    static time_point now() noexcept;

    template <class Duration>
    static
    auto
    from_sys(date::sys_time<Duration> const& tp) noexcept;

    template <class Duration>
    static
    auto
    to_sys(jdate_time<Duration> const& tp) noexcept;
};

template <class Duration>
auto
jdate_clock::from_sys(date::sys_time<Duration> const& tp) noexcept
{
    using namespace date;
    using namespace std;
    using namespace chrono;
    auto d = tp - (sys_days{November/24/-4713}+12h);
    return jdate_time<decltype(d)>{d};
}

template <class Duration>
auto
jdate_clock::to_sys(jdate_time<Duration> const& tp) noexcept
{
    using namespace date;
    auto d = tp - clock_cast<jdate_clock>(sys_days{});
    return sys_time<decltype(d)>{d};
}

jdate_clock::time_point
jdate_clock::now() noexcept
{
    using namespace date;
    using namespace std::chrono;
    return clock_cast<jdate_clock>(system_clock::now());
}

struct loctime
{
    int yyyy;
    int mn;
    int dd;
    int hh;
    int mm;
    float ss;
};

std::ostream&
operator<<(std::ostream& os, loctime const& lt)
{
    os << lt.yyyy << '-' << lt.mn << '-' << lt.dd << ' '
       << lt.hh << ':' << lt.mm << ':' << lt.ss << '\n';
    return os;
}

template <class Duration>
loctime
to_loctime(std::string const& location, date::sys_time<Duration> tp)
{
    using namespace date;
    using namespace std::chrono;

    auto tp_local = locate_zone(location)->to_local(tp);
    auto tp_days = floor<days>(tp_local);
    year_month_day ymd{tp_days};
    auto tod = tp_local - tp_days;
    loctime lt;
    lt.yyyy = int{ymd.year()};
    lt.mn = unsigned{ymd.month()};
    lt.dd = unsigned{ymd.day()};
    lt.hh = duration_cast<hours>(tod).count();
    tod -= hours{lt.hh};
    lt.mm = duration_cast<minutes>(tod).count();
    tod -= minutes{lt.mm};
    lt.ss = duration<float>{tod}.count();
    return lt;
}

loctime
to_loctime(std::string const& location, long double julian_days)
{
    using namespace date;
    using namespace std::chrono;

    jdate_clock::time_point tp{jdate_clock::duration{julian_days}};
    return to_loctime(location, clock_cast<system_clock>(tp));
}

int main()
{
    using namespace std;

    cout << to_loctime("Europe/Bucharest", 2'460'255.218177) << '\n';
}

Next the code introduces this helper function:

template <class Duration>
loctime
to_loctime(std::string const& location, date::sys_time<Duration> tp)

This code converts a system_clock-based time_point to loctime using the time_zone named by location. The time_zone is first used to convert the sys_time to local_time. The remaining lines in the function break this local time_point up into fields, and assign those fields into a loctime.

Finally to_loctime(std::string const& location, long double julian_days) can be easily written by converting the long double into a jdate_clock::time_point, then converting that into a system_clock-based time_point, and then passing that along to the previously defined to_loctime function.

main demonstrates a recent value using "Europe/Bucharest".

HowardHinnant commented 11 months ago

Fwiw, here is the reverse conversion:

jdate_clock::time_point
to_jdate(std::string const& location, loctime const& lt)
{
    using namespace date;
    using namespace std::chrono;

    auto tp_local = local_days{year{lt.yyyy}/lt.mn/lt.dd}
                  + hours{lt.hh} + minutes{lt.mm} + duration<jdate_clock::rep>{lt.ss};
    auto tp_utc = locate_zone(location)->to_sys(tp_local);
    return clock_cast<jdate_clock>(tp_utc);
}

To get the long double out of the result just use .time_since_epoch().count().

chrisgermain commented 11 months ago

Howard, I simply have no words to thank you enough for your kindness and effort. I wish you all the best! God bless you! Cristian

Sent with Proton Mail secure email.

------- Original Message ------- On Monday, November 6th, 2023 at 19:28, Howard Hinnant @.***> wrote:

Thanks for the details. Below is a solution for creating this function:

loctime

to_loctime

(std::string

const

& location,

long

double

julian_days)

To start off with I reuse a Julian date clock that I created years ago, and is documented here: https://stackoverflow.com/questions/33964461/handling-julian-days-in-c11-14/33964462#33964462. I've tweaked the code to be compatible with C++17. It is a chrono clock with long double as the representation and days (using long double) as the period. The epoch is coded in the proleptic Gregorian calendar as November/24/-4713+12h.

The main advantage of this clock is to be able to convert a long double into a type safe chrono time_point, and then convert that time_point to that of system_clock which has an epoch of January/1/1970 00:00:00.

#

include

"

date/tz.h

"

#

include

<

iostream

struct

jdate_clock

;

template

<

class

Duration

using

jdate_time = std::chrono::time_point<jdate_clock,

Duration

;

struct

jdate_clock

{

using

rep =

long

double

;

using

period = date::days::period;

using

duration = std::chrono::duration<rep, period>;

using

time_point = std::chrono::time_point;

static

constexpr

bool

is_steady =

false

;

static

time_point

now

()

noexcept

;

template

<

class

Duration

static

auto

from_sys

(date::sys_time<

Duration

const

& tp)

noexcept

;

template

<

class

Duration

static

auto

to_sys

(jdate_time<

Duration

const

& tp)

noexcept

; };

template

<

class

Duration

auto

jdate_clock::from_sys

(date::sys_time<

Duration

const

& tp)

noexcept

{

using

namespace

date

;

using

namespace

std

;

using

namespace

chrono

;

auto

d = tp - (sys_days{November/

24

/-

4713

}+12h);

return

jdate_time<

decltype

(d)>{d}; }

template

<

class

Duration

auto

jdate_clock::to_sys

(jdate_time<

Duration

const

& tp)

noexcept

{

using

namespace

date

;

auto

d = tp - clock_cast(sys_days{});

return

sys_time<

decltype

(d)>{d}; }

jdate_clock::time_point

jdate_clock::now

()

noexcept

{

using

namespace

date

;

using

namespace

std

::chrono

;

return

clock_cast(

system_clock::now

()); }

struct

loctime

{

int

yyyy;

int

mn;

int

dd;

int

hh;

int

mm;

float

ss; };

std::ostream&

operator

<<(std::ostream& os, loctime

const

& lt) { os << lt.

yyyy

<<

'

-

'

<< lt.

mn

<<

'

-

'

<< lt.

dd

<<

'

'

<< lt.

hh

<<

'

:

'

<< lt.

mm

<<

'

:

'

<< lt.

ss

<<

'

\n

'

;

return

os; }

template

<

class

Duration

loctime

to_loctime

(std::string

const

& location, date::sys_time<

Duration

tp) {

using

namespace

date

;

using

namespace

std

::chrono

;

auto

tp_local =

locate_zone

(location)->

to_local

(tp);

auto

tp_days =

floor

(tp_local); year_month_day ymd{tp_days}; auto tod = tp_local - tp_days; loctime lt; lt. yyyy = int {ymd. year ()}; lt. mn = unsigned {ymd. month ()}; lt. dd = unsigned {ymd. day ()}; lt. hh = duration_cast(tod). count (); tod -= hours{lt. hh }; lt. mm = duration_cast(tod). count (); tod -= minutes{lt. mm }; lt. ss = duration< float >{tod}. count (); return lt; } loctime to_loctime (std::string const & location, long double julian_days) { using namespace date ; using namespace std ::chrono ; jdate_clock::time_point tp{jdate_clock::duration{julian_days}}; return to_loctime (location, clock_cast(tp)); } int main () { using namespace std ; cout << to_loctime ( " Europe/Bucharest " , 2'460'255.218177 ) << ' \n ' ; } Next the code introduces this helper function: template < class Duration > loctime to_loctime (std::string const & location, date::sys_time< Duration > tp) This code converts a system_clock-based time_point to loctime using the time_zone named by location. The time_zone is first used to convert the sys_time to local_time. The remaining lines in the function break this local time_point up into fields, and assign those fields into a loctime. Finally to_loctime(std::string const& location, long double julian_days) can be easily written by converting the long double into a jdate_clock::time_point, then converting that into a system_clock-based time_point, and then passing that along to the previously defined to_loctime function. main demonstrates a recent value using "Europe/Bucharest". — Reply to this email directly, [view it on GitHub](https://github.com/HowardHinnant/date/issues/709#issuecomment-1795591032), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AWCQXNR26P2XR7K62TW5N2DYDEM5TAVCNFSM5GCXPCIKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCNZZGU2TSMJQGMZA). You are receiving this because you authored the thread.Message ID: ***@***.***>
chrisgermain commented 9 months ago

Hi Howard!

I come back to you with another question. Your code works great, but in my project I need to _loctime function to show the result rounded to the nearest second, as most of the times when I turn midnight julian days into localtime I get 23h59min59,9999sec and I need to have it 0h0min0sec. I use double as type for julian days for speed reason. Do you think this would be a problem? Thank you a lot!!

Christian

Sent with Proton Mail secure email.

On Monday, November 6th, 2023 at 7:28 PM, Howard Hinnant @.***> wrote:

Thanks for the details. Below is a solution for creating this function:

loctime

to_loctime

(std::string

const

& location,

long

double

julian_days)

To start off with I reuse a Julian date clock that I created years ago, and is documented here: https://stackoverflow.com/questions/33964461/handling-julian-days-in-c11-14/33964462#33964462. I've tweaked the code to be compatible with C++17. It is a chrono clock with long double as the representation and days (using long double) as the period. The epoch is coded in the proleptic Gregorian calendar as November/24/-4713+12h.

The main advantage of this clock is to be able to convert a long double into a type safe chrono time_point, and then convert that time_point to that of system_clock which has an epoch of January/1/1970 00:00:00.

#

include

"

date/tz.h

"

#

include

<

iostream

struct

jdate_clock

;

template

<

class

Duration

using

jdate_time = std::chrono::time_point<jdate_clock,

Duration

;

struct

jdate_clock

{

using

rep =

long

double

;

using

period = date::days::period;

using

duration = std::chrono::duration<rep, period>;

using

time_point = std::chrono::time_point;

static

constexpr

bool

is_steady =

false

;

static

time_point

now

()

noexcept

;

template

<

class

Duration

static

auto

from_sys

(date::sys_time<

Duration

const

& tp)

noexcept

;

template

<

class

Duration

static

auto

to_sys

(jdate_time<

Duration

const

& tp)

noexcept

; };

template

<

class

Duration

auto

jdate_clock::from_sys

(date::sys_time<

Duration

const

& tp)

noexcept

{

using

namespace

date

;

using

namespace

std

;

using

namespace

chrono

;

auto

d = tp - (sys_days{November/

24

/-

4713

}+12h);

return

jdate_time<

decltype

(d)>{d}; }

template

<

class

Duration

auto

jdate_clock::to_sys

(jdate_time<

Duration

const

& tp)

noexcept

{

using

namespace

date

;

auto

d = tp - clock_cast(sys_days{});

return

sys_time<

decltype

(d)>{d}; }

jdate_clock::time_point

jdate_clock::now

()

noexcept

{

using

namespace

date

;

using

namespace

std

::chrono

;

return

clock_cast(

system_clock::now

()); }

struct

loctime

{

int

yyyy;

int

mn;

int

dd;

int

hh;

int

mm;

float

ss; };

std::ostream&

operator

<<(std::ostream& os, loctime

const

& lt) { os << lt.

yyyy

<<

'

-

'

<< lt.

mn

<<

'

-

'

<< lt.

dd

<<

'

'

<< lt.

hh

<<

'

:

'

<< lt.

mm

<<

'

:

'

<< lt.

ss

<<

'

\n

'

;

return

os; }

template

<

class

Duration

loctime

to_loctime

(std::string

const

& location, date::sys_time<

Duration

tp) {

using

namespace

date

;

using

namespace

std

::chrono

;

auto

tp_local =

locate_zone

(location)->

to_local

(tp);

auto

tp_days =

floor

(tp_local); year_month_day ymd{tp_days}; auto tod = tp_local - tp_days; loctime lt; lt. yyyy = int {ymd. year ()}; lt. mn = unsigned {ymd. month ()}; lt. dd = unsigned {ymd. day ()}; lt. hh = duration_cast(tod). count (); tod -= hours{lt. hh }; lt. mm = duration_cast(tod). count (); tod -= minutes{lt. mm }; lt. ss = duration< float >{tod}. count (); return lt; } loctime to_loctime (std::string const & location, long double julian_days) { using namespace date ; using namespace std ::chrono ; jdate_clock::time_point tp{jdate_clock::duration{julian_days}}; return to_loctime (location, clock_cast(tp)); } int main () { using namespace std ; cout << to_loctime ( " Europe/Bucharest " , 2'460'255.218177 ) << ' \n ' ; } Next the code introduces this helper function: template < class Duration > loctime to_loctime (std::string const & location, date::sys_time< Duration > tp) This code converts a system_clock-based time_point to loctime using the time_zone named by location. The time_zone is first used to convert the sys_time to local_time. The remaining lines in the function break this local time_point up into fields, and assign those fields into a loctime. Finally to_loctime(std::string const& location, long double julian_days) can be easily written by converting the long double into a jdate_clock::time_point, then converting that into a system_clock-based time_point, and then passing that along to the previously defined to_loctime function. main demonstrates a recent value using "Europe/Bucharest". — Reply to this email directly, [view it on GitHub](https://github.com/HowardHinnant/date/issues/709#issuecomment-1795591032), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AWCQXNR26P2XR7K62TW5N2DYDEM5TAVCNFSM5GCXPCIKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCNZZGU2TSMJQGMZA). You are receiving this because you authored the thread.Message ID: ***@***.***>
HowardHinnant commented 9 months ago

Hi Christian,

This should not be a problem. In to_loctime change this:

    auto tp_local = locate_zone(location)->to_local(tp);

to this:

    auto tp_local = round<seconds>(locate_zone(location)->to_local(tp));