c++ Add or subtract values to the date - c++

I couldn't figüre out how to add values to the date. Imagine, I have a date like 2012-5-15. After I parsed it, I obtained the year,month and day seperately and I tried to convert to my exact date to the days by using time_t and mktime. Do I have a chance to convert it back to the date from the days or seconds I found? Here is part of my code trying to do this;
if(operation="+"){
tm tm1=make_tm(year,mon,day);
time_t time1=mktime(&tm1);
int seconds_per_day=60*60*24;
time_t second=time1/seconds_per_day;
int sum=second //Trying to convert to the int from my time_t, don't know
//if it is neccesary
sum=sum+value //the value I want to add
So, basically if I give an input like 2012/5/15. It calculates how many days it passed by using the method but I just need to convert it to date and make it shown. Do you have any idea?

If you are asking for a way to add days to a particular date, here is a way to do it in similar terms to your attempt:
tm addDays(tm t1, int days)
{
time_t epoch = mktime(&t1);
epoch += (60*60*24 * days);
return *localtime(&epoch);
}
Then use the returned tm structure and access the tm_year, tm_mon and tm_mday members to get the yyyymmdd required output. A very rough and ready example (no proper formatting):
tm t1;
std::fill((char*)&t1, (char*)&t1 + sizeof(t1), 0);
t1.tm_year = 2016;
t1.tm_mon = 8;
t1.tm_mday = 2;
auto result = addDays(t1, 30);
std::cout << result.tm_year << '/' << result.tm_mon << '/' << result.tm_mday << std::endl;

Here is a free, open-source C++11/14 library to do what you want with very nice syntax, and unparalleled performance.
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day x = sys_days{2012_y/may/15} + days{55};
std::cout << x << '\n';
}
This outputs:
2012-07-09
If you would prefer to write this yourself, here are the algorithms used by this date library. But the date library wraps everything up in a type-safe manner which catches many errors at compile time.
The date library is also nothing more than an extension of the <chrono> library, and so is very compatible with it:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto x = sys_days{2012_y/may/15} + days{55} + 7h + 30min;
std::cout << x << '\n';
}
2012-07-09 07:30

thanks everybody for your attention but I figured out the problem in another way. Here is my way;
tm tm1=make_tm(year,mon,day);//of course I had an make_tm as struct
time_t time1=mktime(&tm1);
time1=time1+value*86400-86400 //because it was a second, I needed to convert the day to second. Then, I don't know why but it showed extra one day. So, again I subtracted 86400 then the problem solved.
char *cb=ctime(&time1);
cout<<cb<<endl;

Related

Adding over 30 days to 1900-01-01

