/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * 
 */
package jp.mosp.time.bean.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import jp.mosp.framework.base.MospException;
import jp.mosp.framework.utils.DateUtility;
import jp.mosp.platform.bean.workflow.WorkflowReferenceBeanInterface;
import jp.mosp.platform.constant.PlatformConst;
import jp.mosp.platform.dto.workflow.WorkflowDtoInterface;
import jp.mosp.time.base.TimeBean;
import jp.mosp.time.bean.ApplicationReferenceBeanInterface;
import jp.mosp.time.bean.AttendanceCalcBeanInterface;
import jp.mosp.time.bean.AttendanceReferenceBeanInterface;
import jp.mosp.time.bean.DifferenceRequestReferenceBeanInterface;
import jp.mosp.time.bean.GoOutReferenceBeanInterface;
import jp.mosp.time.bean.HolidayRequestReferenceBeanInterface;
import jp.mosp.time.bean.OvertimeRequestReferenceBeanInterface;
import jp.mosp.time.bean.RestReferenceBeanInterface;
import jp.mosp.time.bean.ScheduleDateReferenceBeanInterface;
import jp.mosp.time.bean.ScheduleReferenceBeanInterface;
import jp.mosp.time.bean.SubHolidayRequestReferenceBeanInterface;
import jp.mosp.time.bean.SubstituteReferenceBeanInterface;
import jp.mosp.time.bean.TimeSettingReferenceBeanInterface;
import jp.mosp.time.bean.WorkOnHolidayRequestReferenceBeanInterface;
import jp.mosp.time.bean.WorkTypeItemReferenceBeanInterface;
import jp.mosp.time.bean.WorkTypeReferenceBeanInterface;
import jp.mosp.time.constant.TimeConst;
import jp.mosp.time.constant.TimeMessageConst;
import jp.mosp.time.dto.settings.ApplicationDtoInterface;
import jp.mosp.time.dto.settings.AttendanceDtoInterface;
import jp.mosp.time.dto.settings.DifferenceRequestDtoInterface;
import jp.mosp.time.dto.settings.GoOutDtoInterface;
import jp.mosp.time.dto.settings.HolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.OvertimeRequestDtoInterface;
import jp.mosp.time.dto.settings.RestDtoInterface;
import jp.mosp.time.dto.settings.ScheduleDateDtoInterface;
import jp.mosp.time.dto.settings.ScheduleDtoInterface;
import jp.mosp.time.dto.settings.SubHolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.SubstituteDtoInterface;
import jp.mosp.time.dto.settings.TimeSettingDtoInterface;
import jp.mosp.time.dto.settings.WorkOnHolidayRequestDtoInterface;
import jp.mosp.time.dto.settings.WorkTypeDtoInterface;
import jp.mosp.time.dto.settings.WorkTypeItemDtoInterface;

/**
 * 勤怠データ自動計算クラス。
 */
public class AttendanceCalcBean extends TimeBean implements AttendanceCalcBeanInterface {
	
	/**
	 * 勤務日
	 */
	protected Date										workDate;
	
	/**
	 * 出勤時刻
	 */
	protected Date										startTime;
	
	/**
	 * 退勤時刻
	 */
	protected Date										endTime;
	
	/**
	 * 直行
	 */
	protected boolean									directStart;
	
	/**
	 * 直帰
	 */
	protected boolean									directEnd;
	
	/**
	 * 遅刻理由
	 */
	protected String									lateReason;
	
	/**
	 * 早退理由
	 */
	protected String									leaveEarlyReason;
	
	/**
	 * 勤務時間
	 */
	protected int										workTime;
	
	/**
	 * 所定労働時間
	 */
	protected int										prescribedWorkTime;
	
	/**
	 * 休暇時間
	 */
	protected int										totalRest;
	
	/**
	 * 公用外出
	 */
	protected int										totalPublic;
	
	/**
	 * 遅刻
	 */
	protected int										lateTime;
	
	/**
	 * 早退
	 */
	protected int										leaveEarlyTime;
	
	/**
	 * 私用外出
	 */
	protected int										totalPrivate;
	
	/**
	 * 休憩リスト
	 */
	protected List<RestDtoInterface>					restDtoList;
	
	/**
	 * 公用外出リスト
	 */
	protected List<GoOutDtoInterface>					publicGoOutDtoList;
	
	/**
	 * 私用外出リスト
	 */
	protected List<GoOutDtoInterface>					privateGoOutDtoList;
	
	/**
	 * 減額対象時間
	 */
	protected int										decreaseTime;
	
	/**
	 * 勤怠設定管理DTOインターフェース
	 */
	protected TimeSettingDtoInterface					timeSettingDto;
	
	/**
	 * 勤務形態項目管理DTOインターフェース
	 */
	protected WorkTypeItemDtoInterface					workTypeItemDto;
	
	/**
	 * 休日出勤申請DTOインターフェース
	 */
	protected WorkOnHolidayRequestDtoInterface			workOnHolidayDto;
	
	/**
	 * 休暇申請DTOインターフェースリスト
	 */
	protected List<HolidayRequestDtoInterface>			holidayRequestDtoList;
	
	/**
	 * 代休申請DTOインターフェースリスト
	 */
	protected List<SubHolidayRequestDtoInterface>		subHolidayRequestDtoList;
	
	/**
	 * カレンダマスタDTOインターフェース
	 */
	protected ScheduleDtoInterface						scheduleDto;
	
	/**
	 * 時差出勤申請DTOインターフェース
	 */
	protected DifferenceRequestDtoInterface				differenceDto;
	
	/**
	 * カレンダ日マスタDTOインターフェース
	 */
	protected ScheduleDateDtoInterface					scheduleDateDto;
	
	/**
	 * 残業申請DTOインターフェース
	 */
	protected OvertimeRequestDtoInterface				beforeOvertimeDto;
	/**
	 * 公用外出時間
	 */
	protected int										totalPublicTime;
	
	/**
	 * 残業時間
	 */
	protected int										overtimeTime;
	
	/**
	 * 規定始業時刻
	 */
	protected int										regWorkStart;
	
	/**
	 * 規定終業時刻
	 */
	protected int										regWorkEnd;
	
	/**
	 * 規定労働時間
	 */
	protected int										regWorkTime;
	
	/**
	 * 規定フル労働時間
	 */
	protected int										regFullWorkTime;
	
	/**
	 * 残前休憩
	 */
	protected int										overbefore;
	
	/**
	 * 残業休憩時間(毎)
	 */
	protected int										overper;
	
	/**
	 * 残業休憩時間
	 */
	protected int										overrest;
	
	/**
	 * 法定外残業時間
	 */
	protected int										overTimeOut;
	
	/**
	 * 法定外休憩時間
	 */
	protected int										overRestTime;
	
	/**
	 * 深夜勤務時間
	 */
	protected int										nightWork;
	
	/**
	 * 深夜休憩時間
	 */
	protected int										nightRest;
	
	/**
	 * 深夜労働時間配列
	 */
	protected int[]										nightWorkArray;
	
	/**
	 * 深夜休憩時間配列
	 */
	protected int[]										nightRestArray;
	
	/**
	 * 所定休日勤務時間
	 */
	protected int										predeterminedHolidayWorkTime;
	
	/**
	 * 法定休日勤務時間
	 */
	protected int										legalHolidayWorkTime;
	
	/**
	 * 法内残業
	 */
	protected int										withinStatutoryOvertime;
	
	/**
	 * 規定始業時刻前時間
	 */
	protected int										workBeforeTime;
	
	/**
	 * 規定終業時刻後時間
	 */
	protected int										workAfterTime;
	
	/**
	 * 勤怠計算上の始業時刻
	 */
	protected int										calculatedStart;
	
	/**
	 * 勤怠計算上の終業時刻
	 */
	protected int										calculatedEnd;
	
	/**
	 * 時間外計算上の始業時刻
	 */
	protected int										startTimeOfOvertimeCalculation;
	
	/**
	 * 自動休憩計算開始時刻
	 */
	protected int										autoRestCalcStart;
	
	/**
	 * 遅刻休憩
	 */
	protected Map<Date, Date>							tardinessRestMap;
	
	/**
	 * 早退休憩
	 */
	protected Map<Date, Date>							leaveEarlyRestMap;
	
	/**
	 * 規定終業時刻後時間外前休憩
	 */
	protected Map<Date, Date>							overtimeBeforeRestMap;
	
	/**
	 * 規定終業時刻後時間外休憩
	 */
	protected Map<Date, Date>							overtimeRestMap;
	
	/**
	 * 勤務形態コード
	 */
	protected String									workTypeCode;
	
	/**
	 * 翌日の勤務形態コード
	 */
	protected String									nextDayWorkTypeCode;
	
	/**
	 * 設定適用管理参照インターフェース。<br>
	 */
	private ApplicationReferenceBeanInterface			applicationReference;
	/**
	 * 勤怠設定参照インターフェース。<br>
	 */
	private TimeSettingReferenceBeanInterface			timeSettingReference;
	/**
	 * カレンダ管理参照インターフェース。<br>
	 */
	private ScheduleReferenceBeanInterface				scheduleReference;
	/**
	 * カレンダ日参照インターフェース。<br>
	 */
	private ScheduleDateReferenceBeanInterface			scheduleDateReference;
	/**
	 * 勤怠データ参照インターフェース。<br>
	 */
	private AttendanceReferenceBeanInterface			attendanceReference;
	/**
	 * 残業申請参照インターフェース。<br>
	 */
	private OvertimeRequestReferenceBeanInterface		overtimeReference;
	/**
	 * 休暇申請参照インターフェース。<br>
	 */
	private HolidayRequestReferenceBeanInterface		holidayReference;
	/**
	 * 代休申請参照インターフェース。<br>
	 */
	private SubHolidayRequestReferenceBeanInterface		subHolidayReference;
	/**
	 * 時差出勤申請参照インターフェース。<br>
	 */
	private DifferenceRequestReferenceBeanInterface		differenceReference;
	/**
	 * 休日出勤申請参照インターフェース。<br>
	 */
	private WorkOnHolidayRequestReferenceBeanInterface	workOnHolidayReference;
	/**
	 * 振替休日データ参照インターフェース。<br>
	 */
	private SubstituteReferenceBeanInterface			substituteReference;
	/**
	 * ワークフロー参照インターフェース。<br>
	 */
	private WorkflowReferenceBeanInterface				workflowReference;
	/**
	 * 勤務形態項目参照インターフェース。<br>
	 */
	protected WorkTypeItemReferenceBeanInterface		workTypeItemReference;
	/**
	 * 勤務形態マスタ参照インターフェース。<br>
	 */
	protected WorkTypeReferenceBeanInterface			workTypeReference;
	/**
	 * 勤怠データ休憩情報参照インターフェース。<br>
	 */
	private RestReferenceBeanInterface					restReference;
	/**
	 * 勤怠データ外出情報参照インターフェース。<br>
	 */
	private GoOutReferenceBeanInterface					goOutReference;
	

