Generate a date vector using <ctime> - c++

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.

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.

How to convert unsigned int 64 into time in C++?

I am converting CLI C++ code to standard C++, and i have a piece of code that gets a UINT64 number (from a remote server - so i can't change to format/precision of the time i get) and converts it into DateTime object and later outputs the following value: myDatetime.ToString("dd/MM/yyyy hh:mm:ss.fffffff tt").
I haven't found a way to convert unsigned int 64 into time in C++.
The following code does nothing for numbers so big (that's the 64bit number i get from the server).
time_t rawtime=131274907755873979
localtime_s(&timeinfo, &rawtime);
I need some help :)
My question wan't answered in the thread Convert Epoch Time string to Time since it doesn't work for numbers as large as i need. For example the number 131274907755873979 which is what i get from the server. The function ctime for that value simply returns NULL.
I need a way to convert between the time i get as a unsigned int64 into standard C++ time object.
std::string LongToString(int64_t longDate) {
char buff[128];
std::chrono::duration<int64_t, std::milli> dur(longDate);
auto tp = std::chrono::system_clock::time_point(
std::chrono::duration_cast<std::chrono::system_clock::duration>(dur));
std::time_t in_time_t = std::chrono::system_clock::to_time_t(tp);
strftime(buff, 128, "%Y-%m-%d %H:%M:%S", localtime(&in_time_t));
std::string resDate(buff);
return resDate;
}
This is a case with bsoncxx::types::b_date get_date().to_int64() MongoDB.
The DateTime saved with int64_t.
You have not told us how the existing code converts that number into a DateTime. Let us suppose that it does so by invoking this constructor: DateTime( long long ticks ).
According to the documentation of that constructor of DateTime,
long long ticks A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
On the other hand, according to the documentation of localtime_s and the documentation of time_t, localtime_s() requires
the number of seconds (not counting leap seconds) since 00:00, Jan 1 1970 UTC.
So, you first need to convert 100-nanosecond intervals to seconds, and then convert from January 1, 0001 to January 1, 1970.
Using Howard Hinnant's datetime library this computation can be done quite easily. It works with VS 2013 and later.
#include "tz.h"
#include <cstdint>
#include <string>
#include <iostream>
std::string
FILETIME_to_string(std::uint64_t i)
{
using namespace std;
using namespace std::chrono;
using namespace date;
using FileTime = duration<int64_t, ratio<1, 10000000>>;
auto const offset = sys_days{jan/1/1970} - sys_days{jan/1/1601};
auto tp = sys_days{jan/1/1970} + (FileTime{static_cast<int64_t>(i)} - offset);
return format("%d/%m/%Y %I:%M:%S %p", make_zoned("Etc/GMT-2", tp));
}
int
main()
{
std::cout << FILETIME_to_string(131274907755873979) << '\n';
}
This skips DateTime and goes straight to the string. I wasn't sure what you are wanting with tt in the format. But whatever it is, it can be handled.
This library builds on the C++11 <chrono> library. So the first thing to do is to create a duration to represent the windows tick size (100 ns). Then just compute the offset between the two epochs and subtract it from the input, and form a std::chrono::time_point. Now you can format that time_point however you want.
The program above outputs:
29/12/2016 03:12:55.5873979 PM
If you use VS 2017 you'll be able to make offset constexpr, making the conversion more efficient.

c++ Add or subtract values to the date

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;

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.

Calculating minutes from now until something is due

I have a project that needs me to accept an input for when (during the same day, I assume) an assignment is due. I was trying to follow some code from a similar question but it's giving me an error, which I assume has to do with time_t and int values. Here's my code:
#include <iostream>
#include <ctime>
using namespace std;
int main() {
int hour_input,min_input;
cout << "What hour is your assignment due?\n";
cin >> hour_input;
cout << "What minute is your assignmnet due?\n";
cin >> min_input;
struct tm* tm;
time_t ts = time(NULL);
long int delta;
tm->tm_hour = hour_input;
tm->tm_min = min_input;
delta = mktime(tm) - ts;
delta += 24*60*60;
cout << "There are "<< delta << " minutes until your assignment is due!\n";
return 0;
}
What I'm looking for is some guidance on how to use the functions within the <ctime> header properly, thanks for your help!
double deltaMinutes = difftime(later,earlier) / 60.0;
I'd initialize each time struct to the current date. Note, that 1970 is designated by 70 in member tm::tm_year.
http://www.cplusplus.com/reference/clibrary/ctime/difftime/
The first error is you creating a pointer to struct tm but not pointing it to anything. You don't need a pointer:
struct tm tm;
/* set fields */
time_t timestamp = mktime(&tm);
The second error is that mktime expects a complete data/time in the structure. If the year, month and day all is zero then the returned time will be the first year, month and day. The call may actually fail because date must be between 1 and 31.
This means it's not easy to use struct tm for differences. Instead use difftime as suggested by Sam.
Removing the salient bits from your code (and your declarations are in a very strange order)
struct tm* tm;
tm->tm_hour = hour_input;
tm->tm_min = min_input;
delta = mktime(tm) - ts;
So, tm is uninitialised and pointing to a random location. Moreover, you don't fill the structure in properly, and you have a random (probably invalid) year, month, day and seconds.
mktime will try to make this valid, but if seconds is sufficiently large it'll cause real havoc with your hours and minutes. Assuming the program doesn't crash before it gets that far. You didn't say what error you were getting.