Issue with long and double

Hi,

I have a problem using long and double values on my arduino uno.

My code:

    #include <DS3231.h>

    DS3231  rtc(SDA, SCL);
  
    void setup() { 
      Serial.begin(9600);
     rtc.begin(); // Initialize the rtc object
  //      rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
//  rtc.setTime(19, 35, 0);     // Set the time to 12:00:00 (24hr format)
 // rtc.setDate(30, 12, 2016);   // Set the date to January 1st, 2014
 
    }
void loop()
{
  
  double year;
  double yearFloor;
  double yearRest;
  
  double month;
  double monthFloor;
  double monthRest;

  double week;
  double weekFloor;
  double weekRest;
  
  double day;
  double dayFloor;
  double dayRest;
  
  double hour;
  double hourFloor;
  double hourRest;
  
  double minute;
  double minuteFloor;
  double minuteRest;
  
  double second;
  double secondFloor;
  double secondRest;
  
  long startDate = 1217980800;
  double currentTimeString;
  long unixTimestamp;
  
  unixTimestamp = rtc.getUnixTime(rtc.getTime());
  
  Serial.println(unixTimestamp);
  Serial.println(rtc.getUnixTime(rtc.getTime()));
  
  currentTimeString = (double)unixTimestamp - startDate;
  
  Serial.println(currentTimeString);
  Serial.println(unixTimestamp);



  year = double(currentTimeString)/31536000.00;
  yearFloor = floor(year);  
  yearRest = year - yearFloor;
  
  month = yearRest * 12;
  monthFloor = floor(month);
  monthRest = month - monthFloor;

  week = monthRest * 4;
  weekFloor = floor(week);
  weekRest = week - weekFloor;
  
  day = weekRest * 7;
  dayFloor = floor(day);
  dayRest = day - dayFloor;
  
  hour = dayRest * 24;
  hourFloor = floor(hour);
  hourRest = hour - hourFloor;
  
  minute = hourRest * 60;
  minuteFloor = floor(minute);
  minuteRest = minute - minuteFloor;
  
  second = minuteRest * 60;
  secondFloor = floor(second);
  secondRest = second - secondFloor;
  
  Serial.print("Jahre: ");
  Serial.println(yearRest);
  Serial.println(yearFloor);
  Serial.print("Monate: ");
  Serial.println(monthRest);
  Serial.println(monthFloor);
  Serial.print("Wochen: ");
  Serial.println(weekRest);
  Serial.println(weekFloor);
  Serial.print("Tage: ");
  Serial.println(dayRest);
  Serial.println(dayFloor);
  Serial.print("Minuten: ");
  Serial.println(minuteRest);
  Serial.println(minuteFloor);
  Serial.print("Sekunden: ");
  Serial.println(secondRest);
  Serial.println(secondFloor);
  
  Serial.println(" -- ");
  Serial.println(" -- ");
  
  // Wait one second before repeating
  delay (2000);
}

Output:

0.41
8.00
Monate: 0.90
4.00
Wochen: 0.60
3.00
Tage: 0.17
4.00
Minuten: 0.84
58.00
Sekunden: 0.57
50.00
 -- 
 -- 
1483142804
1483142804
265161984.00
1483142804
Jahre: 0.41
8.00
Monate: 0.90
4.00
Wochen: 0.60
3.00
Tage: 0.17
4.00
Minuten: 0.84
58.00
Sekunden: 0.57
50.00
 -- 
 -- 
1483142807
1483142807
265161984.00
1483142807
Jahre: 0.41
8.00
Monate: 0.90
4.00
Wochen: 0.60
3.00
Tage: 0.17
4.00
Minuten: 0.84
58.00
Sekunden: 0.57
50.00
 -- 
 --

currentTimeString = (double)unixTimestamp - startDate;
Does not work I think, because
Serial.println(currentTimeString);
Does not get updated after each loop iteration.
Serial.println(unixTimestamp);
Works fine..

Can anyone help me to solve my problem, please?

Thanks