	@Override
	public void initBean() throws MospException {
		applicationReference = (ApplicationReferenceBeanInterface)createBean(ApplicationReferenceBeanInterface.class);
		timeSettingReference = (TimeSettingReferenceBeanInterface)createBean(TimeSettingReferenceBeanInterface.class);
		scheduleReference = (ScheduleReferenceBeanInterface)createBean(ScheduleReferenceBeanInterface.class);
		scheduleDateReference = (ScheduleDateReferenceBeanInterface)createBean(ScheduleDateReferenceBeanInterface.class);
		attendanceReference = (AttendanceReferenceBeanInterface)createBean(AttendanceReferenceBeanInterface.class);
		overtimeReference = (OvertimeRequestReferenceBeanInterface)createBean(OvertimeRequestReferenceBeanInterface.class);
		holidayReference = (HolidayRequestReferenceBeanInterface)createBean(HolidayRequestReferenceBeanInterface.class);
		subHolidayReference = (SubHolidayRequestReferenceBeanInterface)createBean(SubHolidayRequestReferenceBeanInterface.class);
		differenceReference = (DifferenceRequestReferenceBeanInterface)createBean(DifferenceRequestReferenceBeanInterface.class);
		workOnHolidayReference = (WorkOnHolidayRequestReferenceBeanInterface)createBean(WorkOnHolidayRequestReferenceBeanInterface.class);
		substituteReference = (SubstituteReferenceBeanInterface)createBean(SubstituteReferenceBeanInterface.class);
		workflowReference = (WorkflowReferenceBeanInterface)createBean(WorkflowReferenceBeanInterface.class);
		workTypeItemReference = (WorkTypeItemReferenceBeanInterface)createBean(WorkTypeItemReferenceBeanInterface.class);
		workTypeReference = (WorkTypeReferenceBeanInterface)createBean(WorkTypeReferenceBeanInterface.class);
		restReference = (RestReferenceBeanInterface)createBean(RestReferenceBeanInterface.class);
		goOutReference = (GoOutReferenceBeanInterface)createBean(GoOutReferenceBeanInterface.class);
	}
	
	/**
	 * @param personalId 個人ID
	 * @param workTypeCode 勤務形態コード
	 * @throws MospException  SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	public void initAttendanceTotal(String personalId, final String workTypeCode) throws MospException {
		// 設定適用マスタDTOインターフェース
		ApplicationDtoInterface applicationDto = applicationReference.findForPerson(personalId, workDate);
		applicationReference.chkExistApplication(applicationDto, workDate);
		if (mospParams.hasErrorMessage()) {
			// 該当する設定適用が存在しない
			return;
		}
		// カレンダマスタDTOインターフェース
		ScheduleDtoInterface scheduleDto = scheduleReference
			.getScheduleInfo(applicationDto.getScheduleCode(), workDate);
		scheduleReference.chkExistSchedule(scheduleDto, workDate);
		if (mospParams.hasErrorMessage()) {
			// 該当するカレンダマスタが存在しない
			return;
		}
		// 勤怠設定
		timeSettingDto = timeSettingReference.getTimeSettingInfo(applicationDto.getWorkSettingCode(), workDate);
		timeSettingReference.chkExistTimeSetting(timeSettingDto, workDate);
		if (mospParams.hasErrorMessage()) {
			// 該当する勤怠設定が存在しない
			return;
		}
		// カレンダ日マスタDTOインターフェース
		scheduleDateDto = scheduleDateReference.getScheduleDateInfo(scheduleDto.getScheduleCode(), workDate, workDate);
		scheduleDateReference.chkExistScheduleDate(scheduleDateDto, workDate);
		if (mospParams.hasErrorMessage()) {
			// 該当するカレンダマスタが存在しない
			return;
		}
		// 残業申請
		if (timeSettingDto.getBeforeOvertimeFlag() == TimeConst.CODE_BEFORE_OVERTIME_VALID) {
			// 始業前の時間外労働が有効である場合
			OvertimeRequestDtoInterface overtimeRequestDto = overtimeReference.findForKeyOnWorkflow(personalId,
					workDate, 1);
			if (overtimeRequestDto != null) {
				WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(overtimeRequestDto
					.getWorkflow());
				if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
					// 承認完了の場合
					beforeOvertimeDto = overtimeRequestDto;
				}
			}
		}
		// 休暇申請
		holidayRequestDtoList = new ArrayList<HolidayRequestDtoInterface>();
		List<HolidayRequestDtoInterface> holidayRequestList = holidayReference.getHolidayRequestList(personalId,
				workDate);
		for (HolidayRequestDtoInterface requestDto : holidayRequestList) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(requestDto.getWorkflow());
			if (workflowDto == null) {
				continue;
			}
			if (PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				// 承認完了の場合
				holidayRequestDtoList.add(requestDto);
			}
		}
		// 代休申請
		subHolidayRequestDtoList = new ArrayList<SubHolidayRequestDtoInterface>();
		List<SubHolidayRequestDtoInterface> subHolidayRequestList = subHolidayReference.getSubHolidayRequestList(
				personalId, workDate);
		for (SubHolidayRequestDtoInterface requestDto : subHolidayRequestList) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(requestDto.getWorkflow());
			if (workflowDto == null) {
				continue;
			}
			if (PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				// 承認完了の場合
				subHolidayRequestDtoList.add(requestDto);
			}
		}
		// 時差出勤
		DifferenceRequestDtoInterface differenceRequestDto = differenceReference.findForKeyOnWorkflow(personalId,
				workDate);
		if (differenceRequestDto != null) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(differenceRequestDto
				.getWorkflow());
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				// 承認完了の場合
				differenceDto = differenceRequestDto;
			}
		}
		// 休日出勤
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHolidayReference.findForKeyOnWorkflow(
				personalId, workDate);
		if (workOnHolidayRequestDto != null) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(workOnHolidayRequestDto
				.getWorkflow());
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				// 承認完了の場合
				workOnHolidayDto = workOnHolidayRequestDto;
				if (workOnHolidayDto.getSubstitute() == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_ON) {
					// 振替休日を申請する場合
					List<SubstituteDtoInterface> list = substituteReference.getSubstituteList(workOnHolidayDto
						.getWorkflow());
					for (SubstituteDtoInterface dto : list) {
						ScheduleDateDtoInterface substituteScheduleDateDto = scheduleDateReference.getScheduleDateInfo(
								scheduleDto.getScheduleCode(), workDate, dto.getSubstituteDate());
						if (substituteScheduleDateDto == null) {
							continue;
						}
						scheduleDateDto = substituteScheduleDateDto;
						break;
					}
				}
			}
		}
		nextDayWorkTypeCode = getWorkTypeCode(personalId, DateUtility.addDay(workDate, 1), 1);
		if (differenceDto != null
				&& (TimeConst.CODE_DIFFERENCE_TYPE_A.equals(workTypeCode)
						|| TimeConst.CODE_DIFFERENCE_TYPE_B.equals(workTypeCode)
						|| TimeConst.CODE_DIFFERENCE_TYPE_C.equals(workTypeCode)
						|| TimeConst.CODE_DIFFERENCE_TYPE_D.equals(workTypeCode) || TimeConst.CODE_DIFFERENCE_TYPE_S
					.equals(workTypeCode))) {
			// 時差出勤の場合
			setFields(scheduleDateDto.getWorkTypeCode());
		} else {
			setFields(workTypeCode);
		}
	}
	
	/**
	 * @param workTypeCode 勤務形態コード
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	protected void setFields(String workTypeCode) throws MospException {
		// カレンダ日マスタの所定休日、法定休日、空欄の場合
		this.workTypeCode = workTypeCode;
		if (workTypeCode != null && !workTypeCode.isEmpty()) {
			if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
				// 法定休日労動又は所定休日労動の場合
				// 始業時刻
				regWorkStart = getDefferenceMinutes(workOnHolidayDto.getRequestDate(), workOnHolidayDto.getStartTime());
				// 終業時刻
				regWorkEnd = getDefferenceMinutes(workOnHolidayDto.getRequestDate(), workOnHolidayDto.getEndTime());
			} else {
				// カレンダ日マスタに勤怠コードが設定されている場合
				WorkTypeDtoInterface workTypeDto = workTypeReference.getWorkTypeInfo(workTypeCode, workDate);
				if (workTypeDto == null) {
					String errorMes = mospParams.getName("Calendar") + mospParams.getName("Day");
					mospParams.addErrorMessage(TimeMessageConst.MSG_APPLICATION_UNSETTING, DateUtility
						.getStringDate(workDate), errorMes);
					return;
				}
				String localWorkTypeCode = workTypeDto.getWorkTypeCode();
				// 出勤時刻,退勤時刻の取得
				// 勤務形態から取得
				// 出勤時刻
				WorkTypeItemDtoInterface workStartDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_WORKSTART);
				if (workStartDto != null) {
					regWorkStart = getDefferenceMinutes(getDefaultStandardDate(), workStartDto.getWorkTypeItemValue());
				}
				// 退勤時刻
				WorkTypeItemDtoInterface workEndDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_WORKEND);
				if (workEndDto != null) {
					regWorkEnd = getDefferenceMinutes(getDefaultStandardDate(), workEndDto.getWorkTypeItemValue());
				}
				// 規定労働時間
				WorkTypeItemDtoInterface workTimeDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_WORKTIME);
				if (workTimeDto != null) {
					int time = getDefferenceMinutes(getDefaultStandardDate(), workTimeDto.getWorkTypeItemValue());
					regWorkTime = time;
					regFullWorkTime = time;
				}
				// 残前休憩
				WorkTypeItemDtoInterface overBeforeDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_OVERBEFORE);
				if (overBeforeDto != null) {
					overbefore = getDefferenceMinutes(getDefaultStandardDate(), overBeforeDto.getWorkTypeItemValue());
				}
				// 残業休憩時間(毎)
				WorkTypeItemDtoInterface overPerDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_OVERPER);
				if (overPerDto != null) {
					overper = getDefferenceMinutes(getDefaultStandardDate(), overPerDto.getWorkTypeItemValue());
				}
				// 残業休憩時間
				WorkTypeItemDtoInterface overRestDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_OVERREST);
				if (overRestDto != null) {
					overrest = getDefferenceMinutes(getDefaultStandardDate(), overRestDto.getWorkTypeItemValue());
				}
				// 前半休
				WorkTypeItemDtoInterface frontStartDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_FRONTSTART);
				WorkTypeItemDtoInterface frontEndDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_FRONTEND);
				if (frontStartDto != null && frontEndDto != null && isPmHalfDayOff() && differenceDto == null) {
					// 午後休で且つ時差出勤でない場合
					regWorkEnd = getDefferenceMinutes(getDefaultStandardDate(), frontEndDto.getWorkTypeItemValue());
					regWorkTime = getDefferenceMinutes(getDefaultStandardDate(), frontEndDto.getWorkTypeItemValue())
							- getDefferenceMinutes(getDefaultStandardDate(), frontStartDto.getWorkTypeItemValue());
				}
				// 後半休
				WorkTypeItemDtoInterface backStartDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_BACKSTART);
				WorkTypeItemDtoInterface backEndDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode,
						workDate, TimeConst.CODE_BACKEND);
				if (backStartDto != null && backEndDto != null && isAmHalfDayOff() && differenceDto == null) {
					// 時差出勤でなく且つ午前休の場合
					regWorkStart = getDefferenceMinutes(getDefaultStandardDate(), backStartDto.getWorkTypeItemValue());
					regWorkTime = getDefferenceMinutes(getDefaultStandardDate(), backEndDto.getWorkTypeItemValue())
							- getDefferenceMinutes(getDefaultStandardDate(), backStartDto.getWorkTypeItemValue());
				}
			}
		}
	}
	
	/**
	 * 勤務時間の出力
	 * @return 勤務時間
	 */
	public int getWorkTime() {
		// 勤怠設定管理DTOが存在しない
		if (timeSettingDto == null) {
			return 0;
		}
		// 丸め単位
		return getRoundMinute(workTime, timeSettingDto.getRoundDailyWork(), timeSettingDto.getRoundDailyWorkUnit());
	}
	
