How to compare dates (ugly as it is)...

  • JohnA

    JohnA - 2009-02-21

    I recently wanted to do relative date comparison in a FM template and
    discovered to my distress, that FM does NOT support date comparison.

    This is in spite of the fact that doing so is simple in java.  dateA > dateB
    is done with the if (  dateA.after( dateB) ) ... and dateA < dateB is
    done with if ( dateA.before( dateB ) )... more over anyDate.getTime()
    returns a long ( milliseconds from Jan 1, 1970 00:00:00 )

    AND there COULD easily be a date object builtin named "millisecs"
    (or some such) that would return the long for any date object and
    allow the following in FM  

    [#if  my-date?millisecs > my-other-date?millisecs ] ... [/#if]

    which would allow any two date objects to be compared using numeric
    comparison operators.  It would also allow relative intervals to be
    computed, which some scientific users might find useful.

    However, UNTIL such an improvement appears it still can be done as
    follows: i am using a lot of separate statements here so reveal how
    it works, but of course all the steps can be combined into 1 IF
    statement condition, if you don't mind run-on long line syntax)

    Assume myDateA and myDateB exist in the data model:

    [#assign  Adate = myDateA?string("yyyyMMdd")?number?int ]
    [#assign  Atime = myDateA?string("HHmmss")?number?int ]
    [#assign  Bdate = myDateB?string("yyyyMMdd")?number?int ]
    [#assign  Btime = myDateB?string("HHmmss")?number?int ]

    [!-- the  Xdate ones MUST be formatted with yyyyMMdd so the most significant
    date parts are in the most significant digits; likewise for the Xtime cases --]

    Now that all the date parts ( assuming the time zones are the same, of course )
    are numbers ( ?int makes sure they are integers so float/double comparison
    is not going to give unexpected results)

    The Adate and Bdate variables can now easily be tested for equal, not equal
    greater than, less than, etc, but be careful, if you want to do before/after
    compares of both date and time, the Xtime vars should only be compared if the
    Xdate vars are equal.  The comparison of the Xdate and Xtime vars is just
    normal FM syntax.

    Also NOTE, if you want to extract more than seconds for the
    Xtime parts and include millisecond extraction as "HHmmssSSS" as the
    time format, you should use ?long, as nine digits is close to the range of ?int.

    Also, be sure you use yyyy and HH ( not yy,  nor hh), as extracting only
    two digit years or 12 Hour format hours, rather than 24 hour format via HH,
    will give you bad result in any comparisons.

    I would be nice if the ?millisecs  and ?before( date ) and ?after( date )
    were added to FM as date built-ins; at least then all the above would
    not be necessary. Date equality would be easy using ?millisecs but
    that can be done by conversion to strings already, using ?datetime.

    regards, john

    • Attila Szegedi

      Attila Szegedi - 2009-02-21

      Hm... I believe we could provide comparison for dates, at least when they're of the same kind (date, time, or datetime). I don't see an obvious problem with allowing comparison operators to work on dates.

      Do you think ?after, ?before, and ?milliseconds[*] would be required even if we allowed comparison operators to work on dates?

      [*] I think we'd need a better name though, as "milliseconds" might imply the milliseconds portion of the date to some people, and not milliseconds-since-UNIX-epoch.

    • JohnA

      JohnA - 2009-02-22

      I believe that ?before and ?after would be a great addition, but they only
      provide greater than and less than comparison, but exclude equals, less
      than or equal, and greater than or equal comparison, even if they are
      likely the most useful ones.

      The advantage of ?millisecs  (or what ever better name you choose)
      is that it allows all relations, and it avoids the issue of matching the
      date types you mentioned, since anydate.getTime() always returns
      a long and they can always be compared, and the "correct" type
      problem is left as the responsibility of the programmer to get that right.
      (perhaps ?epoch_millis   or something similar?)

      It also allows interval computations, in milliseconds, which without
      a built-in to extract the long, would requires the programmer to
      do the extraction and insertion into the data model himself.

      This also holds true for when the current timestamp is needed
      to do a date comparison with 'now'.  I did just that in my web-app
      so i could do such comparison, but being able to reference a "special"
      variable of some name like '.current_timestamp' which returns
      the result of a "current = new Date()" done at the beginning of
      template rendering would be useful addition as well, or it could
      be done when the special variable is referenced, to be more accurate
      at the millisecond level, and eliminate the overhead of creating
      a new Date object for every template rendering operation, which
      might never be used.

      Any Date object that is constructed with just the "date" part still has
      a zero time part so the getTime() just gives back the milliseconds since
      UNIX epoch anyway, although it does return negative epoch-millis if
      you construct a "new Date(0,0,0,13,12,11)" with zeros for the yy,mm,
      and dd.  (and the date ends up Dec 31, 1899, because 0 as dd, is 1 day
      before 1900 )

      As there is no "time only" constructor for the Date() class and calling
      anydate.getTime() forces the date object full computation of the
      datetime (or more correctly resolving the full "timestamp"), getTime
      always returns a result.  (Ref; Date Class API def at

      I see the issue of the date "type" as a formatting only problem, and
      its up to the programmer to handle the essentially deprecated Date
      objects and their comparison correctly themselves. 

      If you want no risk date objects you have to use GregorianCalendar
      class not the Date class, which, if Freemarker is going to move to a
      Java 1.4.2 as minimum runtime requirement, should also
      be a supported "date" object class. 

      Unfortunately, many a SQL object-relational-mappers often still don't support
      mapping SQL TIMESTAMP, DATE, and TIME column types into GregorianCalendar
      objects but still produce Date objects, so many usage cases for freemarker in
      database driven web-applications would still need Date class support.


Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

No, thanks