/* * Copyright (c) 2005-2011 Grameen Foundation USA * All rights reserved. * * Licensed 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. * * See also http://www.apache.org/licenses/LICENSE-2.0.html for an * explanation of the license and how it is applied. */ package org.mifos.calendar; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.mifos.application.admin.servicefacade.InvalidDateException; import org.mifos.application.meeting.util.helpers.RankOfDay; import org.mifos.config.FiscalCalendarRules; import org.mifos.framework.util.helpers.DateUtils; /** * Utility class contain side-effect free functions for processing date and calendar functionality. * * TODO - refactor all methods to use joda-time rather than java.util.Date and Calendar. */ public class CalendarUtils { /** * Set the day of week according to given start day to the require weekday, i.e. so it matches the meeting week day. * * e.g. - If start date is Monday 9 June 2008 and meeting week day is Tuesday, then roll forward the date to Tuesday * 10 June 2008 - or if start date is Sunday 8 June 2008 and meeting week day is Saturday, then roll forward the * date to Saturday 14 June 2008 - or if start date is Tuesday 10 2008 June and meeting week day is Monday, then * roll forward the date to Monday 16 June 2008 - or if start date is Sunday 8 June 2008 and meeting week day is * Sunday, then keep the date as Sunday 8 June 2008 - or if start date is Saturday 7 June 2008 and meeting week day * is Sunday, then roll forward the date to Sunday 9 June 2008. */ public static DateTime getFirstDateForWeek(final DateTime startDate, final int dayOfWeek) { /* * In Joda time MONDAY=1 and SUNDAY=7, so shift these to SUNDAY=1, SATURDAY=7 to match this class */ int calendarDayOfWeek = (dayOfWeek % 7) + 1; final GregorianCalendar firstDateForWeek = new GregorianCalendar(); firstDateForWeek.setTime(startDate.toDate()); int startDateWeekDay = firstDateForWeek.get(Calendar.DAY_OF_WEEK); int amountOfDaysToAdd = calendarDayOfWeek - startDateWeekDay; if (amountOfDaysToAdd < 0) { amountOfDaysToAdd += 7; } firstDateForWeek.add(Calendar.DAY_OF_WEEK, amountOfDaysToAdd); return new DateTime(firstDateForWeek.getTime()); } /** * for monthly on date return the next date falling on the same day. If date has passed, pass in the date of next * month, adjust to day number if day number exceed total number of days in month */ public static DateTime getFirstDateForMonthOnDate(final DateTime startDate, final int dayOfMonth) { final GregorianCalendar gc = new GregorianCalendar(); gc.setTime(startDate.toDate()); int dt = gc.get(GregorianCalendar.DATE); // if date passed in, is after the date on which schedule has to // lie, move to next month if (dt > dayOfMonth) { gc.add(GregorianCalendar.MONTH, 1); } // set the date on which schedule has to lie int M1 = gc.get(GregorianCalendar.MONTH); gc.set(GregorianCalendar.DATE, dayOfMonth); int M2 = gc.get(GregorianCalendar.MONTH); int daynum = dayOfMonth; while (M1 != M2) { gc.set(GregorianCalendar.MONTH, gc.get(GregorianCalendar.MONTH) - 1); gc.set(GregorianCalendar.DATE, daynum - 1); M2 = gc.get(GregorianCalendar.MONTH); daynum--; } return new DateTime(gc.getTime()); } public static DateTime getFirstDayForMonthUsingWeekRankAndWeekday(final DateTime startDate, final int calendarWeekOfMonth, final int dayOfWeek) { /* * In Joda time MONDAY=1 and SUNDAY=7, so shift these to SUNDAY=1, SATURDAY=7 to match this class */ int calendarDayOfWeek = (dayOfWeek % 7) + 1; final GregorianCalendar gc = new GregorianCalendar(); gc.setTime(startDate.toDate()); DateTime scheduleDate = null; // if current weekday is after the weekday on which schedule has to // lie, move to next week if (gc.get(Calendar.DAY_OF_WEEK) > calendarDayOfWeek) { gc.add(Calendar.WEEK_OF_MONTH, 1); } // set the weekday on which schedule has to lie gc.set(Calendar.DAY_OF_WEEK, calendarDayOfWeek); // if week rank is First, Second, Third or Fourth, Set the // respective week. // if current week rank is after the weekrank on which schedule has // to lie, move to next month if (!RankOfDay.getRankOfDay(calendarWeekOfMonth).equals(RankOfDay.LAST)) { if (gc.get(Calendar.DAY_OF_WEEK_IN_MONTH) > calendarWeekOfMonth) { gc.add(GregorianCalendar.MONTH, 1); gc.set(GregorianCalendar.DATE, 1); } // set the weekrank on which schedule has to lie gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, calendarWeekOfMonth); scheduleDate = new DateTime(gc.getTime().getTime()); } else {// scheduleData.getWeekRank()=Last int M1 = gc.get(GregorianCalendar.MONTH); // assumption: there are 5 weekdays in the month gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 5); int M2 = gc.get(GregorianCalendar.MONTH); // if assumption fails, it means there exists 4 weekdays in a // month, return last weekday date // if M1==M2, means there exists 5 weekdays otherwise 4 weekdays // in a month if (M1 != M2) { gc.set(GregorianCalendar.MONTH, gc.get(GregorianCalendar.MONTH) - 1); gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 4); } scheduleDate = new DateTime(gc.getTime().getTime()); } return scheduleDate; } public static DateTime getNextDateForDay(final DateTime startDate, final int every) { final GregorianCalendar gc = new GregorianCalendar(); gc.setTime(startDate.toDate()); gc.add(Calendar.DAY_OF_WEEK, every); return new DateTime(gc.getTime().getTime()); } public static DateTime getNextDateForMonthOnDate(final DateTime startDate, final int dayOfMonth, final int every) { final GregorianCalendar gc = new GregorianCalendar(); gc.setTime(startDate.toDate()); gc.add(GregorianCalendar.MONTH, every); int M1 = gc.get(GregorianCalendar.MONTH); gc.set(GregorianCalendar.DATE, dayOfMonth); int M2 = gc.get(GregorianCalendar.MONTH); int daynum = dayOfMonth; while (M1 != M2) { gc.set(GregorianCalendar.MONTH, gc.get(GregorianCalendar.MONTH) - 1); gc.set(GregorianCalendar.DATE, daynum - 1); M2 = gc.get(GregorianCalendar.MONTH); daynum--; } return new DateTime(gc.getTime().getTime()); } public static DateTime getNextDayForMonthUsingWeekRankAndWeekday(final DateTime startDate, final int weekOfMonth, final int dayOfWeek, final int every) { /* * In Joda time MONDAY=1 and SUNDAY=7, so shift these to SUNDAY=1, SATURDAY=7 to match this class */ int calendarDayOfWeek = (dayOfWeek % 7) + 1; final GregorianCalendar gc = new GregorianCalendar(); gc.setTime(startDate.toDate()); DateTime scheduleDate; if (!RankOfDay.getRankOfDay(weekOfMonth).equals(RankOfDay.LAST)) { // apply month recurrence gc.add(GregorianCalendar.MONTH, every); gc.set(Calendar.DAY_OF_WEEK, calendarDayOfWeek); gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, weekOfMonth); scheduleDate = new DateTime(gc.getTime().getTime()); } else {// weekCount=-1 gc.set(GregorianCalendar.DATE, 15); gc.add(GregorianCalendar.MONTH, every); gc.set(Calendar.DAY_OF_WEEK, calendarDayOfWeek); int M1 = gc.get(GregorianCalendar.MONTH); // assumption: there are 5 weekdays in the month gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 5); int M2 = gc.get(GregorianCalendar.MONTH); // if assumption fails, it means there exists 4 weekdays in a // month, return last weekday date // if M1==M2, means there exists 5 weekdays otherwise 4 weekdays // in a month if (M1 != M2) { gc.set(GregorianCalendar.MONTH, gc.get(GregorianCalendar.MONTH) - 1); gc.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, 4); } scheduleDate = new DateTime(gc.getTime().getTime()); } return scheduleDate; } public static DateTime nearestDayOfWeekTo(final int dayOfWeek, final DateTime date) { DateTime withDayOfWeek = date.withDayOfWeek(dayOfWeek); if (date.getYear() == withDayOfWeek.getYear()) { if (date.getDayOfYear() > withDayOfWeek.getDayOfYear()) { return withDayOfWeek.plusWeeks(1); } return withDayOfWeek; } // back a year if (date.getYear() > withDayOfWeek.getYear()) { return withDayOfWeek.plusWeeks(1); } return withDayOfWeek; } public static List<DateTime> convertListOfDatesToDateTimes(final List<Date> meetingDates) { List<DateTime> convertedDates = new ArrayList<DateTime>(); for (Date meeting : meetingDates) { convertedDates.add(new DateTime(meeting)); } return convertedDates; } public static DateTime getDateFromString(String strDate, Locale locale) throws InvalidDateException { if (StringUtils.isBlank(strDate)) { throw new IllegalArgumentException("strDate cannot be null or empty"); } return new DateTime(DateUtils.getLocaleDate(locale, strDate).getTime()); } public static DateTime nearestWorkingDay(DateTime day) { if (isWorkingDay(day)) { return day; } DateTime nearestWorkingDay = day; do { nearestWorkingDay = nearestWorkingDay.plusDays(1); } while (!isWorkingDay(nearestWorkingDay)); return nearestWorkingDay; } private static boolean isWorkingDay(DateTime day) throws RuntimeException { return new FiscalCalendarRules().isWorkingDay(DateUtils.getCalendar(day.toDate())); } }