作業時間計算用メジャーモード(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)