	/* 自動計算 */
	/**
	 * 労働時間を計算する。<br>
	 * 合わせて休憩時間及び規定終業時刻後時間を計算する。<br>
	 */
	private void calcWorkTime() {
		// 初期化
//		overtimeBeforeRestMap = new TreeMap<Date, Date>();
//		overtimeRestMap = new TreeMap<Date, Date>();
		int totalManualRest = 0;
		for (RestDtoInterface dto : restDtoList) {
			totalManualRest += dto.getRestTime();
		}
		Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
		// 遅刻休憩時間
		for (Entry<Date, Date> entry : tardinessRestMap.entrySet()) {
			map.put(getDefferenceMinutes(workDate, entry.getKey()), getDefferenceMinutes(workDate, entry.getValue()));
		}
		// 早退休憩時間
		for (Entry<Date, Date> entry : leaveEarlyRestMap.entrySet()) {
			map.put(getDefferenceMinutes(workDate, entry.getKey()), getDefferenceMinutes(workDate, entry.getValue()));
		}
		// 休憩時間
		for (RestDtoInterface dto : restDtoList) {
			Date startTime = getRoundMinute(dto.getRestStart(), timeSettingDto.getRoundDailyRestStart(), timeSettingDto
				.getRoundDailyRestStartUnit());
			Date endTime = getRoundMinute(dto.getRestEnd(), timeSettingDto.getRoundDailyRestEnd(), timeSettingDto
				.getRoundDailyRestEndUnit());
			int startTimeInt = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int endTimeInt = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (endTimeInt > regWorkStart) {
				if (startTimeInt < regWorkStart) {
					startTimeInt = regWorkStart;
				}
				if (map.containsKey(startTimeInt)) {
					if (map.get(startTimeInt).intValue() < endTimeInt) {
						map.put(startTimeInt, endTimeInt);
					}
				} else {
					map.put(startTimeInt, endTimeInt);
				}
			}
		}
		// 公用外出時間
		for (GoOutDtoInterface dto : publicGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPublicStart(),
					timeSettingDto.getRoundDailyPublicStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPublicEnd(), timeSettingDto
				.getRoundDailyPublicEndUnit());
			int startTimeInt = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int endTimeInt = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (endTimeInt > regWorkStart) {
				if (startTimeInt < regWorkStart) {
					startTimeInt = regWorkStart;
				}
				if (map.containsKey(startTimeInt)) {
					if (map.get(startTimeInt).intValue() < endTimeInt) {
						map.put(startTimeInt, endTimeInt);
					}
				} else {
					map.put(startTimeInt, endTimeInt);
				}
			}
		}
		// 私用外出時間
		for (GoOutDtoInterface dto : privateGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPrivateStart(),
					timeSettingDto.getRoundDailyPrivateStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPrivateEnd(), timeSettingDto
				.getRoundDailyPrivateEndUnit());
			int startTimeInt = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int endTimeInt = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (endTimeInt > regWorkStart) {
				if (startTimeInt < regWorkStart) {
					startTimeInt = regWorkStart;
				}
				if (map.containsKey(startTimeInt)) {
					if (map.get(startTimeInt).intValue() < endTimeInt) {
						map.put(startTimeInt, endTimeInt);
					}
				} else {
					map.put(startTimeInt, endTimeInt);
				}
			}
		}
		int time = regWorkStart;
		// 労働時間
		int tmpWorkTime = 0;
		int scheduledWorkEndTime = 0;
		for (Entry<Integer, Integer> entry : map.entrySet()) {
			int mapStartTimeInt = entry.getKey();
			int mapEndTimeInt = entry.getValue();
			if (time <= mapStartTimeInt) {
				int addWorkTime = mapStartTimeInt - time;
				if (tmpWorkTime + addWorkTime >= autoRestCalcStart) {
					// 勤務時間に追加勤務時間を加えたものが自動休憩計算開始時刻以上の場合は
					// 時間に自動休憩計算開始時刻から勤務時間を引いたものとする
					scheduledWorkEndTime = time + (autoRestCalcStart - tmpWorkTime);
					break;
				}
				tmpWorkTime += addWorkTime;
				time = mapEndTimeInt;
			}
		}
		if (scheduledWorkEndTime == 0) {
			int remainWorkTime = calculatedEnd - time;
			if (tmpWorkTime + remainWorkTime <= autoRestCalcStart) {
				totalRest = totalManualRest;
				workTime = calculatedEnd - calculatedStart - totalRest - totalPublic - totalPrivate;
				return;
			} else {
				scheduledWorkEndTime = time + (autoRestCalcStart - tmpWorkTime);
			}
		}
		if (scheduledWorkEndTime + overbefore >= calculatedEnd) {
			// 終業時刻が残前休憩終了時刻以前の場合
			int overtimeBeforeTime = 0;
			if (scheduledWorkEndTime < calculatedStart) {
				// 勤怠計算上の始業時刻より前の場合
				overtimeBeforeTime = calculatedEnd - calculatedStart;
				overtimeBeforeRestMap.put(getAttendanceTime(workDate, calculatedStart), getAttendanceTime(workDate,
						calculatedEnd));
			} else {
				overtimeBeforeTime = calculatedEnd - scheduledWorkEndTime;
				overtimeBeforeRestMap.put(getAttendanceTime(workDate, scheduledWorkEndTime), getAttendanceTime(
						workDate, calculatedEnd));
			}
			totalRest = totalManualRest + overtimeBeforeTime;
			workTime = calculatedEnd - calculatedStart - totalRest - totalPublic - totalPrivate;
			return;
		}
		// 規定終業時刻後時間外労働開始時刻
		int afterOvertimeWorkStart = scheduledWorkEndTime + overbefore;
		// 規定終業時刻後時間外前休憩
		int totalOvertimeBeforeRest = 0;
		if (scheduledWorkEndTime >= calculatedStart) {
			// 勤怠計算上の始業時刻以後の場合
			overtimeBeforeRestMap.put(getAttendanceTime(workDate, scheduledWorkEndTime), getAttendanceTime(workDate,
					afterOvertimeWorkStart));
			totalOvertimeBeforeRest += afterOvertimeWorkStart - scheduledWorkEndTime;
		} else if (afterOvertimeWorkStart >= calculatedStart) {
			overtimeBeforeRestMap.put(getAttendanceTime(workDate, calculatedStart), getAttendanceTime(workDate,
					afterOvertimeWorkStart));
			totalOvertimeBeforeRest += afterOvertimeWorkStart - calculatedStart;
		}
		// 残業時間
		int totalOvertimeTime = calculatedEnd - afterOvertimeWorkStart;
		// 残業休憩回数の設定
		int overtimeRestTimes = 0;
		if (overper > 1) {
			overtimeRestTimes = totalOvertimeTime / overper;
		}
		// 残業休憩時間
		int totalOvertimeRest = 0;
		int previousOvertimeRestEndTime = afterOvertimeWorkStart;
		for (int i = 0; i < overtimeRestTimes; i++) {
			int overtimeRestStartTime = previousOvertimeRestEndTime + overper - overrest;
			int overtimeRestEndTime = previousOvertimeRestEndTime + overper;
			if (overtimeRestStartTime >= calculatedStart) {
				overtimeRestMap.put(getAttendanceTime(workDate, overtimeRestStartTime), getAttendanceTime(workDate,
						overtimeRestEndTime));
				totalOvertimeRest += overtimeRestEndTime - overtimeRestStartTime;
			}
			previousOvertimeRestEndTime = overtimeRestEndTime;
		}
		totalRest = totalManualRest + totalOvertimeBeforeRest + totalOvertimeRest;
		workTime = calculatedEnd - calculatedStart - totalRest - totalPublic - totalPrivate;
	}
	
	/**
	 * 法定外時間の計算
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	private void calcLegalOutTime() throws MospException {
		overTimeOut = overtimeTime - withinStatutoryOvertime;
		// 法定外休憩時間設定
		overRestTime = 0;
	}
	
	/**
	 * 公用外出時間の計算
	 */
	private void calcPublicGoOutTime() {
		int total = 0;
		for (GoOutDtoInterface dto : publicGoOutDtoList) {
			total += dto.getGoOutTime();
		}
		totalPublic = total;
	}
	
	/**
	 * 私用外出時間の計算
	 */
	private void calcPrivateGoOutTime() {
		int total = 0;
		for (GoOutDtoInterface dto : privateGoOutDtoList) {
			total += dto.getGoOutTime();
		}
		totalPrivate = total;
	}
	
	/**
	 * 遅刻時間の計算
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	private void calcTardinessTime() throws MospException {
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労動又は所定休日労動の場合
			return;
		}
		Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
		if (differenceDto != null) {
			// 時差出勤の場合
			if (TimeConst.CODE_DIFFERENCE_TYPE_A.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_B.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_C.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_D.equals(differenceDto.getDifferenceType())) {
				// A・B・C・D
				map.put(12 * TimeConst.CODE_DEFINITION_HOUR, 13 * TimeConst.CODE_DEFINITION_HOUR);
			} else if (TimeConst.CODE_DIFFERENCE_TYPE_S.equals(differenceDto.getDifferenceType())) {
				// S
				map.put((DateUtility.getHour(differenceDto.getRequestStart(), workDate) + 3)
						* TimeConst.CODE_DEFINITION_HOUR, (DateUtility.getHour(differenceDto.getRequestStart(),
						workDate) + 4)
						* TimeConst.CODE_DEFINITION_HOUR);
			}
		} else if (!isAmHalfDayOff() && !isPmHalfDayOff()) {
			// 半休でない場合
			String[] restStartArray = new String[]{ TimeConst.CODE_RESTSTART1, TimeConst.CODE_RESTSTART2,
				TimeConst.CODE_RESTSTART3, TimeConst.CODE_RESTSTART4 };
			String[] restEndArray = new String[]{ TimeConst.CODE_RESTEND1, TimeConst.CODE_RESTEND2,
				TimeConst.CODE_RESTEND3, TimeConst.CODE_RESTEND4 };
			for (int i = 0; i < restStartArray.length; i++) {
				WorkTypeItemDtoInterface restStartDto = workTypeItemReference.getWorkTypeItemInfo(workTypeCode,
						workDate, restStartArray[i]);
				if (restStartDto == null) {
					continue;
				}
				int restStart = getDefferenceMinutes(getDefaultStandardDate(), restStartDto.getWorkTypeItemValue());
				WorkTypeItemDtoInterface restEndDto = workTypeItemReference.getWorkTypeItemInfo(workTypeCode, workDate,
						restEndArray[i]);
				if (restEndDto == null) {
					continue;
				}
				int restEnd = getDefferenceMinutes(getDefaultStandardDate(), restEndDto.getWorkTypeItemValue());
				if (restStart == restEnd) {
					continue;
				}
				map.put(restStart, restEnd);
			}
		}
		int exclusion = 0;
		for (Entry<Integer, Integer> entry : map.entrySet()) {
			int restStart = entry.getKey().intValue();
			int restEnd = entry.getValue().intValue();
			if (restStart < calculatedStart) {
				// 休憩開始時刻が勤怠計算上の始業時刻以前の場合
				if (calculatedStart < restEnd) {
					// 休憩終了時刻が勤怠計算上の始業時刻以後の場合
					restEnd = calculatedStart;
				}
				exclusion += restEnd - restStart;
				tardinessRestMap.put(getAttendanceTime(workDate, restStart), getAttendanceTime(workDate, restEnd));
			}
		}
		int result = calculatedStart - regWorkStart - exclusion;
		if (result < 0) {
			// 0未満の場合は0とする
			result = 0;
		}
		if (result > prescribedWorkTime) {
			// 所定労働時間より大きい場合は所定労働時間とする
			result = prescribedWorkTime;
		}
		lateTime = getRoundMinute(result, timeSettingDto.getRoundDailyLate(), timeSettingDto.getRoundDailyLateUnit());
	}
	
	/**
	 * 早退時間の計算
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	private void calcLeaveEarlyTime() throws MospException {
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労動又は所定休日労動の場合
			return;
		}
		Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
		if (differenceDto != null) {
			// 時差出勤の場合
			if (TimeConst.CODE_DIFFERENCE_TYPE_A.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_B.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_C.equals(differenceDto.getDifferenceType())
					|| TimeConst.CODE_DIFFERENCE_TYPE_D.equals(differenceDto.getDifferenceType())) {
				// A・B・C・D
				map.put(12 * TimeConst.CODE_DEFINITION_HOUR, 13 * TimeConst.CODE_DEFINITION_HOUR);
			} else if (TimeConst.CODE_DIFFERENCE_TYPE_S.equals(differenceDto.getDifferenceType())) {
				// S
				map.put((DateUtility.getHour(differenceDto.getRequestStart(), workDate) + 3)
						* TimeConst.CODE_DEFINITION_HOUR, (DateUtility.getHour(differenceDto.getRequestStart(),
						workDate) + 4)
						* TimeConst.CODE_DEFINITION_HOUR);
			}
		} else if (!isAmHalfDayOff() && !isPmHalfDayOff()) {
			// 半休でない場合
			String[] restStartArray = new String[]{ TimeConst.CODE_RESTSTART1, TimeConst.CODE_RESTSTART2,
				TimeConst.CODE_RESTSTART3, TimeConst.CODE_RESTSTART4 };
			String[] restEndArray = new String[]{ TimeConst.CODE_RESTEND1, TimeConst.CODE_RESTEND2,
				TimeConst.CODE_RESTEND3, TimeConst.CODE_RESTEND4 };
			for (int i = 0; i < restStartArray.length; i++) {
				WorkTypeItemDtoInterface restStartDto = workTypeItemReference.getWorkTypeItemInfo(workTypeCode,
						workDate, restStartArray[i]);
				if (restStartDto == null) {
					continue;
				}
				int restStart = getDefferenceMinutes(getDefaultStandardDate(), restStartDto.getWorkTypeItemValue());
				WorkTypeItemDtoInterface restEndDto = workTypeItemReference.getWorkTypeItemInfo(workTypeCode, workDate,
						restEndArray[i]);
				if (restEndDto == null) {
					continue;
				}
				int restEnd = getDefferenceMinutes(getDefaultStandardDate(), restEndDto.getWorkTypeItemValue());
				if (restStart == restEnd) {
					continue;
				}
				map.put(restStart, restEnd);
			}
		}
		int exclusion = 0;
		for (Entry<Integer, Integer> entry : map.entrySet()) {
			int restStart = entry.getKey().intValue();
			int restEnd = entry.getValue().intValue();
			if (restEnd > calculatedEnd) {
				// 休憩終了時刻が勤怠計算上の終業時刻以後の場合
				if (calculatedEnd > restStart) {
					// 休憩開始時刻が勤怠計算上の終業時刻以前の場合
					restStart = calculatedEnd;
				}
				exclusion += restEnd - restStart;
				leaveEarlyRestMap.put(getAttendanceTime(workDate, restStart), getAttendanceTime(workDate, restEnd));
			}
		}
		int result = regWorkEnd - calculatedEnd - exclusion;
		if (result < 0) {
			// 0未満の場合は0とする
			result = 0;
		}
		if (result > prescribedWorkTime) {
			// 所定労働時間より大きい場合は所定労働時間とする
			result = prescribedWorkTime;
		}
		leaveEarlyTime = getRoundMinute(result, timeSettingDto.getRoundDailyLeaveEarly(), timeSettingDto
			.getRoundDailyLeaveEarlyUnit());
	}
	
	/**
	 * 残業時間の計算
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	private void calcOvertimeWork() throws MospException {
		boolean nextDayWorkOnLegalDaysOff = TimeConst.CODE_WORK_ON_LEGAL_HOLIDAY.equals(nextDayWorkTypeCode)
				|| TimeConst.CODE_HOLIDAY_LEGAL_HOLIDAY.equals(nextDayWorkTypeCode);
		boolean nextDayWorkOnPrescribedDaysOff = TimeConst.CODE_WORK_ON_PRESCRIBED_HOLIDAY.equals(nextDayWorkTypeCode)
				|| TimeConst.CODE_HOLIDAY_PRESCRIBED_HOLIDAY.equals(nextDayWorkTypeCode);
		boolean isSpecificHolidayCalendarDay = timeSettingDto.getSpecificHolidayHandling() == 2;
		if (calculatedEnd > 24 * TimeConst.CODE_DEFINITION_HOUR) {
			// 勤怠計算上の終業時刻が24時を過ぎる場合
			
			// 暦日計算フラグ
			boolean calculateFlag = true;
			if (isWorkOnLegalDaysOff() && nextDayWorkOnLegalDaysOff) {
				// 法定休日労動→法定休日労動の場合
				calculateFlag = false;
			} else if (isWorkOnPrescribedDaysOff() && nextDayWorkOnPrescribedDaysOff) {
				// 所定休日労動→所定休日労動の場合
				calculateFlag = false;
			} else if (isWorkOnPrescribedDaysOff() && !nextDayWorkOnLegalDaysOff && !nextDayWorkOnPrescribedDaysOff
					&& !isSpecificHolidayCalendarDay) {
				// 所定休日労動→平日労動且つ所定休日取扱が通常の場合
				calculateFlag = false;
			} else if (!isWorkOnLegalDaysOff() && !isWorkOnPrescribedDaysOff() && nextDayWorkOnPrescribedDaysOff
					&& !isSpecificHolidayCalendarDay) {
				// 平日労動→所定休日労動且つ所定休日取扱が通常の場合
				calculateFlag = false;
			} else if (!isWorkOnLegalDaysOff() && !isWorkOnPrescribedDaysOff() && !nextDayWorkOnLegalDaysOff
					&& !nextDayWorkOnPrescribedDaysOff) {
				// 平日労動→平日労動の場合
				calculateFlag = false;
			}
			if (calculateFlag) {
				// 当日と翌日が異なる場合
				
				// 合計法定休日労動時間
				int totalLegalDaysOffWork = 0;
				// 合計所定休日労働時間
				int totalPrescribedDayOffWork = 0;
				// 合計時間外労働時間
				int totalOvertimeWork = 0;
				
				// 24時以前の合計手動休憩時間
				int before24HourManualRest = 0;
				for (RestDtoInterface dto : restDtoList) {
					Date startTime = getRoundMinute(dto.getRestStart(), timeSettingDto.getRoundDailyRestStart(),
							timeSettingDto.getRoundDailyRestStartUnit());
					Date endTime = getRoundMinute(dto.getRestEnd(), timeSettingDto.getRoundDailyRestEnd(),
							timeSettingDto.getRoundDailyRestEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (start < 24 * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > 24 * TimeConst.CODE_DEFINITION_HOUR) {
							end = 24 * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourManualRest += getRoundMinute(end - start, timeSettingDto.getRoundDailyRestTime(),
								timeSettingDto.getRoundDailyRestTimeUnit());
					}
				}
				// 24時以前の合計公用外出時間
				int before24HourPublicGoOut = 0;
				for (GoOutDtoInterface dto : publicGoOutDtoList) {
					Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPublicStart(),
							timeSettingDto.getRoundDailyPublicStartUnit());
					Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPublicEnd(),
							timeSettingDto.getRoundDailyPublicEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (start < 24 * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > 24 * TimeConst.CODE_DEFINITION_HOUR) {
							end = 24 * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourPublicGoOut += end - start;
					}
				}
				// 24時以前の合計私用外出時間
				int before24HourPrivateGoOut = 0;
				for (GoOutDtoInterface dto : privateGoOutDtoList) {
					Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPrivateStart(),
							timeSettingDto.getRoundDailyPrivateStartUnit());
					Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPrivateEnd(),
							timeSettingDto.getRoundDailyPrivateEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (start < 24 * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > 24 * TimeConst.CODE_DEFINITION_HOUR) {
							end = 24 * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourPrivateGoOut += end - start;
					}
				}
				// 24時以前の規定終業時刻後時間外前休憩時間
				int before24HourOvertimeBeforeRest = 0;
				for (Entry<Date, Date> entry : overtimeBeforeRestMap.entrySet()) {
					int start = getDefferenceMinutes(workDate, entry.getKey());
					int end = getDefferenceMinutes(workDate, entry.getValue());
					if (start < 24 * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > 24 * TimeConst.CODE_DEFINITION_HOUR) {
							end = 24 * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourOvertimeBeforeRest += end - start;
					}
				}
				// 24時以前の合計残業休憩時間
				int before24HourOvertimeRest = 0;
				for (Entry<Date, Date> entry : overtimeRestMap.entrySet()) {
					int start = getDefferenceMinutes(workDate, entry.getKey());
					int end = getDefferenceMinutes(workDate, entry.getValue());
					if (start < 24 * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > 24 * TimeConst.CODE_DEFINITION_HOUR) {
							end = 24 * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourOvertimeRest += end - start;
					}
				}
				
				if (isWorkOnLegalDaysOff()) {
					// 当日が法定休日労動の場合
					int legalDaysOffWork = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR
							- calculatedStart - before24HourManualRest - before24HourOvertimeBeforeRest
							- before24HourOvertimeRest - before24HourPublicGoOut - before24HourPrivateGoOut;
					if (legalDaysOffWork < 0) {
						// 0未満の場合は0とする
						legalDaysOffWork = 0;
					}
					totalLegalDaysOffWork += getRoundMinute(legalDaysOffWork, timeSettingDto.getRoundDailyWork(),
							timeSettingDto.getRoundDailyWorkUnit());
				} else if (isWorkOnPrescribedDaysOff()) {
					// 当日が所定休日労動の場合
					int overtime = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR - calculatedStart
							- before24HourManualRest - before24HourOvertimeBeforeRest - before24HourOvertimeRest
							- before24HourPublicGoOut - before24HourPrivateGoOut;
					if (overtime < 0) {
						// 0未満の場合は0とする
						overtime = 0;
					}
					// 丸め
					overtime = getRoundMinute(overtime, timeSettingDto.getRoundDailyWork(), timeSettingDto
						.getRoundDailyWorkUnit());
					totalOvertimeWork += overtime;
					totalPrescribedDayOffWork += overtime;
				} else {
					// 当日が平日労動の場合
					int overtime = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR - calculatedStart
							- before24HourManualRest - before24HourOvertimeBeforeRest - before24HourOvertimeRest
							- before24HourPublicGoOut - before24HourPrivateGoOut - prescribedWorkTime + lateTime;
					if (overtime < 0) {
						// 0未満の場合は0とする
						overtime = 0;
					}
					totalOvertimeWork += getRoundMinute(overtime, timeSettingDto.getRoundDailyWork(), timeSettingDto
						.getRoundDailyWorkUnit());
				}
				
				// 24時以後の合計手動休憩時間
				int after24HourManualRest = 0;
				for (RestDtoInterface dto : restDtoList) {
					Date startTime = getRoundMinute(dto.getRestStart(), timeSettingDto.getRoundDailyRestStart(),
							timeSettingDto.getRoundDailyRestStartUnit());
					Date endTime = getRoundMinute(dto.getRestEnd(), timeSettingDto.getRoundDailyRestEnd(),
							timeSettingDto.getRoundDailyRestEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (start < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							start = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						after24HourManualRest += getRoundMinute(end - start, timeSettingDto.getRoundDailyRestTime(),
								timeSettingDto.getRoundDailyRestTimeUnit());
					}
				}
				// 24時以後の合計公用外出時間
				int after24HourPublicGoOut = 0;
				for (GoOutDtoInterface dto : publicGoOutDtoList) {
					Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPublicStart(),
							timeSettingDto.getRoundDailyPublicStartUnit());
					Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPublicEnd(),
							timeSettingDto.getRoundDailyPublicEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (start < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							start = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						after24HourPublicGoOut += end - start;
					}
				}
				// 24時以後の合計私用外出時間
				int after24HourPrivateGoOut = 0;
				for (GoOutDtoInterface dto : privateGoOutDtoList) {
					Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPrivateStart(),
							timeSettingDto.getRoundDailyPrivateStartUnit());
					Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPrivateEnd(),
							timeSettingDto.getRoundDailyPrivateEndUnit());
					int start = getDefferenceMinutes(workDate, startTime);
					int end = getDefferenceMinutes(workDate, endTime);
					if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (start < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							start = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						after24HourPrivateGoOut += end - start;
					}
				}
				// 24時以後の規定終業時刻後時間外前休憩時間
				int after24HourOvertimeBeforeRest = 0;
				for (Entry<Date, Date> entry : overtimeBeforeRestMap.entrySet()) {
					int start = getDefferenceMinutes(workDate, entry.getKey());
					int end = getDefferenceMinutes(workDate, entry.getValue());
					if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (start < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							start = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						after24HourOvertimeBeforeRest += end - start;
					}
				}
				// 24時以後の合計残業休憩時間
				int after24HourOvertimeRest = 0;
				for (Entry<Date, Date> entry : overtimeRestMap.entrySet()) {
					int start = getDefferenceMinutes(workDate, entry.getKey());
					int end = getDefferenceMinutes(workDate, entry.getValue());
					if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (start < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							start = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						after24HourOvertimeRest += end - start;
					}
				}
				// 翌日時間
				int nextDayTime = calculatedEnd - (TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR)
						- after24HourManualRest - after24HourOvertimeBeforeRest - after24HourOvertimeRest
						- after24HourPublicGoOut - after24HourPrivateGoOut;
				if (nextDayTime < 0) {
					// 0未満の場合は0とする
					nextDayTime = 0;
				}
				// 丸め
				int roundNextDayTime = getRoundMinute(nextDayTime, timeSettingDto.getRoundDailyWork(), timeSettingDto
					.getRoundDailyWorkUnit());
				if (nextDayWorkOnLegalDaysOff) {
					// 翌日が法定休日の場合
					totalLegalDaysOffWork += roundNextDayTime;
				} else if (nextDayWorkOnPrescribedDaysOff) {
					// 翌日が所定休日の場合
					totalOvertimeWork += roundNextDayTime;
					totalPrescribedDayOffWork += roundNextDayTime;
				} else {
					// 翌日が平日の場合
					totalOvertimeWork += roundNextDayTime;
				}
				legalHolidayWorkTime = totalLegalDaysOffWork;
				predeterminedHolidayWorkTime = totalPrescribedDayOffWork;
				overtimeTime = totalOvertimeWork;
				int afterTime = overtimeTime - workBeforeTime;
				if (afterTime < 0) {
					afterTime = 0;
				}
				workAfterTime = afterTime;
				return;
			}
		}
		if (TimeConst.CODE_WORK_ON_LEGAL_HOLIDAY.equals(workTypeCode)) {
			// 法定休日労動
			legalHolidayWorkTime = getWorkTime();
			return;
		} else if (TimeConst.CODE_WORK_ON_PRESCRIBED_HOLIDAY.equals(workTypeCode)) {
			// 所定休日労動
			overtimeTime = getWorkTime();
			int afterTime = overtimeTime - workBeforeTime;
			if (afterTime < 0) {
				afterTime = 0;
			}
			workAfterTime = afterTime;
			predeterminedHolidayWorkTime = getWorkTime();
			return;
		}
		// 平日労動
		
		// 合計手動休憩時間
		int totalManualRest = 0;
		for (RestDtoInterface dto : restDtoList) {
			Date startTime = getRoundMinute(dto.getRestStart(), timeSettingDto.getRoundDailyRestStart(), timeSettingDto
				.getRoundDailyRestStartUnit());
			Date endTime = getRoundMinute(dto.getRestEnd(), timeSettingDto.getRoundDailyRestEnd(), timeSettingDto
				.getRoundDailyRestEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			totalManualRest += getRoundMinute(end - start, timeSettingDto.getRoundDailyRestTime(), timeSettingDto
				.getRoundDailyRestTimeUnit());
		}
		// 合計公用外出時間
		int totalPublicGoOut = 0;
		for (GoOutDtoInterface dto : publicGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPublicStart(),
					timeSettingDto.getRoundDailyPublicStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPublicEnd(), timeSettingDto
				.getRoundDailyPublicEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			totalPublicGoOut += end - start;
		}
		// 合計私用外出時間
		int totalPrivateGoOut = 0;
		for (GoOutDtoInterface dto : privateGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPrivateStart(),
					timeSettingDto.getRoundDailyPrivateStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPrivateEnd(), timeSettingDto
				.getRoundDailyPrivateEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			totalPrivateGoOut += end - start;
		}
		// 規定終業時刻後時間外前休憩時間
		int totalOvertimeBeforeRest = 0;
		for (Entry<Date, Date> entry : overtimeBeforeRestMap.entrySet()) {
			int start = getDefferenceMinutes(workDate, entry.getKey());
			int end = getDefferenceMinutes(workDate, entry.getValue());
			totalOvertimeBeforeRest += end - start;
		}
		// 合計残業休憩時間
		int totalOvertimeRest = 0;
		for (Entry<Date, Date> entry : overtimeRestMap.entrySet()) {
			int start = getDefferenceMinutes(workDate, entry.getKey());
			int end = getDefferenceMinutes(workDate, entry.getValue());
			totalOvertimeRest += end - start;
		}
		// 残業時間設定
		int overtime = calculatedEnd - calculatedStart - totalManualRest - totalOvertimeRest - totalOvertimeBeforeRest
				- totalPublicGoOut - totalPrivateGoOut - prescribedWorkTime + lateTime;
		if (overtime < 0) {
			// 0未満の場合は0とする
			overtime = 0;
		}
		// 残業時間の設定
		overtimeTime = getRoundMinute(overtime, timeSettingDto.getRoundDailyWork(), timeSettingDto
			.getRoundDailyWorkUnit());
		int afterTime = overtimeTime - workBeforeTime;
		if (afterTime < 0) {
			afterTime = 0;
		}
		workAfterTime = afterTime;
	}
	
	/**
	 * 深夜労働時間の計算
	 */
	private void calcNightWorkTime() {
		// 初期化
		nightWork = 0;
		nightRest = 0;
		nightWorkArray = new int[2];
		nightRestArray = new int[2];
		// 0:00から5:00までの深夜労働時間・深夜休憩時間の計算
		calcNightWorkTime(0, 5 * TimeConst.CODE_DEFINITION_HOUR, 0);
		// 22:00から29:00までの深夜労働時間・深夜休憩時間の計算
		calcNightWorkTime(TimeConst.TIME_NIGHT_WORK_START * TimeConst.CODE_DEFINITION_HOUR,
				TimeConst.TIME_NIGHT_WORK_END * TimeConst.CODE_DEFINITION_HOUR, 1);
		// 合計深夜労働時間
		int totalNightWorkTime = 0;
		for (int time : nightWorkArray) {
			// 深夜労働時間を加算
			totalNightWorkTime += time;
		}
		nightWork = totalNightWorkTime;
		// 合計深夜休憩時間
		int totalNightRestTime = 0;
		for (int time : nightRestArray) {
			// 深夜休憩時間を加算
			totalNightRestTime += time;
		}
		nightRest = totalNightRestTime;
	}
	
	/**
	 * 深夜労働時間の計算
	 * @param regNightWorkStart 規定深夜労働開始時刻
	 * @param regNightWorkEnd 規定深夜労働終了時刻
	 * @param i i
	 */
	private void calcNightWorkTime(int regNightWorkStart, int regNightWorkEnd, int i) {
		if (calculatedStart >= regNightWorkEnd || calculatedEnd <= regNightWorkStart) {
			// 始業時刻が規定深夜労働終了時刻以後又は終業時刻が規定深夜労働開始時刻以前の場合
			nightWorkArray[i] = 0;
			nightRestArray[i] = 0;
			return;
		}
		// 深夜労働開始時刻
		int nightWorkStart = regNightWorkStart;
		if (calculatedStart >= regNightWorkStart) {
			// 始業時刻が規定深夜労働開始時刻以後の場合は
			// 始業時刻を深夜労働開始時刻とする
			nightWorkStart = calculatedStart;
		}
		// 深夜労働終了時刻
		int nightWorkEnd = regNightWorkEnd;
		if (calculatedEnd <= regNightWorkEnd) {
			// 終業時刻が規定深夜労働終了時刻以前の場合は
			// 終業時刻を深夜労働終了時刻とする
			nightWorkEnd = calculatedEnd;
		}
		// 深夜労働開始時刻から深夜労働終了時刻までの合計規定終業時刻後時間外前休憩時間
		int totalOvertimeBeforeRest = 0;
		for (Entry<Date, Date> entry : overtimeBeforeRestMap.entrySet()) {
			int start = getDefferenceMinutes(workDate, entry.getKey());
			int end = getDefferenceMinutes(workDate, entry.getValue());
			if (start < nightWorkEnd && end > nightWorkStart) {
				if (start < nightWorkStart) {
					start = nightWorkStart;
				}
				if (end > nightWorkEnd) {
					end = nightWorkEnd;
				}
				totalOvertimeBeforeRest += end - start;
			}
		}
		// 深夜労働開始時刻から深夜労働終了時刻までの合計残業休憩時間
		int totalOvertimeRest = 0;
		for (Entry<Date, Date> entry : overtimeRestMap.entrySet()) {
			int start = getDefferenceMinutes(workDate, entry.getKey());
			int end = getDefferenceMinutes(workDate, entry.getValue());
			if (start < nightWorkEnd && end > nightWorkStart) {
				if (start < nightWorkStart) {
					start = nightWorkStart;
				}
				if (end > nightWorkEnd) {
					end = nightWorkEnd;
				}
				totalOvertimeRest += end - start;
			}
		}
		// 深夜労働開始時刻から深夜労働終了時刻までの合計手動休憩時間
		int totalManualRest = 0;
		for (RestDtoInterface dto : restDtoList) {
			Date startTime = getRoundMinute(dto.getRestStart(), timeSettingDto.getRoundDailyRestStart(), timeSettingDto
				.getRoundDailyRestStartUnit());
			Date endTime = getRoundMinute(dto.getRestEnd(), timeSettingDto.getRoundDailyRestEnd(), timeSettingDto
				.getRoundDailyRestEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			if (start < nightWorkEnd && end > nightWorkStart) {
				if (start < nightWorkStart) {
					start = nightWorkStart;
				}
				if (end > nightWorkEnd) {
					end = nightWorkEnd;
				}
				totalManualRest += getRoundMinute(end - start, timeSettingDto.getRoundDailyRestTime(), timeSettingDto
					.getRoundDailyRestTimeUnit());
			}
		}
		// 深夜労働開始時刻から深夜労働終了時刻までの合計公用外出時間
		int totalPublicGoOut = 0;
		for (GoOutDtoInterface dto : publicGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPublicStart(),
					timeSettingDto.getRoundDailyPublicStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPublicEnd(), timeSettingDto
				.getRoundDailyPublicEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			if (start < nightWorkEnd && end > nightWorkStart) {
				if (start < nightWorkStart) {
					start = nightWorkStart;
				}
				if (end > nightWorkEnd) {
					end = nightWorkEnd;
				}
				totalPublicGoOut += end - start;
			}
		}
		// 深夜労働開始時刻から深夜労働終了時刻までの合計私用外出時間
		int totalPrivateGoOut = 0;
		for (GoOutDtoInterface dto : privateGoOutDtoList) {
			Date startTime = getRoundMinute(dto.getGoOutStart(), timeSettingDto.getRoundDailyPrivateStart(),
					timeSettingDto.getRoundDailyPrivateStartUnit());
			Date endTime = getRoundMinute(dto.getGoOutEnd(), timeSettingDto.getRoundDailyPrivateEnd(), timeSettingDto
				.getRoundDailyPrivateEndUnit());
			int start = getDefferenceMinutes(workDate, startTime);
			int end = getDefferenceMinutes(workDate, endTime);
			if (start < nightWorkEnd && end > nightWorkStart) {
				if (start < nightWorkStart) {
					start = nightWorkStart;
				}
				if (end > nightWorkEnd) {
					end = nightWorkEnd;
				}
				totalPrivateGoOut += end - start;
			}
		}
		// 深夜労働時間
		int night = nightWorkEnd - nightWorkStart - totalManualRest - totalOvertimeBeforeRest - totalOvertimeRest
				- totalPublicGoOut - totalPrivateGoOut;
		if (night < 0) {
			// 深夜労働時間が0未満の場合は0とする
			night = 0;
		}
		nightWorkArray[i] = getRoundMinute(night, timeSettingDto.getRoundDailyWork(), timeSettingDto
			.getRoundDailyWorkUnit());
		nightRestArray[i] = totalManualRest + totalOvertimeBeforeRest + totalOvertimeRest;
	}
	
	/**
	 * 減額対象時間の計算
	 */
	private void calcReducedTargetTime() {
		// 私用外出時間
		int result = totalPrivate;
		if (!TimeConst.CODE_TARDINESS_WHY_TRAIN.equals(lateReason)
				&& !TimeConst.CODE_TARDINESS_WHY_COMPANY.equals(lateReason)) {
			// 遅刻理由が電車遅延でなく且つ会社指示でない場合は遅刻時間を加算
			result += lateTime;
		}
		if (!TimeConst.CODE_LEAVEEARLY_WHY_COMPANY.equals(leaveEarlyReason)) {
			// 早退理由が会社指示でない場合は早退時間を加算
			result += leaveEarlyTime;
		}
		if (result < 0) {
			// 0未満の場合は0とする
			result = 0;
		}
		decreaseTime = getRoundMinute(result, timeSettingDto.getRoundDailyDecreaseTime(), timeSettingDto
			.getRoundDailyDecreaseTimeUnit());
	}
	
	/**
	 * 所定労働時間の計算
	 */
	private void calcPrescribedWorkTime() {
		if (workOnHolidayDto != null && workOnHolidayDto.getSubstitute() == 2) {
			// 休日労動の場合は0とする
			prescribedWorkTime = 0;
			// 休日労動の場合は勤怠設定の所定労働時間とする
			autoRestCalcStart = DateUtility.getHour(timeSettingDto.getGeneralWorkTime())
					* TimeConst.CODE_DEFINITION_HOUR + DateUtility.getMinute(timeSettingDto.getGeneralWorkTime());
			return;
		}
		if (regWorkTime > 8 * TimeConst.CODE_DEFINITION_HOUR) {
			// 規定労働時間が法定労働時間を超える場合は法定労働時間を所定労働時間とする
			prescribedWorkTime = 8 * TimeConst.CODE_DEFINITION_HOUR;
			autoRestCalcStart = 8 * TimeConst.CODE_DEFINITION_HOUR;
			return;
		}
		// 規定労働時間を所定労働時間とする
		prescribedWorkTime = regWorkTime;
		autoRestCalcStart = regFullWorkTime;
	}
	
	/**
	 * 法内残業の計算
	 */
	private void calcWithinStatutoryOvertime() {
		int differenceTime = 8 * TimeConst.CODE_DEFINITION_HOUR - prescribedWorkTime;
		if (differenceTime < 0) {
			// 法定労働時間から所定労働時間を引いたものが0未満の場合
			return;
		} else if (differenceTime <= overtimeTime) {
			// 法定労働時間から所定労働時間を引いたものが時間外労働時間以下の場合
			withinStatutoryOvertime = differenceTime;
			return;
		}
		// 法定労働時間から所定労働時間を引いたものが時間外労働時間超の場合
		withinStatutoryOvertime = overtimeTime;
	}
	
	/**
	 * 自動計算
	 * @throws MospException SQLの作成に失敗した場合、或いはSQL例外が発生した場合
	 */
	protected void setAutoCalc() throws MospException {
		if (startTime != null) {
			// 勤怠計算上の始業時刻・時間外計算上の始業時刻・規定始業時刻前時間の計算
			calcCalculatedStart();
		}
		if (endTime != null) {
			// 勤怠計算上の終業時刻の計算
			calcCalculatedEnd();
		}
		if (startTime == null || endTime == null) {
			return;
		}
		// 所定労働時間の計算
		calcPrescribedWorkTime();
		// 遅刻時間の計算
		calcTardinessTime();
		// 早退時間の計算
		calcLeaveEarlyTime();
		// 公用外出時間の計算
		calcPublicGoOutTime();
		// 私用外出時間の計算
		calcPrivateGoOutTime();
		// 労働時間・休憩時間・規定終業時刻後時間の計算
		calcWorkTime();
		// 時間外労働時間・法定休日労働時間・所定休日労動の計算
		calcOvertimeWork();
		// 深夜労働時間の計算
		calcNightWorkTime();
		// 減額対象時間の計算
		calcReducedTargetTime();
		// 法内残業の計算
		calcWithinStatutoryOvertime();
		// 法定外残業時間、法定外休憩時間の計算
		calcLegalOutTime();
	}
	
	/**
	 * 計算基準値設定
	 * @param attendanceDto 勤怠データ
	 * @param restDtoList 休憩データ
	 * @param publicGoOutDtoList 公用データ
	 * @param privateGoOutDtoList 私用データ
	 */
	public void setCalcInfo(AttendanceDtoInterface attendanceDto, List<RestDtoInterface> restDtoList,
			List<GoOutDtoInterface> publicGoOutDtoList, List<GoOutDtoInterface> privateGoOutDtoList) {
		// 初期化
		workTime = 0;
		totalRest = 0;
		totalPublic = 0;
		lateTime = 0;
		leaveEarlyTime = 0;
		totalPrivate = 0;
		decreaseTime = 0;
		timeSettingDto = null;
		workTypeItemDto = null;
		workOnHolidayDto = null;
		holidayRequestDtoList = new ArrayList<HolidayRequestDtoInterface>();
		subHolidayRequestDtoList = new ArrayList<SubHolidayRequestDtoInterface>();
		scheduleDto = null;
		differenceDto = null;
		scheduleDateDto = null;
		beforeOvertimeDto = null;
		totalPublicTime = 0;
		overtimeTime = 0;
		regWorkStart = 0;
		regWorkEnd = 0;
		regWorkTime = 0;
		regFullWorkTime = 0;
		overbefore = 0;
		overper = 0;
		overrest = 0;
		overTimeOut = 0;
		overRestTime = 0;
		nightWork = 0;
		nightRest = 0;
		nightWorkArray = new int[0];
		nightRestArray = new int[0];
		predeterminedHolidayWorkTime = 0;
		legalHolidayWorkTime = 0;
		withinStatutoryOvertime = 0;
		workBeforeTime = 0;
		workAfterTime = 0;
		calculatedStart = 0;
		calculatedEnd = 0;
		startTimeOfOvertimeCalculation = 0;
		tardinessRestMap = new TreeMap<Date, Date>();
		leaveEarlyRestMap = new TreeMap<Date, Date>();
		overtimeBeforeRestMap = new TreeMap<Date, Date>();
		overtimeRestMap = new TreeMap<Date, Date>();
		workTypeCode = "";
		// 勤務日
		workDate = attendanceDto.getWorkDate();
		// 出勤時刻
		startTime = attendanceDto.getStartTime();
		// 退勤時刻 
		endTime = attendanceDto.getEndTime();
		// 直行
		directStart = attendanceDto.getDirectStart() == 1;
		// 直帰
		directEnd = attendanceDto.getDirectEnd() == 1;
		// 遅刻理由
		lateReason = attendanceDto.getLateReason();
		// 早退理由
		leaveEarlyReason = attendanceDto.getLeaveEarlyReason();
		// 休憩リスト
		this.restDtoList = restDtoList;
		// 公用外出リスト
		this.publicGoOutDtoList = publicGoOutDtoList;
		// 私用外出リスト
		this.privateGoOutDtoList = privateGoOutDtoList;
	}
	
	/**
	 * 計算結果設定
	 * @param attendanceDto 勤怠データ
	 */
	public void getCalcInfo(AttendanceDtoInterface attendanceDto) {
		// 勤怠計算上の始業時刻をセットする
		attendanceDto.setStartTime(startTime == null ? startTime : getAttendanceTime(workDate, calculatedStart));
		// 勤怠計算上の終業時刻をセットする
		attendanceDto.setEndTime(endTime == null ? endTime : getAttendanceTime(workDate, calculatedEnd));
		attendanceDto.setDecreaseTime(decreaseTime);
		attendanceDto.setGeneralWorkTime(prescribedWorkTime);
		attendanceDto.setLateNightTime(nightWork);
		// 遅刻理由が電車遅延又は会社指示の場合は遅刻時間を0とする
		attendanceDto.setLateTime(TimeConst.CODE_TARDINESS_WHY_TRAIN.equals(lateReason)
				|| TimeConst.CODE_TARDINESS_WHY_COMPANY.equals(lateReason) ? 0 : lateTime);
		if (attendanceDto.getLateTime() > 0 && attendanceDto.getLateReason().isEmpty()) {
			// 遅刻理由がない場合は個人都合とする
			attendanceDto.setLateReason(TimeConst.CODE_TARDINESS_WHY_INDIVIDU);
		}
		// 早退理由が会社指示の場合は早退時間を0とする
		attendanceDto.setLeaveEarlyTime(TimeConst.CODE_LEAVEEARLY_WHY_COMPANY.equals(leaveEarlyReason) ? 0
				: leaveEarlyTime);
		if (attendanceDto.getLeaveEarlyTime() > 0 && attendanceDto.getLeaveEarlyReason().isEmpty()) {
			// 早退理由がない場合は個人都合とする
			attendanceDto.setLeaveEarlyReason(TimeConst.CODE_LEAVEEARLY_WHY_INDIVIDU);
		}
		attendanceDto.setLegalWorkTime(legalHolidayWorkTime);
		attendanceDto.setNightRestTime(nightRest);
		attendanceDto.setOverRestTime(overRestTime);
		attendanceDto.setOvertime(overtimeTime);
		attendanceDto.setOvertimeIn(withinStatutoryOvertime);
		attendanceDto.setOvertimeOut(overTimeOut);
		attendanceDto.setOvertimeAfter(workAfterTime);
		attendanceDto.setOvertimeBefore(workBeforeTime);
		attendanceDto.setPrivateTime(totalPrivate);
		attendanceDto.setPublicTime(totalPublic);
		attendanceDto.setRestTime(totalRest);
		attendanceDto.setSpecificWorkTime(predeterminedHolidayWorkTime);
		attendanceDto.setWorkTime(getWorkTime());
	}
	
	/**
	 * 法定休日労動かどうか確認。<br>
	 * @return 法定休日労働であればtrue、法定休日労動でなければfalse
	 */
	public boolean isWorkOnLegalDaysOff() {
		return TimeConst.CODE_WORK_ON_LEGAL_HOLIDAY.equals(workTypeCode);
	}
	
	/**
	 * 所定休日労動かどうか確認。<br>
	 * @return 所定休日労動であればtrue、所定休日労動でなければfalse
	 */
	public boolean isWorkOnPrescribedDaysOff() {
		return TimeConst.CODE_WORK_ON_PRESCRIBED_HOLIDAY.equals(workTypeCode);
	}
	
	/**
	 * 午前休かどうか確認。<br>
	 * @return 午前休であればtrue、午前休でなければfalse
	 */
	public boolean isAmHalfDayOff() {
		boolean amHalfDayOff = false;
		boolean pmHalfDayOff = false;
		// 休暇申請
		for (HolidayRequestDtoInterface dto : holidayRequestDtoList) {
			int range = dto.getHolidayRange();
			if (range == TimeConst.CODE_HOLIDAY_RANGE_AM) {
				// 午前休
				amHalfDayOff = true;
			} else if (range == TimeConst.CODE_HOLIDAY_RANGE_PM) {
				// 午後休
				pmHalfDayOff = true;
			}
		}
		// 代休申請
		for (SubHolidayRequestDtoInterface dto : subHolidayRequestDtoList) {
			int range = dto.getHolidayRange();
			if (range == TimeConst.CODE_HOLIDAY_RANGE_AM) {
				// 午前休
				amHalfDayOff = true;
			} else if (range == TimeConst.CODE_HOLIDAY_RANGE_PM) {
				// 午後休
				pmHalfDayOff = true;
			}
		}
		return amHalfDayOff && !pmHalfDayOff;
	}
	
	/**
	 * 午後休かどうか確認。<br>
	 * @return 午後休であればtrue、午後休でなければfalse
	 */
	public boolean isPmHalfDayOff() {
		boolean amHalfDayOff = false;
		boolean pmHalfDayOff = false;
		// 休暇申請
		for (HolidayRequestDtoInterface dto : holidayRequestDtoList) {
			int range = dto.getHolidayRange();
			if (range == TimeConst.CODE_HOLIDAY_RANGE_AM) {
				// 午前休
				amHalfDayOff = true;
			} else if (range == TimeConst.CODE_HOLIDAY_RANGE_PM) {
				// 午後休
				pmHalfDayOff = true;
			}
		}
		// 代休申請
		for (SubHolidayRequestDtoInterface dto : subHolidayRequestDtoList) {
			int range = dto.getHolidayRange();
			if (range == TimeConst.CODE_HOLIDAY_RANGE_AM) {
				// 午前休
				amHalfDayOff = true;
			} else if (range == TimeConst.CODE_HOLIDAY_RANGE_PM) {
				// 午後休
				pmHalfDayOff = true;
			}
		}
		return pmHalfDayOff && !amHalfDayOff;
	}
	
	@Override
	public void attendanceCalc(AttendanceDtoInterface attendanceDto) throws MospException {
		// 休憩リストの取得
		List<RestDtoInterface> restList = restReference.getRestList(attendanceDto.getPersonalId(), attendanceDto
			.getWorkDate(), attendanceDto.getTimesWork());
		// 公用外出リストの取得
		List<GoOutDtoInterface> publicGoOutList = goOutReference.getGoOutList(attendanceDto.getPersonalId(),
				attendanceDto.getWorkDate(), attendanceDto.getTimesWork(), 1);
		// 私用外出リストの取得
		List<GoOutDtoInterface> privateGoOutList = goOutReference.getGoOutList(attendanceDto.getPersonalId(),
				attendanceDto.getWorkDate(), attendanceDto.getTimesWork(), 2);
		attendanceCalc(attendanceDto, restList, publicGoOutList, privateGoOutList);
	}
	
	@Override
	public void attendanceCalc(AttendanceDtoInterface attendanceDto, List<RestDtoInterface> restList,
			List<GoOutDtoInterface> publicGoOutList, List<GoOutDtoInterface> privateGoOutList) throws MospException {
		// 計算基準値設定
		setCalcInfo(attendanceDto, restList, publicGoOutList, privateGoOutList);
		// 基本情報取得
		initAttendanceTotal(attendanceDto.getPersonalId(), attendanceDto.getWorkTypeCode());
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 自動計算
		setAutoCalc();
		// 計算結果設定
		getCalcInfo(attendanceDto);
	}
	
	@Override
	public AttendanceDtoInterface calcDraft(String personalId, Date workDate) throws MospException {
		AttendanceDtoInterface dto = attendanceReference.findForKey(personalId, workDate, 1);
		if (dto == null) {
			return null;
		}
		WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(dto.getWorkflow());
		if (workflowDto == null || !PlatformConst.CODE_STATUS_DRAFT.equals(workflowDto.getWorkflowStatus())) {
			// 下書でない場合
			return null;
		}
		// 下書の場合
		DifferenceRequestDtoInterface differenceRequestDto = differenceReference.findForKeyOnWorkflow(dto
			.getPersonalId(), dto.getWorkDate());
		if (differenceRequestDto != null) {
			// 時差出勤申請されている場合
			WorkflowDtoInterface differenceRequestWorkflowDto = workflowReference
				.getLatestWorkflowInfo(differenceRequestDto.getWorkflow());
			if (differenceRequestWorkflowDto != null
					&& PlatformConst.CODE_STATUS_COMPLETE.equals(differenceRequestWorkflowDto.getWorkflowStatus())) {
				// 時差出勤区分をセット
				dto.setWorkTypeCode(differenceRequestDto.getDifferenceType());
			}
		}
		// 自動計算
		attendanceCalc(dto);
		return dto;
	}
	
	@Override
	public void calcStartEndTime(AttendanceDtoInterface attendanceDto) throws MospException {
		// 計算基準値設定
		setCalcInfo(attendanceDto, new ArrayList<RestDtoInterface>(), new ArrayList<GoOutDtoInterface>(),
				new ArrayList<GoOutDtoInterface>());
		// 基本情報取得
		initAttendanceTotal(attendanceDto.getPersonalId(), attendanceDto.getWorkTypeCode());
		if (mospParams.hasErrorMessage()) {
			return;
		}
		// 自動計算
		if (startTime != null) {
			// 勤怠計算上の始業時刻・時間外計算上の始業時刻・規定始業時刻前時間の計算
			calcCalculatedStart();
		}
		if (endTime != null) {
			// 勤怠計算上の終業時刻の計算
			calcCalculatedEnd();
		}
		// 計算結果設定
		// 勤怠計算上の始業時刻をセットする
		attendanceDto.setStartTime(startTime == null ? startTime : getAttendanceTime(workDate, calculatedStart));
		// 勤怠計算上の終業時刻をセットする
		attendanceDto.setEndTime(endTime == null ? endTime : getAttendanceTime(workDate, calculatedEnd));
	}
	
	/**
	 * 勤怠計算上の始業時刻を計算する。<br>
	 * 合わせて時間外計算上の始業時刻及び規定始業時刻前時間を計算する。<br>
	 */
	protected void calcCalculatedStart() {
		// 実際の始業時刻
		int actualWorkBegin = getRoundMinute(getDefferenceMinutes(workDate, startTime), timeSettingDto
			.getRoundDailyStart(), timeSettingDto.getRoundDailyStartUnit());
		if (regWorkStart <= actualWorkBegin) {
			// 規定始業時刻以後に始業した場合
			if (directStart) {
				// 直行の場合は規定始業時刻を勤怠計算上の始業時刻とする
				calculatedStart = regWorkStart;
				startTimeOfOvertimeCalculation = regWorkStart;
				return;
			}
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労働又は所定休日労働の場合
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		// 勤務前残業予定時間
		int planBeforeOvertime = 0;
		if (beforeOvertimeDto != null) {
			planBeforeOvertime = beforeOvertimeDto.getRequestTime();
		}
		if (planBeforeOvertime <= 0) {
			// 勤務前残業予定時間が0以下の場合
			calculatedStart = regWorkStart;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		// 勤務前残業時間
		int beforeOvertimeTime = regWorkStart - actualWorkBegin;
		if (beforeOvertimeTime <= planBeforeOvertime) {
			// 勤務前残業時間が勤務前残業予定時間以下の場合
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			workBeforeTime = beforeOvertimeTime;
			return;
		}
		calculatedStart = regWorkStart - planBeforeOvertime;
		startTimeOfOvertimeCalculation = regWorkStart;
		workBeforeTime = planBeforeOvertime;
	}
	
	/**
	 * 勤怠計算上の終業時刻を計算する。<br>
	 */
	protected void calcCalculatedEnd() {
		// 実際の終業時刻
		int actualWorkEnd = getRoundMinute(getDefferenceMinutes(workDate, endTime), timeSettingDto.getRoundDailyEnd(),
				timeSettingDto.getRoundDailyEndUnit());
		if (isPmHalfDayOff() && actualWorkEnd > regWorkEnd) {
			// 午後休で且つ実際の終業時刻が午前休の終了時刻より後の場合は
			// 午前休の終了時刻を勤怠計算上の終業時刻とする
			calculatedEnd = regWorkEnd;
			return;
		}
		if (directEnd && actualWorkEnd < regWorkEnd) {
			// 直帰で且つ実際の終業時刻が規定終業時刻より前の場合は
			// 規定終業時刻を勤怠計算上の終業時刻とする
			calculatedEnd = regWorkEnd;
			return;
		}
		calculatedEnd = actualWorkEnd;
	}
	
	/**
	 * 勤務形態コードを取得する
	 * @param personalId 個人ID
	 * @param workDate 勤務日
	 * @param timesWork 勤務回数
	 * @return 勤務形態コード
	 * @throws MospException 例外処理発生時
	 */
	protected String getWorkTypeCode(String personalId, Date workDate, int timesWork) throws MospException {
		AttendanceDtoInterface dto = attendanceReference.findForKey(personalId, workDate, timesWork);
		if (dto != null) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(dto.getWorkflow());
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				return dto.getWorkTypeCode();
			}
		}
		List<SubstituteDtoInterface> substituteList = substituteReference.getSubstituteList(personalId, workDate);
		for (SubstituteDtoInterface substituteDto : substituteList) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(substituteDto.getWorkflow());
			if (workflowDto == null || !PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				continue;
			}
			if (substituteDto.getSubstituteRange() == 1) {
				return substituteDto.getSubstituteType();
			}
		}
		DifferenceRequestDtoInterface differenceRequestDto = differenceReference.findForKeyOnWorkflow(personalId,
				workDate);
		if (differenceRequestDto != null) {
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(differenceRequestDto
				.getWorkflow());
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				return differenceRequestDto.getDifferenceType();
			}
		}
		Date scheduleWorkDate = workDate;
