はじめに
D言語で、3か月分のカレンダーを表示したいと思います。
D言語の標準ライブラリ(Phobos)やUFCSを駆使して、なるべく少ない行数での実装を目指してみました。
結果として、19行(空行を除く)ほどのソースコードになりました。
ソースコード
import std;
string getCalendar(Date dt)
{
string s = format("%04d / %02d%19s", dt.year, dt.month, "");
s ~= format("%(%s %) ", [EnumMembers!DayOfWeek]);
s ~= " ".cycle.take(Date(dt.year, dt.month, 1).dayOfWeek * 4).to!string;
dt.daysInMonth.iota.each!(d => s ~= format(" %2d ", d + 1));
return ( format("%-224s", s) );
}
void main()
{
Date dt = Clock.currTime().to!Date;
auto r = roundRobin(
getCalendar(Date(dt.year, dt.month, 1).add!"months"(-1)).chunks(28),
getCalendar(Date(dt.year, dt.month, 1)).chunks(28),
getCalendar(Date(dt.year, dt.month, 1).add!"months"( 1)).chunks(28),
"\n".cycle.take(8).to!string.chunks(1));
r.each!(s => writef(" %s", s));
}
コンパイルと実行結果
d:\Dev> dmd -m64 cal.d
d:\Dev> cal
2021 / 12 2022 / 01 2022 / 02
sun mon tue wed thu fri sat sun mon tue wed thu fri sat sun mon tue wed thu fri sat
1 2 3 4 1 1 2 3 4 5
5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12
12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19
19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26
26 27 28 29 30 31 23 24 25 26 27 28 29 27 28
30 31
ソースコード補足説明
getCalendar
ひと月分のカレンダー情報を返す関数です。
横幅28
文字、縦幅8
行の計224(=28*8)
バイトの文字列を返します。
format("%04d / %02d%19s", dt.year, dt.month, "")
で対象年月をYYYY / MM
形式に整形します。
format ※formatの書式
import std;
void main()
{
Date dt = Clock.currTime().to!Date;
writeln(dt); // 2020-Sep-19
writefln(format("%04d / %02d%19s", dt.year, dt.month, "")); // 2020 / 09
}
format("%(%s %) ", [EnumMembers!DayOfWeek])
ですべての曜日を文字列で取得します。
EnumMembers
DayOfWeek
import std;
void main()
{
writeln(format("%(%s %) ", [EnumMembers!DayOfWeek]));
// sun mon tue wed thu fri sat
}
Date(dt.year, dt.month, 1).dayOfWeek
で対象年月の1日(ついたち)が何曜日かを取得します。
" ".cycle.take(Date(dt.year, dt.month, 1).dayOfWeek * 4).to!string
で1日(ついたち)の曜日まで、半角スペース" "
で埋める文字列を取得します。4
は、各日にちの表示幅です。
Date
dayOfWeek
cycle
take
to
dt.daysInMonth
で対象年月の末日を取得します。うるう年の2月も自動的に計算してくれます。
import std;
void main()
{
writeln(Date(2020, 1, 1).daysInMonth); // 31
writeln(Date(2020, 2, 1).daysInMonth); // 29
writeln(Date(2021, 2, 1).daysInMonth); // 28
}
dt.daysInMonth.iota.each!(d => s ~= format(" %2d ", d + 1))
で末日までの日付を取得します。
daysInMonth
iota
each
import std;
void main()
{
Date dt = Date(2020, 9, 1);
string s;
dt.daysInMonth.iota.each!(d => s ~= format(" %2d ", d + 1));
writeln(s);
// 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
}
main
3か月のカレンダーを表示します。
Clock.currTime().to!Date
で今日の日付を取得し、dt.add!"months"
で前月、今月、来月の日付を計算しています。
Clock
currTime
to
Date
add
import std;
void main()
{
Date dt = Clock.currTime().to!Date;
writeln(dt); // 2020-Sep-19
writeln(dt.add!"months"(1)); // 2020-Oct-19
}
chunks
を使って、引数の固定サイズごとに文字列を分割します。
引数28
は、横幅の1週間を28文字(4文字*7日)で表しているもので、
縦幅を8
行固定で取得できる前提で、roundRobin
を使って3か月のそれぞれの行を順番に並べます。
chunks
roundRobin
import std;
void main()
{
int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8];
auto arr2 = arr1.chunks(4).array;
writeln(arr2); // [[1, 2, 3, 4], [5, 6, 7, 8]]
auto arr3 = roundRobin(arr2[0], arr2[1]);
writeln(arr3); // [1, 5, 2, 6, 3, 7, 4, 8]
}
あとはeach
を使って、順番に表示します。
each
writef ※formatの書式
更新履歴
2020.9.19 初回投稿
2022.1.26 dmd version 2.098で動作確認
2023.12.18 dmd version 2.106で動作確認、ソースコード修正