In Freemarker, I can use .now?time to get the current time, but how do I compare that to another time?
For example, how would I properly test if the current time is between 8:00 AM and 5:00 PM?
<#if .now?time gte (?) && .now?time lte (?)>
//do something
<#/if>
I don't know what to put in the (?) because I'm not sure how to create date scalars in Freemarker.
Although you can compare two java.util.Date-s as far as they are both of the same kind (like both are time-only values), then it's up to you to ensure that the internal representations are consistent (like a time-only value is in the epoch-day VS on some other day). Thus it's mostly meant to be used for comparing date-time values (timestamps) where no such issue exists. So, since you know that you want an epoch-based time, you could do the following hack:
<#assign nowTime = .now?time?string.iso?time.iso>
Now it's correctly comparable with "8:00:00 AM"?time, however, I recommend using ISO format, because the locale-specific format is kind of fragile (like if you change locale, it won't work): '08:00:00'?time.iso. Note that for this you need at least FreeMarker 2.3.21.
I read the source of freemarker 2.3.21 and found that current freemarker does not support comparing .now?time and time object such as "8:00:00 AM"?time.
That is because.now?time is internally treated as current time such as 2014-10-21 15:11:30,
and "8:00:00 AM"?time is treated as 1970-01-01 08:00:00.
Therefore, <#if .now?time gte "8:00:00 AM"?time > is always true.
If you think it's a bug you may report to http://sourceforge.net/p/freemarker/bugs/ or send a question in freemarker's mailing list.
So the current workaround is to create the Date object and compare it in the java code.
If you happen to use Liferay you can try the compareTo() function:
It returns negative if the date is earlier, positive if it is later and zero if they are equal.
<#assign time1 = "11:55:00"?time>
<#assign time2 = "11:56:00"?time>
${dateUtil.compareTo(time1, time2)} <#-- Return -1 -->
${dateUtil.compareTo(time2, time1)} <#-- Return 1 -->
${dateUtil.compareTo(time1, time1)} <#-- Return 0 -->
Related
In the Java 8 DateTime API, there are 2 ways to format a date that at first glance seem to do the same thing:
DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm").format(LocalDateTime.now());
LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"));
These both return a String representing the date and time at runtime.
Is there a significant difference between these 2?
No, they're equivalent - and they're even documented to be equivalent, at least for the default implementation. ChronoLocalDateTime.format includes this:
The default implementation must behave as follows:
return formatter.format(this);
Sometimes one form is useful, sometimes the other is - it can depend on what you're chaining together in a longer expression.
i am streaming a list which has data corresponding to dates and am using this filter filter(s->s.getTime().before(enddate)) to filter the values from the stream between two dates inclusive of the lastdate.
What can I change here so that it includes the endDate? I tried with replacing before with equals but that doesn't help.
Assuming you are using java.util.Date you could try
filter(s-> {return s.before(enddate) || s.equals(enddate);})
or simply (thanks to chai-t-rex)
filter(s-> !s.after(enddate));
By my understanding this should return true for all s of type Date before or equal to enddate of type Date
To Keep things simple you can just use long values :
filter(s-> (s.getTime()<=enddate.getTime()))
Since this uses only a single filter, and works with (long) numbers, so you can change the filter as per your liking very easily
We have a requirement to present two p:calendar components to the user, representing a start and end date each. Both datetimes have dates, hours and minutes.
PrimeFaces has perfect mindate, maxdate, minHour, maxHour, minMinute, and minMinute attributes available.
The requirement now is:
It is impossible to set the start datetime to anything greater than or equal to the end datetime.
It is impossible to set the end datetime to anything less than or equal to the end datetime.
The following equation should hold true:
begin datetime < end datetime
Now we tried the following JSF:
<p:calendar id="begin-date"
value="#{debugManager.selectedBeginDate}"
mindate="#{debugManager.minBeginDate}"
maxdate="#{debugManager.maxBeginDate}"
maxHour="#{debugManager.maxBeginHour}"
maxMinute="#{debugManager.maxBeginMinute}"
pattern="yyyy-MM-dd HH:mm"
showButtonPanel="true"
readonlyInput="true"
navigator="true"
showOn="button"
required="true">
<p:ajax event="dateSelect" update="end-date" />
</p:calendar>
<p:calendar id="end-date"
value="#{debugManager.selectedEndDate}"
mindate="#{debugManager.minEndDate}"
minHour="#{debugManager.minEndHour}"
minMinute="#{debugManager.minEndMinute}"
pattern="yyyy-MM-dd HH:mm"
showButtonPanel="true"
readonlyInput="true"
navigator="true"
showOn="button">
<p:ajax event="dateSelect" update="begin-date" />
</p:calendar>
Here's an examplary min/max method (mindate of end-date):
public Date getMinEndDate()
{
return this.getSelectedBeginDate();
}
As you can see, the minimum end date is the currently AJAX-selected begin date. Setting an end date correctly disallows setting the begin date past the end date.
The problems start when involving the time into the equation...
Since the interface of p:calendar has separate methods, the bean has to provide the logic:
public int getMinEndHour()
{
Date selectedBeginDate = this.getSelectedBeginDate();
Date selectedEndDate = this.getSelectedEndDate();
if ( selectedBeginDate != null && DateUtil.isSameDay( selectedBeginDate, selectedEndDate ) )
{
return DateUtil.getHourOf( selectedBeginDate );
}
return ComplianceConstants.DEFAULT_COMPLIANCE_CASE_MIN_END_HOUR;
}
This basically only says if a begin date has been set and it the begin and end dates are currently the same, restrict the selectable end hour (minHour of end-date) to the begin hour.
Operations:
Set the begin datetime to 2013-04-20 12:34 (legit)
Set the end datetime to 2013-04-22 00:00 (legit)
Now the time for end date sits on 00:00 and selecting a calendar date 2013-04-20 should be allowed as long as the end time is somehow adjusted to at least 12:35.
The p:calendar component however cannot know this and now
sets the end datetime to 2013-04-20 00:00 (legit, but false)
...
The problem now is that when the user presses a certain new end date in the calendar, the mindate/maxdate attributes cannot restrict the user to hit the the same as the begin date. If the end date time now happens to be before the same begin date's time there's nothing we can do about it (which is wrong).
The followup problem now is that the user is able to close the calendar and just press the submit button to insert false data into the DB. Of course, a validator could/should be run, but we have to somehow achieve this without a validator.
What we were trying next was to patch the setSelectedBeginDate( Date selectedBeginDate ) and setSelectedEndDate( Date selectedEndDate ) methods to adjust the set java.util.Date time portions if the dates were on the same day. Something like this:
public void adjustSelectedEndDate()
{
if ( this.selectedEndDate != null )
{
this.log.infov( "adjustSelectedEndDate: b-hour = {0}, e-hour = {1}", DateUtil.getHourOf( this.selectedBeginDate ), DateUtil.getHourOf( this.selectedEndDate ) );
if ( DateUtil.isSameDay( this.selectedBeginDate, this.selectedEndDate ) &&
( DateUtil.getHourOf( this.selectedEndDate ) < DateUtil.getHourOf( this.selectedBeginDate ) ) ||
DateUtil.getHourOf( this.selectedEndDate ) == DateUtil.getHourOf( this.selectedBeginDate ) && DateUtil.getMinuteOf( this.selectedEndDate ) <= DateUtil.getMinuteOf( this.selectedBeginDate ) )
{
this.log.info( "Adjusting selected end date!" );
this.selectedEndDate = DateUtil.addOneMinuteTo( DateUtil.copyTime( this.selectedBeginDate, this.selectedEndDate ) );
}
}
}
This required us to add #this to the update attribute of each p:calendar so that the respective getters (getSelectedBeginDate() and getSelectedEndDate + the min/max limiters) will be called during update.
Placing an #this on the update however confuses the p:calendar components, making the time sliders only slidable once. Subsequent slider events are simply ignored, behaving broken.
Q's
How do you generally approach solving this?
Is using p:remoteCommand the way to achieve what we want?
Optional Q:
Why hasn't the PrimeFaces p:calendar been implemented to provide a single minDateTime and maxDateTime, which could potentially solve the problems at hand?
I bet this scenario I described has already been solved before. I'd very much appreciate if you could describe the approach you managed to solve this (or even share a partly solution).
Preface:
I don't work with JSF, but there are a couple of things that might steer you back to where you want to be:
a) when working with just the date portion of a dateTime in a standard calendar, consider using:
someCalendar.set(Calendar.MILLISECOND, 0)
b) consider using joda-time, as it seems to be frequently recommended (here, here , and many other places) over the standard library for correctness, performance, and ease of use in many situations.
c) Make sure your bean scope is surviving each ajax call (not redirecting, only sending standard post-backs, etc) and each event handler is getting the faces context (eg. FacesContext facesContext = FacesContext.getCurrentInstance();)
d) mindate and the like probably don't work like you expect , and I don't expect that automatic behavior can be quite so easily interjected.
When those options aren't available, and you have to do it all yourself with what you have:
Philisophical / UX:
The first thing I would do is remove the expectation of arrangement or perspective from the pair of dates. Don't treat the pair as a vector that exposes or expects a direction on the timeline.
In other words, is a start or from date always less than or earlier than an end or to date? No, as can be seen for a query of historical data, or for applying corrections to events that have either yet to happen or have already happened?
This connotation can easily confuse a user as to whether they are going 'back to' or 'forward from' (and can easily confuse yourself). Instead I would treat a pair of dates with a time-period between them as just and simply that a pair of dates or a range or a period that declares an interval, and infer their relative position on the timeline depending on the any consequently chosen values. In this way you can honor the respective and inherent requirements that the dates never be equal, and the left is always to the left, the right always to the right.
We can't infer what 'start' or 'from' means, but we can infer some meaning and relative relationship: a right, a left, and a between on a chronological timeline. Note: Always resolve dates to UTC before doing any calculation or comparison.
long oneDateValue = oneDate.toUtc().toMilliseconds();
long anotherDateValue = anotherDate.toUtc().toMilliseconds();
long right = max (oneDateValue, anotherDateValue);
long left = min (oneDateValue, anotherDateValue);
Evaluating Precision:
The second thing I would look at when working with a range of dates in any language is similar to how you might deal with floating point numbers. For comparisons, do not compare for equality, but instead compare the delta to an "acceptable error level". In other words, the application is really only concerned with a certain degree of precision, so make sure that only that precision is captured and considered:
const int dateTimeResolutionInMs = 86400000; // milliseconds per day
public bool areEssentiallySame(long left, long right) {
// the difference between right and left is less than our precision
// requires, thus dates are effectively the same
return (right - left < dateTimeResolutionInMs);
}
Coercing Precision:
Thirdly, how do we resolve the difference in values even if within the range of the resolution? (Out application was given more precision than it can handle or expect or needs).
long diff = value % dateTimeResolutionInMs;
Truncate: return value - diff;
Nearest (w/bias): return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;
Others: there are lots of other strategies for either shrinking or expanding a value to a preferred resolution or precision
Addendum:
As far as getting post-backs/Ajax calls to return a view with the values you expect for the events fired by a calendar element, you may want to separate that concern off to a new question if the note in the preface didn't get you anywhere, and you know for certain your bean is properly registered and recognized. You may have some browser/browser-version specific issues that contribute to the undesired behavior, and like anything else, there are issues, both known and unknown.
From an external service I get objects with Date+Time fields as String's in format 2012-03-07 12:12:23.547 and I need to compare these fields to get a correct order of the objects. I am well aware that I can create Date objects via e.g. SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") and compare the two Date's to achieve this but my question is if I can rely on a correct sorting order if I compare them as Strings such as String.compareTo(String)? Some light testing gives me the impression that it works but I am wondering if anyone is aware of any scenarios where it would NOT give me the correct result? Also, are there any performance considerations, pros or cons, of comparing String's Vs parsing into Dates to compare?
Assuming the hours are in 24 hour format, then yes, that's a sortable date/time format - and one of its well-known benefits is that you can sort without actually parsing.
One downside: if you get bad data, you won't spot it - you'll just neatly sort it into the "right" place, ignoring the fact that you've been given (say) February 30th.
If you need the value as a date/time later on, then I'd parse it first and then compare. But if you only need this in terms of ordering, the string comparison may be faster than parsing everything. Worth benchmarking, of course... especially as comparing two strings on the same day will require several characters-worth of checking, whereas if you've parsed it once you can then probably just compare long values.
No, the better approach would be parse the string in a date object and then compare with other date object.
I wouldn't worry about performance unless you have some reason to think this code will be a bottleneck (called lots of times within loops) and even then I'd wait till you could do some concrete performance testing.
Comparing them as dates will make your code clearer and will mean you can more easily change the date format in future (to something which doesn't sort as a string).
It works provided
You order the fields from most significant to least significant.
You use number fields (not Jan/Feb) and they are the same width. e.g. 2:15 is after 12:15 but 02:15 is before as expected. Note: For years after 9999 and before 0001 this will not work.
You accept that invalid dates may not be detected.
Use the type that it represents - in this case it's a date so use a date. Dates can easily be sorted chronologically by adding them to a collection and doing Collections.sort(dates) on the dates.
See http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html
I think Date is compared or can be compared through miliseconds (long) which is faster. It's probably safer way, you don't need to think about when string comparation would not fit.
I'm trynig to write a proto file that has a Date field which is not defined as a type into Protocol buffer.
I have read the following post but I couldn't figure out a proper solution that suits me :
What the best ways to use decimals and datetimes with protocol buffers?.
I'm trying to convert the proto file to a java .
My answer in the linked post relates mainly to protobuf-net; however, since you are coming at this from java I would recommend: keep it simple.
For dates, I would suggest just using the time (perhaps milliseconds) into an epoch (1 Jan 1970 is traditional). For times, just the size in that same unit (milliseconds etc). For decimal, maybe use fixed point simply by scaling - so maybe treat 1.05 as the long 1050 and assert always exactly 3dp (hence fixed point).
This is simple and pragmatic, and covers most common scenarios without making things complicated.
I'm not sold on this idea, but I'm really not sold on the idea of storing dates (which aren't instants in time) as a timestamp, so here's my suggestion.
Convert your date into a human-readable integer (e.g. 2014-11-3 becomes 20141103) and store this integer value. It contains exactly the data you need, is simple to create and parse, and takes up minimal space. Additionally, it is ordered and has a one-to-one mapping of dates to valid values (granted, invalid numbers are possible, such as 20149999, but these are easy to detect). In contrast, there are approximately 86400 valid timestamps that represent each day.
NB: There is a discussion on DBA SE criticizing this method of date storage, but in that context a specialized date type exists, which obviously isn't the case here.