adafruit / RTClib

A fork of Jeelab's fantastic RTC Arduino library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Check for invalid date in DateTime

ondras12345 opened this issue · comments

  • Arduino board: Arduino MEGA 2560
  • Arduino IDE version (found in Arduino -> About Arduino menu): 1.8.9

The DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) constructor does not contain any user input checking, so it's possible to enter invalid values, such as month=13.

I can handle it in my code that uses this library, but it's harder to do so with incorrect day values, because I would have to check for leap. The library already does that and I don't want to have to create a new array in my sketch as it would take up more space in the memory than reusing the array that is already part of this library.

Setters in DateTime #70 (if someone implements them) would also need this checking.

It's bad to store incorrect date values, because some RTC don't check this either, eg. DS3231 datasheet, page 12:

Illogical time and date entries result in undefined operation.

I may submit a pull request implementing both this and the setters, but I need to know what should happen if an invalid value is encoutered. Setters could just return false without changing anything, but I think the constructor would have to replace the value by a valid one because it can't just fail and do nothing.

Hello. I am wondering if you managed to implement the error checking?

Hello. I am wondering if you managed to implement the error checking?

I haven't tried it yet, but it should be quite simple to do.

Thanks for the reminder, I'm putting it on my TODO list.

I've actually worked on it on my fork here. I added setters for the attributes with error checking. But I realized there is also a need to have a method to change the date (year, month, day) all at once instead of using just setters.

Thanks.

I think the setters should also return false if the value is invalid.

Also, please don't use tabs to indent the code. The rest of the library uses spaces... (Well, except for the char* DateTime::toString(char* buffer) function)
Edit: You can do something like

sed 's/\t/    /g' file > newfile

to automatically replace tabs by 4 spaces (note: although I don't like the Arduino standard, most of the library uses 2 spaces to indent, so it might be better to use that).

You should also add the new keywords to the keywords.txt file.

Hi,
you have removed a feature in this commit. If the year is <2000, your version does not work while the documentation clearly states that 2 or 4 digits may be used to express the year.

Incorporate this into your code, please:

if (year >= 2000)
        year -= 2000;

void setYear(uint16_t year), bool DateTime::validDate(uint16_t year, uint8_t month, uint8_t day), bool DateTime::changeDate(uint16_t year, uint8_t month, uint8_t day) and DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) need it.

Also, please explain the error checking in the documentation of DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) - maybe something like:

If an invalid date is passed to the function, it gets replaced by 2000-01-01.
If an invalid value in any part of the time is encountered, it is replaced by 0.

Update: There are also some comments on this commit.

The constructor that takes year, month, day... as separate parameters does little more than store its arguments in the object. It is thus pretty easy to build an invalid DateTime with it. One can, however, check the validity of the constructed DateTime after the fact.

I just figured out a very simple way to check whether a DateTime is valid: convert to Unix time and back. You get the original DateTime if and only if it is valid.

My implementation:

bool DateTime::isValid() const {
    if (yOff >= 100) return false;
    DateTime other(unixtime());
    return yOff==other.yOff && m==other.m && d==other.d
           && hh==other.hh && mm==other.mm && ss==other.ss;
}

The initial test for yOff >= 100 is there because only dates prior to 2100 are officially supported.

Is there interest in me submitting a pull request with this added method?

Is there interest in me submitting a pull request with this added method?

Absolutely! Personally, I don't understand why this hasn't been added before. Is there a reason it can't be added directly into that constructor? That seems like a cleaner solution to me.

Thanks for your suggestion, and I support any method of DateTime validation within the library!

@wRorsjakz has a fork with his version (and I have an open PR there), but he doesn't seem to respond.
Your solution looks much better, though.

@jadonmmiller wrote:

Absolutely!

OK, will do.

Is there a reason it can't be added directly into that constructor?

Yes, a constructor cannot do something else that construct an object. The best it can do, in case it detects an error, is to return an invalid object. Turns out that's already what it does. ;-)

An isValid() method is all that's needed to formalize the concept of “invalid object”. This also seems to be the approach taken by Qt.

@ondras12345 wrote to @wRorsjakz:

Please add something like: “If an invalid date is passed to the function, it gets replaced by 2000-01-01. [...]”.

I don't quite like the idea, as 2000-01-01 is a perfectly valid date.

The best it can do, in case it detects an error, is to return an invalid object. Turns out that's already what it does. ;-)

That was a dumb oversight on my part! I would've had it set the object to the earliest or latest date the system is capable of, but now that I think about it, that doesn't get us anywhere! 🤔 😄