1. 程式人生 > >java判斷一個Date時間在不在某段Date時間範圍之內

java判斷一個Date時間在不在某段Date時間範圍之內

            有時候我們會遇到這類問題,比如在OA系統提交年假申請時,系統如何校驗當前提交的休假時間,有沒有在之前的單據中提交併審批通過的(通過所有流程的年假單據通常會存在一張年假資訊表裡,記錄了年假的開始結束日期)時間發生重疊呢?  下面我們討論一下這個時間校驗的問題:一個Date型別時間或一段Date型別時間在不在某段Date時間範圍之中。

           舉一個栗子:某員工在9月初提交了一條年假申請單(申請時間為9月6日上午至9月7日上午,1天半)並被終審通過了,而該員工後來又提交了一條年假申請單,可能由於粗心,忘記了9月7日上午已經請過年假了,又想把9月7日全天申請了,這個時候系統該如何進行校驗呢?(系統不能讓本單提交成功),以下為例子的真實應用場景。


 實現步驟分析:

         一、要實現本功能的前提條件是,資料庫中存放審批通過的年假資訊的表中的資料儲存格式,是否滿足業務需求


經過調查發現 儲存 請假開始時間與結束時間的欄位為 oracle 的 Timestamp 型別欄位,而由於受資料庫時間格式制約(需要進行設定),這種儲存的時間資料在查詢語句中判斷不出上午或下午,看上圖所示,雖然顯示資料查詢出上午下午,但是在查詢語句條件中很難加入對上午下午的判斷。故造成的影響是,當天的資訊不得不取到後臺中進行判斷了(取出之後轉成Date型別)。

後臺執行的查詢語句發現,之前的1天半年假,系統將其轉化成兩條記錄記錄存入年假資訊表中(後臺對年假以天為維度進行拆分的)

程式開始進行年假校驗,參考程式碼如下

 //校驗年假  
  //fdChanged 申請人, mark1  >= ,  mark2  <= ,  fdStartDate 使用者錄入開始日期, fdEndDate 使用者錄入結束日期, fdStartNoon 使用者錄入 開始日期上下午標誌, fdEndNoon 使用者錄入結束日期上下午標誌
      Map<String,String> fdReviewNoMap = checkAnnualLeave(fdChanged, mark1, mark2, fdStartDate, fdEndDate, fdStartNoon, fdEndNoon);
      StringBuffer info = new StringBuffer();
	  if(fdReviewNoMap.size() > 0){
		  Iterator<Entry<String,String>> iter = fdReviewNoMap.entrySet().iterator();
		  while (iter.hasNext()) {
			  Map.Entry entry = (Map.Entry) iter.next();
			  Object val = entry.getValue();
			  info.append(val).append(",");
		  }
		  System.out.println("本次年假申請時間與以前的年假申請單年假時間發生重疊,重疊的年假單據號為:"+info);
		  map.put("info", "本次年假申請時間與以前的年假申請單年假時間發生重疊,重疊的年假單據號為:"+info);
		  list.add(map);
	  }
