DateTime calculations

I need to declare a DateTime variable, give it a value (Year, Month, Day, hour and minute),
and then compare (subtract) it to the current time that I get from my RTC module.

Can someone please help me with this? My C++ skills are very low.

I can get date and time from the RTC, but the rest is greek to me.

Jozi68:
I need to declare a DateTime variable, give it a value (Year, Month, Day, hour and minute),
and then compare (subtract) it to the current time that I get from my RTC module.

Can someone please help me with this? My C++ skills are very low.

I can get date and time from the RTC, but the rest is greek to me.

Would it help to have a function that calculates the date difference in days?

Here is some code (no extra time library needed):

int dateDiff(int year1, int mon1, int day1, int year2, int mon2, int day2)
{
    int ref,dd1,dd2,i;
    ref = year1;
    if(year2<year1)
    ref = year2;
    dd1=0;
    dd1=dater(mon1);
    for(i=ref;i<year1;i++)
    {
        if(i%4==0)
        dd1+=1;
    }
    dd1=dd1+day1+(year1-ref)*365;
    dd2=0;
    for(i=ref;i<year2;i++)
    {
        if(i%4==0)
        dd2+=1;
    }
    dd2=dater(mon2)+dd2+day2+((year2-ref)*365);
    return dd2-dd1;
}

int dater(int x)
{ const int dr[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  return dr[x-1];
}

void setup()
{
  Serial.begin(9600);
  int x=dateDiff(2015,12,24, 2017,1,1 );
  Serial.println(x);
}

void loop()
{
}

Can you then calculate the minute difference from hours/minutes by yourself?

@jurs:

What about leap years?

Try these pairs of dates:

2015,12,31, 2016,1,1  // should be 1 day

2016,2,28, 2016,3,1   // should be 2 days

2016,12,31, 2017,1,1  // should be 1 day

2017,2,28, 2017,3,1   // should be 1 day

What I am giving you is (or should be) a standard test for leap year bugs.

Thank you very much for all the replies.
I am not concerned about leap years. In fact, the difference between current time and my time will always be a few hours at most.

Thank you for the code Jurs. I think it would have worked if I worked with days instead of hours. I don't know how to change it to work on hours and minutes.

I've declared a variable like this:
DateTime MyTime;
I suspect this is correct.

Now I need to put a date and time (eg. 2015-12-27 15:27) into this variable. How do I do that?

The next step would be to get the difference between current time and MyTime.

Here is a simple example using the Time library :slight_smile:

#include <Time.h>

void setup()
{
  Serial.begin(9600);

  tmElements_t T1;
  tmElements_t T2;

  T1.Hour = 22; 
  T1.Minute = 49; 
  T1.Second = 59; 
  T1.Day = 24;
  T1.Month = 12; 
  T1.Year = 2015 - 1970; // because Year is offset from 1970

  T2.Hour = 0; 
  T2.Minute = 0; 
  T2.Second = 0; 
  T2.Day = 25;
  T2.Month = 12; 
  T2.Year = 2015 - 1970; // because Year is offset from 1970

  // convert T1 and T2 to seconds since 1/1/1970
  time_t T1sec = makeTime( T1 );
  time_t T2sec = makeTime( T2 );
  Serial.print( "T1 in seconds since 1970: " );
  Serial.println( T1sec );
  Serial.print( "T2 in seconds since 1970: " );
  Serial.println( T2sec );

  // differences in seconds
  int32_t diff = T2sec - T1sec;
  Serial.print( "Difference between T1 and T2 in seconds: " );
  Serial.println( diff );

  // show difference in hours, minutes and seconds
  uint32_t hours = diff / 3600;
  uint8_t minutes = (diff / 60) % 60;
  uint8_t seconds = diff % 60;
  Serial.print( "T1 was " );
  Serial.print( hours );
  Serial.print( " hour(s), " );
  Serial.print( minutes );
  Serial.print( " minute(s) and " );
  Serial.print( seconds );
  Serial.println( " second(s) before T2." );
}

void loop()
{
}
T1 in seconds since 1970: 1450997399
T2 in seconds since 1970: 1451001600
Difference in seconds: 4201
T1 was 1 hour(s), 10 minute(s) and 1 second(s) before T2.

Off: someone/something is spamming Karma! I had 108 yesterday :stuck_out_tongue:

Delta_G:
I think that code in reply #2 erroneously assumes that every fourth year is a leap year. I guess it will work as long as all the dates are recent.

Yes, you are right, the code is wrong if used for dates after year 2099!

The code in reply #2 is made for practical use and has some limitations. It assumes that every fourth year is a leap year, which will be absolutely no problem until the year reaches 2100, so the code has a year-2100 problem (but that could be fixed easily).

So the problem in that code perfectly fits the typical RTC modules, which use 2-digits years only and also assume that every fourth year is a leap year.

But fortunately the code is not using 32-bit Unix timestamps, which have a year-2038 problem.

Signed 32-bit integers will overflow after 03:14:07 UTC on 19 January 2038, and the code I posted above has no problem when this overflow occurs.

What about the all the suggested libraries using 32-bit unix timestamps?

Jozi68:
I am not concerned about leap years.

If you are going to work with dates (not just times), you should be concerned about leap years. What will happen when you run your program on February 29?

In fact, the difference between current time and my time will always be a few hours at most.

So why are you worried about dates??

As for how to work with hours and minutes: you could use something like this

int minutesSinceMidnightA = (hoursA*60) + minutesA;

int minutesSinceMidnightB = (hoursB*60) + minutesB;

int intervalMinutes = minutesSinceMidnightB - minutesSinceMidnightA;

but beware that your answer might be too high or too low by 1440. For example, if you use 23:59 for A and 00:00 for B, the interval will come out as -1439 (note the minus sign) minutes rather than 1 minute.
(Why 1440? Because that's how many minutes there are in one whole day, and we are ignoring days.)

odometer:
@jurs:
What about leap years?
Try these pairs of dates:

Yes, you are right. Although every fourth year is considered to be a leap year already, the leap year handling is sometimes off by one day under certain circumstances. I'll have to work over the algorithm, I think.

Jozi68:
The next step would be to get the difference between current time and MyTime.

So both your timestamps are precise up to one minute only (no seconds)?
And you need the time difference in "total minutes"?
You don't need the difference in "days - hours - minutes"?
Or what?

jurs:
Yes, you are right. Although every fourth year is considered to be a leap year already, the leap year handling is sometimes off by one day under certain circumstances. I'll have to work over the algorithm, I think.

Just use one of your "YMD to straight days" functions on each of the two dates, and then subtract.

jurs:
But fortunately the code is not using 32-bit Unix timestamps, which have a year-2038 problem.

Unfortunately, the RTC only stores two BCD digits, offset in software from 1970. So it has a year 2070 problem, unless the RTC chip and the RTC library you support a century bit. Even that would carry you only to 2170.

Battery will be dead and will need replacing by 2024 leap year; will the project even still be in place by then?

odometer:
Just use one of your "YMD to straight days" functions on each of the two dates, and then subtract.

Yes, using the "Julian Day Number" for getting the difference would be a good solution.

Makes the programming logic simple.

Here is a code providing a Julian Day function as well as functions calculating the number of days between dates and the number of minutes between time stamps that are accurate to one minute.

Example sketch:

long JD(int year, int month, int day)
{ // COMPUTES THE JULIAN DATE (JD) GIVEN A GREGORIAN CALENDAR
  return day-32075+1461L*(year+4800+(month-14)/12)/4+367*(month-2-(month-14)/12*12)/12-3*((year+4900+(month-14)/12)/100)/4;
}

long daysDiff(int year1, int mon1, int day1, int year2, int mon2, int day2)
{
  return JD(year2, mon2, day2) - JD(year1, mon1, day1);
}

long minutesDiff(int year1, int mon1, int day1, int hour1, int min1, int year2, int mon2, int day2, int hour2, int min2 )
{
  int mDiff= (hour2*60+min2) - (hour1*60+min1);
  return daysDiff(year1, mon1, day1, year2, mon2, day2)*1440 + mDiff;
}

void setup()
{
  Serial.begin(9600);
  long x= minutesDiff(2015,12,24,23,59, 2017,1,1,0,0 );
  Serial.println(x);

  x= minutesDiff(2015,12,31,0,1, 2016,1,1,0,1);
  Serial.println(x);

  x=minutesDiff(2016,2,28,23,59, 2016,3,1,0,0 );
  Serial.println(x);

  x=minutesDiff(2016,12,31,23,59, 2017,1,1,0,0);
  Serial.println(x);

  x=minutesDiff(2017,2,28,23,59, 2017,3,1,0,0 );
  Serial.println(x);
}

void loop()
{
}

Thanx for the code, guix. It looked promising, but I could not get it to work. I downloaded the time library, clicked on Import library... Add library... and selected it. But when I tried to upload, I got this error: fatal error: time.h: No such file or directory

Thanx for the minutesSinceMidnight idea, odometer. That is working perfectly. Both my times are always after midnight, so there will be no calculation errors. I trust my RTC module can handle leap years; if not, it is no crisis. This is simply to calculate a time to switch lights on and off in my chicken house.

Thank you everybody for your inputs, I have a working sketch and I've learned a lot.

C++ is case sensitive. It's "Time.h". If you have trouble with the library installer, just bypass it. All it really does is unpack and place the library in the libraries directory. You can do that manually, very easily.

Jozi68:
Thanx for the minutesSinceMidnight idea, odometer. That is working perfectly. Both my times are always after midnight, so there will be no calculation errors. I trust my RTC module can handle leap years; if not, it is no crisis. This is simply to calculate a time to switch lights on and off in my chicken house.

So... it looks like this was really an XY problem.

Jozi68:
This is simply to calculate a time to switch lights on and off in my chicken house.

What you are describing is exactly what the TimeAlarms library was designed to do.
So I'd definitely use the Time and TimeAlarms library.
The Time library will sync with your RTC and the TimeAlarms library (which uses the Time library) can be used to create an event that occurs at a particular time. The alarm event can be once only or repeating.
It can be every day or on a particular day.
So if you want lights on at 6:00am and off at 10:00pm everyday that would be two repeating TimeAlarm alarms.

Alternatively, you could get an X10 module and let it do the control including running the HighVoltage for the lights.
The X10 computer interface can be programmed through a serial port to set the times to go on/off and it even can be programmed based on sunset/sunrise.

--- bill

bperrybap:
What you are describing is exactly what the TimeAlarms library was designed to do.
So I'd definitely use the Time and TimeAlarms library.
The Time library will sync with your RTC and the TimeAlarms library (which uses the Time library) can be used to create an event that occurs at a particular time. The alarm event can be once only or repeating.
It can be every day or on a particular day.
So if you want lights on at 6:00am and off at 10:00pm everyday that would be two repeating TimeAlarm alarms.

I doubt that the OP (Jozi68) wants the same times every day.

And the Time and TimeAlarms libraries are complicated enough that, if you don't really need them, you probably shouldn't be using them.

odometer:
I doubt that the OP (Jozi68) wants the same times every day.

And the Time and TimeAlarms libraries are complicated enough that, if you don't really need them, you probably shouldn't be using them.

I often hear that the Time libraries are "complicated". I really don't understand that.
I guess in my view, doing all the math when time is in "human" form is more complicated than using a pre-written library, and then, unless you really take into account all the possibilities, trying to do your own math typically only "mostly" works.

Whereas when using an epoch based time, everything gets pretty simple, and using pre-written libraries makes it even easier.

Even if you don't want the same times every day, the Time & TimeAlarms libraries makes that easy as well.
You simply set the alarm, and when the alarm goes off you set the next alarm for whatever datetime you want next.
The libraries take care of the RTC interface and all the time and interval calculations.

--- bill