Skip to content

Ops.Date: promote characters to Dates?

5 messages · Ben Bolker, Gabor Grothendieck, Brian Ripley

#
Ops.Date  <- function (e1, e2)
{
    if (nargs() == 1)
        stop("unary ", .Generic, " not defined for Date objects")
    boolean <- switch(.Generic, "<" = , ">" = , "==" = , "!=" = ,
        "<=" = , ">=" = TRUE, FALSE)
    if (!boolean)
        stop(.Generic, " not defined for Date objects")
+    if (!nchar(e1)) e1 <- as.Date(e1)
+    if (!nchar(e2)) e2 <- as.Date(e2)
    NextMethod(.Generic)
}

  adding the above two lines to Ops.Date makes, e.g.

as.Date("1999-12-13") == "1999-06-14"

behave as "expected".

  Does it seem like a good idea?
  (I was inspired by a student's confusion, and by
the fact that Ops.factor does a similar thing -- although
in this case it seems more sensible to promote the
character to a Date rather than demoting the Date
to a character (in which case comparisons might not
work right?))

  Similar questions might apply to Ops.POSIXt ...

  Ben Bolker
#
Note that the first argument still cannot be character
since Ops.Date won't get dispatched in that case.
On 6/24/06, Ben Bolker <bolker at ufl.edu> wrote:
1 day later
#
Believe it or not, it works either way: I haven't fully
figured out the logic yet (except to note that I had the
logic reversed below):


Ops.Date  <- function (e1, e2)
  {
    if (nargs() == 1)
         stop("unary ", .Generic, " not defined for Date objects")
     boolean <- switch(.Generic, "<" = , ">" = , "==" = , "!=" = ,
         "<=" = , ">=" = TRUE, FALSE)
     if (!boolean)
         stop(.Generic, " not defined for Date objects")
     if (nchar(e1)) e1 <- as.Date(e1)
     if (nchar(e2)) e2 <- as.Date(e2)
     NextMethod(.Generic)
}


datechar1 = "1999-12-03"
datechar2 = "2000-12-07"
date1 = as.Date(datechar1)
date2 = as.Date(datechar2)

date1 == datechar1
datechar1 == date1

datechar1 < date2
date2 > datechar1

    I also propose

as.Date.numeric <- function(x, ...) {
    class(x) <- "Date"
    x
}

   this takes a number of seconds since 1970 and
converts it into a Date object ...

    cheers
      Ben
Gabor Grothendieck wrote:
#
On Sun, 25 Jun 2006, Ben Bolker wrote:

            
This is as documented (White Book p.473, referenced on the help page for 
'Ops').  [Surely part of the homework that the posting guide asks for.]

I have recently been expanding the help pages for group generics (and 
trying to remove some of the confusion caused by adding S4 group generics 
to the page for S3 group generics).  This now says (about S3 group 
generics only)

         2.  Group '"Ops"':

            *  '"+"', '"-"', '"*"', '"/"', '"^"', '"%%"', '"%/%"'

            *  '"&"', '"|"', '"!"'

            *  '"=="', '"!="', '"<"', '"<="', '">="', '">"'

         The classes of both arguments are considered in dispatching.
         If a method is found for just one or the same method is found
         for both, it is used.  If different methods are found, there is
         a warning about 'incompatible methods': in that case or if no
         method is found for either argument the default method is used.
Eh?  nchar is non-zero for almost all objects to be passed through here, 
including any coercible to Date.  (Remember nchar will itself coerce to 
character.)  And nchar(e1) will be a vector, not a scalar: perhaps 
is.character(e1) was what was intended?


I believe the issue is more general: naive users (those who confuse an 
object with its printed representation) expect to be able to compare 
classed objects to character strings, and for the classed objects to be 
converted by as.character() when doing so.  That is not what is done: 
coerceVector(x, STRSXP) is used internally.  At some point we need to 
consider changing that, but it is not easy as method dispatch depends on 
the environment, something that is often not known when coerceVector is 
invoked.
That it would not do so is further evidence of the good reasons for not 
providing such a method for people to misinterpret.  (It would be the 
number of days since 1970-01-01, but probably one would want to take 
floor() of a number here.)
Depends who is doing the expecting ....

  
    
#
Prof Brian Ripley wrote:
Yes.  I was following Ops.factor too closely and not thinking
carefully enough.
(My suggestion actually went the other way, converting the character
string to a date: I think the results would be the same in this case
since the particular character representation yyyy-mm-dd sorts in
the same order as dates ...)

 That is not what is done:
[snip as.numeric.Date]
oops again: typo.

   I think my main point is that there are several aspects of working
with dates at this point that are confusing to the naive user, and
it would be nice if there were a consistent way to make them
less confusing without breaking anything.  I only offered the
suggestions above to try to start a conversation about it.

For example, not only is

"1999-12-02"==as.Date("1999-12-03")

FALSE, but so is

"1999-12-02"<as.Date("1999-12-03")
(I believe it is equivalent to
"1999-12-02"<as.character(as.numeric(as.Date("1999-12-03")))

You could just say "don't do that"; I would submit that it is
a reasonable thing for a naive user to do (particularly when
they have been conditioned by having a similar strategy
work for factors) and could get them
into trouble.

  cheers
    Ben Bolker