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.
Related
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.
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;
I have a time string like this "132233" (Time only no date) and i want to convert it into local time.
So, in order to use the function localtime(), I first converted my string into time_t using mktime() (thanks to How to convert a string variable containing time to time_t type in c++? )and then printed the time after conversion using strftime as shown in (http://www.cplusplus.com/reference/ctime/strftime/)
I am getting a serious run time error. Can any one please tell me whats wrong. Thanks in advance
int main()
{
string time_sample="132233";
std::string s_hrs (time_sample.begin(), time_sample.begin()+2);
std::string s_mins (time_sample.begin()+2,time_sample.begin()+4);
std::string s_secs (time_sample.begin()+4,time_sample.begin()+6);
int hrs = atoi(s_hrs.c_str());
int mins = atoi(s_mins.c_str());
int secs = atoi(s_secs.c_str());
struct tm time_sample_struct = {0};
time_sample_struct.tm_hour = hrs;
time_sample_struct.tm_min = mins;
time_sample_struct.tm_sec = secs;
time_t converted_time;
converted_time = mktime(&time_sample_struct);
struct tm * timeinfo;
char buffer[80];
timeinfo = localtime(&converted_time);
strftime(buffer,80,"%I:%M:%S",timeinfo);
puts(buffer);
cout<<endl;
getch();
return 0;
}
Your problem is that if time_t is a 32 bit value, the earliest possible date it's capable of encoding (given a 1970-1-1 epoch) is 1901-12-13.
However you're not setting the date fields of your tm struct, which means it is defaulting to 0-0-0 which represents 1900-1-0 (since tm_day is 1-based, you actually end up with an invalid day-of-month).
Since this isn't representable by a 32-bit time_t the mktime function is failing and returning -1, a situation you're not checking for.
Simplest fix is to initialise the date fields of the tm struct to something a time_t can represent:
time_sample_struct.tm_year = 114;
time_sample_struct.tm_mday = 1;
I am executing below code.
int main()
{
struct tm storage={0,0,0,0,0,0,0,0,0};
char *p = NULL;
p = (char *)strptime("2012-08-25 12:23:12","%Y-%m-%d %H:%M:%S",&storage);
char buff[1024]={0};
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
storage.tm_sec += 20;
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
mktime(&storage);
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
return 0;
}
If above Program executed, It prints ' 2012-08-25 13:23:32' instead of '2012-08-25 12:23:32'. Please Help, why it is increasing tm_hour value.
This works correctly if I put input date as '2012-02-25 12:23:32' in program, which is confusing.
OUtput ->
[user#rtpkvm55-vm2 root]$ ./a.out
2012-08-25 12:23:12
2012-08-25 12:23:32
2012-08-25 13:23:32
[user#rtpkvm55-vm2 root]$
Date Info on my system, -->
[user#rtpkvm55-vm2 root]$ date
Sat Aug 25 08:28:26 EDT 2012
What happens
The date you specified has daylight savings in effect but when calling mktime, storage.tm_isdst is zero. mktime sees this and thinks "hey, they gave me a date with an incorrect daylight savings flag, lets fix it". Then it sets tm_isdst to 1 and changes tm_hour.
See also this answer.
To fix it
use timegm instead of mktime
set the timezone to UTC before calling mktime (see also example from timegm) :
setenv("TZ", "", 1);
tzset();
mktime();
use a good date-time library (like boost::locale::date_time/boost::date_time, but read the Q&A section on the boost::locale::date_time page before picking one)
Wow, there just is no way around it. It must be a bug in your system's implementation of mktime(3). mktime(3) should not alter the struct tm * passed to it.
I would suggest checking the value of storage.tm_isdst. Try setting it to 0 to ensure it's not confused about DST. If that doesn't work, try setting it to -1 to let it auto determine the proper value.
mktime - convert broken-down time into time since the Epoch
A positive or 0 value for tm_isdst causes mktime() to presume initially that Daylight Savings Time, respectively, is or is not in effect for the specified time. A negative value for tm_isdst causes mktime() to attempt to determine whether Daylight Saving Time is in effect for the specified time.
I was wrong about mktime(3) not modifying struct tm *. It is the correct behavior to normalize the value.
You have to set tm_isdst in the tm struct otherwise it is uninitialised, and thus gets set to a random garbage value. Then, when you call mktime depending on which random garbage is in tm_isdst variable, it either applies daylight saving time or it doesn't, seemingly unpredictably.
However, if you set it to -1, you tell mktime that you don't know whether daylight saving time is in effect, so the first call to mktime will fix it.
Therefore, the simplest way to fix this issue is adding:
storage.tm_isdst = -1;
before calling mktime.
Here is a trick to fix your code:
int main()
{
// Need to know if daylight saving is on or off, use the following trick
// Get the current time in seconds since epoch, convert it to local time,
// tm_isdst(is daylight saving) value, in the tm variable returned by the localtime(), will be set accordingly
time_t now = time(0);
struct tm *tm2= localtime(&now);
struct tm storage={0,0,0,0,0,0,0,0,tm2->tm_isdst}; // Note: used the is daylight saving on flag fetched above
char *p = NULL;
p = (char *)strptime("2012-08-25 12:23:12","%Y-%m-%d %H:%M:%S",&storage);
char buff[1024]={0};
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
storage.tm_sec += 20;
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
mktime(&storage);
strftime(buff,1024,"%Y-%m-%d %H:%M:%S",&storage);
cout << buff << endl;
return 0;
}
What I want to do is convert an epoch time (seconds since midnight 1/1/1970) to "real" time (m/d/y h:m:s)
So far, I have the following algorithm, which to me feels a bit ugly:
void DateTime::splitTicks(time_t time) {
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time % 24;
time /= 24;
year = DateTime::reduceDaysToYear(time);
month = DateTime::reduceDaysToMonths(time,year);
day = int(time);
}
int DateTime::reduceDaysToYear(time_t &days) {
int year;
for (year=1970;days>daysInYear(year);year++) {
days -= daysInYear(year);
}
return year;
}
int DateTime::reduceDaysToMonths(time_t &days,int year) {
int month;
for (month=0;days>daysInMonth(month,year);month++)
days -= daysInMonth(month,year);
return month;
}
you can assume that the members seconds, minutes, hours, month, day, and year all exist.
Using the for loops to modify the original time feels a little off, and I was wondering if there is a "better" solution to this.
Be careful about leap years in your daysInMonth function.
If you want very high performance, you can precompute the pair to get to month+year in one step, and then calculate the day/hour/min/sec.
A good solution is the one in the gmtime source code:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: gmtime.c,v 1.4 91/04/22 13:20:27 ceriel Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
The standard library provides functions for doing this. gmtime() or localtime() will convert a time_t (seconds since the epoch, i.e.- Jan 1 1970 00:00:00) into a struct tm. strftime() can then be used to convert a struct tm into a string (char*) based on the format you specify.
see: http://www.cplusplus.com/reference/clibrary/ctime/
Date/time calculations can get tricky. You are much better off using an existing solution rather than trying to roll your own, unless you have a really good reason.
An easy way (though different than the format you wanted):
std::time_t result = std::time(nullptr);
std::cout << std::asctime(std::localtime(&result));
Output:
Wed Sep 21 10:27:52 2011
Notice that the returned result will be automatically concatenated with "\n".. you can remove it using:
std::string::size_type i = res.find("\n");
if (i != std::string::npos)
res.erase(i, res.length());
Taken from: http://en.cppreference.com/w/cpp/chrono/c/time
time_t t = unixTime;
cout << ctime(&t) << endl;
This code might help you.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
To convert a epoch string to UTC
string epoch_to_utc(string epoch) {
long temp = stol(epoch);
const time_t old = (time_t)temp;
struct tm *oldt = gmtime(&old);
return asctime(oldt);
}
and then it can be called as
string temp = "245446047";
cout << epoch_to_utc(temp);
outputs:
Tue Oct 11 19:27:27 1977
If your original time type is time_t, you have to use functions from time.h i.e. gmtime etc. to get portable code. The C/C++ standards do not specify internal format (or even exact type) for the time_t, so you cannot directly convert or manipulate time_t values.
All that is known is that time_t is "arithmetic type", but results of arithmetic operations are not specified - you cannot even add/subtract reliably. In practice, many systems use integer type for time_t with internal format of seconds since epoch, but this is not enforced by standards.
In short, use gmtime (and time.h functionality in general).