package org.jblooming.agenda;
import org.jblooming.system.SystemConstants;
import org.jblooming.tracer.Tracer;
import org.jblooming.utilities.DateUtilities;
import org.jblooming.utilities.JSP;
import org.jblooming.waf.constants.Fields;
import org.jblooming.waf.constants.OperatorConstants;
import org.jblooming.waf.settings.ApplicationState;
import org.jblooming.ontology.SerializedList;
import java.util.*;
public class CompanyCalendar extends GregorianCalendar {
public static final long MILLIS_IN_MINUTE = 1000 * 60;
public static final long MILLIS_IN_HOUR = MILLIS_IN_MINUTE * 60;
public static final long MILLIS_IN_3_HOUR = MILLIS_IN_HOUR * 3;
public static final long MILLIS_IN_6_HOUR = MILLIS_IN_3_HOUR * 2;
public static final long MILLIS_IN_12_HOUR = MILLIS_IN_6_HOUR * 2;
public static final long MILLIS_IN_DAY = MILLIS_IN_HOUR * 24;
public static final long MILLIS_IN_WEEK = MILLIS_IN_DAY * 7;
public static final long MILLIS_IN_2_WEEK = MILLIS_IN_WEEK * 2;
public static final long MILLIS_IN_MONTH = (long) (MILLIS_IN_DAY * 30.4375);
public static final long MILLIS_IN_3_MONTH = MILLIS_IN_MONTH * 3;
public static final long MILLIS_IN_YEAR = (long) (MILLIS_IN_DAY * 365.25);
public static final long MILLIS_IN_2_YEAR = MILLIS_IN_YEAR * 2;
public static final long MILLIS_IN_5_YEAR = MILLIS_IN_YEAR * 5;
public static int ALLDAYS[] = {Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY,
Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY};
/**
* Set of MM_dd format dates
*/
private static SerializedList<String> holyDays = null;
public static int WORKING_HOUR_BEGIN = 8 * 60 * 60 * 1000;
public static int WORKING_HOUR_END = 20 * 60 * 60 * 1000;
public static int WORKING_HOUR_TOTAL = 8 * 60 * 60 * 1000;
/**
* first millisecond of day: always 0
*/
public final static int MIN_TIME = 0;
/**
* last millisecond of day: always 0
*/
public final static int MAX_TIME = 24 * 60 * 60 * 1000 - 1;
/**
* is the minimal date the system can handle
* this works between 970 ad and 2969 ad (SPQR)
* had to do it in this way as Long.MAX_VALUE does not work
*/
public final static Date MIN_DATE = new Date(-1000L * 3600L * 24L * 365L * 1000L);
/**
* is the max date the system can handle
*/
public final static Date MAX_DATE = new Date(1000L * 3600L * 24L * 365L * 1000L);
/**
* this variables are reset when the application read values from global.properties
*/
public static long MILLIS_IN_WORKING_DAY = MILLIS_IN_HOUR * 8;
public static boolean SATURDAY_IS_WORKING_DAY = false;
public static boolean SUNDAY_IS_WORKING_DAY = false;
public static boolean FRIDAY_IS_WORKING_DAY = true;
public static int WORKING_DAYS_PER_WEEK=5;
private Locale locale;
public CompanyCalendar(long millis) {
this(new Date(millis));
}
public CompanyCalendar(Date date) {
this();
setTime(date);
}
public CompanyCalendar() {
this(ApplicationState.SYSTEM_LOCALE);
}
public CompanyCalendar(Date date, Locale l) {
this(l);
setTime(date);
}
public CompanyCalendar(Locale l) {
super(l);
locale=l;
}
public static void setup(){
String val = ApplicationState.getApplicationSetting(SystemConstants.FRIDAY_IS_WORKING_DAY);
FRIDAY_IS_WORKING_DAY = !JSP.ex(val) || Fields.TRUE.equals(val);
SATURDAY_IS_WORKING_DAY = Fields.TRUE.equals(ApplicationState.getApplicationSetting(SystemConstants.SATURDAY_IS_WORKING_DAY));
SUNDAY_IS_WORKING_DAY = Fields.TRUE.equals(ApplicationState.getApplicationSetting(SystemConstants.SUNDAY_IS_WORKING_DAY));
try {
MILLIS_IN_WORKING_DAY = (int) DateUtilities.millisFromHourMinuteSmart(ApplicationState.getApplicationSetting(OperatorConstants.FLD_WORKING_HOUR_TOTAL));
} catch (NumberFormatException e) {
Tracer.platformLogger.warn("you wrote horrible things in FLD_WORKING_HOUR_TOTAL in global.properties: look at this " +
ApplicationState.getApplicationSetting(OperatorConstants.FLD_WORKING_HOUR_TOTAL));
}
WORKING_DAYS_PER_WEEK=7-(FRIDAY_IS_WORKING_DAY?0:1) - (SATURDAY_IS_WORKING_DAY?0:1) - (SUNDAY_IS_WORKING_DAY?0:1);
}
/**
* Is used to get the start working time
*
* @return The number of millis elapsed from midnight.
* If the operator do not have specific setting the general one is returned.
*/
public int getWorkingTimeStart() {
return CompanyCalendar.WORKING_HOUR_BEGIN;
}
/**
* Is used to get the end working time
*
* @return The number of millis elapsed from midnight.
* If the operator do not have specific setting the general one is returned.
*/
public int getWorkingTimeEnd() {
return WORKING_HOUR_END;
}
public Date setAndGetTimeToDayStart() {
this.set(Calendar.HOUR_OF_DAY, 0);
this.set(Calendar.MINUTE, 0);
this.set(Calendar.SECOND, 0);
this.set(Calendar.MILLISECOND, 0);
return this.getTime();
}
public Date setAndGetTimeToDayEnd() {
this.set(Calendar.HOUR_OF_DAY, 0);
this.set(Calendar.MINUTE, 0);
this.set(Calendar.SECOND, 0);
this.set(Calendar.MILLISECOND, 0);
//why at 999 it goes to next day ?
this.set(Calendar.MILLISECOND, 998);
this.set(Calendar.SECOND, 59);
this.set(Calendar.MINUTE, 59);
this.set(Calendar.HOUR_OF_DAY, 23);
return this.getTime();
}
public boolean isWorkingDay() {
return !(isSaturday() && !SATURDAY_IS_WORKING_DAY) &&
!(isSunday() && !SUNDAY_IS_WORKING_DAY) &&
!(isFriday() && !FRIDAY_IS_WORKING_DAY) &&
!isHolyDay();
}
public boolean isHolyDay() {
return isFixedHolyDay()|| isVariableHolyDay();
}
public boolean isFixedHolyDay() {
return getHolyDays().contains(DateUtilities.dateToString(getTime(), "MM_dd")) ;
}
public boolean isVariableHolyDay() {
return getHolyDays().contains(DateUtilities.dateToString(getTime(), "yyyy_MM_dd"));
}
public boolean isSaturday() {
return (get(DAY_OF_WEEK) == SATURDAY);
}
public boolean isSunday() {
return (get(DAY_OF_WEEK) == SUNDAY);
}
public boolean isFriday() {
return (get(DAY_OF_WEEK) == FRIDAY);
}
public void moveToClosestWorkingDay() {
moveToClosestWorkingDay(true);
}
public void moveToClosestWorkingDay(boolean forward) {
while (!isWorkingDay()) {
add(CompanyCalendar.DAY_OF_YEAR, forward ? 1 : -1);
}
}
public void addWorkingDays(int wd) {
boolean forward = wd >= 0;
int interval = forward ? 1 : -1;
for (int i = 0; i < Math.abs(wd); i++) {
add(CompanyCalendar.DAY_OF_YEAR, interval);
moveToClosestWorkingDay(forward);
}
}
public void setHoliday(boolean isFixed) {
String oc ="";
if (isFixed)
oc = DateUtilities.dateToString(getTime(), "MM_dd");
else
oc = DateUtilities.dateToString(getTime(), "yyyy_MM_dd");
if (!getHolyDays().contains(oc))
getHolyDays().add(oc);
}
public void removeHoliday() {
getHolyDays().remove(DateUtilities.dateToString(getTime(), "MM_dd"));
getHolyDays().remove(DateUtilities.dateToString(getTime(), "yyyy_MM_dd"));
}
/**
* >0
*/
public static int getWorkingDaysCountInPeriod(Period period, boolean onlyWorkingDaysComputation) {
int days = 1;
int watchDog = 10000;
if (period.getEnd() != null && period.getStart() != null) {
CompanyCalendar cc = new CompanyCalendar();
cc.setTime(period.getEnd());
Date end = cc.setAndGetTimeToDayStart();
cc.setTime(period.getStart());
cc.setAndGetTimeToDayStart();
while (cc.getTime().before(end)) {
// only workingDays are added
if (!onlyWorkingDaysComputation || (onlyWorkingDaysComputation && cc.isWorkingDay()))
days++;
if (days > watchDog) {
Tracer.platformLogger.error("getWorkingDaysCountInPeriod for a period with more then 10000 days -> forcing return 10000; period id=" + period.getId()+"; time interval "+period);
break;
}
cc.add(CompanyCalendar.DAY_OF_YEAR, 1);
}
} else
days = Integer.MAX_VALUE;
return days;
}
public static int getWorkingDaysCountInPeriod(Period period) {
return getWorkingDaysCountInPeriod(period, true);
}
/**
* teoros added
* @param date1
* @param date2
* @param onlyWorkingDaysComputation :: if true it computes only workingDays :: if false it computes the complete days interval
*/
public static int getDistanceInWorkingDays(Date date1, Date date2, boolean onlyWorkingDaysComputation) {
Period absPeriod = new Period(Math.min(date1.getTime(), date2.getTime()), Math.max(date1.getTime(), date2.getTime()));
int interval = getWorkingDaysCountInPeriod(absPeriod, onlyWorkingDaysComputation);
return interval * (date1.getTime() > date2.getTime() ? -1 : (date1.getTime() < date2.getTime() ? 1 : 0));
}
/**
* signed: may return negative, 0 , positive results
*/
public static int getDistanceInWorkingDays(Date date1, Date date2) {
return getDistanceInWorkingDays(date1, date2, true);
}
public boolean isToday() {
return isSameDay(new Date());
}
public boolean isSameDay(Date date) {
Calendar cal = new CompanyCalendar();
cal.setTime(date);
if ((this.get(Calendar.YEAR) == cal.get(Calendar.YEAR)) && (this.get(Calendar.DAY_OF_YEAR) == cal.get(Calendar.DAY_OF_YEAR)))
return true;
else
return false;
}
public static int getMillisFromMidnight(Date aTime) {
Calendar cal = new CompanyCalendar();
long endTime = aTime.getTime();
cal.setTime(aTime);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(MINUTE,0);
cal.set(SECOND,0);
cal.set(MILLISECOND,0);
return new Long(endTime - cal.getTime().getTime()).intValue();
}
public int getMillisFromMidnight() {
final Calendar cal = new CompanyCalendar();
cal.setTime(getTime());
cal.set(HOUR_OF_DAY, 0);
cal.set(MINUTE,0);
cal.set(SECOND,0);
cal.set(MILLISECOND,0);
return new Long(this.getTimeInMillis() - cal.getTimeInMillis()).intValue();
}
public void setMillisFromMidnight(int lotOfMillis) {
set(HOUR_OF_DAY, 0);
set(MINUTE,0);
set(SECOND,0);
set(MILLISECOND,0);
add(MILLISECOND, lotOfMillis);
}
/**
* @return the easter date for the current's year
* Gregorian
*/
public Date getEaster() {
int a, b, c, p, q, r;
int year = this.get(Calendar.YEAR);
a = year % 19;
b = (year / 100);
c = year % 100;
p = ((19 * a + b - (b / 4) - ((b - ((b + 8) / 25) + 1) / 3) + 15) % 30);
q = ((32 + 2 * ((b % 4) + (c / 4)) - p - (c % 4)) % 7);
r = (p + q - 7 * ((a + 11 * p + 22 * q) / 451) + 114);
CompanyCalendar cc = new CompanyCalendar();
cc.set(Calendar.YEAR, year);
cc.set(Calendar.MONTH, (r / 31) - 1);
cc.set(Calendar.DATE, (r % 31) + 1);
return cc.getTime();
}
public Date getDayAfterEaster() {
if (this.getEaster() != null) {
Calendar cc = new CompanyCalendar();
cc.setTime(this.getEaster());
cc.add(Calendar.DATE, 1);
return cc.getTime();
} else
return null;
}
public boolean isEaster() {
if (this.getEaster() != null)
return isSameDay(this.getEaster());
else
return false;
}
public boolean isDayAfterEaster() {
if (this.getDayAfterEaster() != null) {
return isSameDay(this.getDayAfterEaster());
} else
return false;
}
public SerializedList<String> getHolyDays() {
if (holyDays == null) {
holyDays = SerializedList.deserialize(ApplicationState.getApplicationSetting("HOLIDAYS_LIST"));
}
return holyDays;
}
/**
* put holidays in ApplicationSettings and dump it on file
*/
public void storeHolidays() {
if (holyDays != null) {
ApplicationState.applicationSettings.put("HOLIDAYS_LIST", holyDays.serialize());
ApplicationState.dumpApplicationSettings();
}
}
public static Date getFirstDayOfYear (int currentYear) {
CompanyCalendar cal = new CompanyCalendar();
cal.set(Calendar.YEAR, currentYear );
cal.set(Calendar.MONTH, Calendar.JANUARY );
cal.set(Calendar.DAY_OF_YEAR, 1 );
return cal.setAndGetTimeToDayStart();
}
public static Date getLastDayOfYear(int currentYear) {
CompanyCalendar cal = new CompanyCalendar();
cal.set(Calendar.YEAR, currentYear );
cal.set(Calendar.MONTH, Calendar.DECEMBER );
cal.set(Calendar.DAY_OF_YEAR, 365 );
return cal.setAndGetTimeToDayEnd();
}
/**
* changes calenday date moving forward until it meets dayOfWeek
* @param dayOfWeek
*/
public void moveToNextDay(int dayOfWeek) {
int dow = get(CompanyCalendar.DAY_OF_WEEK);
if (dow!=dayOfWeek){
add(CompanyCalendar.DAY_OF_YEAR,1);
moveToNextDay(dayOfWeek);
}
}
public Locale getLocale(){
return locale;
}
public String format(String format){
return DateUtilities.dateToString(getTime(),format);
}
}