Short summary about my issue:
Code:
Serial.println(rtc.getUnixTime(rtc.getTime()));
Serial.println(float (rtc.getUnixTime(rtc.getTime())));

Output:
1483146725
1483146752.00

According to this site:

Your clock set to far ahead.

I live in Berlin, so for me the timestamp is correct. But I do not know why the float cat does not cast the timestamp correctly..
Serial.println(rtc.getUnixTime(rtc.getTime()));
Serial.println(float (rtc.getUnixTime(rtc.getTime())));

You know double and float are the same thing on most Arduinos? You don't always get the extra precision that you expect.

But I do not know why the float cat does not cast the timestamp correctly..

Floats are a mathematical conversion of a number based on the IEEE 754 standard, and on the Arduino the resulting number is held in four bytes and has only 6-7 decimal digits of precision. That means the total number of digits, not the number to the right of the decimal point.
https://www.arduino.cc/en/Reference/Float

The unix time stamp is a 32bit (4 byte) integer value. uint32_t or unsigned long.

The four bytes of the float are not the same thing as the four bytes of the long integer.

You don't need to go through double math to do what you want to do... just use the fact that integer math will floor things for you if you keep all variables as long or unsigned long

yearFloor = currentTimeString/31536000l; will give you the value right away (note the 'l' at the end of the number to denote a long type litteral. Make sure of course that currentTimeString is long not double, same for yearFloor

Why do you call your variable currentTimeString, it's not a string...

Thanks for all your replies. I will try using your recommendation J-M-L.

But the problem is, that "currentTimeString" (yes I will change the name of this variable later) gets not updated:

  long startDate = 1217980800;
  float currentTimeString;
  long unixTimestamp;
  
  unixTimestamp = rtc.getUnixTime(rtc.getTime());
  

  currentTimeString = unixTimestamp - startDate;
  
  Serial.println(unixTimestamp);
  Serial.println(currentTimeString);

Each iteration, the Serial.println(unixTimestamp); prints the current timestamp, but the Serial.println(currentTimeString); does not print the current timestamp (only after several iterations it will print a new timestamp, but not the current timestamp)

I do not understand this behavior..

post your full code or an example that shows the behavior you describe

here is a full example.

Code:

    #include <DS3231.h>

    DS3231  rtc(SDA, SCL);
  
    void setup() { 
      Serial.begin(9600);
     rtc.begin(); // Initialize the rtc object
  //      rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
//  rtc.setTime(19, 35, 0);     // Set the time to 12:00:00 (24hr format)
 // rtc.setDate(30, 12, 2016);   // Set the date to January 1st, 2014
 
    }
void loop()
{

  long startDate = 1217980800;
  float newTimestamp;
  long unixTimestamp;
  
  unixTimestamp = rtc.getUnixTime(rtc.getTime());
  newTimestamp = unixTimestamp - startDate;
  
  Serial.print("unixTimestamp (type long): ");
  Serial.println(unixTimestamp);
  Serial.print("newTimestamp (type float): ");
  Serial.println(newTimestamp);
  Serial.println("--");
  Serial.println("--");
  
  
  // Wait one second before repeating
  delay (2000);
}

Serial monitor output:

unixTimestamp (type long): 1483184536
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184539
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184541
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184543
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184545
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184547
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184549
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184551
newTimestamp (type float): 265203744.00
--
--
unixTimestamp (type long): 1483184553
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184555
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184557
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184559
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184561
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184563
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184565
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184567
newTimestamp (type float): 265203760.00
--
--
unixTimestamp (type long): 1483184569
newTimestamp (type float): 265203776.00
--
--
unixTimestamp (type long): 1483184571
newTimestamp (type float): 265203776.00
--
--
unixTimestamp (type long): 1483184573
newTimestamp (type float): 265203776.00
--
--
unixTimestamp (type long): 1483184575
newTimestamp (type float): 265203776.00
--
--
unixTimestamp (type long): 1483184577
newTimestamp (type float): 265203776.00
--
--

#include <DS3231.h>which library do you use? (is that the library from Henning Karlsen?)

Yes it is the library from Henning Karlsen
http://www.rinkydinkelectronics.com/library.php?id=73

OK - actually the library has nothing to do with it. You are getting bitten by the float limits to represent high numbers.

try this code to see the behavior:

void setup() {

  long startDate = 1217980800l;
  long unixTimestamp = 1483184541l;
  long newTimestamp1 = unixTimestamp - startDate; // should be 265,203,741
  float newTimestamp2 = unixTimestamp - startDate; // should be 265,203,741.00
  float newTimestamp3 = (float) unixTimestamp - (float) startDate; // should be 265,203,741.00
  
  Serial.begin(115200);

  Serial.print("unixTimestamp (type long): ");
  Serial.println(unixTimestamp);

  Serial.print("newTimestamp (type long): ");
  Serial.println(newTimestamp1);

  Serial.print("newTimestamp (type float, with long calculus): ");
  Serial.println(newTimestamp2);

  Serial.print("newTimestamp (type float, with float calculus): ");
  Serial.println(newTimestamp3);
}

void loop(){}

you'll see that the result differs and only the long calculation (newTimestamp1) gives the right result (265,203,741):

unixTimestamp (type long): 1483184541
[color=limegreen]newTimestamp (type long): 265203741[/color]
newTimestamp (type float): 265203[color=red]744.00[/color]
newTimestamp (type float, with float calculus): 265203[color=red]712.00[/color]

Yeah so if I cannot cast longs to floats... have you an idea how I can convert the timestamp to years, and calculating the rest?

you can, but if they are too big, you lose in precision what you gain in ability to represent larger or fractional numbers. you should not use them for simple integer math.

here is how to extract the time info between two dates

const long secondsInAMin   = 60l; // 60 sec
const long secondsInAnHour = 60l  * secondsInAMin;  // 60 minutes in an hour
const long secondsInADay   = 24l  * secondsInAnHour; // 24 hours in a day
const long secondsInAYear  = 365l * secondsInADay; // 365 days in a year (or so :) )

void setup() {

  Serial.begin(115200);

  long startDate = 1217980800l;
  long unixTimestamp = 1483184541l;
  long delta = unixTimestamp - startDate;

  Serial.print("Delta = "); Serial.print(delta); Serial.println(" s");

  long nbYear = delta / secondsInAYear ;
  delta = delta - (nbYear * secondsInAYear);
  long nbDay = delta / secondsInADay;
  delta = delta - (nbDay * secondsInADay);
  long nbHour = delta / secondsInAnHour;
  delta = delta - (nbHour * secondsInAnHour);
  long nbMin = delta / secondsInAMin;
  delta = delta - (nbMin * secondsInAMin);
  long nbSec = delta;

  Serial.print(nbYear); Serial.print(" Year ");
  Serial.print(nbDay);  Serial.print(" Day ");
  Serial.print(nbHour); Serial.print(" Hour ");
  Serial.print(nbMin);  Serial.print(" Min ");
  Serial.print(nbSec);  Serial.println(" Sec ");

}

void loop() {}

(assuming startDate is before unixTimestamp and years of 365 days, so no corrections for leap years)

Ah great. Thanks! Do you know how I can consider leapyears and the exact days for each month?

Days in months are fixed expect for February of leap years

In the Gregorian calendar, a normal year consists of 365 days.

Because the actual length of a sidereal year (the time required for the Earth to revolve once about the Sun) is actually 365.25635 days, a "leap year" of 366 days is used once every four years to eliminate the error caused by three normal (but short) years. Any year that is evenly divisible by 4 is a leap year: for example, 1988, 1992, and 1996 are leap years.

However, there is still a small error that must be accounted for. To eliminate this error, the Gregorian calendar stipulates that a year that is evenly divisible by 100 (for example, 1900) is a leap year only if it is also evenly divisible by 400.

For this reason, the following years are not leap years:
1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600
This is because they are evenly divisible by 100 but not by 400.

The following years are leap years:
1600, 2000, 2400
This is because they are evenly divisible by both 100 and 400

an by the way tonight we are playing with leap second - a leap second is a one-second adjustment that is occasionally applied to Coordinated Universal Time (UTC) in order to keep its time of day close to the mean solar time, or UT1. Without such a correction, time reckoned by Earth's rotation drifts away from atomic time because of irregularities in the Earth's rate of rotation. Since this system of correction was implemented in 1972, 26 leap seconds have been inserted, the most recent on June 30, 2015 at 23:59:60 UTC,[1] and the next leap second will be inserted tonight - December 31, 2016, at 23:59:60 UTC. ==> A positive leap second is inserted between second 23:59:59 of a chosen UTC calendar date and second 00:00:00 of the following date. This extra second is displayed on UTC clocks as 23:59:60.

So who has a correct clock which will show 23:59:60 tonight? :slight_smile:

J-M-L:
here is how to extract the time info between two dates

  ...

long nbYear = delta / secondsInAYear ;
  delta = delta - (nbYear * secondsInAYear);
  ...

Yes, this is the "divide modulo" method. There's no need to do the second multiplication-subtract operation to find the remainder however. The remainder is already computed (internally) when you do the divide.

Pretty much all divide routines generate the remainder as part of the process. When you just do an integer division, and nothing more, then it simply discards that remainder. If however you need to use the remainder (modulo % operator) then any decent compiler will just use the result from the immediately prior division. So there is basically no extra work to do it that way.

For example, the following two lines are actually done together with a single divide operation. :slight_smile:

  ...
  long nbYear = delta / secondsInAYear ;
  delta = delta % secondsInAYear;
  ...

stuart0:
Yes, this is the "divide modulo" method. There's no need to do the second multiplication-subtract operation to find the remainder however. The remainder is already computed (internally) when you do the divide.

Good point if it does the optimisation - I tend to not use modulo often because it does not work well with negative numbers but in that case that's OK

here is my current version of my code:

#include <Time.h>
#include <TimeLib.h>

    #include <DS3231.h>

    DS3231  rtc(SDA, SCL);
  
    Time t;

void setup() {

  Serial.begin(115200);
  rtc.begin();
  

}

void loop() {

  
  t.hour = 0;
  t.min = 0;
  t.sec = 0;
  t.year = 2008;
  t.mon = 8;
  t.date = 6;
  
  
  long startDate = rtc.getUnixTime(t);
  long unixTimestamp = rtc.getUnixTime(rtc.getTime());
  long delta = unixTimestamp - startDate;
 
 Serial.print("Startdate: ");
 Serial.println(startDate);
 Serial.print("Current: ");
 Serial.print(unixTimestamp);
 Serial.print(" -- ");
 Serial.println(rtc.getTimeStr());
 
  long years =  delta / 31557600;

  delta = delta - (years * 31557600);
  
  
  long months = delta / 2629800;
  delta = delta - (months * 2629800);

  long weeks = delta / 604800;
  delta = delta - (weeks * 604800);
  
  long days = delta / 86400;
  delta = delta - (days * 86400);

  long hours = delta / 3600;
  delta = delta - (hours * 3600);

  long minutes = delta / 60;
  delta = delta - (minutes * 60);
  
  long seconds = delta;
  
  Serial.print(years); 
  Serial.print(" Years, ");
  Serial.print(months);
  Serial.print(" Months, ");
  Serial.print(weeks);
  Serial.print(" Weeks, ");
  Serial.print(days);
  Serial.print(" Days,");
  Serial.print(hours);
  Serial.print(" Hours,");
  Serial.print(minutes);
  Serial.print(" Minutes, ");
  Serial.print(seconds);
  Serial.print(" Seconds");
  Serial.println(" -- ");
  Serial.println(" -- ");
  
  delay(10000);

}

It is works fine except the output of Hours. It prints 23 hours, but (at this time in my timezone) 17 were correct. I cannot find any error in the code and the timestamp is also fine. Any idea?