How can I add over 30 days in C++ to 1900-01-01 date approx. over 1000 days and then format the time_t after the addition to get a non-broken date.
This is what I have tried so far:
int tmp = 1000;
struct std::tm tm;
std::istringstream ss("1900-01-01");
ss >> std::get_time(&tm, "%Y-%m-%d");
tm.tm_mday = tm.tm_mday + tmp;
return mktime(&tm);
In addition to Joseph Larson's very good suggestion to check out the date/time library to use, I'll show how you could get further using your current idea.
You also have much support in std::chrono nowadays so read about that too.
You try to add the days in the wrong domain, to std::tm. Instead, convert the std::tm to time_t and add the days to that - then convert the result back to std::tm.
Example:
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
int main() {
int days = 1000;
std::tm tm{};
std::istringstream ss("1900-01-01");
if(ss >> std::get_time(&tm, "%Y-%m-%d")) {
tm.tm_isdst = -1; // let mktime "guess" if DST is effect
// convert to time_t and add 1000 days. 1 day = 24*60*60 seconds
std::time_t result = std::mktime(&tm) + days * 60*60*24;
// back to std::tm
tm = *std::localtime(&result);
// print result
std::cout << std::put_time(&tm, "%Y-%m-%d") << '\n';
}
}
Note: This technique will sometimes get the wrong answer. If for the computer's local time zone the UTC offset at 1900-01-01 is greater than it is at 1900-01-01 + days, then the result will be one day less than it should. This happens (for example) with the IANA time zone America/Anchorage with days == 232. It happens again with Africa/Cairo at days == 273.
A better option is clearly to use the facilities in chrono or Howard Hinnant's date library as demonstrated by Howard.
Date/time handling in C++ is awkward as awkward can be. Howard Hinnant has a great library you may want to look at:
https://github.com/HowardHinnant/date
The problem is complicated. If you use local dates, you can't add a fixed amount of time due to daylight savings time and leap seconds. You could use GMT, but you're still subject to leap seconds.
But Howard's library make make this much easier for you. I'd take a peek.
If you are using the latest Visual Studio 2019, then you have C++20 <chrono> which can solve this problem without the errors associated with the mktime/localtime technique demonstrated in Ted Lyngmo's answser.
#include <chrono>
#include <iostream>
#include <sstream>
int
main()
{
// Hold the amount to add in the type std::chrono::days (a chrono duration)
std::chrono::days days{1000};
// std::chrono::sys_days is a Unix Time chrono::time_point with precision days
std::chrono::sys_days tp;
std::istringstream ss("1900-01-01");
if(ss >> std::chrono::parse("%F", tp)) {
// No need to involve time zones.
// Just add and print out
std::cout << tp + days << '\n'; // 1902-09-28
}
}
This program has the same behavior and output as Ted's answser. But if there's no need to read the "constant" 1900-01-01 out of a stream, then we can do even better. C++20 can make 1900-01-01 a compile-time constant:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono_literals;
std::chrono::days days{1000};
constexpr std::chrono::sys_days tp = 1900y/01/01; // Compile-time date literal
// Just add and print out
std::cout << tp + days << '\n'; // 1902-09-28
}
These solutions don't involve time zones at all. It is simply adding a number of days to a date. The simplicity makes for efficient code and reduces the chance for errors associated with increased complexity.
If you don't have the latest Visual Studio 2019, or otherwise don't have access to C++20, you can use Howard's free, open-source, header-only "date.h" C++20 chrono preview library referred to in Joseph's answer with nearly identical syntax.
#include "date/date.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace date::literals;
date::days days{1000};
constexpr date::sys_days tp = 1900_y/01/01; // Compile-time date literal
// Just add and print out
using date::operator<<;
std::cout << tp + days << '\n'; // 1902-09-28
}
The C++20 chrono additions are in namespace date instead of namespace std::chrono.
The year literal is spelled _y instead of y.
The time_point streaming operators won't be found by ADL and have to be manually exposed in namespace date.

Generate a date vector using <ctime>