checlAnnualLeave() 方法為 年假校驗方法
/**
   * 檢查年假
   * @param fdChanged 申請人
   * @param mark1
   * @param mark2
   * @param fdStartDate 使用者錄入開始日期
   * @param fdEndDate  使用者錄入結束日期
   * @param fdStartNoon  使用者錄入 開始日期上下午標誌
   * @param fdEndNoon 使用者錄入結束日期上下午標誌
   * @return Map<String,String> fdReviewNoMap
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  public Map<String,String> checkAnnualLeave(String fdChanged,String mark1,String mark2,String fdStartDate,String fdEndDate,
		               String fdStartNoon,String fdEndNoon) throws Exception{
	  //檢查本次年假是否已經請過了(走完流程的)
      StringBuffer sql = new StringBuffer();
      sql.append(" select   t.fd_review_no,t.fd_start,t.fd_end,t.fd_count from km_attendanceinfol_leave t where 1=1 ");
      sql.append(" and t.fd_holiday_type = '0' "); //假期型別為年假
      sql.append(" and t.fd_type = '1' ");    //流程型別  1: 請假 2: 外出 3: 銷假 4:調休
      sql.append(" and t.fd_attendance_id = '").append(fdChanged).append("'"); //申請人
      sql.append(" and to_char(t.fd_start,'yyyy-mm-dd hh12:mi:ss') ").append(mark1).append("'").append(fdStartDate).append(" 12:00:00'");
      sql.append(" and to_char(t.fd_end,'yyyy-mm-dd hh12:mi:ss') ").append(mark2).append("'").append(fdEndDate).append(" 12:00:00'");
      Session session = this.sysOrgPersonService.getBaseDao().getHibernateSession();
	  List<Object[]> querylist = session.createSQLQuery(sql.toString()).list();
      
      System.out.println("查詢年假sql:"+sql.toString());
      String fdReviewNo = "";  //年假單據號
      Map<String,String> fdReviewNoMap = new HashMap<String,String>();
      Timestamp tstart = null,tend = null;//開始時間,結束時間
      Date dstart = null, dend = null;
      int checkNoon = 0;  //判斷資料是上午: 1 ,下午: 2 , 全天:  3
      if(querylist.size() != 0){
    	  for(Object[] obj : querylist){
    		  fdReviewNo = (String)obj[0];
    		  System.out.println(obj[0]+" start:"+obj[1]+" end:"+obj[2]+" count:"+obj[3]);
    		  /**受Oracle 欄位型別Timestamp的條件限制(判斷不出上午或下午)只能將當天資料都查出來 , 所以並不是查出來單據就一定是重疊的,需要進行判斷 
    		  1.1  使用者輸入資料如果是同一天,則將錄入資料與後臺資料進行比較,看時間是否有重疊部分
    		  1.2  使用者輸入資料跨天 ,要看是否有重疊的部分
    		    		   請假開始日期 fdStartDate:2017-09-07 fdStartNoon:1
			    		   請假結束日期 fdEndDate:2017-09-07 fdEndNoon:1
			    		  當前時間:2017-09-07 00:00:00
			    		  當前時間:2017-09-07 12:00:00
    		  */
    		  tstart = (Timestamp)obj[1]; //得到 Timestamp
    		  tend = (Timestamp)obj[2];
    		  dstart = tstart;                //得到 date
    		  dend = tend;
    		  if(fdStartDate.equals(fdEndDate)){//使用者輸入資料如果是同一天
    			  //將後臺資料與當天中午時間進行校驗,判斷資料是上午: 1 ,下午: 2 , 全天:  3 
    			  //判斷後臺數據是上午,下午 ,全天
    			  checkNoon = checkForenoon(dstart,dend);
    			  System.out.println("checkNoon:"+checkNoon);
    			  switch(checkNoon){
    			       case 1:  //後臺資料是上午 ,開始校驗使用者輸入資料
    			           if(fdStartNoon.equals("0") && fdEndNoon.equals("0") ){
    			        	   //使用者輸入資料也是上午,時間發生重疊
    			        	   fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			           }else if(fdStartNoon.equals("0") && fdEndNoon.equals("1") ){
    			        	   //使用者輸入資料是全天,時間發生重疊
    			        	   fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			           }
    			           break;
    			       case 2:  //後臺資料是下午
    			           if(fdStartNoon.equals("1") && fdEndNoon.equals("1") ){
    			        	   //使用者輸入資料也是下午,時間發生重疊
    			        	   fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			           }else if(fdStartNoon.equals("0") && fdEndNoon.equals("1") ){
    			        	   //使用者輸入資料是全天,時間發生重疊
    			        	   fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			           }
    			           break;
    			       case 3:  //後臺資料是全天
    			           //使用者輸入資料不管是上午或下午都會發生重疊,因為當天年假已經被用掉了
    			           fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			           break;
    			       default: //後臺資料有錯誤
    						System.out.println("default 後臺資料有錯誤");
    						break;
    			  }
    		  }else{//使用者輸入資料跨天
    			  //跨天的情況,跨天並不代表一定是重疊的,需要進行判斷是否有重疊部分,結合後臺資料 與 使用者輸入資料進行校驗
    			  //使用者輸入時間資料比後臺資料範圍要大 因此需要 判斷 後臺時間在不在使用者輸入時間範圍內即可
    			  if(checkInTime(dstart,dend,fdStartDate, fdStartNoon, fdEndDate, fdEndNoon)){
    				  //後臺資料時間在使用者輸入時間範圍之內,有重疊
    			      fdReviewNoMap.put(fdReviewNo, fdReviewNo);  //將單據號放入Map中(進行去重操作)
    			  }
    		  }
    	  }
    	  System.out.println("fdReviewNoMap.size():"+fdReviewNoMap.size());
      }
      return fdReviewNoMap;
  } 

