LoginSignup
2
0

D言語19行で3か月カレンダー表示

Last updated at Posted at 2020-09-19

はじめに

D言語で、3か月分のカレンダーを表示したいと思います。
D言語の標準ライブラリ(Phobos)やUFCSを駆使して、なるべく少ない行数での実装を目指してみました。
結果として、19行(空行を除く)ほどのソースコードになりました。

ソースコード

cal.d
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));
}

コンパイルと実行結果

cal.dの実行結果
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で動作確認、ソースコード修正

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0