/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.jasig.portlet.calendar.util; import java.util.Date; import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.time.FastDateFormat; /** * AllDayUtil determines whether a particular event should be classified as an "all-day" event in * the user's time zone. This implementation classifies an all-day event as starting at 12:00:00 AM * in the user's time zone, ends at 12:00:00 AM, and lasts approximately one day. * * @author Jen Bourey, jbourey@unicon.net * @version $Revision$ */ public class AllDayUtil { private static final String TIME_FORMAT = "Hms"; private static final String EXPECTED_TIME = "000"; private static final int MIN_DAY = 22 * 60 * 60 * 1000; // 22 hours private static final int MAX_DAY = 26 * 60 * 60 * 1000; // 26 hours /** Internal cache of timezone-specific date format objects */ private static ConcurrentHashMap<TimeZone, FastDateFormat> dateFormatCache = new ConcurrentHashMap<TimeZone, FastDateFormat>(); /** * Determine if a given event is an "all-day" event in the specified time zone. * * @param event * @param timezone * @return <code>true</code> for all-day events, <code>false</code> otherwise */ public static boolean isAllDayEvent(Date startDate, Date endDate, TimeZone timezone) { /** * Get a DateFormat instance for the current user's time zone from the cache. If none exists, * create a new one and add it to the cache */ FastDateFormat df; if (dateFormatCache.contains(timezone)) { df = dateFormatCache.get(timezone); } else { df = FastDateFormat.getInstance(TIME_FORMAT, timezone); dateFormatCache.put(timezone, df); } /** * Check if this event starts at 12:00:00 AM in the user's time zone. We currently convert the * event start date to a short string that encodes the hour, minute, and second in the indicated * timezone, then compare that to the expected string. */ String start = df.format(startDate); if (!EXPECTED_TIME.equals(start)) { return false; } /** * Check if the event ends at 12:00:00 AM the next day and if the duration of the event suggests * the end date is midnight one day after the start date. * * <p>Note: We've elected to use this approach rather than simply checking the length of the * event against exactly 24 hours to handle potential complications like daylight savings time * changes and leap seconds. */ if (endDate == null) { return true; } // check the end time of the event String end = df.format(endDate); if (!EXPECTED_TIME.equals(end)) { return true; } // get the duration of this event in milliseconds long duration = endDate.getTime() - startDate.getTime(); // check the duration against our max and min fields if (duration < MIN_DAY || duration > MAX_DAY) { return false; } // if the tests above passed, return true return true; } }