How can I build a vector containing all dates (daily period) between two given dates using <ctime> library? For example, given January 1, 2019 and January 10, 2019, a vector containing the 10 dates in between (inclusive)?
I don’t really mind about the representation of the dates, could be a string or any other structure but I would like to understand how to manipulate <ctime> objects.
If there is a better library for time representation in C++, I’d be glad to hear about it.
With the C++20 date library (a.k.a. Howard Hinnant's date library):
#include "date.h"
auto get_dates(date::sys_days first, date::sys_days last)
{
std::vector<date::sys_days> dates;
for (; first <= last; first += date::days{1})
dates.push_back(first);
return dates;
}
(live demo)
Here's a small, quick demo program - it makes a vector of struct tm, and then prints the time out. Each new entry to the vector is one day ahead of the old one, seamlessly wrapping around months and years as it rolls past them.
Time is often stored in a time_t, which is the count of seconds since y2k. This data type seems easier to manipulate than a struct tm - we'll use it, coupled with how many seconds are in a day, to create a vector of struct tm as we go. We'll start at time0, and we'll continue 20 days, to time19, adding a new struct tm for every day we count.
#include <iostream>
#include <ctime>
#include <vector>
int main(void) {
double secs_in_a_day = 86400;
time_t time0; //your start time here
time(&time0); //i'm using the current time
//20 days later
time_t time19 = time0 + (20 * secs_in_a_day); //your end time here
std::vector<struct tm > timevec;
for(time_t i = time0; i <= time19; i += secs_in_a_day) {
struct tm t = *(gmtime(&i));
std::cout << i << "\n";
timevec.push_back(t);
}
char buffer[80];
for(struct tm tim : timevec) {
strftime(buffer, 80, "Time: %d / %m / %y\n", &tim);
puts(buffer);
}
return 0;
}
Note that the for loop increments by the number of seconds in a day. It's likely possible to do this directly with struct tm or struct tm * variables, but then there would be a good deal of chasing pointers around. Given that it's so easy to convert to time_t, it's probably worth saving the headache to use it instead.
Hope this helps - C++ admittedly leaves a bit to be desired when dealing with time.

Converting Epoch time with timezone to Excel format time?

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.

localtime_s fails where gmtime_s succeeds with dates before 1-1-1970

I'm trying to get the current year stored in a date from before 1970 using an std::chrono::time_point<std::chrono::system_clock>, however I've run into an issue regarding the reading from its contents into a std::tm struct.
I convert the time_point to a time_t first, after which I read its values to get the tm_year value. However, when trying to do so, the code fails when using localtime_s, however it succeeds when I'm using gmtime_s. This is only for dates before 1-1-1970, dates after that work fine using both functions.
The code below reproduces the error. If terstGmTimeVsLocalTime is called with utc=true it works, if it is called with utc=false it doesn't produce the correct output.
#include <iomanip>
#include <time.h>
#include <iostream>
void testGmTimeVsLocaltime(const bool& utc) {
// Create time
std::tm timeInfoWrite = std::tm();
timeInfoWrite.tm_year = 1969 - 1900; // Year to parse, here it is 1969
timeInfoWrite.tm_mon = 0;
timeInfoWrite.tm_mday = 1;
timeInfoWrite.tm_hour = 1;
timeInfoWrite.tm_min = 0;
timeInfoWrite.tm_sec = 0;
timeInfoWrite.tm_isdst = -1;
std::chrono::time_point<std::chrono::system_clock> timePoint = std::chrono::system_clock::from_time_t(utc ? _mkgmtime(&timeInfoWrite) : std::mktime(&timeInfoWrite));
// Convert to time_t
std::time_t timeT = std::chrono::system_clock::to_time_t(timePoint);
// Read values
std::tm timeInfoRead;
if (utc) {
gmtime_s(&timeInfoRead, &timeT);
} else {
localtime_s(&timeInfoRead, &timeT);
}
// Output result
std::cout << (timeInfoRead.tm_year + 1900) << '\n';
// Wait for input
std::getchar();
}
int main() {
testGmTimeVsLocaltime(true); // Set to false to show bug
return 0;
}
utc=true outputs 1969, as would be expected. However, utc=false outputs 1899 (presumably since an error occurs and tm_year gets set to -1).
Is there anything I'm missing? The documentation doesn't specifically specify that localtime_s should fail for dates before 1-1-1970.
I'm on Windows 10 x64 if it makes a difference.
Using Howard Hinnant's free, open-source date lib, you can completely side-step the clumsy, error and bug-prone C api, and work directly with a modern <chrono>-based system:
#include "chrono_io.h"
#include "date.h"
#include <iostream>
void
testGmTimeVsLocaltime()
{
using namespace date;
// Create time
auto timeInfoWrite = 1969_y/jan/1;
sys_days timePoint = timeInfoWrite; // this is a chrono::time_point
std::cout << timePoint.time_since_epoch() << '\n'; // -365 days
// Convert to time_t
// no need
// Read values
year_month_day timeInfoRead = timePoint;
// Output result
std::cout << timeInfoRead.year() << '\n';
}
int
main()
{
testGmTimeVsLocaltime();
}
Output:
-365[86400]s
1969
There are literals to make it easy to fill in a year_month_day struct which is the analog to the year, month and day parts of a tm. You can easily convert this to a std::chrono::time_point<system_clock, days> (sys_days). This is the same as a system_clock::time_point, but with a precision of days instead. It itself will implicitly convert to a seconds-precision time_point (typedef'd to sys_seconds), or to a system_clock::time_point.
Above I just output its time_since_epoch() which shows that it is -365 days prior to the epoch.
There's never really any need to convert to C API data structures, but it is easy if you want to. For example, assuming time_t is seconds since 1970-01-01:
std::time_t timeT = sys_seconds{timePoint}.time_since_epoch().count();
std::cout << timeT << '\n';
which outputs:
-31536000
The reverse conversion (back to year_month_day) is just as easy. If you want to convert from timeT it is just slightly more involved:
year_month_day timeInfoRead = floor<days>(sys_seconds{seconds{timeT}});
This first converts time_t to chrono::seconds, and then to a seconds-precsion time_point, and then to a days-precsion time_point, finally to the year_month_day field type (tm-like).
Finally year_month_day has a year() getter member function which is streamable. You can explicitly convert year to int if desired:
int{timeInfoRead.year()}
But I think it best to keep things like years, months and days as distinct types so that the compiler can help you catch when you accidentally mix them up.
Finally, if you really meant that you wanted 1969-01-01 00:00:00 in your computer's local timezone, there's a library to do that as well. And it is just a minor modification of the simple program above.
#include "tz.h"
#include <iostream>
void
testGmTimeVsLocaltime()
{
using namespace date;
using namespace std::chrono;
// Create time
auto timeInfoWrite = 1969_y/jan/1;
auto timePoint = make_zoned(current_zone(), local_days{timeInfoWrite});
// Convert to time_t
std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count();
std::cout << timeT << '\n';
// Read values
timePoint = sys_seconds{seconds{timeT}};
year_month_day timeInfoRead{floor<days>(timePoint.get_local_time())};
// Output result
std::cout << timeInfoRead.year() << '\n';
}
int
main()
{
testGmTimeVsLocaltime();
}
Output:
-31518000
1969
Now you create a zoned_seconds using the computer's current_zone() timezone, and converting your timeInfoWrite to local_days instead of to sys_days.
You can get the local time or the system time out of timePoint. For converting to time_t, system time makes the most sense:
std::time_t timeT = timePoint.get_sys_time().time_since_epoch().count();
And now the output (for me) is 5h later (18000s).
-31518000
You can get back either the local year, or the system (UTC) year, by either using .get_local_time() or .get_sys_time(). For me it makes no difference ("America/New_York"). But if you're in "Australia/Sydney", you'll get 1968 if you request the UTC year instead of 1969. And that's all very easy to simulate by simply substituting "Australia/Sydney" or "America/New_York" for current_zone() in the program above.
Yes, it works on Windows, VS-2013 and later. There is some installation required for the timezone lib: https://howardhinnant.github.io/date/tz.html#Installation

Comparing two timestamp strings in C++

I have two timestamps stored as string variables. The timestamps are in the format dd/mm/yyyy-hh:mm:ss
I am trying to find the difference in seconds between the two timestamps (ignoring the dates).
(I haven't assigned strings to a and b but they hold a timestamp each)
It always outputs 0 for the number of seconds difference and I can't work out why.
std::string a, b; // hold timestamps
struct tm t, t1;
double seconds;
t.tm_hour = stoi(a.substr(11,2)); // stoi() cast substring to int
t.tm_min = stoi(a.substr(14,2));
t.tm_sec = stoi(a.substr(17,2));
t1.tm_hour = stoi(b.substr(11,2));
t1.tm_min = stoi(b.substr(14,2));
t1.tm_sec = stoi(b.substr(17,2));
seconds = difftime(mktime(&t1), mktime(&t));
std::cout<<seconds;
Don't use hardcoded substring values (1 minute vs 11 minute might make you go off if 01 notation isn't used... and you have months ,days and hours also to take into account).
Instead of hardcoding the offset try to go after the unique characters (for you to get the "seconds" , take account the only the string after the 2nd occurrence of ":" ).
Add following code after the defintions and before the assignments
// initialize time structures with all the details for 'now'
time_t ts;
time( &ts );
t = * localtime( &ts );
t1 = t;
I suggest use CTime to work with timestamp.
http://www.cplusplus.com/reference/ctime/
You can use this for storage and later, if you need, convert to string.
This would be a great reason to start with the Boost libraries, because Boost.Date_Time has exactly what you need. See the documentation about time durations.
Here is an example program:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
int main()
{
boost::posix_time::time_duration duration1 = boost::posix_time::duration_from_string("10:11:12");
boost::posix_time::time_duration duration2 = boost::posix_time::duration_from_string("10:12:15");
std::cout << (duration2 - duration1).total_seconds() << "\n";
}
Output: 63
Since you are already using substr and std::stoi, it should be easy for you to get the proper substrings from a and b to be passed to boost::posix_time::duration_from_string.