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.
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.
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 www.sun.com)
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.
Sign up for the SourceForge newsletter:
You seem to have CSS turned off.
Please don't fill out this field.