//		int scheduleTimesWork = timesWork;
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHolidayReference.findForKeyOnWorkflow(
				personalId, workDate);
		if (workOnHolidayRequestDto != null) {
			long workflow = workOnHolidayRequestDto.getWorkflow();
			WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(workflow);
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				int substitute = workOnHolidayRequestDto.getSubstitute();
				if (substitute == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_ON) {
					// 振り替える場合
					List<SubstituteDtoInterface> list = substituteReference.getSubstituteList(workflow);
					for (SubstituteDtoInterface substituteDto : list) {
						scheduleWorkDate = substituteDto.getSubstituteDate();
//						scheduleTimesWork = substituteDto.getTimesWork();
						break;
					}
				} else if (substitute == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_OFF) {
					// 振り替えない場合
					if (TimeConst.CODE_HOLIDAY_LEGAL_HOLIDAY.equals(workOnHolidayRequestDto.getWorkOnHolidayType())) {
						// 法定休日労動の場合
						return TimeConst.CODE_WORK_ON_LEGAL_HOLIDAY;
					} else if (TimeConst.CODE_HOLIDAY_PRESCRIBED_HOLIDAY.equals(workOnHolidayRequestDto
						.getWorkOnHolidayType())) {
						// 所定休日労動の場合
						return TimeConst.CODE_WORK_ON_PRESCRIBED_HOLIDAY;
					}
				}
			}
		}
		ApplicationDtoInterface applicationDto = applicationReference.findForPerson(personalId, scheduleWorkDate);
		if (applicationDto == null) {
			return null;
		}
		ScheduleDtoInterface scheduleDto = scheduleReference.getScheduleInfo(applicationDto.getScheduleCode(),
				scheduleWorkDate);
		if (scheduleDto == null) {
			return null;
		}
		ScheduleDateDtoInterface scheduleDateDto = scheduleDateReference.getScheduleDateInfo(scheduleDto
			.getScheduleCode(), scheduleDto.getActivateDate(), scheduleWorkDate);
		if (scheduleDateDto == null) {
			return null;
		}
		return scheduleDateDto.getWorkTypeCode();
	}
	
	@Override
	public int getDefferenceMinutes(Date startTime, Date endTime) {
		return super.getDefferenceMinutes(startTime, endTime);
	}
	
	/**
	 * 時刻を日付オブジェクトに変換し取得する。<br>
	 * 時刻は分単位とする。<br>
	 * 基準日は、変換の際に基準として用いる。<br>
	 * @param date 基準日
	 * @param time 時刻
	 * @return 日付オブジェクト
	 */
	private Date getAttendanceTime(Date date, int time) {
		return getAttendanceTime(date, Integer.toString(time / TimeConst.CODE_DEFINITION_HOUR), Integer.toString(time
				% TimeConst.CODE_DEFINITION_HOUR));
	}
	
	@Override
	public Date getAttendanceTime(Date date, String hour, String minute) {
		StringBuffer sb = new StringBuffer();
		if (hour.length() == 1) {
			sb.append(0);
		}
		sb.append(hour);
		if (minute.length() == 1) {
			sb.append(0);
		}
		sb.append(minute);
		return getAttendanceTime(date, sb.toString());
	}
	
	@Override
	public Date getRoundMinute(Date time, int type, int unit) {
		if (time == null) {
			return null;
		}
		long milliseconds = time.getTime();
		if (milliseconds == 0 || type == 0 || unit <= 0) {
			return time;
		}
		if (type == 1 || type == 2) {
			// 丸め単位を分単位からミリ秒単位に変換
			long millisecondsUnit = unit * TimeConst.CODE_DEFINITION_HOUR * 1000;
			// 余り
			long remainder = milliseconds % millisecondsUnit;
			if (remainder == 0) {
				// 余りが0の場合はそのまま返す
				return time;
			}
			long rounded = milliseconds - remainder;
			if (type == 1) {
				// 切捨て
				return new Date(rounded);
			} else if (type == 2) {
				// 切上げ
				return new Date(rounded + millisecondsUnit);
			}
		}
		return time;
	}
	
	@Override
	public int getRoundMinute(int time, int type, int unit) {
		return super.getRoundMinute(time, type, unit);
	}
	
}
