At the End of the Day (Java Tip)
Of all clichés people over-use, the worst has to be "At the end of the day...". It always makes me think of David Beckham's insightful post-match analysis.
These past few days I've been struggling to get my tiny mind round the simple notion of the "end of the day" and how to calculate it in code.
Here in the UK we're all in the same time zone. Midnight is at the same time wherever you happen to be. If you need a website to work out whether a date-based deadline has passed, it's easy.
That's not so in America where the east coast is hours ahead of the west. If you have a cutoff date in America then you have to decide where in America the cutoff time applies. If you have a bookings management system and you stop taking bookings at midnight East Coast time then you'll anger those on the West Coast who unwittingly missed the deadline by a few hours.
To get round this you can use midnight at the last location in America where the day ends - Hawaii. Then you don't upset anybody.
The problem with Hawaii is that it doesn't observe Daylight Saving Time (DST), so working out the time in Hawaii, relative to your web server gets a bit tricky as it varies by an hour for half the year.
Time calculations need to be independent of the location of the server on which the code runs. The simplest way to do this is always use the "universal" GMT time at the meridian as the starting point.
As Hawaii doesn't do DST it is always 10 hours behind GMT. So we can work out the time in Hawaii using this code:
private Calendar getTimeInHawaii(){ //We always want to start with GMT so that DST isn't a factor TimeZone reference = TimeZone.getTimeZone("GMT"); Calendar hst = Calendar.getInstance(reference); hst.add(Calendar.HOUR, -10); return hst; }
We can then use the Hawaii time to work out if the day we're interested in has ended there by using a method like this one:
public boolean hasDateEndedInAmerica(Calendar date){ Calendar cutoffTimeInHawaii = getTimeInHawaii(); //Copy date (m, d, y) parts from closingDate! cutoffTimeInHawaii.set(Calendar.DATE, date.get(Calendar.DATE)); cutoffTimeInHawaii.set(Calendar.MONTH, date.get(Calendar.MONTH)); cutoffTimeInHawaii.set(Calendar.YEAR, date.get(Calendar.YEAR)); //Make sure it's the end of the day!! cutoffTimeInHawaii.set(Calendar.HOUR_OF_DAY, 23); cutoffTimeInHawaii.set(Calendar.MINUTE, 59); cutoffTimeInHawaii.set(Calendar.SECOND, 59); Calendar nowTimeInHawaii = getTimeInHawaii(); return nowTimeInHawaii.after(cutoffTimeInHawaii); }
Where usage is like this:
if ( hasDateEndedInAmerica( getNotesFieldValueAsCalendar(doc, "ClosingDate") ) ){ //don't allow code to proceed! }
In turn this relies on the following utility method to return a date (stored as text in mm/dd format) in a Notes field as a Calendar object:
public static Calendar getNotesFieldValueAsCalendar( Document doc, String fieldName ) throws Exception { DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Calendar cal = Calendar.getInstance(); cal.setTime((Date)formatter.parse(doc.getItemValueString(fieldName))); return cal; }
Although if you do the right thing and store dates in DateTime fields then that last part is a bit easier.
Summary
This might all seem a bit obvious to some, but to me it was a bit of a mind-bender and took me a while to realise how simple it becomes once you start using GMT as the starting point, which you do initialising your Calendar object with a GMT TimeZone reference. Easy after that.
Oh date/time fields are a whole can of worms. I've lost count of the number of times I've encountered "issues" with them. The old US vs UK date format is an old favourite.
Had an interesting one a few months back where time zones figured. Notes Tasks do not normally have a date component associated with them (unless you set a deadline). But they are given a default time value of the time you created the entry. A blank date value simply defaults to the universal default absolute (1st Jan 1970). This is not normally a problem. Except where time zones are concerned.
Creating a Task on an Australian server and viewing it on an American server can result in a date/time shift that can send a date field into a negative value. Servers don't like negative dates.
When you say "Time calculations need to be independent of the location of the server on which the code runs" you need to clarify it a bit. Shifting "Time" without a Date can raise issues. Though they are related Date and Time calculations should be handled with great care.
Reply
Fortunately for you I have a cold and I'm not quite up to a purely cliche based response. But time will tell.
I think the Names field is instructive here as a way to cope with time and date issues.
Best practice is to store canonical or at least abbreviated and then display common. The data is the data but the representation of the data needs to be human friendly.
So, store date-time info always with time zone info - the "fully qualified time" if you will. Logic then needs to take this into account when evaluating and displays are free to present a human friendly time.
Not so much thinking out of the box as taking a page from tried and true methods, but at the end of the day... it's horses for courses.
Reply
Google Spanner (see latest whitepaper) deals with dates and times in an interesting way. Maybe a little OTT for what is being discusssed!
Reply
Date-Time is a can of worms on any platform.
The current jdk has 3 implementations: java.util.Date, java.util.Calendar and java.sql.Date. For years now they are trying to add a fourth more simpler API to the jre api, based on joda-times, an openSource api, that has been used in 20% projects I worked in over the last 5 years (JSR-310). They say that it will be released in milestone 6 of java8 of january 2013.
A very pragmatic and good book about junit testing, Tomek Kaczanowski, Practical Unit Testing with TestNG and Mockito, dedicates an own chapter to the topic of Date-Time stuff.
Reply
As an occasional quiz-writer, I just thought I'd point this out: If you include the US Minor Outlying Islands, Midway Atoll is 1 hour behind Hawaii. Also American Samoa (a US unincorporated territory) is 1 hour behind Hawaii. Curiously, the most westerly point of the USA, by longitude, is on the Aleutian island of Attu, which is on the same time as Hawaii.
I am sad.
Phil
Reply