Details
-
Type:
Improvement
-
Status: Closed
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: EE-4.0.0.GA, EE-3.3.0.GA_P03
-
Fix Version/s: EE-4.1.0.RC1, EE-4.1.0.GA, EE-3.3.0.GA_P04, 4.2.BETA, 4.2
-
Component/s: ACE-Components
-
Labels:None
-
Environment:jsf2 ace
-
Assignee Priority:P2
-
Support Case References:Support Case 13652:-https://icesoft.my.salesforce.com/5007000001XOeY6
Description
Currently when using the popup version of the ace:dateTimeEntry widget, the "Now" button does not honor the timeZone entry.
change Showcase "popup" example of this component to:-
<h:panelGrid style="margin-left: auto; margin-right: auto; text-align: center;" width="100">
<ace:dateTimeEntry id="cal"
value="#{datePopup.selectedDate}"
timeZone="#{datePopup.selectedTimeZone} "
pattern="MMM/dd/yyyy hh:mm z"
renderAsPopup="#{datePopup.popup}">
</ace:dateTimeEntry>
<h:commandButton id="submit" value="Submit Date"/>
and allow the user to select a timeZone:-
<h:panelGroup>
<h:outputLabel for="tzinput" value="Select TimeZone:-"/>
<h:selectOneMenu id="tzinput" value="#{datePopup.selectedTimeZone}">
<f:selectItems value="#{datePopup.timeZones}"/>
</h:selectOneMenu>
</h:panelGroup>
....
with DatePopupBean:-
(in constructor)...
String[] ids = TimeZone.getAvailableIDs();
for (String id : ids){
timeZones.add(new SelectItem(id, displayTimeOffset(id) ));
}
public String displayTimeOffset(String id){
TimeZone zone = TimeZone.getTimeZone(id);
long hours = TimeUnit.MILLISECONDS.toHours(zone.getRawOffset());
long minutes = TimeUnit.MILLISECONDS.toMinutes(zone.getRawOffset())
- TimeUnit.HOURS.toMinutes(hours);
minutes = Math.abs(minutes);
String result = "";
String hrs = String.valueOf(hours) ;
// System.out.println(" hours="+hours+" hrs="+hrs);
if (hours > 0) {
result = "(GMT+"+hrs+":"+hrs+") "+ String.valueOf(minutes);
} else {
result = "(GMT"+hrs+":"+hrs+")"+ String.valueOf(minutes);
}
return result;
}
...
when you use the "now" button on the popup of the widget, you can change the timeZone and then select a time, but the "Now" button will always reflect the "Now" for the browser locale.
Should this not reflect the "Now" in the timeZone that is currently selected? OTherwise, do we improve it to have another attribute that forces this?
change Showcase "popup" example of this component to:-
<h:panelGrid style="margin-left: auto; margin-right: auto; text-align: center;" width="100">
<ace:dateTimeEntry id="cal"
value="#{datePopup.selectedDate}"
timeZone="#{datePopup.selectedTimeZone} "
pattern="MMM/dd/yyyy hh:mm z"
renderAsPopup="#{datePopup.popup}">
</ace:dateTimeEntry>
<h:commandButton id="submit" value="Submit Date"/>
and allow the user to select a timeZone:-
<h:panelGroup>
<h:outputLabel for="tzinput" value="Select TimeZone:-"/>
<h:selectOneMenu id="tzinput" value="#{datePopup.selectedTimeZone}">
<f:selectItems value="#{datePopup.timeZones}"/>
</h:selectOneMenu>
</h:panelGroup>
....
with DatePopupBean:-
(in constructor)...
String[] ids = TimeZone.getAvailableIDs();
for (String id : ids){
timeZones.add(new SelectItem(id, displayTimeOffset(id) ));
}
public String displayTimeOffset(String id){
TimeZone zone = TimeZone.getTimeZone(id);
long hours = TimeUnit.MILLISECONDS.toHours(zone.getRawOffset());
long minutes = TimeUnit.MILLISECONDS.toMinutes(zone.getRawOffset())
- TimeUnit.HOURS.toMinutes(hours);
minutes = Math.abs(minutes);
String result = "";
String hrs = String.valueOf(hours) ;
// System.out.println(" hours="+hours+" hrs="+hrs);
if (hours > 0) {
result = "(GMT+"+hrs+":"+hrs+") "+ String.valueOf(minutes);
} else {
result = "(GMT"+hrs+":"+hrs+")"+ String.valueOf(minutes);
}
return result;
}
...
when you use the "now" button on the popup of the widget, you can change the timeZone and then select a time, but the "Now" button will always reflect the "Now" for the browser locale.
Should this not reflect the "Now" in the timeZone that is currently selected? OTherwise, do we improve it to have another attribute that forces this?
This enhancement is not as simple as it seemed at first. The main issue is accounting for Daylight Savings Time (DST) in a reliable and accurate approach. While failing to account for the DST accurately would only result in a difference of one hour, in production environments this could have negative consequences that could be critical, depending on the nature of the application.
There's no native support for multiple time zones in Javascript. The browser is only aware of the local time zone. The naive approach to converting a Date to a time zone that is different to the local one would look like this.
Basically, the approach consists in calculating the UTC time (which we know that does not observe DST), and then we add (or subtract) the time zone offset of the target location. The Date.getTimezoneOffset() method accounts for DST (relying on the host OS), which leads us to an accurate UTC time. However, the browser doesn't have any DST information on the target location's time zone. Because the dates and times at which the various time zones enter and leave the DST can greatly vary, it's not possible to reliably calculate a date or time in a time zone (different that the one used by the browser) without external help.
There are two possible approaches to know the target location's time zone offset while accounting for DST; one of them involves the server and the other one requires an additional Javascript library.
The server approach consists in using the java.util.TimeZone class. We would call TimeZone.getOffset() to obtain the time zone offset in milliseconds (this value would replace the 'offsetTarget_msec' variable above), and we would always send this value to the Calendar constructor in the client. This class and method account for DST, as can be seen in the API documentation:
https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getOffset(int,%20int,%20int,%20int,%20int,%20int)
The server approach is simple enough, but there's an edge case in which the offset wouldn't be accurate: If the page is loaded minutes before the hour where the DST comes into effect in a given time zone, and then the user presses the 'Now' button a minute after the DST comes into effect, then the offset won't be accurate, and it will be off by one hour.
The other approach doesn't require the server help and doesn't have the edge case problem described in the last paragraph, but it requires an additional Javascript library called moment.js. This library was specifically created to parse, validate, manipulate and display dates in any time zone. It uses the MIT license and seems to be getting widely adopted in the industry. This library contains all the information for all the time zones in the world and accounts for DST accurately to the minute, as can be seen in this reference page:
http://momentjs.com/timezone/docs/#/zone-object/parse/
These two approaches are solid enough, however they still rely on the user's machine to calculate the UTC time. If the user's machine's clock is not accurate, then the time set by the 'Now' button won't be accurate either. This situation could be improved by having a servlet that serves the server time or that references some centralized time authority. However, that might be too much for the scope of this component. Applications where time is critical may prefer to implement their own methods to ensure that the selected time is accurate, anyway. It's also easy to add a separate button to a page to set the value of the ace:dateTimeEntry component to another time zone's time, based on the server time at that moment.