given a date string dd/mm/yyyy hh:ii:ss, we wish to convert it to a unix timestamp (10 digits) through a function.
long foo(int yyyy, int mm = 0, int dd = 0, int hh = 0, int ii = 0, int ss = 0) { }
i couldn't figure out the exact formula that gives accurate results, ones that match unixtimestamp.com.
Unix time is a system for describing instants in time, defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds.
As mentioned in the comments, strptime can do the parsing for you to create a struct tm. Then you can call mktime to get a time_t:
#include <stdio.h>
#include <stdlib.h>
#define _XOPEN_SOURCE
#include <time.h>
int main(void)
{
struct tm mytm;
time_t t;
strptime("31/10/2015 08:33:00","%d/%m/%Y %H:%M:%S",&mytm);
t = mktime(&mytm);
printf("t=%ld\n",t);
return 0;
}
Result:
t=1446294780
You can create a tm struct (from < time.h>/< ctime>) and passing those parameters to it, then you can simple call the mktime() funtion or the timegm() funtion, which depends on you want to take the input as local time or GMT time.
One thing you should carefully remember is that the actual year stored in tm struct is (year-1990), and month should be (month-1). Don't forget to set the tm's tm_isdst flag. You can set is as -1 which can automatically detect whether daylight saving time is applied.
For example, you want to convert GMT 11/02/1990 00:16:50.
tm* t;
t->tm_sec = 50;
t->tm_min = 16;
t->tm_hour = 0;
t->tm_mday = 11;
t->tm_mon = 1; // 2-1, not 2!
t->tm_year = 90; // 1990-1900, not 1990!
t->tm_isdst = -1;
Then you can call timegm(t) which will give you the result 634695410.
on arduino using the time library github.com/PaulStoffregen/Time
#include <Time.h>
struct unix {
long get(int y, int m = 0, int d = 0, int h = 0, int i = 0, int s = 0) {
setTime(h, i, s, d, m, y);
adjustTime(-10800); // +3
return now();
}
} unix;
void setup() {
Serial.begin(19200);
Serial.setTimeout(0);
Serial.println("..");
//Serial.println(unix.get(2015));
//Serial.println(unix.get(2015, 10));
//Serial.println(unix.get(2015, 10, 31));
//Serial.println(unix.get(2015, 10, 31, 18));
//Serial.println(unix.get(2015, 10, 31, 18, 41));
//Serial.println(unix.get(2015, 10, 31, 18, 41, 0));
Serial.println("Enter the date (dd/mm/yyyy hh:ii:ss am/pm). Example: \"31/10/2015 7:27 pm\".");
}
void loop() {
while (Serial.available() > 0) {
int dd = Serial.readStringUntil('/').toInt();
int mm = Serial.readStringUntil('/').toInt();
int yyyy = Serial.readStringUntil(' ').toInt();
int hh = Serial.readStringUntil(':').toInt();
int ii = Serial.readStringUntil(':').toInt();
int ss = Serial.readStringUntil(' ').toInt();
String pm = Serial.readStringUntil('\n');
if (pm == 0) pm = "am";
Serial.print(dd); Serial.print("/");
Serial.print(mm); Serial.print("/");
Serial.print(yyyy); Serial.print(" ");
Serial.print(hh); Serial.print(":");
Serial.print(ii); Serial.print(":");
Serial.print(ss); Serial.print(" ");
Serial.print(pm); Serial.print(" = ");
if (pm == "pm") hh += 12;
Serial.println(unix.get(yyyy, mm, dd, hh, ii, ss));
Serial.println();
Serial.println("Enter the date (dd/mm/yyyy hh:ii:ss am/pm).");
}
delay(50);
}
Related
I need to get input from user in string (for example 20.10.2020) and add 7 days to it. So far, I have something like that but do not know how what to do next.
This is whatI have so far:
#include <stdio.h>
static int days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day, month, year;
unsigned short day_counter;
int is_leap(int y) {
return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0);
}
void next_day()
{
day += 1; day_counter++;
if (day > days_in_month[month]) {
day = 1;
month += 1;
if (month > 12) {
month = 1;
year += 1;
if (is_leap(year)) {
days_in_month[2] = 29;
} else {
days_in_month[2] = 28;
}
}
}
}
void set_date(int d, int m, int y)
{
m < 1 ? m = 1 : 0;
m > 12 ? m = 12 : 0;
d < 1 ? d = 1 : 0;
d > days_in_month[m] ? d = days_in_month[m] : 0;
if (is_leap(y)){
days_in_month[2] = 29;
} else {
days_in_month[2] = 28;
}
day = d;
month = m;
year = y;
}
void skip_days(int x)
{
int i;
for (i=0;i<x;i++) next_day();
}
void print_date()
{
printf (" %d.%d.%d\n", day, month, year);
}
int main(int argc, char **argv)
{
int i;
set_date(5, 2, 1980);
skip_days(7);
day_counter = 0;
print_date();
return 0;
}
You needn't reinvent the wheel. A very good suggestion in the comments is to use the standard functions mktime and strftime, although the given snippet might leave some members of the tm structure uninitialized and distort the result, and although it is for your purpose not necessary to bother with the epoch. When we use
struct tm
{
…
int tm_mday; // day of the month — [1, 31]
int tm_mon; // months since January — [0, 11]
int tm_year; // years since 1900
…
}
we can take advantage of the circumstance that the original values of these components are not restricted to the ranges indicated above. On successful completion, the values are forced to the ranges indicated above. This means mktime takes care of overflows into the next month or year. So, your whole program can be reduced to:
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv)
{
int d, m, y;
// get date input from user on the command line
if (argc < 2 || sscanf(argv[1], "%d.%d.%d", &d, &m, &y) < 3) return 1;
// put that into the time structure, adding 7 days
struct tm date = { .tm_mday = d+7, .tm_mon = m-1, .tm_year = y-1900 };
// force to the ranges
mktime(&date);
char s[11];
strftime(s, sizeof s, "%d.%m.%Y", &date);
puts(s);
}
for the past few hours I've been trying to figure out how to write a programme in C to calculate the end date based on the start date and number of days. (I haven't found the forum for this exact problem, yet).
So let's say you input 27/1/2021 as the starting date and then 380 days. The programme should now calculate and show you the end date 11/2/2022.
I don't know how to move forward, the help would be appreciated.
#include <stdio.h>
#include <stdlib.h>
int day, month, year, numberDays;
int leapYear(int year) {
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
}
int monthYear[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main(void) {
printf("Enter starting date: ");
scanf("%d %d %d", &day, &month, &year);
printf("Enter number of days: ");
scanf("%d", &numberDays);
leapYear(year);
int wholeYears, rest;
if (leapYear(year)) {
wholeYears = numberDays / 366;
rest = numberDays % 366;
}
else {
wholeYears = numberDays / 365;
rest = numberDays % 365;
}
int resultYears = year + wholeYears;
int midDays = day + rest;
int resultMonths;
return 0;
}
I can't move any further. I'd need help.
So let's say you input 27/1/2021 as the starting date and then 380 days. The programme should now calculate and show you the end date 11/2/2022.
An easy approach is to use mktime() to bring a date into its standard range.
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm start = {.tm_year = 2021 - 1900, .tm_mon = 1 - 1, .tm_mday = 27,
.tm_hour = 12}; // Midday to avoid DST issues.
start.tm_mday += 380;
printf("%d/%d/%d\n", start.tm_mday, start.tm_mon + 1, start.tm_year + 1900);
time_t t = mktime(&start);
printf("%d/%d/%d %s\n", start.tm_mday, start.tm_mon + 1, start.tm_year + 1900,
t == -1 ? "failed" : "OK");
}
Output
407/1/2021
11/2/2022 OK
Otherwise with discrete code, one is effectively re-writing the year/month/day portion of mktime().
I don't know how to move forward, the help would be appreciated.
Add the 380 to d of y, m, d. Maybe add some value to m in other cases. Then perform basic range reductions. There are 12 months to every year. Month range [1...12]. There are 365*400 + 97 days every 400 years. Day of the month is at least 1.
Last step is to handle a day outside the month range.
A simply, though inefficient, approach: test if the data is more than the days in that month. If so subtract the days in that month and advance to the next month.
I left out some code details as OP's goal is some ideas to move forward.
#define DAYSPER400YEARS (365 * 400 + 97)
#define JANUARY 1
#define FEBRUARY 2
#define DECEMBER 12
#define MONTHPERYEAR 12
static int is_leap_year(long long year) {
// If not divisible by 4 ...
// If not divisible by 100 ...
// If not divisible by 400 ...
}
static int days_per_month(long long year, int month) {
static const signed char dpm[] = { 0, //
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month != FEBRUARY) {
return ....;
}
return .... + is_leap_year(year);
}
/*
* Bring a date into it primary range.
* https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
* The year, month, day may be any value INT_MIN ... INT_MAX.
* Return error flag.
*/
bool ymd_to_primary_range(int *year, int *month, int *day) {
long long y = *year;
y += *month / 12;
*month %= 12; // month now in -11 to 11 range
while (*month < JANUARY) {
*month += MONTHPERYEAR;
year--;
}
y += (*day / DAYSPER400YEARS) * 400;
*day %= DAYSPER400YEARS;
while (*day < 1) {
*day += DAYSPER400YEARS;
y -= 400;
}
int dpm;
while (*day > (dpm = days_per_month(y, *month))) {
*day -= dpm;
(*month)++;
if (*month > ...) {
*month -= ....;
y++;
}
}
if (y < INT_MIN) {
*year = INT_MIN;
return true;
}
if (y > INT_MAX) {
*year = INT_MAX;
return true;
}
*year = (int) y;
return false;
}
Smaple
int main(void) {
int y = 2021;
int m = 1;
int d = 27;
d += 380;
printf("Error: %d\n", ymd_to_primary_range(&y, &m, &d));
printf("Date (dmy): %2d/%02d/%04d\n", d, m, y);
}
Output
Error: 0
Date (dmy): 11/02/2022
I am trying to calculate number of days elapsed from a given GMT time.
Well, I am able to make it work with iterative approach of calculation (finding number of normal years
and leap years)
The function get_number_of_leap_years_from_base_year is iterating over all the years from 1970 till the given date and checking every year whether its a leap or not and finally add all days.
Is there any other way (formula) based to calculating number normal & leap years elapsed.
/* so-prg-2: Calculating number normal & leap years passed */
#include <stdio.h>
#include <string.h>
#include <time.h>
#define BASE_YEAR 1970
void print_time_readable_format(struct tm tm);
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt);
int get_number_of_leap_years_from_base_year(int start_year, int end_year);
int calculate_days_elapsed_from_epoch(struct tm tm);
int main()
{
int days = 0;
char gmt_time_fmt[] = "Dec 28 18:40:01 2020 GMT";
//char gmt_time_fmt[] = "Jan 20 19:00:01 2019 GMT";
//char gmt_time_fmt[] = "Dec 27 14:52:30 2020 GMT";
//char gmt_time_fmt[] = "Jan 01 00:00:01 1970 GMT";
days = convert_gmt_date_time_to_tm_format(gmt_time_fmt);
printf("GMT = %s and days are %d\n", gmt_time_fmt, days);
return 0;
}
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt)
{
struct tm tm;
char tm_time_fmt[255];
//set tm struture to 0
memset(&tm, 0, sizeof(struct tm));
// convert gmt_time_fmt to format required by 'tm' structure
strptime(gmt_time_fmt, "%B %d %H:%M:%S %Y GMT", &tm);
strftime(tm_time_fmt, sizeof(tm_time_fmt), "%s", &tm);
printf("tm_time_fmt = %s\n", tm_time_fmt);
print_time_readable_format(tm);
return calculate_days_elapsed_from_epoch(tm);
}
int calculate_days_elapsed_from_epoch(struct tm tm)
{
int days_by_month [2][12] = {
/* normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
/* leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
};
int current_year = tm.tm_year+1900;
int total_years_passed = current_year - BASE_YEAR;
/* -1, to skip the current year */
int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year-1);
int normal_years = total_years_passed - nleap_years_passed;
int total_days_passed = (normal_years*365 + nleap_years_passed*366 );
printf(" **Years total_days_passed =%d\n", total_days_passed);
total_days_passed += days_by_month[(current_year%4 == 0) - (current_year%100 == 0) + (current_year%400 == 0)][tm.tm_mon];
total_days_passed += tm.tm_mday - 1; /* to skip the current day */
printf(" **total_days_passed =%d\n", total_days_passed);
return total_days_passed;
}
int get_number_of_leap_years_from_base_year(int start_year, int end_year)
{
int leap_year_count = 0;
int year = start_year;
while( year <= end_year)
{
if( (year%4 == 0) - (year%100 == 0) + (year%400 == 0) )
leap_year_count++;
year++;
}
printf("leap_year_count = %d\n", leap_year_count);
return leap_year_count;
}
void print_time_readable_format(struct tm tm)
{
printf("tm.tm_year = %d ", tm.tm_year);
printf("tm.tm_mon = %d ", tm.tm_mon);
printf("tm.tm_mday = %d ",tm.tm_mday);
printf("tm.tm_hour = %d ", tm.tm_hour);
printf("tm.tm_min = %d ", tm.tm_min );
printf("tm.tm_sec = %d\n", tm.tm_sec );
}
Use mktime()
Since your code is allowed to use both Standard C strftime() and POSIX strptime(), there's no reason not to use Standard C mktime() either.
It gives you a time_t value which is the number of seconds since The Epoch.
int calculate_days_elapsed_from_epoch(struct tm tm)
{
time_t t = mktime(&tm);
return t / 86400; // 24 * 60 * 60 = 86400
}
But if the goal is to calculate the seconds since The Epoch, you have the answer immediately from mktime().
Note that mktime() is passed a struct tm pointer, and it accepts values that are 'out of range' and normalizes the result. See also the example code in the section 'Demonstrating mktime()'.
Calculating leap days
I have a function jl_dmy_conversion() lurking in my library which converts a combination of year, month, day to a number of days since 1899-12-31 (so in this system, day 1 was 1900-01-01). But it includes a calculation for number of leap days. This code is internal to a package where the parameters are already validated as valid within the date range 0001-01-01 .. 9999-12-31, so it does not do much to protect itself from invalid data. There is another function that invokes this that does the data validation. Some of the information shown here comes from a header, most from the source file containing the implementation.
typedef int Date;
enum { DATE_NULL = -2147483648 }; /* Informix NULL DATE */
#define LEAPYEAR(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
#define PRId_Date "d"
/*
** In 400 years, there are 97 leap years (because the three years
** divisible by 100 but not by 400 are not leap years). This also
** happens to be exactly 20871 weeks.
*/
#define DAYS_IN_400_YEARS (400*365+97)
#define DAYS_IN_2000_YEARS (5*DAYS_IN_400_YEARS)
enum
{
DAYS_IN_JANUARY = 31,
DAYS_IN_FEBRUARY = 28,
DAYS_IN_MARCH = 31,
DAYS_IN_APRIL = 30,
DAYS_IN_MAY = 31,
DAYS_IN_JUNE = 30,
DAYS_IN_JULY = 31,
DAYS_IN_AUGUST = 31,
DAYS_IN_SEPTEMBER = 30,
DAYS_IN_OCTOBER = 31,
DAYS_IN_NOVEMBER = 30,
DAYS_IN_DECEMBER = 31
};
static const int days_in_month[][2] =
{
{ 0, 0 },
{ DAYS_IN_JANUARY, DAYS_IN_JANUARY },
{ DAYS_IN_FEBRUARY, DAYS_IN_FEBRUARY+1 },
{ DAYS_IN_MARCH, DAYS_IN_MARCH },
{ DAYS_IN_APRIL, DAYS_IN_APRIL },
{ DAYS_IN_MAY, DAYS_IN_MAY },
{ DAYS_IN_JUNE, DAYS_IN_JUNE },
{ DAYS_IN_JULY, DAYS_IN_JULY },
{ DAYS_IN_AUGUST, DAYS_IN_AUGUST },
{ DAYS_IN_SEPTEMBER, DAYS_IN_SEPTEMBER },
{ DAYS_IN_OCTOBER, DAYS_IN_OCTOBER },
{ DAYS_IN_NOVEMBER, DAYS_IN_NOVEMBER },
{ DAYS_IN_DECEMBER, DAYS_IN_DECEMBER }
};
/* Return date as number of days since 31st December 1899 - no range check */
static Date jl_dmy_conversion(int d, int m, int y)
{
int leap;
int i;
Date daynum;
/* No need to assert here - calling functions have checked basics */
DB_TRACE(1, "[[-- jl_dmy_conversion (d = %2d, m = %2d, y = %4d) ", d, m, y);
leap = LEAPYEAR(y);
if (d > days_in_month[m][leap])
{
DB_TRACE(1, "<<-- NULL (invalid day of month)\n");
return(DATE_NULL);
}
/* Number of days so far this month */
daynum = d;
/* Days so far this year prior to this month */
for (i = 1; i < m; i++)
daynum += days_in_month[i][leap];
DB_TRACE(4, "YDAY = %3ld ", daynum);
/*
** Now compute number of days to 1st of January of this year. Add
** 2000 years (5 periods of 400 years) to ensure that numbers
** resulting from subtraction are positive, even when dates back to
** 0001-01-01 are allowed, and then remove the number of days found
** in 2000 years. This assumes int is 32-bit or longer.
**
** NB: Things begin to go haywire when (y - 1901) yields -4, which
** is when y == 1897. Things get worse before 1601. The result is
** usually, but not always, off by one. Adding 2000 years and then
** subtracting the appropriate number of days sidesteps the
** problems.
*/
y += 2000;
daynum += 365 * (y - 1900); /* Ignoring leap years */
DB_TRACE(4, "INC1 = %7d ", 365 * (y - 1900));
daynum += (y - 1901) / 4; /* Allowing for leap years */
DB_TRACE(4, "INC2 = %4d ", (y - 1901) / 4);
daynum -= (y - 1901) / 100; /* Allowing for non-leap years */
DB_TRACE(4, "INC3 = %3d ", -(y - 1901) / 100);
daynum += (y - 1601) / 400; /* Allowing for leap years */
DB_TRACE(4, "INC4 = %2d ", (y - 1601) / 400);
daynum -= DAYS_IN_2000_YEARS;
DB_TRACE(1, " (r = %7" PRId_Date ") --]]\n", daynum);
return(daynum);
}
The DB_TRACE macro is derived from the code shown in #define a macro for debug printing in C?. The DB_TRACE macro is available in my SOQ (Stack Overflow Questions) repository on GitHub as files debug.c and debug.h in the src/libsoq sub-directory.
The formatting gives a single line showing the calculation steps.
The code above compiles with the debug.h header and <stdio.h> included, and a minimal main(), plus linking with the code from debug.c:
int main(void)
{
int dd = 28;
int mm = 12;
int yyyy = 2020;
Date dt = jl_dmy_conversion(dd, mm, yyyy);
printf("%.4d-%.2d-%.2d = %d\n", yyyy, mm, dd, dt);
return 0;
}
Demonstrating mktime()
As mentioned above, mktime() is passed a struct tm pointer, and it is documented that it accepts values that are 'out of range' and normalizes the result — modifying the structure it is passed. It also sets the tm_wday and tm_yday fields — it ignores them as inputs.
If you have a struct tm value for 2020-12-28 08:20:26 and you want to know the time_t value for 6 days, 18 hours, 43 minutes, 32 seconds later, you can use code like this:
#include <stdio.h>
#include <time.h>
static void print_time(time_t t, const struct tm *tm)
{
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %A", tm);
printf("%lld: %s (%d)\n", (long long)t, buffer, tm->tm_yday);
}
int main(void)
{
struct tm tm = { .tm_year = 2020 - 1900, .tm_mon = 12 - 1, .tm_mday = 28,
.tm_hour = 8, .tm_min = 20, .tm_sec = 26 };
time_t t0 = mktime(&tm);
print_time(t0, &tm);
tm.tm_mday += 6;
tm.tm_hour += 18;
tm.tm_min += 43;
tm.tm_sec += 32;
time_t t1 = mktime(&tm);
print_time(t1, &tm);
return 0;
}
When run (in US/Mountain standard time zone — UTC-7), it produces:
1609168826: 2020-12-28 08:20:26 Monday (362)
1609754638: 2021-01-04 03:03:58 Monday (3)
The conditions for leap year are summarized as:
leap year if perfectly visible by 400
not a leap year if visible by 100 but not divisible by 400
leap year if not divisible by 100 but divisible by 4
all other years are not leap year
So the logic could be expressed as:
if (((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
{
leap_year_count++;
}
year++;
But not sure if re-factoring your existing logic will add any speed advantage.
I am asking what exactly what title says. Obviously something goes wrong in my following code:
#include <stdio.h>
#include <time.h>
int main(void){
struct tm t1 , t2;
t1.tm_sec = 0;
t1.tm_min = 0;
t1.tm_hour = 0;
t1.tm_isdst = -1;
t2 = t1;
printf("Enter 2 dates:\n");
scanf("%d/%d/%d %d/%d/%d" , &t1.tm_mday , &t1.tm_mon , &t1.tm_year ,// Sorry for
&t2.tm_mday , &t2.tm_mon , &t2.tm_year);// lazy coding here
time_t tm1 , tm2;
tm1 = mktime(&t1);
tm2 = mktime(&t2);
printf("%d" , (int) difftime(tm1 , tm2));
}
The output is always zero. The output when I try to run it like this is:
C:\Users\...>program
Enter 2 dates:
3/2/2016 12/2/2017
0
I suspect that your problems are a consequence of two factors:
You don't subtract 1900 from the year values entered by the user, nor do you subtract 1 from the month number — see C11 §7.27.1 components of time for information about the slightly weird encoding for the tm_year and tm_mon elements of struct tm.
Your system uses 32-bit time_t, rather than 64-bit time_t.
Here's a variant of your code, running on a 64-bit system (a MacBook Pro running macOS Mojave 10.14.6, using GCC 9.3.0).
#include <stdio.h>
#include <time.h>
static void dump_time(const char *tag, time_t t);
static void dump_struct_tm(const char *tag, const struct tm *tm);
int main(void)
{
struct tm t1 = { 0 }; /* Probably not essential, but a good idea */
printf("sizeof(time_t) = %d\n", (int)sizeof(time_t));
t1.tm_sec = 0;
t1.tm_min = 0;
t1.tm_hour = 0;
t1.tm_isdst = -1;
struct tm t2 = t1;
printf("Enter 2 dates:\n");
int n = scanf("%d/%d/%d %d/%d/%d", &t1.tm_mday, &t1.tm_mon, &t1.tm_year,
&t2.tm_mday, &t2.tm_mon, &t2.tm_year);
if (n != 6)
{
fprintf(stderr, "failed to read two dates (n = %d)\n", n);
return 1;
}
struct tm t3 = t1;
struct tm t4 = t2;
time_t tm1, tm2;
tm1 = mktime(&t1);
tm2 = mktime(&t2);
dump_struct_tm("t1", &t1);
dump_struct_tm("t2", &t2);
dump_time("time-1", tm1);
dump_time("time-2", tm2);
printf("%d\n", (int) difftime(tm1, tm2));
t3.tm_year -= 1900;
t3.tm_mon -= 1;
t4.tm_year -= 1900;
t4.tm_mon -= 1;
time_t tm3 = mktime(&t3);
time_t tm4 = mktime(&t4);
dump_struct_tm("t3", &t3);
dump_struct_tm("t4", &t4);
dump_time("time-3", tm3);
dump_time("time-4", tm4);
printf("%d\n", (int) difftime(tm3, tm4));
return 0;
}
static void dump_time(const char *tag, time_t t)
{
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&t));
printf("%s = %lld (%s)\n", tag, (long long)t, buffer);
}
static void dump_struct_tm(const char *tag, const struct tm *tm)
{
printf("%s: year = %5d, month = %2d, day = %2d, "
"hour = %2d, minute = %2d, second = %2d, DST = %d\n",
tag, tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_isdst);
}
The two dumping functions are illustrative of a handy technique I use when debugging — a function that dumps a structure, along with a tag that identifies what the code is dumping this time.
When run (as date37, compiled from date37.c), I get:
$ date37
sizeof(time_t) = 8
Enter 2 dates:
3/2/2016 12/2/2017
t1: year = 2016, month = 2, day = 3, hour = 0, minute = 0, second = 0, DST = 0
t2: year = 2017, month = 2, day = 12, hour = 0, minute = 0, second = 0, DST = 1
time-1 = 61415132400 (3916-03-03 00:00:00)
time-2 = 61447442400 (3917-03-12 00:00:00)
-32310000
t3: year = 116, month = 1, day = 3, hour = 0, minute = 0, second = 0, DST = 0
t4: year = 117, month = 1, day = 12, hour = 0, minute = 0, second = 0, DST = 0
time-3 = 1454482800 (2016-02-03 00:00:00)
time-4 = 1486882800 (2017-02-12 00:00:00)
-32400000
$
The difference between the two values that result from difftime() is 90000 seconds, the number of seconds in a day (86400) plus the number of seconds in an hour (3600). The "1 day" component is because 2016 was a leap year and the difference spans 2016-02-29, but even though 3916 is a leap year, the difference is between two dates in March in consecutive years, which doesn't span 3916-02-29. The "1 hour" component is because the time zone I'm in, US/Mountain or America/Denver (UTC-07:00 in standard time, UTC-06:00 in daylight saving time), switches between winter time (standard time) and summer time (daylight saving time) on Sunday 3917-03-11 at 02:00. Note that strftime() assumes local time; using gmtime() instead of localtime() changes the apparent time values.
Try this code on your machine. I expect that you're running into overflows because you have 32-bit time_t and the dates in the fourth millennium have overflowed. I would not be surprised to find that mktime() returns -1 for both dates in the first (uncorrected) fragment because the values cannot be represented accurately (and the difference between -1 and -1 is indeed 0).
If you have 32-bit time_t values, you run into trouble after a time_t value reaches:
0x7FFFFFFF = 2147483647 = 2038-01-19 03:14:07Z (aka UTC)
A machine with 64-bit time_t does not run into that problem.
I need a function that can calculate the difference between two datetime (year, month, day, hours, minute, seconds). and then return the difference in the same format.
int main (){
struct datetime dt_from;
init_datetime(&dt_from, 1995, 9, 15, 10, 40, 15);
struct datetime dt_to;
init_datetime(&dt_to, 2004, 6, 15, 10, 40, 20);
struct datetime dt_res;
datetime_diff(&dt_from, &dt_to, &dt_res);
return 0;
}
void datetime_diff(struct datetime *dt_from, struct datetime *dt_to
, struct datetime *dt_res) {
//What can I do here to calculate the difference, and get it in the dt_res?
}
Please have a look and try this example which uses time.h and should be portable. It calculates the difference in days between the dates in your question. You can change the program a little so that it works the way you want.
#include <stdio.h>
#include <time.h>
#include <math.h>
int main() {
time_t start_daylight, start_standard, end_daylight, end_standard;;
struct tm start_date = {0};
struct tm end_date = {0};
double diff;
printf("Start date: ");
scanf("%d %d %d", &start_date.tm_mday, &start_date.tm_mon, &start_date.tm_year);
printf("End date: ");
scanf("%d %d %d", &end_date.tm_mday, &end_date.tm_mon, &end_date.tm_year);
/* first with standard time */
start_date.tm_isdst = 0;
end_date.tm_isdst = 0;
start_standard = mktime(&start_date);
end_standard = mktime(&end_date);
diff = difftime(end_standard, start_standard);
printf("%.0f days difference\n", round(diff / (60.0 * 60 * 24)));
/* now with daylight time */
start_date.tm_isdst = 1;
end_date.tm_isdst = 1;
start_daylight = mktime(&start_date);
end_daylight = mktime(&end_date);
diff = difftime(end_daylight, start_daylight);
printf("%.0f days difference\n", round(diff / (60.0 * 60 * 24)));
return 0;
}
Test
Start date: 15 9 1995
End date: 15 6 2004
3195 days difference
Or even simpler for non-interactive code and with standard or daylight savings time:
#include <stdio.h>
#include <time.h>
#include <math.h>
int main()
{
time_t start_daylight, start_standard, end_daylight, end_standard;;
struct tm start_date = {0};
struct tm end_date = {0};
double diff;
start_date.tm_year = 1995;
start_date.tm_mon = 9;
start_date.tm_mday = 15;
start_date.tm_hour = 10;
start_date.tm_min = 40;
start_date.tm_sec = 15;
end_date.tm_mday = 15;
end_date.tm_mon = 6;
end_date.tm_year = 2004;
end_date.tm_hour = 10;
end_date.tm_min = 40;
end_date.tm_sec = 20;
/* first with standard time */
start_date.tm_isdst = 0;
end_date.tm_isdst = 0;
start_standard = mktime(&start_date);
end_standard = mktime(&end_date);
diff = difftime(end_standard, start_standard);
printf("%.0f days difference\n", round(diff / (60.0 * 60 * 24)));
/* now with daylight time */
start_date.tm_isdst = 1;
end_date.tm_isdst = 1;
start_daylight = mktime(&start_date);
end_daylight = mktime(&end_date);
diff = difftime(end_daylight, start_daylight);
printf("%.0f days difference\n", round(diff / (60.0 * 60 * 24)));
return 0;
}
Here is the basic idea:
Convert your datetime into an integral type (preferable long long or unsigned long long) which represents your datetime value as it's smallest unit (second in your case). How to achieve that? Easy transform the single values into seconds and add everything together. (seconds + minutes * 60 + hours * 3600 ...)
Do this for both values and then subtract the integer values.
Now convert the single integer value, the time difference, back to a datetime. How? Start with the biggest unit (years) and divide the difference by the amount of seconds within one year (60 * 60 * 24 * 365). Now you know how many years are in between your two datetimes. Take the rest and divide it by the amount of seconds per month, and so on...
(Obviously I ignored everything rather complicated, like daylight saving time for example)
However I would highly recommend using struct tm from time.h as mentioned in the comments. It is portable and you can use difftime.