作業時間計算用メジャーモード(timesheet-mode.el)

以下のように設定(cl , holidays , japanese-holidays が必要です)。
timesheet-setting-alistに会社ごとの設定を入れます。
※複数の会社の作業時間に対応。
※Emacs 27.1で動作確認。

(require 'timesheet-mode)

(add-to-list 'timesheet-setting-alist
 '(sample1 ;; 某A社用設定。社名をシンボルで入れる。
   ((start-time    "9:00")   ;;; 始業時間。
					         ;;; 例:09:00 からの場合
	      					      ;;; 9:00
    (end-time	    "17:30") ;;; 定時の終業時間。
						     ;;; 例:17:30 までの場合
		      			     ;;; 17:30
    (fixed-start-time   nil) ;;; 始業時間の制限。始業時間より前の時間は無視される。
		      			     ;;; nil で、時間の制限を無くす。
    (fixed-end-time	 nil)    ;;; 終業時間の制限。これより後の時間は無視される。
	      					  ;;; nilで、時間の制限を無くす。
    (time-slice   	    15)	  ;;; 最小時間単位(単位:分)
    (working-hours	    "7:30")   ;;; 一日の残業時間計算用定時間の定義
    (working-hours-for-total-calc "7:30");;; 月単位の残業時間計算用定時間の定義
    (fixed-half-payedleave        "3:45");;; 半休の充当時間が固定の場合,その時間。
    (format-work-time-float       t)	 ;;; 時間を時分ではなく、実数(8.5など)で出力
    (all-time-only                t)	 ;;; 合計時間のみ出力する
    ;;; 休憩時間のリスト
    (break-time            (("12:30" "13:30")
						    ("17:30" "18:00")
						    ("22:00" "23:00")
						    ("02:00" "02:30")
						    ("04:00" "09:00"))))))

(add-to-list 'timesheet-setting-alist
  '(sample2  ;; 某B社用設定
            ((start-time          "9:00")
	        (end-time		     "17:30")
		    (fixed-start-time    nil)
		    (fixed-end-time		 nil)
		    (time-slice   	     15)
		    (working-hours		          "7:30")
		    (working-hours-for-total-calc "7:30")
		    (fixed-half-payedleave        nil)
		    (format-work-time-float       t)
		    (all-time-only                t)
		    (break-time   	 (("12:00" "13:00")
						      ("17:30" "18:00")
						      ("21:00" "22:00")
						      ("01:00" "01:30")
						      ("04:30" "05:30")
						      ("07:30" "09:00"))))))

