added format() methods for Date and Datetime #599

Merged
merged 5 commits into from Nov 28, 2016

Projects

None yet

4 participants

@eddelbuettel
Member

also rolled dev version in config.h

also adds operator<<() which doesn't seem to get picked up by compiler, ie we get the underlying date number or seconds since epoch.

@eddelbuettel eddelbuettel added format() methods for Date and Datetime
rolled dev version in config.h
08a2cff
@nathan-russell
Contributor

This looks good to me; my only suggestion would be possibly dropping the trailing << std::endl;, as there may be cases where the extra newline and call to flush is not desirable.

@eddelbuettel
Member

Thanks for looking at these. These were so far mostly for seeing what it would -- where I really want them is in the vector case of the new classes. And yes, the forced newline would be annoying there. For an individual item it may still make sense. Not sure yet how to balance it, and when to line break for vectors (every N maybe?).

@eddelbuettel
Member

Also annoying:

R> library(Rcpp)
R> cppFunction('void foo(Date d) { Rcpp::Rcout << d << std::endl; } ')
R> foo(Sys.Date())
17132
R> cppFunction('void bar(Date d) { Rcpp::Rcout << d.format() << std::endl; } ')
R> bar(Sys.Date())
2016-11-27
R>

I'd love for foo() to do what bar() does.

@eddelbuettel
Member

There is something else going on. I don't even think the new operator<<() gets called there.

+ return std::string(txtsec);
+ }
+
+ inline std::ostream &operator<<(std::ostream & s) const {
@coatless
coatless Nov 27, 2016 edited Contributor

Might be naive on my part, but: Rcpp::Rcout or Rcpp::Rstreambuf instead of std::ostream?

@eddelbuettel
eddelbuettel Nov 27, 2016 Member

No, do a ag operator\<\< inside the repo directory. All operator<< go to ostream from which they get redirected.

But @dcdillon just reminded me that I can't do this as a member function. So it was a null-op ;-)

@nathan-russell
Contributor

I think foo is failing because operator double() is being called instead of operator<<. Changing the definition to this works for me:

        // inline std::ostream &operator<<(std::ostream & s) const {
        //     s << this->format() << std::endl;
        //     return s;
        // }
        friend std::ostream& operator<<(std::ostream& os, const Date& d) {
            os << d.format() << std::endl;
            return os;
        }
@eddelbuettel
Member

Yes, @dcdillon and I chatted about that. I now have this working

  // end of public block
  friend inline std::ostream &operator<<(std::ostream & s, const Date d);

  // outside class
    inline std::ostream &operator<<(std::ostream & os, const Date d) {
        os << d.format();
        return os;
    }

and that worketh even ;-)

R> library(Rcpp)
R> cppFunction('void foo(Date d) { Rcpp::Rcout << d << std::endl; } ')
R> foo(Sys.Date())
2016-11-27
R>
eddelbuettel added some commits Nov 27, 2016
@eddelbuettel eddelbuettel corrections to operator<<() for Date(time)
now it even works.  the amazement.
d41a48d
@eddelbuettel eddelbuettel adding operator<< for Date(time)Vector
d85db8e
@eddelbuettel
Member

If anybody else would like to 'bless' (or even "review") it I'd feel better before merging my own PR :)

inst/include/Rcpp/date_datetime/Date.h
+ char txt[32];
+ struct tm temp = m_tm;
+ temp.tm_year -= baseYear(); // adjust for fact that system has year rel. to 1900
+ ::strftime(txt, 31, fmt, &temp);
@kevinushey
kevinushey Nov 28, 2016 Contributor

Should we check the return value of ::strftime() here? What happens to txt if the user passes in a fmt string not recognized by ::strftime()?

@eddelbuettel
eddelbuettel Nov 28, 2016 Member

Then strftime() returns zero. I guess we could check and return an explicit std::string(""). Would be cleaner.

inst/include/Rcpp/date_datetime/Date.h
+ struct tm temp = m_tm;
+ temp.tm_year -= baseYear(); // adjust for fact that system has year rel. to 1900
+ ::strftime(txt, 31, fmt, &temp);
+ return std::string(txt);
@kevinushey
kevinushey Nov 28, 2016 Contributor

Since txt isn't zero-initialized (it's a plain C array), it seems like it could be possible that no null byte exists at the end of txt. Or does ::strftime() write a null byte at the end? What if the output of ::strftime() doesn't fit in txt?

@eddelbuettel
eddelbuettel Nov 28, 2016 Member

Per previous point and man strftime, by catching a return of zero we should catch that.

+ char txtiso[64], txtsec[64];
+ time_t t = static_cast<time_t>(std::floor(m_dt));
+ struct tm temp = *localtime(&t); // localtime, not gmtime
+ ::strftime(txtiso, 63, fmt, &temp);
@kevinushey
kevinushey Nov 28, 2016 Contributor

Similar questions here re: zero-initialization, checking of return value?

@kevinushey
Contributor

Some paranoid checks re: state of the char array used in conjunction with ::strftime() but otherwise LGTM.

@eddelbuettel eddelbuettel explicit checks of return value in format
b264d8a
@eddelbuettel
Member

Thanks, @kevinushey . I extended both format() functions to be a wee bit more explicit on the return values.

@kevinushey
Contributor

Okay, LGTM!

@eddelbuettel
Member

Thank you. I just added two (small) sets of unit tests which I'll in a minute and then I'll merge.

@eddelbuettel eddelbuettel unit tests for Date(time) formatters
06fb890
@eddelbuettel eddelbuettel merged commit 7a22d21 into master Nov 28, 2016

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
@eddelbuettel eddelbuettel deleted the feature/format_dates branch Nov 28, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment