コマンドライン上で実行できるカレンダーを作成しました。
休日として祝日もサポートしていますので、日常的なカレンダーとして使用できると思います。
土曜日、日曜日、祝日を強調表示します。
祝日名も表示します。
UNIX 上の cal コマンドに不満がある方はどうぞ。
まあ、いまは便利なカレンダーコマンドが多数ありますけど、そのうちの一つとして...
私は Z shell 上で次のように alias してしまっています。 便利ですから。
alias cal=cal.rb
引数がない場合は、当年当月です。 引数として、「1970 〜 2037」までは年、「1 〜 12」までは月として扱います。 年と月の指定の順番はどちらでも OK です。
$ cal.rb
April 1999
Su Mo Tu We Th Fr Sa
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 29:緑の日
$ cal.rb 2000 1
January 2000
Su Mo Tu We Th Fr Sa
1 1:元旦
2 3 4 5 6 7 8
9 10 11 12 13 14 15 10:成人の日
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。
#! /usr/local/bin/ruby -Ke
# -*- mode:ruby; coding:euc-jp -*-
# /home/tetsu/src/ruby/time/cal.rb
# Created: May 07,1998 Thursday 21:34:45
# Author: tetsu(WATANABE Tetsuya)
# $Id: cal.rb,v 1.21 2007/12/22 14:16:47 tetsu Exp tetsu $
# usage: 月 年 または 年 月 どちらも省略可
class TTerminfo
def initialize
if ENV['TERM'] == 'dumb' || ENV['TERM'] == 'emacs'
@sgr0 = ''
@smso = ''
@bold = ''
@home = ''
@clear = ''
return
end
pipe =
begin
IO.popen('infocmp', 'r')
rescue
end or
begin
IO.popen('untic', 'r')
rescue
end
@sgr0 = "\C-[[m"
@smso = "\C-[[0;7m"
@bold = "\C-[[1m"
@home = "\C-[[H"
@clear = "\C-[[H\C-[[2J"
foo = pipe.gets # drop header
foo = pipe.read
pipe.close
foo.split(/[\s,]+/).each do |bar|
bar.sub!(/\$<\d+>$/, '')
case bar
when /^sgr0/
@sgr0 = bar.split('=')[1].sub(/\\\E/, "\C-[").sub(/(\\[0-7]{3})/) do $1[1,3].oct.chr end
when /^smso/
@smso = bar.split('=')[1].sub(/\\\E/, "\C-[")
when /^home/
@home = bar.split('=')[1].sub(/\\\E/, "\C-[")
when /^bold/
@bold = bar.split('=')[1].sub(/\\\E/, "\C-[")
when /^clear/
@clear = bar.split('=')[1].gsub(/\\\E/, "\C-[")
end
end
end
attr :sgr0
attr :smso
attr :bold
attr :home
attr :clear
end
require 'tcal'
# mon_arr 0..11
year = 0
month = 0
ARGV.each do |arg|
foo = arg.to_i
if foo >= 1 and foo <= 12
month = foo
elsif foo >= 1903 and foo <= 2037
year = foo
end
end
# 週の文字列
# 月の配列
# 12 か月分の配列
term = TTerminfo.new
cal = TCalendar.new(year, month)
print cal.header
holiday_name = []
last_wday = 0
1.upto(31) do |d|
wday = cal.wday(d)
break unless wday
last_wday = wday
status = cal.status(d)
if (d == 1)
print ' ' * wday
elsif wday == 0
puts holiday_name.compact.join(', ')
holiday_name = []
end
if cal.today?(d)
print term.bold
end
case status
when 'weekend'
printf('%s%2d%s', term.smso, d, term.sgr0)
when 'holiday'
printf('%s%2d%s', term.smso, d, term.sgr0)
holiday_name.push(d.to_s + ':' + (cal.holiday_name[d] || '振替休日'))
when 'workday'
printf('%2d', d)
end
if cal.today?(d)
print term.sgr0
end
print ' '
end
if holiday_name.size > 0
print ' ' * (6 - last_wday)
puts holiday_name.compact.join(', ')
else
puts
end
クラスライブラリです。
環境変数 RUBYLIB の通っているところなどに入れて置いてください。
漢字コードは EUC-JP を想定しています。
#! /usr/local/bin/ruby -Ke
# -*- mode:ruby; coding:euc-jp -*-
# /home/tetsu/src/ruby/class/tcal.rb
# created: September 24,2005 Saturday 13:38:32
# author: tetsu(WATANABE Tetsuya)
# $Id: tcal.rb,v 1.9 2008/10/22 14:25:13 tetsu Exp $
# usage:
# 振替休日 http://ja.wikipedia.org/wiki/%E6%8C%AF%E6%9B%BF%E4%BC%91%E6%97%A5
class TCalendar
def initialize(year = 0, month = 0)
@now = Time.now
@year = if year == 0 then @now.year else year end
@month = if month == 0 then @now.month else month end
@holiday = []
@holiday_name = []
@mday_arr = []
1.upto(31) do |d|
begin
@mday_arr[d] = Time.local(@year, @month, d, 0, 0, 0)
if d > 28 and @mday_arr[d].month != @month
@mday_arr[d] = nil
end
rescue ArgumentError
@mday_arr[d] = nil
end
@holiday[d] = false
end
month_name = @mday_arr[1].strftime('%b')
# このデータ形式は次のようになっています。
# 月名<space>日付<space>有効年<space>コメント
# 行の先頭の「#」以降はコメント
# 木村さん感謝!
# HM2 Happy Monday(2nd monday)
# HM3 Happy Monday(3rd monday)
holiday_str = '
Jan 1 0 元旦
Jan 15 -1999 成人の日
Jan HM2 2000- 成人の日
Feb 11 0 建国記念の日
Mar SHUNBUN 0 春分の日
Apr 29 -1988 天皇誕生日
Apr 29 1989-2006 みどりの日
Apr 29 2007- 昭和の日
May 3 0 憲法記念日
# May 4 1986-2006 国民の休日
May 4 2007- みどりの日
May 5 0 こどもの日
Jul 20 1996-2002 海の日
Jul HM3 2003- 海の日
Sep 15 -2002 敬老の日
Sep HM3 2003- 敬老の日
Sep SYUBUN 0 秋分の日
Oct 10 -1999 体育の日
Oct HM2 2000- 体育の日
Nov 3 0 文化の日
Nov 12 2009 天皇即位20年
Nov 23 0 勤労感謝の日
Dec 23 1989- 天皇誕生日
'
holiday_str.split(/\n/).each do |l|
next if l == '' or l =~ /^\#/
l.sub!(/\#.*$/, '')
m, d, y, c = l.split(/\s+/, 4)
if y != '0'
if y[0,1] == '-'
next if @year > y[1,4].to_i
elsif y[-1,1] == '-'
next if @year < y[0,4].to_i
elsif y[4,1] == '-'
next if @year < y[0,4].to_i || @year > y[5,4].to_i
else
next if @year != y.to_i
end
end
if month_name == m
case d
when 'SHUNBUN'
d = syunbun(@year).to_s
when 'SYUBUN'
d = syubun(@year).to_s
when 'HM2'
d = nMonday(2).to_s
when 'HM3'
d = nMonday(3).to_s
end
@holiday[d.to_i] = true
@holiday_name[d.to_i] = c
end
end
if @year >= 1986
i = 0
while i < 31 - 2
# 「国民の休日」判定
# 当日が祝日 次の日が祝日でない 日曜日でない 次の次の日が祝日
if @holiday[i] and @holiday[i + 1] == false and @mday_arr[i + 1].wday != 0 and @holiday[i + 2]
@holiday[i + 1] = true
@holiday_name[i + 1] = '国民の休日'
i += 1 # skip
end
i += 1
end
end
end
attr_reader :holiday_name, :year, :month
def nMonday(n)
count = 0
@mday_arr.each_index do |d|
next if d < 1
count += 1 if @mday_arr[d].wday == 1
return d if count == n
end
end
def today?(mday)
@now.year == @year and @now.month == @month and @now.mday == mday
end
def wday(mday)
return nil unless @mday_arr[mday]
@mday_arr[mday].wday
end
# 2005 からの振替休日 連続する祝日が日曜日にかかると祝日の終りの次の平日を振替休日に
def furikae2005(mday, wday)
year = @mday_arr[mday].year
if year < 2005
return 'workday'
end
if mday <= wday
return 'workday'
end
(1..wday).each do |i|
if @holiday[mday - i] == false
return 'workday'
end
end
'holiday'
end
def status(mday)
return nil unless @mday_arr[mday]
return 'holiday' if @holiday[mday]
wday = @mday_arr[mday].wday
case wday
when 1
if @mday_arr[mday].year >= 1973 and mday > 1 and @holiday[mday - 1]
'holiday'
else
'workday'
end
when 2..5
furikae2005(mday, wday)
when 0, 6
'weekend'
end
end
def header
msg = @mday_arr[1].strftime('%B %Y').center(20)
msg + "\n" + 'Su Mo Tu We Th Fr Sa' + "\n"
end
#| From: hajima atmark crimson.gen.u-tokyo.ac.jp (Ryoichi Hajima)
#| Newsgroups: fj.questions.misc
#| Subject: Re: vernal/autumnal equinox
#| Message-ID: <HAJIMA.94Jul13161542@tanelorn.gen.u-tokyo.ac.jp>
#| Date: 13 Jul 94 07:15:42 GMT
#|
#| 春分日 (31y+2213)/128-y/4+y/100 (1851年-1999年通用)
#| (31y+2089)/128-y/4+y/100 (2000年-2150年通用)
#|
#| 秋分日 (31y+2525)/128-y/4+y/100 (1851年-1999年通用)
#| (31y+2395)/128-y/4+y/100 (2000年-2150年通用)
def syunbun(year)
if year > 2150
STDERR.print "over year's: #{year}\n" #'
exit 1
end
v = if year < 2000 then 2213 else 2089 end
(31 * year + v)/128 - year/4 + year/100
end
def syubun(year)
if year > 2150
STDERR.print "over year's: #{year}\n" #'
exit 1
end
v = if year < 2000 then 2525 else 2395 end
(31 * year + v)/128 - year/4 + year/100
end
end
if $0 == __FILE__
puts 'テスト'
cal = TCalendar.new(2007, 9)
print cal.header
p cal
end
「年」として有効なものは、UNIX としての一般的年の 1970 〜 2037 までです。
ターミナルタイプについては、まだちょっと不十分です。
Linux での infocmp(1) というコマンドか、HP-UX での untic(1) というコマンドに依存してます。
コマンドがない場合には、vt100 系のターミナルの制御コードを使います(たぶん Win 系でも動くと思います)。
terminfo(5) 形式の出力からターミナル情報を取りだしています。
祝日はとても簡単な形で実現しています。 定義中「XX」の場合には、不定のため計算します。 この計算については、同じ月に不定の祝日が 2 日もないと決めつけていますので、もし対応できないような場合になれば修正が必要です。
休日の形式は単純なので、個人の休日などを加えるのは容易と思います。 ただ、「年」の指定が、当年だけというフォーマットは、現状ではありません。
ちょっと無駄なことをしています。 出力する「月」について、毎日の Time クラスのオブジェクトを生成しています。 曜日の計算を自分で行えば、こういうことはしなくてすむとは思いますが、便利なのでついつい。
引数の「年」と「月」には、指定のための順番がありません。 好きな順番で指定してください。 これは、UNIX での cal コマンドの指定がどうも私には合わないためです。
2009/11/12 天皇即位20年の祝日対応
coding:euc-jp を追加しました。
2005 年から振替休日の扱いが変更されています。 連続する祝日が日曜日にかかると次の平日が振替休日になるそうです。 日曜日と月曜日が祝日の場合火曜日が振替休日になります。
クラスライブラリを別ファイルにしました。
「みどりの日」を「緑の日」にしていたので修正です。
2007 年からの改訂対応です。 4/29 が「昭和の日」。 5/4 が「みどりの日」。
「国民の休日」の扱いを、プログラム的に追加。 祝日と祝日にはさまれた平日は、「国民の休日」です。
1/1 が日曜日のときに「振り替え休日」扱いになっていなかったのを修正。
確認用に co してしまったのでした。
Tera Term 対応。 vt100 で $<2> が入ってきたので外す。
EUC を前提に -Ke を指定する。 処理系が SJIS の場合には -Ks にしてください。
2002-07-29 対応
祝日名の表示をサポート。 最近、HAPPY MONDAY が増えて、何の日かわからなくなってきたから。
bug fix です。 秋分の日のつづりを間違っていました。
ruby 1.7.1 2001-08-06 対応です。
新しい happy monday は 2003 年からでした。
海の日と敬老の日が happy monday になるそうです。 第三月曜日とのことでした。