(provide 'setup-timesheet)

以下の書式で記述。
2020年11月1日だと以下の通りです(最初の行にモード設定を書いておき、
コマンド(timesheet-skeleton)で一月分のカレンダーを作成します。
timesheet-setting-nameを変えると別の会社の設定に切り替えられます。

-*- mode: timesheet ; timesheet-setting-name: sample1 -*-
11/01:*
11/02 月: 
11/03:*文化の日
11/04 水: 
11/05 木: 
11/06 金: 
11/07:*
11/08 日:*
11/09 月: 
11/10 火: 
11/11 水: 
11/12 木: 
11/13 金: 
11/14:*
11/15:*
11/16 月: 
11/17 火: 
11/18 水: 
11/19 木: 
11/20 金: 
11/21:*
11/22:*
11/23:*勤労感謝の日
11/24 火: 
11/25 水: 
11/26 木: 
11/27 金: 
11/28:*
11/29:*
11/30 月: 

以下のような感じで作業時間を入力します。
作業開始時間、作業終了時間、その他の休憩時間の順です。

-*- mode: timesheet ; timesheet-setting-name: sa -*-
11/01:*
11/02 月: 09:00 17:30 0:00
11/03:*文化の日
11/04 水: 09:00 17:30 0:00
11/05 木: 09:00 20:30 0:00
11/06:/13:00 17:30 0:00
11/07:*
11/08 日:*
11/09 月:\09:00 12:30 0:00
11/10 火: 09:00 19:30 0:00
11/11 水: 09:00 17:30 0:00
11/12 木: 09:00 17:30 0:00
11/13 金: 09:00 17:30 0:00
11/14:*
11/15:*
11/16 月: 09:00 17:30 0:00
11/17 火: 09:00 16:30 0:00
11/18 水: 09:00 17:30 0:00
11/19 木: 09:00 17:30 0:00
11/20 金: 09:00 17:30 0:00
11/21:*
11/22:*
11/23:*勤労感謝の日
11/24 火: 09:00 17:30 0:00
11/25 水: 09:00 17:30 0:00
11/26 木: 09:00 17:30 0:00
11/27 金: 09:00 17:30 0:00
11/28:*
11/29:*
11/30:+

その次にtimesheet-eval-bufferを実行すると、作業時間を計算します。
※頭が*の場合休日。
※頭が-の場合欠勤。
※頭が+の場合有給休暇。
※頭が/の場合午前半休。
※頭が\の場合午後半休。
※その他の作業時間の計算ルールはtimesheet-setting-alistの設定に沿って計算。

-*- mode: timesheet ; timesheet-setting-name: sample1 -*-
11/01:*
11/02 月: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/03:*文化の日
11/04 水: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/05 木: 09:00 20:30 0:00| 10:00| 09:00 20:30 1.50 10.00
11/06:/13:00 17:30 0:00| 04:00| 13:00 17:30 0.50 4.00
11/07:*
11/08 日:*
11/09 月:\09:00 12:30 0:00| 03:30| 09:00 12:30 0.00 3.50
11/10 火: 09:00 19:30 0:00| 09:00| 09:00 19:30 1.50 9.00
11/11 水: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/12 木: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/13 金: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/14:*
11/15:*
11/16 月: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/17 火: 09:00 16:30 0:00| 06:30| 09:00 16:30 1.00 6.50
11/18 水: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/19 木: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/20 金: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/21:*
11/22:*
11/23:*勤労感謝の日
11/24 火: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/25 水: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/26 木: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/27 金: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
11/28:*
11/29:*
11/30:+

TOTAL working hours: 130.50/17.50(126.50 + 4.00): 18 days worked/19 business days/overtime +3.00

計算結果は以下のようにになっています。

                          | の後ろが計算結果
11/02 月: 09:00 17:30 0:00| 07:30| 09:00 17:30 1.00 7.50
                          作業時間  作業開始/終了 休憩時間 作業時間

最後の行に表示される情報

TOTAL working hours: 131.50/17.50(127.50 + 4.00): 
                     作業時間/休憩時間(時間内作業時間 + 時間外作業時間)

18 days worked/19 business days/overtime +3.00
 実働営業日      月の営業日                  残業時間

こちらが本体(cl , holidays , japanese-holidays が必要)

;;; -*- fill-column: 150 -*-
;;;
;;; Timesheet for Attendance management
;;;
;;
;; timesheet-eval-region
;; timesheet-eval-buffer
;; timesheet-skeleton

(eval-when-compile
 (require 'cl))
(require 'holidays)
(require 'japanese-holidays)

;;; mode map
;;
(defvar timesheet-mode-map nil)
(if timesheet-mode-map
   nil
 (setf timesheet-mode-map (make-sparse-keymap))
 (define-key timesheet-mode-map "\C-x\C-s" 'timesheet-save-buffer)
 (define-key timesheet-mode-map [f2]       'timesheet-save-buffer)
 (define-key timesheet-mode-map "\C-c\C-c" 'timesheet-eval-buffer)
 (make-local-variable 'timesheet-setting-name))
(defun timesheet-save-buffer ()
 (interactive)
 (timesheet-eval-buffer)
 (save-buffer)
 (timesheet-eval-buffer))

;;; 時間の設定
;;

(defgroup timesheet nil
 "calc worktimes."
 :tag "TimeSheet"
 :version "24"
 :group 'emacs)

(defcustom timesheet-holiday-mark-char   "*"
 "休日のマーク。日付の後にこのマークを置くと、休日として扱う。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-absence-mark-char   "-"
 "欠勤のマーク。日付の後にこのマークを置くと、欠勤として扱う。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-payedleave-mark-char   "+"
 "有給休暇のマーク。日付の後にこのマークを置くと、有給休暇として扱う。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-morning-half-payedleave-mark-char   "/"
 "午前半休のマーク。日付の後にこのマークを置くと、午前半休として扱う。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-afternoon-half-payedleave-mark-char   "\\"
 "午後半休のマーク。日付の後にこのマークを置くと、午後半休として扱う。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-total-header-str "TOTAL working hours"
 "一月のトータル情報のヘッダ文字列。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-actual-header-str "Actual hours worked"
 "今日までのトータル情報のヘッダ文字列。"
 :type 'string
 :version "24"
 :group 'timesheet)

(defcustom timesheet-setting-name 'default
 "タイムシートの個別設定名"
 :type 'symbol
 :version "24"
 :group 'timesheet)

(defcustom timesheet-setting-alist
 '((default ((start-time                   "09:00");;; 始業時間。
             (end-time                     "17:30");;; 定時の終業時間。
             (fixed-start-time             nil)    ;;; 始業時間の制限。始業時間より前の時間は無視される。
                                                   ;;; nil で、時間の制限を無くす。
             (fixed-end-time               nil)    ;;; 終業時間の制限。これより後の時間は無視される。
                                                   ;;; nilで、時間の制限を無くす。
             (time-slice                   15)     ;;; 最小時間単位(単位:分)
             (working-hours                "7:30") ;;; 一日の残業時間計算用定時間の定義
             (working-hours-for-total-calc "7:30") ;;; 月単位の残業時間計算用定時間の定義
             (fixed-half-payedleave        nil)    ;;; 半休の充当時間が固定の場合。その時間。
             (format-work-time-float       t)      ;;; 時間を時分ではなく、実数(8.5など)で出力
             (all-time-only                t)      ;;; 合計時間のみ出力する
                                                   ;;; 休憩時間のリスト
             (break-time                   (("02:00" "02:30")
                                            ("04:30" "09:00")
                                            ("12:00" "13:00")
                                            ("17:30" "18:00")
                                            ("22:00" "23:00"))))))
 "タイムシートの個別設定"
 :type '(repeat (list symbol (repeat (list symbol sexp))))
 :version "24"
 :group 'timesheet)


(defconst timesheet-number-pattern        "[[:digit:]]+"
 "数字の正規表現")

(defconst timesheet-time-pattern          (concat timesheet-number-pattern ":" timesheet-number-pattern)
 "時間文字列の正規表現")

(defconst timesheet-date-pattern          (concat timesheet-number-pattern "/" timesheet-number-pattern)
 "日付文字列の正規表現")

(defconst timesheet-time-group-pattern    (concat "\\(" timesheet-time-pattern "\\)")
 "時間文字列の正規表現グループ")

(defconst timesheet-monthday-week-pattern (concat timesheet-date-pattern " [^:]+")
 "日付曜日の正規表現")

(defconst timesheet-dayflag-pattern       "\\([^ \t][ \t]*\\|[ \t]+\\)"
 "日の種別(出勤日/休日/有給)の正規表現")

(defconst timesheet-linepattern           (format "^%s:%s\\(%s[ \t]+%s[ \t]+%s[ \t]*\\)?"
                                                 timesheet-monthday-week-pattern timesheet-dayflag-pattern
                                                 timesheet-time-group-pattern timesheet-time-group-pattern timesheet-time-group-pattern)
 "行の正規表現")

;;
;; mode funtions
(defun timesheet-mode ()
 "タイムシート作成モードです。

\\C-c \\C-c でバッファ内の記述を評価します。
詳細は、timesheet-eval-region() を参照してください。"
 (interactive)
 (kill-all-local-variables)
 (use-local-map timesheet-mode-map)
 (setf mode-name "TIMESHEET"
       major-mode 'timesheet-mode)
 (make-local-variable 'timesheet-setting-name)
 (run-mode-hooks 'timesheet-mode-hook))

(defun timesheet-eval-buffer ()
 "`timesheet-eval-region' のバッファ版

詳細は、`timesheet-eval-region' を参照。"
 (interactive)
 (timesheet-eval-region (point-min) (point-max)))

(defstruct timesheet-total
 (time                      0)
 (onedaytime-day            0) 
 (onedaytime-night          0) 
 (work-days                 0) 
 (days                      0) 
 (breaktime                 0) 
 (morning-half-payedleave   0) 
 (afternoon-half-payedleave 0) 
 (payedleave                0) 
 (month-days                0))

(defun timesheet-eval-region (rstart rend)
 "タイムシートの評価を行います。
まず、M-x `timesheet-skeleton' でタイムシートの骨組みを作成します。
その後で、以下のようなフォーマットで時間を記入します。
|
01/20 Wed:  8:10 17:16 0:00
01/21 Thu:  8:01 18:40 0:00
01/22 Fri:  9:30 19:06 0:30
01/23 Sat: 10:10 15:00 0:00
01/24 Sun:  7:31 14:50 0:00
|
順番に、開始時間:終了時間:その他の休憩時間です。
評価すると以下のようになります。
|
01/20 Wed:  8:10 17:16 0:00| 08:00| 08:15 - 17:15 . 01:00 . 08:00
01/21 Thu:  8:01 18:40 0:00| 08:45| 08:15 - 18:30 . 01:30 . 08:45
01/22 Fri:  9:30 19:06 0:30| 07:30| 09:30 - 19:00 . 02:00 . 07:30
01/23 Sat: 10:10 15:00 0:00| 03:45| 10:15 - 15:00 . 01:00 . 03:45
01/24 Sun:  7:31 14:50 0:00| 06:00| 07:45 - 14:45 . 01:00 . 06:00
|
TOTAL working hours: 34:00/06:30(32:15 + 01:45): 5 day(s)/overtime -03:30
|
結果は、見ての通りです。
TOTALで、総合時間 / 休憩時間 (時間内 . 時間外): 日数/残業時間 が出ます。"
 (interactive "r")
 (catch 'timesheet-error
   ;; 時間の記述を探す。
   (save-excursion
     (goto-char rstart)
     (beginning-of-line)
     (save-restriction
       (narrow-to-region rstart rend)
       (let ((totalinfo (make-timesheet-total))
             (rest-not-holiday-marks (list timesheet-absence-mark-char timesheet-payedleave-mark-char)))
         (while (re-search-forward timesheet-linepattern (point-max) t)
           (let ((dend             (match-end 0))
                 (kind-of-day-mark (if (match-beginning 1) (buffer-substring (match-beginning 1) (match-end 1)) ""))
                 (exists-daytime   (match-beginning 2)))
             ;; 日数加算
             (multiple-value-bind (inc-days inc-payedleave inc-morning-half-payedleave inc-afternoon-half-payedleave)
                 (timesheet-inc-days-values kind-of-day-mark exists-daytime)
               (incf (timesheet-total-days                      totalinfo) inc-days)
               (incf (timesheet-total-payedleave                totalinfo) inc-payedleave)
               (incf (timesheet-total-morning-half-payedleave   totalinfo) inc-morning-half-payedleave)
               (incf (timesheet-total-afternoon-half-payedleave totalinfo) inc-afternoon-half-payedleave))
             ;; 休日でない場合時間計算
             (unless (or (not exists-daytime)
                         (member kind-of-day-mark rest-not-holiday-marks))
               ;; 作業時間記載有り
               (incf (timesheet-total-work-days totalinfo) 1)
               (let ((str-starttime    (buffer-substring (match-beginning 3) (match-end 3)))
                     (str-endtime      (buffer-substring (match-beginning 4) (match-end 4)))
                     (str-breaktime    (buffer-substring (match-beginning 5) (match-end 5))))
                 ;; 一日の時間を計算
                 (multiple-value-bind (hstt hend onedaytime onedaytime-day onedaytime-night breaktime)
                     (timesheet-calc-oneday kind-of-day-mark
                                            str-starttime str-endtime str-breaktime)
                   ;; トータル時間加算
                   (incf (timesheet-total-breaktime        totalinfo) breaktime)
                   (incf (timesheet-total-onedaytime-day   totalinfo) onedaytime-day)
                   (incf (timesheet-total-onedaytime-night totalinfo) onedaytime-night)
                   (incf (timesheet-total-time             totalinfo) onedaytime)
                   ;; 1日分の集計結果
                   (let ((result-of-day (format "| %s| %s"
                                                (timesheet-int2timstr onedaytime)
                                                (timesheet-format-date hstt hend breaktime onedaytime
                                                                       onedaytime-day onedaytime-night))))
                     (if (eolp)
                         ;; 集計結果がない
                         (insert result-of-day) ; 1日分の集計結果挿入
                       ;; 集計結果がある
                       (unless (equal result-of-day (timesheet-currentline-substring :start-point dend))
                         ;; 過去と現在の集計結果が異なる場合1日分の集計結果更新
                         (goto-char dend)
                         (timesheet-delete-line)
                         (insert result-of-day)))))))))
         ;; 休日を除く日数を計算
         (goto-char (point-min))
         (while (re-search-forward
                 (format "^%s:\\([%s%s%s%s ]\\)"
                         timesheet-monthday-week-pattern
                         timesheet-absence-mark-char
                         timesheet-payedleave-mark-char
                         timesheet-morning-half-payedleave-mark-char
                         timesheet-afternoon-half-payedleave-mark-char)
                 (point-max) t)
           (incf (timesheet-total-month-days totalinfo) 1))
         ;;
         ;; 今日までの集計結果を表示
         (message (timesheet-format-total-info timesheet-actual-header-str totalinfo t))
         ;; 集計
         (let ((total-str (timesheet-format-total-info timesheet-total-header-str  totalinfo nil)))
           (goto-char (point-min))
           ;; 過去の集計結果検索
           (if (re-search-forward (format "^%s" timesheet-total-header-str) (point-max) t)
               ;; 過去の集計結果が存在する場合
               (unless (equal total-str (timesheet-currentline-substring))
                 ;; 過去と現在の集計結果が一致しない場合置換
                 (beginning-of-line)
                 (while (not (eobp))
                   (timesheet-delete-line))
                   (insert total-str))
             ;; 過去の集計結果がない場合
             (goto-char (point-max))
             (insert "\n")
             (insert total-str))))))))

(defun timesheet-eol-point ()
 (end-of-line)
 (point))

(defun timesheet-delete-line ()
 (delete-region (point) (timesheet-eol-point)))

(defun* timesheet-currentline-substring (&key (start-point nil) (end-point nil))
 (save-excursion
   (unless start-point (beginning-of-line) (setf start-point (point)))
   (unless end-point   (end-of-line)       (setf end-point   (point)))
   (buffer-substring start-point end-point)))

(defun timesheet-calc-oneday (kind-of-day-mark str-starttime str-endtime str-breaktime)
 (let ((breaktime        0)
       (hstt             0)
       (hend             0)
       (onedaytime       0)
       (onedaytime-day   0)
       (onedaytime-night 0))
   ;; 始業時間の計算
   (setf hstt (timesheet-calc-hs str-starttime 1))
   ;; 始業時間の補正
   (when (and (timesheet-time-fixed-start-time) (< hstt (timesheet-timestr2int (timesheet-time-start-time))))
     (setf hstt (timesheet-timestr2int (timesheet-time-start-time))))
   ;; 終業時間の計算
   (setf hend (timesheet-calc-hs str-endtime -1))
   ;; 終業時間の補正
   (when (and (timesheet-time-fixed-end-time)   (> hend (timesheet-timestr2int (timesheet-time-end-time))))
     (setf hend (timesheet-timestr2int (timesheet-time-end-time))))
   ;; その他休憩時間
   (setf breaktime   (timesheet-calc-hs str-breaktime -1))
   ;; 休憩時間の計算&始業終業時間補正
   (multiple-value-bind (calc-breaktime calc-hstt calc-hend)
       (timesheet-calc-breaktime-and-fix-start-end-time breaktime hstt hend)
     (setf breaktime calc-breaktime
	    hstt      calc-hstt
	    hend      calc-hend))
   ;; 1日の作業時間
   (setf onedaytime (- (- hend hstt) breaktime))
   ;; 1日の作業時間の内の時間内、時間外を計算
   (if (equal kind-of-day-mark
              timesheet-holiday-mark-char)
       ;; 休日は、時間外
       (progn (setf onedaytime-day   0
		     onedaytime-night onedaytime))
     ;; その他は計算
     (if (and (timesheet-working-hours)
              (> onedaytime (timesheet-timestr2int (timesheet-working-hours))))
         (progn (setf onedaytime-day   (timesheet-timestr2int (timesheet-working-hours))
		       onedaytime-night (- onedaytime
                                          (timesheet-timestr2int (timesheet-working-hours)))))
       (setf onedaytime-day   onedaytime
	      onedaytime-night 0)))
   (values hstt hend onedaytime onedaytime-day onedaytime-night breaktime)))

(defun timesheet-inc-days-values (kind-of-day-mark exists-daytime)
 (let ((inc-days                      0)
       (inc-payedleave                0)
       (inc-morning-half-payedleave   0)
       (inc-afternoon-half-payedleave 0))
   (cond ((equal kind-of-day-mark timesheet-holiday-mark-char)
          ;; 休日
          ())
         ((equal kind-of-day-mark timesheet-absence-mark-char)
          ;; 無給休暇
          (setf inc-days 1))
         ((equal kind-of-day-mark timesheet-payedleave-mark-char)
          ;; 有給休暇
          (setf inc-payedleave 1
		 inc-days       1))
         ((equal kind-of-day-mark timesheet-morning-half-payedleave-mark-char)
          ;; 午前半休
          (setf inc-morning-half-payedleave 1
		 inc-days                    1))
         ((equal kind-of-day-mark timesheet-afternoon-half-payedleave-mark-char)
          ;; 午後半休
          (setf inc-afternoon-half-payedleave 1
		 inc-days                      1))
         (t
          (when exists-daytime
            (setf inc-days 1))))
   (values inc-days
           inc-payedleave
           inc-morning-half-payedleave
           inc-afternoon-half-payedleave)))

(defun timesheet-setting ()
 (cadr (assoc timesheet-setting-name timesheet-setting-alist)))

(defun timesheet-time-start-time ()
 "始業時間。
 例:09:00 からの場合
 9:00"
 (cadr (assoc 'start-time (timesheet-setting))))

(defun timesheet-time-end-time ()
 "定時の就業時間。
 例:17:30 までの場合
 17:30"
 (cadr (assoc 'end-time (timesheet-setting))))

(defun timesheet-time-fixed-start-time ()
 "始業時間の制限。始業時間より前の時間は無視される。
nil で、時間の制限を無くす。"
 (cadr (assoc 'fixed-start-time (timesheet-setting))))

(defun timesheet-time-fixed-end-time ()
 "終業時間の制限。終業時間より後の時間は無視される。
nilで、時間の制限を無くす。"
 (cadr (assoc 'fixed-end-time (timesheet-setting))))

(defun timesheet-time-slice ()
 "最小単位時間の定義
1時間をいくつに区切るかを定義する。
4の場合は15分単位。2で30分単位。"
 (cadr (assoc 'time-slice (timesheet-setting))))

(defun timesheet-all-time-only ()
 "合計時間のみ出力する"
 (cadr (assoc 'all-time-only (timesheet-setting))))

(defun timesheet-format-work-time-float ()
 "時間を時分ではなく、実数(8.5など)で出力"
 (cadr (assoc 'format-work-time-float (timesheet-setting))))

(defun timesheet-working-hours ()
 "一日の残業時間計算用定時間の定義"
 (cadr (assoc 'working-hours (timesheet-setting))))

(defun timesheet-working-hours-for-total-calc ()
 "月単位の残業時間計算用定時間の定義"
 (cadr (assoc 'working-hours-for-total-calc (timesheet-setting))))

(defun timesheet-fixed-half-payedleave ()
 "半休の充当時間が固定の場合。その時間。"
 (cadr (assoc 'fixed-half-payedleave (timesheet-setting))))

(defun timesheet-breaktime-alist ()
 "休憩時間のリスト。"
 (cadr (assoc 'break-time (timesheet-setting))))

(defun timesheet-calc-xxx-half-payedleave-time (fn-half-time sign)
 "半休の時間"
 (if (timesheet-fixed-half-payedleave)
     (timesheet-timestr2int (timesheet-fixed-half-payedleave))
   (let ((xxx-time (timesheet-timestr2int (funcall fn-half-time)))
         (breaktime-alist  (timesheet-breaktime-alist))
         (hss)
         (diff))
     (catch 'calc-result
       (while breaktime-alist
         (setf hss  (timesheet-timestr2int (car (pop breaktime-alist)))
		diff (* sign (- hss xxx-time)))
         (when (plusp diff)
           (throw 'calc-result diff)))
       (error "cannot calc morning-half-payedleave-time")))))

(defun timesheet-calc-morning-half-payedleave-time ()
 "午前半休の時間"
 (timesheet-calc-xxx-half-payedleave-time #'timesheet-time-start-time 1))

(defun timesheet-calc-afternoon-half-payedleave-time ()
 "午後半休の時間"
 (timesheet-calc-xxx-half-payedleave-time #'timesheet-time-end-time   -1))

(defun timesheet-calc-breaktime-and-fix-start-end-time (breaktime hstt hend)
 (dolist (breaktime-1 (timesheet-breaktime-alist))
   (let ((hss (timesheet-timestr2int (nth 0 breaktime-1)))
         (hse (timesheet-timestr2int (nth 1 breaktime-1))))
     (when (<= hss  hend hse ) (setf hse hend)) ;; 計算無し、補正
     (when (<= hss  hstt hse ) (setf hss hstt)) ;; 計算無し、補正
     (when (<= hstt hss  hend) (incf breaktime (- hse hss)))))
 (values breaktime hstt hend))

(defun timesheet-format-total-info (total-name totalinfo current)
 "月の総合情報を文字列に直す。"
 (let* ((total-days (if current (timesheet-total-days totalinfo) (timesheet-total-month-days totalinfo)))
        (ovtime (- (+ (timesheet-total-time totalinfo)
                      (* (timesheet-total-morning-half-payedleave totalinfo) (timesheet-calc-morning-half-payedleave-time))
                      (* (timesheet-total-afternoon-half-payedleave totalinfo) (timesheet-calc-afternoon-half-payedleave-time)))
                   (* (- total-days (timesheet-total-payedleave totalinfo))
                      (timesheet-timestr2int (timesheet-working-hours-for-total-calc))))))
   (format
    "%s: %s/%s(%s + %s): %d day%s worked/%d business day%s/overtime %s"
    total-name
    (timesheet-format-time (timesheet-total-time             totalinfo))
    (timesheet-format-time (timesheet-total-breaktime        totalinfo))
    (timesheet-format-time (timesheet-total-onedaytime-day   totalinfo))
    (timesheet-format-time (timesheet-total-onedaytime-night totalinfo))
    (timesheet-total-work-days totalinfo)
    (timesheet-int2pluralS (timesheet-total-work-days totalinfo))
    total-days
    (timesheet-int2pluralS total-days)
    (timesheet-format-time ovtime t))))

(defun timesheet-int2pluralS (value)
 (if (> value 1) "s" ""))

(defun timesheet-timestr2hs (strtime)
 (when (and strtime (string-match (concat "^" timesheet-time-group-pattern "$") strtime))
   (let ((timestr (split-string (substring strtime (match-beginning 1) (match-end 1)) ":")))
     (values (string-to-number (nth 0 timestr))
             (string-to-number (nth 1 timestr))))))

(defun timesheet-calc-hs (strtime round-flag)
 "時刻の補正を行って数値で返す。
※分の倍数で返す。"
 (multiple-value-bind (hn sn) (timesheet-timestr2hs strtime)
   (let* ((mins (timesheet-time-slice))
          (mods (% sn mins))
          (us   (/ sn mins)))
     (when (and (> mods 0) (> round-flag 0))
       (incf us))
     (+ (* hn 60) (* us mins)))))

(defun timesheet-int2timstr (tim)
 "時刻を文字列で返す。"
 (format "%02d:%02d" (/ tim 60) (% tim 60)))

(defun timesheet-timestr2int (strtime)
 (multiple-value-bind (hour min) (timesheet-timestr2hs strtime)
   (+ (* hour 60) min)))

(defun timesheet-skeleton (year month day)
 "タイムシートの骨組みを作成します。
実行すると、指定した日付から1ヶ月分の日付と曜日の列が作成され、
また、`japanese-holidays' で設定された休日が追加されます。

実行例:2017/02/11を指定した場合

02/11 土:*建国記念日
02/12 日:*
02/13 月: 
02/14 火: 
02/15 水: 
02/16 木: 
02/17 金: 
02/18 土:*
02/19 日:*
02/20 月: 
02/21 火: 
02/22 水: 
02/23 木: 
02/24 金: 
02/25 土:*
02/26 日:*
02/27 月: 
02/28 火: 
03/01 水: 
03/02 木: 
03/03 金: 
03/04 土:*
03/05 日:*
03/06 月: 
03/07 火: 
03/08 水: 
03/09 木: 
03/10 金: "
 (interactive "nStart Year: \nnStart Month: \nnStart Day: ")
 (save-excursion
   (let* ((now-time  (encode-time 0 0 0 day month year))
	   (next-month (if (= month 12) 1         (1+ month)))
	   (next-year  (if (= month 12) (1+ year) year))
	   (last-time  (encode-time 0 0 0 day next-month next-year)))
     (while (or (< (nth 0 now-time) (nth 0 last-time))
                (and (= (nth 0 now-time) (nth 0 last-time))
                     (< (nth 1 now-time) (nth 1 last-time))))
       (insert (concat (format-time-string "%m/%d %a:" now-time)
                       (timesheet-time-to-holiday-mark now-time)
                       "\n"))
       ;; 1日ずつ時間を進める。
	(setf now-time (time-add now-time (days-to-time 1)))
	(unless (listp now-time)
	  (setf now-time (encode-time (decode-time now-time))))))))

(defun time-to-month-day-year (tim)
 (let ((dec-time (decode-time tim)))
   (list (nth 4 dec-time) (nth 3 dec-time) (nth 5 dec-time))))

(defun calendar-check-holidays-by-time (tim)
 (calendar-check-holidays (time-to-month-day-year tim)))

(defun timesheet-is-weekend (tim)
 (let ((dow (nth 6 (decode-time tim))))
   (member dow japanese-holiday-weekend)))

(defun timesheet-time-to-holiday-mark (tim)
 (cond ((calendar-check-holidays-by-time tim) (concat timesheet-holiday-mark-char (first (calendar-check-holidays-by-time tim))))
       ((timesheet-is-weekend tim)            timesheet-holiday-mark-char)
       (t                                     " ")))

(defun timesheet-format-date (hstt hend breaktime onedaytime
                                  onedaytime-day onedaytime-night)
 (format "%s %s %s %s"
         (timesheet-int2timstr hstt)
         (timesheet-int2timstr hend)
         (timesheet-format-time breaktime)
         (if (timesheet-all-time-only)
             (format "%s" (timesheet-format-time onedaytime))
           (format "%s %s" (timesheet-int2timstr onedaytime-day) (timesheet-int2timstr onedaytime-night)))))

(defun timesheet-format-time (inttime &optional sign)
 (let ((rt-sign (cond ((and sign (> inttime 0)) "+")
                      ((and sign (= inttime 0)) " ")
                      ((< inttime 0)            "-")
                      (t                        "")))
       (abstime (abs inttime)))
   (concat rt-sign (if (timesheet-format-work-time-float)
                       (format "%2.2f" (/ abstime 60.0))
                     (timesheet-int2timstr abstime)))))

(provide 'timesheet-mode)

いいなと思ったら応援しよう!