校驗時間範圍checkInTime()方法
	/**
	 * 校驗時間範圍
	 * @param start 後臺資料開始時間
	 * @param end  後臺資料結束時間
	 * @param fdStartDate 使用者錄入開始日期
	 * @param fdStartNoon 使用者錄入 開始日期上下午標誌
	 * @param fdEndDate 使用者錄入結束日期
	 * @param fdEndNoon 使用者錄入結束日期上下午標誌
	 * @return true 後臺資料時間與使用者錄入時間有重疊   false 沒有重疊
	 * @throws ParseException 
	 */
	public boolean checkInTime(Date start,Date end,String fdStartDate,String fdStartNoon,String fdEndDate,String fdEndNoon) throws ParseException{
		
		  //將使用者錄入開始日期與結束日期 轉成 Date型別
		  String startTime = fdStartNoon.equals("1") ? fdStartDate+" 12:00:00" : fdStartDate+" 00:00:00";
		  String endTimes  = fdEndNoon.equals("1") ? fdEndDate+" 23:59:59" : fdEndDate+" 12:00:00";
		  Date beginTime = strFormatDate(startTime);
		  Date endTime = strFormatDate(endTimes);
		  //判斷後臺數據時間在不在使用者錄入時間範圍之中
		  boolean falg1 = belongCalendar(start,beginTime,endTime);
		  boolean falg2 = belongCalendar(end,beginTime,endTime);
		  if(falg1 && falg2){
			  return true;
		  }		  
		  return false;
	}

判斷時間是否在某段時間段範圍內belongCalendar()
	/**
	 * 判斷時間是否在時間段內
	 * @param nowTime
	 * @param beginTime
	 * @param endTime
	 * @return
	 */
	public  boolean belongCalendar(Date nowTime, Date beginTime, Date endTime) {
	    Calendar date = Calendar.getInstance();
	    date.setTime(nowTime);
	    Calendar begin = Calendar.getInstance();
	    begin.setTime(beginTime);
	    Calendar end = Calendar.getInstance();
	    end.setTime(endTime);
	    if (date.after(begin) && date.before(end)) {
	        return true;
	    }else if(nowTime.compareTo(beginTime)==0 || nowTime.compareTo(endTime) == 0 ){
	    	return true;
	    }else {
	        return false;
	    }
	}

判斷某時間段屬於上午下午還是全天 checkForenoon()
	/**
	 * 判斷某時間段屬於上午下午還是全天
	 * @param start 開始時間
	 * @param end  結束時間
	 * @return 1 是上午  2 是下午  3 全天  0 錯誤
	 * @throws ParseException 
	 */
	private int checkForenoon(Date start,Date end) throws ParseException{
		 String sday = dateFormatStr(start);
		 Date  middle = strFormatDate(sday+" 12:00:00"); //得到當天中午資料
		 if(start.compareTo(middle) < 0 && end.compareTo(middle) == 0){ //上午 start:2017-09-07 00:00:00.0 end:2017-09-07 12:00:00.0
			 return 1;
		 }
		 if(start.compareTo(middle) == 0 && end.compareTo(middle) > 0){ //下午 2016-08-05 12:00:00.0 end:2016-08-05 23:59:59.0
			 return 2;
		 }
		 if(start.compareTo(middle) < 0 && end.compareTo(middle) > 0){//全天 start:2017-09-06 00:00:00.0 end:2017-09-06 23:59:59.0
			 return 3;
		 }
		 return 0 ;
	}

日期格式化方法 dateFormatStr()
	/**
	 * 日期格式化
	 * @param Date
	 * return  格式化字串如 2017-09-07
	 * @param date
	 */
	private String dateFormatStr(Date date) {
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		String time = format.format(date);
//		System.out.println("時間:" + time);
		return time;
	}

字串日期轉date
	/**
	 * 字串日期轉為Date
	 * @param ld
	 * @throws ParseException
	 */
	private Date strFormatDate(String ld) throws ParseException {
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date lendDate = format.parse(ld);
//		System.out.println(lendDate);
		return lendDate;
	}

時間格式化
	/**
	 * 時間格式化
	 * @param Date
	 * return  格式化字串如 2017-09-07 12:00:00
	 * @param date
	 */
	private String timeFormatStr(Date date) {
		if(date == null)
		return null;
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String time = format.format(date);
//		System.out.println("時間:" + time);
		return time;
	}


通過以上方法,可以在特定場合下解決了年假校驗的問題了。