/*
 * 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;
	
	/**
	 * 残業申請DTOインターフェース(終業後)
	 */
	protected OvertimeRequestDtoInterface				afterOvertimeDto;
	
	/**
	 * 公用外出時間
	 */
	protected int										totalPublicTime;
	
	/**
	 * 残業時間
	 */
	protected int										overtimeTime;
	
	/**
	 * 規定始業時刻
	 */
	protected int										regWorkStart;
	
	/**
	 * 規定終業時刻
	 */
	protected int										regWorkEnd;
	
	/**
	 * 規定労働時間
	 */
	protected int										regWorkTime;
	
	/**
	 * 規定フル始業時刻
	 */
	protected int										regFullWorkStart;
	
	/**
	 * 規定フル労働時間
	 */
	protected int										regFullWorkTime;
	
	/**
	 * 前半休と後半休の間の時間
	 */
	protected int										betweenHalfHolidayTime;
	
	/**
	 * 残前休憩
	 */
	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, 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;
		}
		// 残業申請
		OvertimeRequestDtoInterface overtimeRequestDto = overtimeReference
			.findForKeyOnWorkflow(personalId, workDate, 2);
		if (overtimeRequestDto != null) {
			WorkflowDtoInterface workflowDto = workflowReference
				.getLatestWorkflowInfo(overtimeRequestDto.getWorkflow());
			if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
				// 承認完了の場合
				afterOvertimeDto = overtimeRequestDto;
			}
		}
		if (timeSettingDto.getBeforeOvertimeFlag() == TimeConst.CODE_BEFORE_OVERTIME_VALID) {
			// 始業前の時間外労働が有効である場合
			OvertimeRequestDtoInterface beforeDto = overtimeReference.findForKeyOnWorkflow(personalId, workDate, 1);
			if (beforeDto != null) {
				WorkflowDtoInterface workflowDto = workflowReference.getLatestWorkflowInfo(beforeDto.getWorkflow());
				if (workflowDto != null && PlatformConst.CODE_STATUS_COMPLETE.equals(workflowDto.getWorkflowStatus())) {
					// 承認完了の場合
					beforeOvertimeDto = beforeDto;
				}
			}
		}
		// 休暇申請
		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()) {
			return;
		}
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労動又は所定休日労動の場合
			// 始業時刻
			int daysOffWorkStart = getDefferenceMinutes(workOnHolidayDto.getRequestDate(), workOnHolidayDto
				.getStartTime());
			regWorkStart = daysOffWorkStart;
			regFullWorkStart = daysOffWorkStart;
			// 終業時刻
			regWorkEnd = getDefferenceMinutes(workOnHolidayDto.getRequestDate(), workOnHolidayDto.getEndTime());
			return;
		}
		// カレンダ日マスタに勤怠コードが設定されている場合
		WorkTypeDtoInterface workTypeDto = workTypeReference.getWorkTypeInfo(workTypeCode, workDate);
		if (workTypeDto == null) {
			addWorkTypeNotExistErrorMessage(workDate);
			return;
		}
		String localWorkTypeCode = workTypeDto.getWorkTypeCode();
		// 出勤時刻,退勤時刻の取得
		// 勤務形態から取得
		// 出勤時刻
		WorkTypeItemDtoInterface workStartDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode, workDate,
				TimeConst.CODE_WORKSTART);
		if (workStartDto != null) {
			int time = getDefferenceMinutes(getDefaultStandardDate(), workStartDto.getWorkTypeItemValue());
			regWorkStart = time;
			regFullWorkStart = time;
		}
		// 退勤時刻
		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);
		WorkTypeItemDtoInterface backStartDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode, workDate,
				TimeConst.CODE_BACKSTART);
		WorkTypeItemDtoInterface backEndDto = workTypeItemReference.getWorkTypeItemInfo(localWorkTypeCode, workDate,
				TimeConst.CODE_BACKEND);
		int frontStart = 0;
		if (frontStartDto != null) {
			frontStart = getDefferenceMinutes(getDefaultStandardDate(), frontStartDto.getWorkTypeItemValue());
		}
		int frontEnd = 0;
		if (frontEndDto != null) {
			frontEnd = getDefferenceMinutes(getDefaultStandardDate(), frontEndDto.getWorkTypeItemValue());
		}
		int backStart = 0;
		if (backStartDto != null) {
			backStart = getDefferenceMinutes(getDefaultStandardDate(), backStartDto.getWorkTypeItemValue());
		}
		int backEnd = 0;
		if (backEndDto != null) {
			backEnd = getDefferenceMinutes(getDefaultStandardDate(), backEndDto.getWorkTypeItemValue());
		}
		if (isPmHalfDayOff()) {
			// 午後休の場合
			regWorkStart = frontStart;
			regWorkEnd = frontEnd;
			regWorkTime = frontEnd - frontStart;
			betweenHalfHolidayTime = backStart - frontEnd;
		}
		if (isAmHalfDayOff()) {
			// 午前休の場合
			regWorkStart = backStart;
			regWorkEnd = backEnd;
			regWorkTime = backEnd - backStart;
			betweenHalfHolidayTime = backStart - frontEnd;
		}
	}
	
	/**
	 * 勤務時間の出力
	 * @return 勤務時間
	 */
	public int getWorkTime() {
		// 勤怠設定管理DTOが存在しない
		if (timeSettingDto == null) {
			return 0;
		}
		// 丸め単位
		return getRoundMinute(workTime, timeSettingDto.getRoundDailyWork(), timeSettingDto.getRoundDailyWorkUnit());
	}
	
	/* 自動計算 */
	/**
	 * 労働時間を計算する。<br>
	 * 合わせて休憩時間及び規定終業時刻後時間を計算する。<br>
	 */
	protected 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().intValue();
			int mapEndTimeInt = entry.getValue().intValue();
			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 = totalWorkTime();
				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 = totalWorkTime();
			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 totalOvertimeRest = 0;
		if (!isWorkOnLegalDaysOff() && !isWorkOnPrescribedDaysOff()) {
			// 法定休日労働でなく且つ所定休日労働でない場合
			// 残業休憩回数の設定
			int overtimeRestTimes = 0;
			if (overper > 1) {
				overtimeRestTimes = (calculatedEnd - afterOvertimeWorkStart) / overper;
			}
			int previousOvertimeRestEnd = afterOvertimeWorkStart;
			for (int i = 0; i < overtimeRestTimes; i++) {
				int overtimeRestStart = previousOvertimeRestEnd + overper - overrest;
				int overtimeRestEnd = previousOvertimeRestEnd + overper;
				if (overtimeRestStart >= calculatedStart) {
					overtimeRestMap.put(getAttendanceTime(workDate, overtimeRestStart), getAttendanceTime(workDate,
							overtimeRestEnd));
					totalOvertimeRest += overtimeRestEnd - overtimeRestStart;
				}
				previousOvertimeRestEnd = overtimeRestEnd;
			}
		}
		totalRest = totalManualRest + totalOvertimeBeforeRest + totalOvertimeRest;
		workTime = totalWorkTime();
	}
	
	/**
	 * 勤務時間を計算する。
	 * @return 勤務時間
	 */
	protected int totalWorkTime() {
		return 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例外が発生した場合
	 */
	protected 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 > TimeConst.TIME_DAY_ALL_HOUR * 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 < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							end = TimeConst.TIME_DAY_ALL_HOUR * 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 < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							end = TimeConst.TIME_DAY_ALL_HOUR * 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 < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							end = TimeConst.TIME_DAY_ALL_HOUR * 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 < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							end = TimeConst.TIME_DAY_ALL_HOUR * 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 < TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
						if (end > TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
							end = TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR;
						}
						before24HourOvertimeRest += end - start;
					}
				}
				if (isWorkOnLegalDaysOff()) {
					// 当日が法定休日労動の場合
					int legalDaysOffWork = getOvertimeWorkCaseLegalDay(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 = getcalcOvertimeWorkCasePrescribedDay(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 = getcalcOvertimeWorkCaseWorkDay(calculatedStart, before24HourManualRest,
							before24HourOvertimeBeforeRest, before24HourOvertimeRest, before24HourPublicGoOut,
							before24HourPrivateGoOut, prescribedWorkTime, lateTime, leaveEarlyTime);
					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 = getNextDayTime(calculatedEnd, 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 (isWorkOnLegalDaysOff()) {
			// 法定休日労動
			legalHolidayWorkTime = getWorkTime();
			return;
		} else if (isWorkOnPrescribedDaysOff()) {
			// 所定休日労動
			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 = calcOverTime(totalManualRest, totalOvertimeRest, totalOvertimeBeforeRest, totalPublicGoOut,
				totalPrivateGoOut);
		// 残業時間の設定
		overtimeTime = getRoundMinute(overtime, timeSettingDto.getRoundDailyWork(), timeSettingDto
			.getRoundDailyWorkUnit());
		int afterTime = overtimeTime - workBeforeTime;
		if (afterTime < 0) {
			afterTime = 0;
		}
		workAfterTime = afterTime;
	}
	
	/**
	 * 当日が法定休日労動の場合
	 * @param calculatedStart 勤怠計算上の始業時刻
	 * @param before24HourManualRest 24時以前の合計手動休憩時間
	 * @param before24HourOvertimeBeforeRest 24時以前の規定終業時刻後時間外前休憩時間
	 * @param before24HourOvertimeRest 24時以前の合計残業休憩時間
	 * @param before24HourPublicGoOut 24時以前の合計公用外出時間
	 * @param before24HourPrivateGoOut 24時以前の合計私用外出時間
	 * @return 法定休日労動残業時間
	 */
	protected int getOvertimeWorkCaseLegalDay(int calculatedStart, int before24HourManualRest,
			int before24HourOvertimeBeforeRest, int before24HourOvertimeRest, int before24HourPublicGoOut,
			int before24HourPrivateGoOut) {
		return TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR - calculatedStart - before24HourManualRest
				- before24HourOvertimeBeforeRest - before24HourOvertimeRest - before24HourPublicGoOut
//				- before24HourPrivateGoOut
		;
	}
	
	/**
	 * 残業時間を計算する。
	 * 暦日計算：24時以前の合計残業時間を計算する際の当日が所定休日労動の場合。
	 * @param calculatedStart 勤怠計算上の始業時刻
	 * @param before24HourManualRest 24時以前の合計手動休憩時間
	 * @param before24HourOvertimeBeforeRest 24時以前の規定終業時刻後時間外前休憩時間
	 * @param before24HourOvertimeRest 24時以前の合計残業休憩時間
	 * @param before24HourPublicGoOut 24時以前の合計公用外出時間
	 * @param before24HourPrivateGoOut 24時以前の合計私用外出時間
	 * @return 所定休日労動残業時間
	 */
	protected int getcalcOvertimeWorkCasePrescribedDay(int calculatedStart, int before24HourManualRest,
			int before24HourOvertimeBeforeRest, int before24HourOvertimeRest, int before24HourPublicGoOut,
			int before24HourPrivateGoOut) {
		return TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR - calculatedStart - before24HourManualRest
				- before24HourOvertimeBeforeRest - before24HourOvertimeRest - before24HourPublicGoOut
//				- before24HourPrivateGoOut
		;
	}
	
	/**
	 * 残業時間を計算する。
	 * 暦日計算：24時以前の残業時間を計算する際の当日が平日労動の場合。
	 * @param calculatedStart 勤怠計算上の始業時刻
	 * @param before24HourManualRest 24時以前の合計手動休憩時間
	 * @param before24HourOvertimeBeforeRest 24時以前の規定終業時刻後時間外前休憩時間
	 * @param before24HourOvertimeRest 24時以前の合計残業休憩時間
	 * @param before24HourPublicGoOut 24時以前の合計公用外出時間
	 * @param before24HourPrivateGoOut 24時以前の合計私用外出時間
	 * @param prescribedWorkTime 所定労働時間
	 * @param lateTime 遅刻時間
	 * @param leaveEarlyTime 早退時間
	 * @return 平日労動残業時間
	 */
	protected int getcalcOvertimeWorkCaseWorkDay(int calculatedStart, int before24HourManualRest,
			int before24HourOvertimeBeforeRest, int before24HourOvertimeRest, int before24HourPublicGoOut,
			int before24HourPrivateGoOut, int prescribedWorkTime, int lateTime, int leaveEarlyTime) {
		return TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR - calculatedStart - before24HourManualRest
				- before24HourOvertimeBeforeRest - before24HourOvertimeRest - before24HourPublicGoOut
//				- before24HourPrivateGoOut
				- prescribedWorkTime + lateTime + leaveEarlyTime;
	}
	
	/**
	 * 残業時間を計算する。
	 * 暦日計算：翌日時間を計算する際
	 * @param calculatedEnd 勤怠計算上の終業時刻
	 * @param after24HourManualRest 24時以後の合計手動休憩時間
	 * @param after24HourOvertimeBeforeRest 24時以後の規定終業時刻後時間外前休憩時間
	 * @param after24HourOvertimeRest 24時以後の合計残業休憩時間
	 * @param after24HourPublicGoOut 24時以後の合計公用外出時間
	 * @param after24HourPrivateGoOut 24時以後の合計私用外出時間
	 * @return 翌日残業時間
	 */
	protected int getNextDayTime(int calculatedEnd, int after24HourManualRest, int after24HourOvertimeBeforeRest,
			int after24HourOvertimeRest, int after24HourPublicGoOut, int after24HourPrivateGoOut) {
		return calculatedEnd - (TimeConst.TIME_DAY_ALL_HOUR * TimeConst.CODE_DEFINITION_HOUR) - after24HourManualRest
				- after24HourOvertimeBeforeRest - after24HourOvertimeRest - after24HourPublicGoOut
//				- after24HourPrivateGoOut
		;
	}
	
	/**
	 * 残業時間の計算をする。
	 * @param totalManualRest 合計手動休憩時間
	 * @param totalOvertimeRest 合計残業休憩時間
	 * @param totalOvertimeBeforeRest 規定終業時刻後時間外前休憩時間
	 * @param totalPublicGoOut 合計公用外出時間
	 * @param totalPrivateGoOut 合計私用外出時間
	 * @return 残業時間
	 */
	protected int calcOverTime(int totalManualRest, int totalOvertimeRest, int totalOvertimeBeforeRest,
			int totalPublicGoOut, int totalPrivateGoOut) {
		// 残業時間設定
		int overtime = calculatedEnd - calculatedStart - totalManualRest - totalOvertimeRest - totalOvertimeBeforeRest
				- totalPublicGoOut
//				- totalPrivateGoOut
				- prescribedWorkTime + lateTime + leaveEarlyTime;
		if (overtime < 0) {
			// 0未満の場合は0とする
			return 0;
		}
		return overtime;
	}
	
	/**
	 * 深夜労働時間の計算
	 */
	private void calcNightWorkTime() {
		// 初期化
		nightWork = 0;
		nightRest = 0;
		nightWorkArray = new int[3];
		nightRestArray = new int[3];
		// 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);
		// 46:00から48:00までの深夜労働時間・深夜休憩時間の計算
		calcNightWorkTime((TimeConst.TIME_NIGHT_WORK_START + TimeConst.TIME_DAY_ALL_HOUR)
				* TimeConst.CODE_DEFINITION_HOUR, 48 * TimeConst.CODE_DEFINITION_HOUR, 2);
		// 合計深夜労働時間
		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 = nightWorkTime(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;
	}
	
	/**
	 * 深夜労働時間を計算する。
	 * @param nightWorkEnd 深夜労働終了時刻
	 * @param nightWorkStart 深夜労働始業時刻
	 * @param totalManualRest 深夜時間を含む合計手動休憩時間
	 * @param totalOvertimeBeforeRest 深夜時間を含む残業前休憩
	 * @param totalOvertimeRest 深夜時間を含む残業休憩時間
	 * @param totalPublicGoOut 深夜時間を含む公用外出時間
	 * @param totalPrivateGoOut 深夜時間を含む私用外出時間
	 * @return 深夜労働時間
	 */
	protected int nightWorkTime(int nightWorkEnd, int nightWorkStart, int totalManualRest, int totalOvertimeBeforeRest,
			int totalOvertimeRest, int totalPublicGoOut, int totalPrivateGoOut) {
		// 深夜労働時間
		return nightWorkEnd - nightWorkStart - totalManualRest - totalOvertimeBeforeRest - totalOvertimeRest
				- totalPublicGoOut - totalPrivateGoOut;
	}
	
	/**
	 * 減額対象時間の計算
	 */
	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());
	}
	
	/**
	 * 所定労働時間の計算
	 */
	protected void calcPrescribedWorkTime() {
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労働又は所定休日労働の場合は0とする
			prescribedWorkTime = 0;
			// 法定休日労動又は所定休日労働の場合は勤怠設定の所定労働時間とする
			autoRestCalcStart = DateUtility.getHour(timeSettingDto.getGeneralWorkTime())
					* TimeConst.CODE_DEFINITION_HOUR + DateUtility.getMinute(timeSettingDto.getGeneralWorkTime());
			return;
		}
		// 規定労働時間が法定労働時間(8時間)を超える場合
		if (regWorkTime > TimeConst.TIME_WORKING_HOUR * TimeConst.CODE_DEFINITION_HOUR) {
			// 規定労働時間が法定労働時間を超える場合は法定労働時間(8時間)を所定労働時間とする
			prescribedWorkTime = TimeConst.TIME_WORKING_HOUR * TimeConst.CODE_DEFINITION_HOUR;
			autoRestCalcStart = TimeConst.TIME_WORKING_HOUR * TimeConst.CODE_DEFINITION_HOUR;
			if (isAmHalfDayOff()) {
				// 午前半休の場合
				autoRestCalcStart = regWorkTime;
			}
			return;
		}
		// 規定労働時間を所定労働時間とする
		prescribedWorkTime = regWorkTime;
		autoRestCalcStart = regFullWorkTime;
		if (isAmHalfDayOff()) {
			// 午前半休の場合
			autoRestCalcStart = regWorkTime;
		}
	}
	
	/**
	 * 法内残業の計算
	 */
	protected void calcWithinStatutoryOvertime() {
		// 法定労働時間と所定労働時間の差に遅刻時間、早退時間及び私用外出時間を加算する
		int differenceTime = TimeConst.TIME_WORKING_HOUR * TimeConst.CODE_DEFINITION_HOUR - prescribedWorkTime
				+ lateTime + leaveEarlyTime + totalPrivate;
		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(true);
		}
		if (endTime != null) {
			// 勤怠計算上の終業時刻の計算
			calcCalculatedEnd(true);
		}
		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;
		afterOvertimeDto = null;
		totalPublicTime = 0;
		overtimeTime = 0;
		regWorkStart = 0;
		regWorkEnd = 0;
		regWorkTime = 0;
		regFullWorkStart = 0;
		regFullWorkTime = 0;
		betweenHalfHolidayTime = 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);
		// 残業休憩時間の重複チェック
		checkOvertimeRest();
	}
	
	@Override
	public void calcStartEndTime(AttendanceDtoInterface attendanceDto, boolean useBetweenTime) 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(useBetweenTime);
		}
		if (endTime != null) {
			// 勤怠計算上の終業時刻の計算
			calcCalculatedEnd(useBetweenTime);
		}
		// 計算結果設定
		// 勤怠計算上の始業時刻をセットする
		attendanceDto.setStartTime(startTime == null ? startTime : getAttendanceTime(workDate, calculatedStart));
		// 勤怠計算上の終業時刻をセットする
		attendanceDto.setEndTime(endTime == null ? endTime : getAttendanceTime(workDate, calculatedEnd));
	}
	
	/**
	 * 勤怠計算上の始業時刻を計算する。<br>
	 * 合わせて時間外計算上の始業時刻及び規定始業時刻前時間を計算する。<br>
	 * @param useBetweenTime 前半休と後半休の間の時間を使う場合true、そうでない場合false
	 */
	protected void calcCalculatedStart(boolean useBetweenTime) {
		int betweenTime = betweenHalfHolidayTime;
		if (!useBetweenTime) {
			betweenTime = 0;
		}
		// 実際の始業時刻
		int actualWorkBegin = getRoundMinute(getDefferenceMinutes(workDate, startTime), timeSettingDto
			.getRoundDailyStart(), timeSettingDto.getRoundDailyStartUnit());
		if (isWorkOnLegalDaysOff() || isWorkOnPrescribedDaysOff()) {
			// 法定休日労働又は所定休日労働の場合
			// 実際の始業時刻を規定始業時刻とする
			regWorkStart = actualWorkBegin;
			// 実際の始業時刻を勤怠計算上の始業時刻とする
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		if (actualWorkBegin >= regWorkStart) {
			// 実際の始業時刻が規定始業時刻以後の場合
			if (directStart) {
				// 直行の場合は規定始業時刻を勤怠計算上の始業時刻とする
				calculatedStart = regWorkStart;
				startTimeOfOvertimeCalculation = regWorkStart;
				return;
			}
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		// 始業前労働予定時間
		int planBeforeOvertime = 0;
		if (beforeOvertimeDto != null) {
			planBeforeOvertime = beforeOvertimeDto.getRequestTime();
		}
		if (planBeforeOvertime <= 0) {
			// 始業前労働予定時間が0以下の場合
			calculatedStart = regWorkStart;
			startTimeOfOvertimeCalculation = regWorkStart;
			return;
		}
		if (isAmHalfDayOff()) {
			// 前半休の場合
			// 始業前労働可能時間
			int possibleBeforeTime = 0;
			if (planBeforeOvertime >= betweenTime) {
				// 始業前労働予定時間が前半休と後半休の間の時間以上の場合
				possibleBeforeTime = betweenTime;
			} else {
				possibleBeforeTime = planBeforeOvertime;
			}
			if (actualWorkBegin >= regWorkStart - possibleBeforeTime) {
				// 実際の始業時刻が規定始業時刻から始業前労働可能時間を引いたもの以後の場合
				calculatedStart = actualWorkBegin;
				startTimeOfOvertimeCalculation = regWorkStart;
				// 規定始業時刻前時間の計算
				calcWorkBeforeTime();
				return;
			}
			// 実際の始業時刻が規定始業時刻から始業前労働可能時間を引いたものより前の場合
			calculatedStart = regWorkStart - betweenTime;
			startTimeOfOvertimeCalculation = regWorkStart;
			// 規定始業時刻前時間の計算
			calcWorkBeforeTime();
			return;
		}
		if (actualWorkBegin >= regWorkStart - planBeforeOvertime) {
			// 実際の始業時刻が規定始業時刻から始業前労働予定時間を引いたもの以後の場合
			calculatedStart = actualWorkBegin;
			startTimeOfOvertimeCalculation = regWorkStart;
			// 規定始業時刻前時間の計算
			calcWorkBeforeTime();
			return;
		}
		calculatedStart = regWorkStart - planBeforeOvertime;
		startTimeOfOvertimeCalculation = regWorkStart;
		// 規定始業時刻前時間の計算
		calcWorkBeforeTime();
	}
	
	/**
	 * 規定始業時刻前時間を計算する。<br>
	 */
	protected void calcWorkBeforeTime() {
		// 休憩時間
		int rest = 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 = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int end = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (start < regWorkStart && end > calculatedStart) {
				// 休憩開始時刻が規定始業時刻より前で且つ
				// 休憩終了時刻が勤怠計算上の始業時刻より後の場合
				if (start < calculatedStart) {
					// 休憩開始時刻が勤怠計算上の始業時刻より前の場合は、
					// 勤怠計算上の始業時刻を休憩開始時刻とする
					start = calculatedStart;
				}
				if (end > regWorkStart) {
					// 休憩終了時刻が規定始業時刻より後の場合は、
					// 規定始業時刻を休憩終了時刻とする
					end = regWorkStart;
				}
				rest += end - start;
			}
		}
		// 公用外出時間
		int publicGoOut = 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 = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int end = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (start < regWorkStart && end > calculatedStart) {
				// 外出開始時刻が規定始業時刻より前で且つ
				// 外出終了時刻が勤怠計算上の始業時刻より後の場合
				if (start < calculatedStart) {
					// 外出開始時刻が勤怠計算上の始業時刻より前の場合は、
					// 勤怠計算上の始業時刻を外出開始時刻とする
					start = calculatedStart;
				}
				if (end > regWorkStart) {
					// 外出終了時刻が規定始業時刻より後の場合は、
					// 規定始業時刻を外出終了時刻とする
					end = regWorkStart;
				}
				publicGoOut += end - start;
			}
		}
		// 私用外出時間
		int privateGoOut = 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 = DateUtility.getHour(startTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(startTime);
			int end = DateUtility.getHour(endTime, workDate) * TimeConst.CODE_DEFINITION_HOUR
					+ DateUtility.getMinute(endTime);
			if (start < regWorkStart && end > calculatedStart) {
				// 外出開始時刻が規定始業時刻より前で且つ
				// 外出終了時刻が勤怠計算上の始業時刻より後の場合
				if (start < calculatedStart) {
					// 外出開始時刻が勤怠計算上の始業時刻より前の場合は、
					// 勤怠計算上の始業時刻を外出開始時刻とする
					start = calculatedStart;
				}
				if (end > regWorkStart) {
					// 外出終了時刻が規定始業時刻より後の場合は、
					// 規定始業時刻を外出終了時刻とする
					end = regWorkStart;
				}
				privateGoOut += end - start;
			}
		}
		int beforeTime = getbeforeTime(regWorkStart, calculatedStart, rest, publicGoOut, privateGoOut);
		if (beforeTime < 0) {
			beforeTime = 0;
		}
		workBeforeTime = beforeTime;
	}
	
	/**
	 * 規定始業時刻前時間を計算する。
	 * @param regWorkStart 規定始業時刻前時間を計算
	 * @param calculatedStart 勤怠計算上の始業時刻
	 * @param rest 勤怠計算上の休憩時間
	 * @param publicGoOut 勤怠計算上の公用外出時間
	 * @param privateGoOut 勤怠計算上の私用外出時間
	 * @return 規定始業時刻前時間
	 */
	protected int getbeforeTime(int regWorkStart, int calculatedStart, int rest, int publicGoOut, int privateGoOut) {
		return regWorkStart - calculatedStart - rest - publicGoOut - privateGoOut;
	}
	
	/**
	 * 勤怠計算上の終業時刻を計算する。<br>
	 * @param useBetweenTime 前半休と後半休の間の時間を使う場合true、そうでない場合false
	 */
	protected void calcCalculatedEnd(boolean useBetweenTime) {
		int betweenTime = betweenHalfHolidayTime;
		if (!useBetweenTime) {
			betweenTime = 0;
		}
		// 実際の終業時刻
		int actualWorkEnd = getRoundMinute(getDefferenceMinutes(workDate, endTime), timeSettingDto.getRoundDailyEnd(),
				timeSettingDto.getRoundDailyEndUnit());
		if (actualWorkEnd <= regWorkEnd) {
			// 実際の終業時刻が規定終業時刻以前の場合
			if (directEnd) {
				// 直帰の場合は規定終業時刻を勤怠計算上の終業時刻とする
				calculatedEnd = regWorkEnd;
				return;
			}
			calculatedEnd = actualWorkEnd;
			return;
		}
		if (isPmHalfDayOff()) {
			// 午後休の場合
			// 終業後労働予定時間
			int planAfterOvertime = 0;
			if (afterOvertimeDto != null) {
				planAfterOvertime = afterOvertimeDto.getRequestTime();
			}
			if (planAfterOvertime <= 0) {
				// 終業後労働予定時間が0以下の場合
				calculatedEnd = regWorkEnd;
				return;
			}
			// 終業後労働可能時間
			int possibleAfterTime = 0;
			if (planAfterOvertime >= betweenTime) {
				// 終業後労働予定時間が前半休と後半休の間の時間以上の場合
				possibleAfterTime = betweenTime;
			} else {
				possibleAfterTime = planAfterOvertime;
			}
			if (actualWorkEnd <= regWorkEnd + possibleAfterTime) {
				// 実際の終業時刻が規定終業時刻に終業後労働可能時間を足したもの以前の場合
				calculatedEnd = actualWorkEnd;
				return;
			}
			// 実際の終業時刻が規定終業時刻に終業後労働可能時間を足したものより後の場合
			calculatedEnd = regWorkEnd + betweenTime;
			return;
		}
		calculatedEnd = actualWorkEnd;
	}
	
	/**
	 * 残業休憩時間との重複チェック。<br>
	 */
	protected void checkOvertimeRest() {
		// 休憩時間
		for (RestDtoInterface dto : restDtoList) {
			String[] array = getOverlapOvertimeRestArray(dto.getRestStart(), dto.getRestEnd());
			if (array.length == 0) {
				continue;
			}
			// 残業休憩時間と重複している場合
			StringBuffer sb = new StringBuffer();
			sb.append(mospParams.getName("RestTime"));
			sb.append(mospParams.getName("Time"));
			addOverlapOvertimeRestErrorMessage(sb.toString(), array[0], array[1]);
			break;
		}
		// 公用外出時間
		for (GoOutDtoInterface dto : publicGoOutDtoList) {
			String[] array = getOverlapOvertimeRestArray(dto.getGoOutStart(), dto.getGoOutEnd());
			if (array.length == 0) {
				continue;
			}
			// 残業休憩時間と重複している場合
			StringBuffer sb = new StringBuffer();
			sb.append(mospParams.getName("Official"));
			sb.append(mospParams.getName("GoingOut"));
			sb.append(mospParams.getName("Time"));
			addOverlapOvertimeRestErrorMessage(sb.toString(), array[0], array[1]);
			break;
		}
	}
	
	/**
	 * 残業休憩時間との重複時間を取得する。<br>
	 * @param startTime 開始時刻
	 * @param endTime 終了時刻
	 * @return 重複時間配列
	 */
	protected String[] getOverlapOvertimeRestArray(Date startTime, Date endTime) {
		for (Entry<Date, Date> entry : overtimeRestMap.entrySet()) {
			Date overtimeRestStart = entry.getKey();
			Date overtimeRestEnd = entry.getValue();
			if (isOverlap(startTime, endTime, overtimeRestStart, overtimeRestEnd)) {
				String[] array = new String[2];
				array[0] = DateUtility.getStringTime(overtimeRestStart, workDate);
				if (startTime.after(overtimeRestStart)) {
					array[0] = DateUtility.getStringTime(startTime, workDate);
				}
				array[1] = DateUtility.getStringTime(overtimeRestEnd, workDate);
				if (endTime.before(overtimeRestEnd)) {
					array[1] = DateUtility.getStringTime(endTime, workDate);
				}
				return array;
			}
		}
		return new String[0];
	}
	
	/**
	 * 時間帯の重複チェック。<br>
	 * @param startTime 開始時刻
	 * @param endTime 終了時刻
	 * @param targetStartTime 対象開始時刻
	 * @param targetEndTime 対象終了時刻
	 * @return 重複している場合true、そうでない場合false
	 */
	protected boolean isOverlap(Date startTime, Date endTime, Date targetStartTime, Date targetEndTime) {
		if (startTime == null || endTime == null || targetStartTime == null || targetEndTime == null) {
			return false;
		}
		return startTime.before(targetEndTime) && endTime.after(targetStartTime);
	}
	
	/**
	 * 残業休憩時間と時間帯が重複している場合のエラーメッセージ設定。<br>
	 * @param nameOfTime 時間の名称
	 * @param start 開始時刻
	 * @param end 終了時刻
	 */
	protected void addOverlapOvertimeRestErrorMessage(String nameOfTime, String start, String end) {
		StringBuffer sb = new StringBuffer();
		sb.append(mospParams.getName("OvertimeWork"));
		sb.append(mospParams.getName("RestTime"));
		sb.append(mospParams.getName("Time"));
		addOverlapErrorMessage(nameOfTime, sb.toString(), start, end);
	}
	
	/**
	 * 時間帯が重複している場合のエラーメッセージ設定。<br>
	 * @param nameOfTime 時間の名称
	 * @param nameOfTargetTime 対象時間の名称
	 * @param start 開始時刻
	 * @param end 終了時刻
	 */
	protected void addOverlapErrorMessage(String nameOfTime, String nameOfTargetTime, String start, String end) {
		mospParams.addErrorMessage(TimeMessageConst.MSG_TIME_DUPLICATION_CHECK_2, new String[]{ nameOfTime,
			nameOfTargetTime, start, end });
	}
	
	/**
	 * 勤務形態コードを取得する
	 * @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);
		WorkflowDtoInterface workflowDto = null;
		if (dto != null) {
			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 substituteWorkflowDto = workflowReference.getLatestWorkflowInfo(substituteDto
				.getWorkflow());
			if (substituteWorkflowDto == null
					|| !PlatformConst.CODE_STATUS_COMPLETE.equals(substituteWorkflowDto.getWorkflowStatus())) {
				continue;
			}
			if (substituteDto.getSubstituteRange() == TimeConst.CODE_WORK_ON_HOLIDAY_SUBSTITUTE_ON) {
				return substituteDto.getSubstituteType();
			}
		}
		DifferenceRequestDtoInterface differenceRequestDto = differenceReference.findForKeyOnWorkflow(personalId,
				workDate);
		if (differenceRequestDto != null) {
			WorkflowDtoInterface differenceRequestWorkflowDto = workflowReference
				.getLatestWorkflowInfo(differenceRequestDto.getWorkflow());
			if (differenceRequestWorkflowDto != null
					&& PlatformConst.CODE_STATUS_COMPLETE.equals(differenceRequestWorkflowDto.getWorkflowStatus())) {
				return differenceRequestDto.getDifferenceType();
			}
		}
		Date scheduleWorkDate = workDate;
//		int scheduleTimesWork = timesWork;
		// 休日出勤申請からレコードを取得
		WorkOnHolidayRequestDtoInterface workOnHolidayRequestDto = workOnHolidayReference.findForKeyOnWorkflow(
				personalId, workDate);
		if (workOnHolidayRequestDto == null) {
			if (workflowDto != null) {
				return dto.getWorkTypeCode();
			}
		} else {
			// ワークフロー番号取得
			long workflow = workOnHolidayRequestDto.getWorkflow();
			WorkflowDtoInterface workOnHolidayRequestWorkflowDto = workflowReference.getLatestWorkflowInfo(workflow);
			// 最新のワークフローを取得でき承認済状態の場合
			if (workOnHolidayRequestWorkflowDto != null
					&& PlatformConst.CODE_STATUS_COMPLETE.equals(workOnHolidayRequestWorkflowDto.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 日付オブジェクト
	 */
	protected 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);
	}
	
}
