I wan't to iterate day by day in a for loop. I created a Boost ptime that represent a day like '2012-01-01 00:00:00' at my local time. (Germany)
It currently looks like this (ptime start_t, ptime end_t):
for( posix_time::ptime i = start_t; i < end_t; i += gregorian::days(1) ) {
...
}
The date 25.10.2009 has 23 hours because of the switch between the daylight saving times. But the command gregorian::days(1) adds 24 hours.
Does anyone have a good solution to iterate day-wise by involving the time zone without generating my own timezone database?
For a simple day iteration, you can use the day_iterator. However, as far as I know, the Gregorian Date System of boost is agnostic to DST (DST is a property of time, not of date). Similarly, the Posix Time System "defines a non-adjusted time system". I think the Local Time System is appropriate for your task.
Example:
#include <boost/date_time.hpp>
int main()
{
using namespace boost;
// POSIX time zone string for Germany
//local_time::time_zone_ptr zone(
// new local_time::posix_time_zone("CET-1CEST,M3.5.0,M10.5.0/3") );
// load from a database
local_time::tz_database db;
db.load_from_file("path_to_boost/libs/date_time"
"/data/date_time_zonespec.csv");
local_time::time_zone_ptr zone = db.time_zone_from_region("Europe/Berlin");
local_time::local_date_time ldt =
local_time::local_sec_clock::local_time(zone);
posix_time::ptime pt = posix_time::second_clock::local_time();
while(true)
{
ldt += gregorian::days(1);
pt += gregorian::days(1);
std::cout << "local_date_time: " << ldt << '\n';
std::cout << "ptime: " << pt << '\n';
std::cin.ignore();
}
}
Note: I don't know how to get the time zone from the user's / system's settings, nor could I find a reliable source for the POSIX time zone string. There's the boost documentation plus what wikipedia says, but better you check it yourself.
Thanks to Matt Johnson for pointing out a mistake in the POSIX time string and providing the alternative solution via a database.
Thanks for your answer!
It helped me a lot and I also got a good solution from a coworker: the boost::locale methods and classes.
It's pretty easy to get the correct local timezone with these namespace.
For further information: http://www.boost.org/doc/libs/1_54_0/libs/locale/doc/html/index.html
Example:
for( boost::locale::date_time i = start; i.time < end; += period::day(1) ) {
...
}
Related
I'm trying to find the best way to see if the current time is before a specified time. Say I want to see if it's before 14:32. What's the best way to do this in C++? Ideally I'd be able to build some time object that represents 14:32, then compare it with the current time as some object.
This is what I'm doing right now. Pretty messy and uses 3 different representations of time.
int hour_ = 14;
int min_ = 32;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm utc_tm = *gmtime(&tt);
if ((utc_tm.tm_hour < hour_) || (utc_tm.tm_hour == hour_ && utc_tm.tm_min < min_) ) {
std::cout << "It's before " << hour_ << ":" << min_ << std::endl;
}
Here is how you can do it in C++20. Later I will show how to convert this to use a free, open-source C++20 chrono preview library which works with C++11/14/17.
#include <chrono>
bool
is_now_before(std::chrono::minutes local_config_tod)
{
using namespace std::chrono;
auto tz = current_zone();
auto now = system_clock::now();
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
return now < utc_config;
}
The parameter has type minutes which will be interpreted to be the local time of day in minutes. For example 14:32 is represented by minutes{872}. This representation is compact (one integer), and it is trivial to convert {hours, minutes} to just minutes (shown below).
current_zone() gets the computer's current local time zone. This information is needed twice in this function, so it is best to just get it once. Not only does this save the result, but it also sidesteps the problem of the local time zone changing out from under you (between multiple calls) in a mobile device.
Next the current time is obtained (just once) via system_clock. This gives the current time in UTC.
Now we have a choice:
We could do the comparison in UTC, or
We could do the comparison in local time.
Doing the comparison in UTC is less error prone in the corner case that the UTC offset is changing in the current local day (such as going on or off of daylight saving).
To convert the local config time-of-day (local_config_tod) to a UTC time_point one first has to find out what the current local day is. In general this can be different than the current UTC day. So the current UTC now has to be converted to local time, and then truncated to days-precision:
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
Now a local time_point can be created simply by summing local_day and local_config_tod. This local time_point can then be converted back into UTC (a time_point based on system_clock but with seconds precision):
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
The line of code above handles the corner cases for you. If there is not a unique (one-to-one) mapping from local time to UTC, then an exception is thrown. The .what() of the exception type will have a detailed description about how this mapping is either ambiguous, or non-existent.
Assuming the above mapping does not throw an exception, you can simply compare these two UTC time_points:
return now < utc_config;
The precision of this comparison is with whatever precision your system_clock has (typically microseconds to nanoseconds).
This can be exercised like so:
int hour_ = 14;
int min_ = 32;
using namespace std::chrono;
auto b = is_now_before(hours{hour_} + minutes{min_});
If 14 and 32 are literals (and you're in C++14 or later), it can be shortened to:
auto b = is_now_before(14h + 32min);
If you are using a standard prior to C++17, the zoned_time constructions will require an explicit template parameter:
auto local_day = floor<days>(zoned_time<system_clock::duration>{tz, now}.get_local_time());
auto utc_config = zoned_time<minutes>{tz, local_day + local_config_tod}.get_sys_time();
If you would like to use the free, open-source C++20 chrono preview library, add #include "date/tz.h" and using namespace date;. Some installation is required.
If you would like to avoid an exception in the case that local_day + local_config_tod does not have a unique mapping to UTC, that is also possible with minor changes to is_now_before. But you will have to decide things such as: Do I want to compare against the first or second local_config_tod of the local_day (in case the UTC offset has been decreased).
Oops! Is the config time already UTC?
On re-reading your question it occurred to me that I may have misread your question. If 14:32 is UTC, then things get much, much simpler! And rather than removing my answer showing the local 14:32 interpretation, I thought it would be better to add this, so future readers could pick either solution.
Assuming the config is a UTC time, then time zones play no role at all:
#include <chrono>
bool
is_now_before(std::chrono::minutes utc_config_tod)
{
using namespace std::chrono;
auto now = system_clock::now();
auto utc_day = floor<days>(now);
return now < utc_day + utc_config_tod;
}
The current day in UTC is simply:
auto utc_day = floor<days>(now);
And now the config date-time is simply utc_day + utc_config_tod. This is just drop-dead simple.
If you can't use C++20, the free, open-source C++20 chrono preview library is also much simpler now as it is header-only, requiring no installation at all. Just #include "date/date.h" and add using namespace date;.
In C++ we can use the mt_structure from the date/time functions (documentation here: https://en.cppreference.com/w/cpp/chrono/c/tm) Here is how I would print the date, and check to see if it's past a certain time
#include <iostream>
#include <ctime>
#include <chrono>
using namespace std;
int main()
{
time_t t = time(0); // get time now
tm* now = localtime(&t);
cout << (now->tm_year + 1900) << '-'
<< (now->tm_mon + 1) << '-'
<< now->tm_mday << ", "
<< now->tm_hour << ":" << now->tm_min
<< "\n";
int hour = 7, minute = 30;
if((now->tm_hour > hour) || (now->tm_hour == hour && now->tm_min >= minute))
cout << "it's past 7:30\n";
else
cout << "it's not past 7:30";
}
prints:
2021-10-27, 20:40
it's past 7:30
I need to be able to convert Epoch time to Excel time.
Why Excel, because working with the numeric excel time is faster that any parsing done on display formats.
Current time of 2018-06-08 12:46:58 CDTwith UTC 1528480019 should give 0.5326157.
But converted to the New_York time or 2018-06-08 13:46:58 EDT will give 0.574282367.
I only need to convert the time field to Excel style.
Here is my incomplete code:
double GetTime(Datetime currtime, std::string tz = "TZ=America/New_York")
{
std::time_t t = currtime;
//tzset(tz);
std::tm tm = *std::localtime(&t);
return ((tm.tm_hour * 3600 + (tm.tm_min) * 60.0 + tm.tm_sec) / 86400.0);
}
The code works, but only for local time which is "America/Chicago".
I have been unable to use the set the timezone to the one I might need.
Also tm seems to be limited to seconds, but I need to handle milliseconds and microseconds as well.
Furthermore, I need it to be fast and the the current implementation parses the time into separate fields and then I combine it into what I need which seems to do a lot of extra work.
This problem can easily be solved with Howard Hinnant's free, open-source date/time/timezone library, which is very efficient. This library is also in the current C++20 working draft, under namespace std::chrono. So in the future, porting your code to just use the std::lib ought to be as easy as changing a few namespaces.
double
GetTime(std::chrono::system_clock::time_point currtime,
date::time_zone const* tz = date::current_zone())
{
using namespace date;
using namespace std::chrono;
zoned_time<system_clock::duration> zt{tz, currtime};
auto lt = zt.get_local_time();
auto ld = floor<days>(lt);
using ExcelTime = duration<double, days::period>;
ExcelTime tod = lt - ld;
return tod.count();
}
Instead of taking a Datetime it takes a std::chrono::system_clock::time_point, and instead of a std::string, a date::time_zone const*.
On the three big platforms (llvm/gcc/MSVS), the coarsest system_clock::time_point is microseconds, which meets your precision goals.
Step one is to create a zoned_time which is a pairing of a time_point with a time_zone. From this one can get a local_time.
floor<days> truncates the precision of a time_point to days. If one subtracts the day-precision time_point from the finer-precision time_point, one gets the local time-of-day.
If you store this local time-of-day in a chrono::duration that has a double as its representation, and a period of 1 day, then you get the Excel Time-Of-Day format.
This can be used like:
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << std::fixed << std::setprecision(9);
std::cout << GetTime(sys_seconds{1528480019s}, locate_zone("America/Chicago")) << '\n';
zoned_time<system_clock::duration> zt{"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s};
std::cout << GetTime(zt.get_sys_time(), zt.get_time_zone()) << '\n';
}
which outputs:
0.532627315
0.574282407
Above, I struggled to come as close as possible to your existing API. However if you adopt this library, you can make it even simpler, and slightly more efficient, by adopting a "more native" API:
std::chrono::duration<double, date::days::period>
GetTime(const date::zoned_time<std::chrono::system_clock::duration>& zt)
{
using namespace date;
auto lt = zt.get_local_time();
return lt - floor<days>(lt);
}
Now GetTime takes just a single parameter of type zoned_time<system_clock::duration>, and returns a duration<double, days::period>. All that's left for GetTime to do is truncate the local time to days-precision and subtract to get time-of-day.
The demo in main is also simplified:
std::cout << GetTime({"America/Chicago", sys_seconds{1528480019s}}).count() << '\n';
std::cout << GetTime({"America/New_York",
local_days{2018_y/6/8} + 13h + 46min + 58s}).count() << '\n';
And gives the same output as before.
I am in the middle of developing a cross platform application that changes the system date and time to a specified value. I have completed the part for Windows.
How can I set the system date and time from a C++ program in Linux? I am looking for a function similar to SetSystemTime(SYSTEMTIME &x).
As far as I understood settimeofday() does nothing with the date and I am not sure about the usage of function stime(). I hope mktime() has nothing to do with my need.
Can anybody help me.
You understand wrongly. settimeofday(2) is setting the Epoch time. which is both date and time. Read time(7)
So you if you start from a string expressing a date, convert that string with strptime(3) to a struct tm then convert that to a Unix time with mktime(3) then feed that to settimeofday (i.e. the tv_sec field).
However, settimeofday requires root privilege and I believe you usually should avoid calling it (at least on usual, Internet-connected, computers). Better set some NTP client service on your Linux PC (e.g. run ntpd or chrony and more generally read the sysadmin chapter on keeping time...). See also adjtimex(2)
BTW, changing abruptly the system time on a multi-tasking system -like Linux or Windows- is a very dangerous operation (since it will upset and disturb a lot of system tasks depending or using the time). There are few good reasons to do that (it is a very bad idea in general). If you do that, do that with very few programs & services running (e.g. single user mode Linux). You should not do that in ordinary application code.
I write this piece of code to set Date and Time under Linux.
#include <time.h>
struct tm time = { 0 };
time.tm_year = Year - 1900;
time.tm_mon = Month - 1;
time.tm_mday = Day;
time.tm_hour = Hour;
time.tm_min = Minute;
time.tm_sec = Second;
if (time.tm_year < 0)
{
time.tm_year = 0;
}
time_t t = mktime(&time);
if (t != (time_t) -1)
{
stime(&t);
}
Note that stime requires root privileges.
Example using clock_settime instead of stime since, as Mehmet Fide pointed out, stime is now deprecated. I like the reference code from Converting between timespec & std::chrono for this:
#include <time.h>
#include <chrono>
using std::chrono; // for example brevity
constexpr timespec timepointToTimespec(
time_point<system_clock, nanoseconds> tp)
{
auto secs = time_point_cast<seconds>(tp);
auto ns = time_point_cast<nanoseconds>(tp) -
time_point_cast<nanoseconds>(secs);
return timespec{secs.time_since_epoch().count(), ns.count()};
}
const char* timePointToChar(
const time_point<system_clock, nanoseconds>& tp) {
time_t ttp = system_clock::to_time_t(tp);
return ctime(&ttp);
}
const time_point system_time = system_clock::now();
cout << "System time = " << timePointToChar(system_time) << endl;
const timespec ts = timepointToTimespec(system_time);
clock_settime(CLOCK_REALTIME, &ts);
I'm wondering how I can accurately work out the local time in New York using Boost and C++, even if I run the code on a servers located in a range of different countries?
What I have tried
I have tried looking at the Boost C++ examples but I couldn't seem to find anything.
For the OP, as well as Matt Johnson, who wanted to see the tz database in use.
#include "boost/date_time/posix_time/posix_time.hpp"
using namespace boost::posix_time;
using namespace boost::gregorian;
#include "boost/date_time/local_time/local_time.hpp"
// form an empty database
boost::local_time::tz_database tz_database;
// load the time zone database which comes with boost
tz_database.load_from_file( "../../boost/libs/date_time/data/date_time_zonespec.csv" );
// obtain a specific time zone from the database
boost::local_time::time_zone_ptr tzNewYork;
tzNewYork = tz_database.time_zone_from_region("America/New_York");
// example universal/local conversion (from boost example code)
// ptime now = boost::posix_time::microsec_clock::universal_time();
ptime now(date(2004,Nov,5), hours(10));
boost::local_time::local_date_time ny(now, tzNewYork );
ny.utc_time(); // 10am 2004-Nov-5
ny.local_time(); // 5am 2004-Nov-5
First, you need to get the UTC time: you can do that with boost::posix_time::second_clock::universal_time().
From the link you just gave:
//eastern timezone is utc-5
typedef boost::date_time::local_adjustor<ptime, -5, us_dst> us_eastern;
// ...
ptime t3 = us_eastern::utc_to_local(t2);//back should be the same
std::cout << to_simple_string(t2) << " UTC is "
<< to_simple_string(t3) << " New York time "
<< "\n\n";
To convert it to NY time, just define a local_adjustor for your timezone, and call utc_to_local from it.
in my c++ software I've used Boost in some parts and also for the local time.
OK, now my problem is to make a check if in my machine is active or not the DST.
With the follow part of code I can know only the difference from the UTC time. In my case the difference is 2 hours because is active the DST
ptime tLoc = second_clock::local_time();
ptime tUTC = second_clock::universal_time();
time_duration tDiff = tUTC - tLoc;
local_time_zone = tDiff.hours();
I think that the boolean funcion has_dst() can help, right?
My system is Debian GNU/Linux.
Thanks
Look at plain C functions in time.h/ctime
localtime will return a struct tm*
struct tm has as its last field a flag telling if it is under DST or not.
I believe the function you are looking for is local_date_time_base<>::is_dst(). All date_time data types in Boost.DateTime are derived from local_date_time_base<>. The following should give you the required result:
namespace lt = boost::local_time;
// for example, use central time zone
lt::time_zone_ptr zone(new lt::posix_time_zone(
"CST-06:00:00CDT+01:00:00,M3.2.0/02:00:00,M11.1.0/02:00:00"));
lt::local_date_time tloc = lt::local_sec_clock::local_time(zone);
std::cout << "I'm " << (tloc.is_dst() ? "" : "not ") << "in DST";