はもちくわ

コードについて自分なりの解釈を書いてます。

【Excel】関数で万年業務カレンダー(日付固定)を作ってみる〜第2回〜第nX曜日の日付計算・・・第3火曜って何日??

 

前回に引き続き、下のような万年業務カレンダーをつくっていきます。

全5回の記事になっておりまして、構成は下のようになっています。内容が重なるものはサラッと書きますので、詳しく知りたい方は、過去回をご確認ください。

万年業務カレンダーの作成全5回

それでは、第3火曜日のような、第nX曜日は何日になるか?を計算していきます。
まず、最初に思い当たる面倒くさそうなところが、曜日は7種類あるってところですね。素直に関数で条件分岐すると、、、、もう、考えたくもないです(笑)
うーん、どうしましょうか。。。いっそ、カレンダーの利用者に曜日を計算できる数値で指定してもらうとか、、、、、
いやいや、、、ユーザビリティが大事とか言われている、この時代にそれはないですわな(笑)

なので!文字列の曜日を数値に変換して使いたいと思います!第1回で使ったTEXT関数の反対です。
とは言え、そんな曜日に特化した便利な関数はありません!!

どうするのかといいますと、文字列の中から、含まれている文字を探す、FIND関数を使います。
例えば、「水」を、月曜始まりのWEEKDAY関数の実行結果(以下、戻り値)と同じになるようにするには、

=FIND("水","月火水木金土日")

このようにすれば、WEEKDAYと同じ戻り値「3」になります。
関数の使い方、仕組みは、見たままですが、"月火水木金土日"という文字列の中に、「水」が左から何番目にあるか?を調べているんです。
VBAなどのプログラミングなんかでも、配列を使って似たようなことをしますが、関数に配列はないので代用ですね。
WEEKDAY関数の曜日始まりに合わせて文字列の並びを変えて使えば別のことにも使えますね。さらに、この方法は他でも応用が効くので、覚えておくと便利ですよ。


はい、これで曜日は数字に置き換えることができました。それでは日付をどう導き出すか考えていきましょう。

下の図は、水曜日始まりのカレンダーを使って、指定されるX曜日の位置関係と、n週目のnが増えるとどうなるかを整理したものです。

割とまとめるのに苦労しました。これで伝わるのでしょうか??心配だ(笑)

月によって始まりの「ついたち」の曜日が変わるし、カレンダーの1段目(1週目)にX曜日があるかないか?によって第1週目の日付の数え方が変わりはしますが、実は考え方って結構単純で、この第1週目のX曜日の日付さえわかってしまえば、2週目以降は、nー1週×7だけのことです!1週目だけ考えればいいなんて簡単じゃないですかっ!!
さあ、1週目のX曜日をみつけましょう!

まず、上の図の1段目にX曜日があるかどうか?を判定しましょう。
判定にはWEEKDAY関数を使います。月初とX曜日のWEEKDAY関数の戻り値とカレンダーの1段目の関係を下の図で整理しました。

このように、指定されたX曜日の戻り値が月初の戻り値以上であれば、X曜日はカレンダーの1段目にあることになります。当然ですが月初の戻り値より小さければ、1段目にはないので、2段めの月曜日以降にあることになります。
これで判定ができましたので、早速日付を計算しましょう。

まずは、1段目に曜日があるパターンです。上の水曜日始まりのカレンダーでX曜日が金曜日として考え、式に表してみます。

ややこしそうですが、式としては単純ですね。X曜日と月初の差を出して、X曜日自身を足せばOKということです。

次は、同じように2段目に曜日があるパターンを考えます。次は火曜日でみてみましょう。

いきなり8がでてきましたね(笑)どこからでてきたのかといいますと、WEEKDAY関数の戻り値は、月曜始まりで1から7で構成されていて、上の図のカレンダーをみてわかるように、カレンダーの1段目の「位置」を表しています。この1段目のどこかから月が始まるわけですが、X曜日は含んでないというのが今回の条件ですね。
ということは、2段めにX曜日があるので、X曜日の日付を調べるには、1段目の今月の日付はすべてカウントする必要があります。そうすると、カレンダーの「位置」として2段めの月曜を考えると。。。「8番目」です。なので「8」から月初の戻り値を引けば、今月の1段目の日数がわかるわけです。
さらに、この2段目の月曜から、X曜日の戻り値分日付を加算させれば、求めたい日付となるわけです。

さあ、これで日付の計算はできました。あとは実際の条件分岐を入れ込みながら、作成していきましょう、、、、といきたいところですが、、、、もう少し整理します。

上で説明した1段目に曜日があるパターンと2段目に曜日があるパターンの式ですが、よく見ると似てますよね。
そこで、次のように1段目の式を2段目の式に寄せていくように式の順番を入れ替えてみると、、

はい、実は1か8だけが違うだけで、同じ式でした!こうすれば条件分岐は短くできますね〜
それでは実際に下の箇所に式をいれて作成していきます。

式ですが、ちょっと長くなるので改行をいれて書いていきます。(Excel上でも改行OKです。)

=IF(M9="","",
   IF(WEEKDAY(DATE($C$1,$E$1,1),2)>FIND(N9,"月火水木金土日"),8,1)
    +FIND(N9,"月火水木金土日")-WEEKDAY(DATE($C$1,$E$1,1),2)+(M9-1)*7
 )

短くなったとは言え、長いですね(笑)。
この条件判定の式と計算式に同じような関数があります。これは、

WEEKDAY(DATE($C$1,$E$1,1),2)
↑月初の戻り値のこと

FIND(N9,"月火水木金土日")
↑X曜日の戻り値のこと

を意味しています。
「セルM9に何も入力がなければ空白を表示する」という条件が大外についてますが、式の判定や計算は、これまで説明してきたとおりです。
あとは、入力したセルから下方向に必要な分だけコピーすればOKです。


さて、いろいろと簡潔に式を書くためにやってきましたが、、、やったがために、あとで見返して分からなくなるでは困っちゃいます。説明書を書くつもりで、ほどほどの整理ぐらいの式の形にしておくのも手です。維持管理するのは自分だけじゃないですから(笑)まあ、このへんは好き好きです。ご都合の良い方法を選んでください!

さあ、これで曜日設定の表に第何週の何曜日かを入れると日付が表示されるようになりました。
カレンダーに予定を表示させるのは第5回までおあずけです。日付設定と予定が被る可能性があるので、そうなったら2つとも表示させたいですからね。そんな表示方法をそのときにご説明します。

それでは今回はここまでです。読んで頂きありがとうございました。

【Excel】関数で万年業務カレンダー(日付固定)を作ってみる〜第1回〜月末判定と曜日の文字列表示

カレンダーには、よく家庭で見る曜日固定のカレンダーと、仕事などの業務上でみる日付固定のカレンダーとありますね。
曜日固定の万年カレンダーは過去記事の「関数で万年カレンダーをつくってみる」で作りましたので、今回は、業務に特化したカレンダーを、日付固定で作ってみます。

また、業務でよく使う、前営業日、翌営業日の判定や、第3火曜日って何日?のような、任意の第nX曜日は何日になるのか?なども計算してカレンダーに反映させましょう。給与支給日なんか前営業日とか翌営業日とか使われますよね。第3月曜日は営業計画を提出だとか(笑)、、、 まっ、、今の私には無用なものですが(泣)

では!早速やっていきましょう!!

今回は下のように記事を5回に分けてアップしていきます。通して書いていたら膨大な量になったので分割することにしました(笑)
記事は作成しながら順番にアップしますが、アップしたらその都度、各記事にリンク貼るようにします。よかったら通して読んでいただけたらうれしいです。

万年業務カレンダーの作成全5回



それでは、5回をとおして、このようなカレンダーを作っていきます。

年と月は数値として別々のセルに入っていて、これを変えると、月末日付と曜日が変化します。
業務予定は日付で指定するものと、第nX曜日のように、曜日で指定するものを準備し、入力するとカレンダーに反映されます。日付指定の予定だけ前営業日か翌営業日を選択できるようになっています。なお、前、翌営業日の選択については、土日だけを休業日とするパターンと、祝日や休業日が別にあるパターンと分けて作成します。
そして、日付、曜日で指定したそれぞれの予定が重複してしまった場合ですが、どちらもカレンダーに表示されるようにします。



それでは、今回は月末と曜日の表示ですので、まず、月末日付の連動から説明していきます。

月末日付の求め方については、過去記事(曜日固定カレンダー)で詳しく解説してますので、ここではおらさらいです。
まず、月末日付の求め方ですが、DATE関数を使って、翌月「1日(ついたち)」の1日前とすれば、求めることができました。さらに、指定方法として、「翌月の0日」とすれば良かったですね。そして日付だけを抽出するには、DAY関数でくくればOKでした。
今回のサンプルで式を書くと、

=DAY(DATE($C$1,$E$1+1,0))

このようになります。
注意点として、DATE関数は「数値」から「日付データ(シリアル)」にする関数ですので、年、月、日は「数値」で指定しなければいけません。
過去記事から見ている方は、あれ?DATE関数の指定の仕方がちょっと違う・・・と感じたかもしれません。
曜日固定カレンダーのときは「日付データ」から年と月を抜き出すために、YEAR関数、MONTH関数を使ってましたが、今回は使っていません。それは、今回、年と月がすでに数値になっていますので、そのまま参照すればいいからです。

さて、このカレンダーには当月分だけ日付表示させるようにしたいですね。
ということは、日付数字がこの式の結果以下、つまり月末以下なら表示するという条件分岐を作れば実現できます。それでは、カレンダー後半に差し込みます。
このサンプルでは次のように作りました。

日付数字は1〜24まではベタ打ちしてあり、25日から計算式が入っています。本来、月の日数がかわることを考えなければならないは28日以降なのですが、変わる日付を手入力で直接変更されて関数が潰れる可能性もあります。こんなときは、バックアップ用に、多めに関数を準備しておくとメンテナンスやリカバリーに便利です。極端な話、この式を2日から入れても構いません。ただ、もしそのようにする場合は、16日から列が分かれるのでご注意ください。



次は曜日を表示します。
これはいくつか方法がありますが、今回は見た目と値が同じになるように設定します。
は?何の話??ってなりますわな(笑)
こちらも過去記事(曜日固定カレンダー)で詳しく解説してますが、もし、曜日だけ表示するなら、日付データに対して表示形式「aaa」と指定するだけで表示されます。この場合は、見た目「曜日」、値「日付」になります。このままCOUNTIF関数なんかで今月の水曜日の数をカウントしようとして、検索値を"水"にすると、水曜日は「0回」という結果になってしまいます。曜日絡みの集計は、だいたいの場合、WEEKDAY関数を使うので問題はないでしょうが、今回はこんな方法もあるよってことで、見た目も、値も「曜日」となるようにします。
使う関数はTEXT関数です。使い方は、

=TEXT( 値 , "表示形式")

となります。
この表示形式というのは、Excelのリボンにある表示形式と同じです。組み込み形式の形をある程度知っていれば、関数側で表示コントロールできるという便利な関数です。例えば、値によって、ちょっとだけ表示を変えちゃうみたいなことを、関数でコントロールしたいなってときなんかにいいんじゃないでしょうか?色は変わらないし、値も文字列になってしまうので使える幅は少ないですけど。。。
ちょっとうろ覚えですが、、、確か、関数使った直後に×1すると数値として扱えるとか、何かで見た気がします、、、、まあ、そんな面倒くさいことでは多分使わないので検証はしませんけど(笑)。ご興味のあるかたは試してみては、、、

はい、それでは、カレンダーの初め、1日の曜日欄に次の式を入れます。

=TEXT(DATE($C$1,$E$1,A3),"aaa")

これで曜日が文字列として表示されます。
これで、SUNIFやCOUNTIFを使う時、曜日そのままで検索できますね!
関係ないですが、私、Excelを使い始めの若かりし頃、曜日表示はこの方法ばっかり使ってました(笑)売上日報に仕込んだり、曜日別売上、人件費を集計したりと、、、結構頻繁に出現しましたね(笑)年と月と日付がバラバラにある場合はこれ便利ですよ!

はい、これでカレンダーの日付表示機能は完成です。このサンプルは、条件付き書式で曜日が土、日のときはグレーに塗りつぶす設定がしてあります。条件付き書式については過去記事「関数で万年カレンダーをつくってみる」を御覧ください。

それでは今回はこれで終了です。
次回は第nX曜日の日付計算をします。この計算は、結局月に1回ぐらいしか使わないかもしれませんが、、、、よかったら見てください。
ここまで読んで頂きありがとうございます。

【Excel】関数で万年カレンダー(曜日固定)をつくってみる(後編 祝日の反映、条件付きの書式)

 

こんな万年カレンダーを作るぞ!ということで後編です。前編を見てない方は前編からどうぞ。

hamo440.hatenablog.com

さて、前編では表示する月に合わせた日付の計算をし、カレンダーに自動表示させました。
続いて、後編では祝日の自動表記と、条件によって日付数字の色分けがされるようにしていきます。
早速、祝日から取り掛かりましょう!



さて、この祝日なのですが、、、
残念ながら、計算で導き出すことはできません!

できないのか〜い!!(笑)

では、どうするのか?といいますと、、、、内閣府のホームページに「国民の祝日について」というページがあって、その中に、昭和30年から1年後ぐらいまでの、祝日一覧データが入ったCSVファイルが公開されております。ご丁寧に、ダウンロード用のURLも掲載されているので、このデータをダウンロードして使いましょう!
いっそ、スクレイピングを試してみても、、、と思いましたが、私の知識とスキルでは、PHPでもあまりうまくできなかったので、、、やめます(笑)開発環境では成功したのに、サーバー上ではできなかったんですよね。。。
まあ、祝日データは年に1回ぐらいのメンテでしょうから、そこは手動で頑張りましょう!(笑)内閣府のページは、ネット検索で「祝日」と検索すれば一発ででてきます。

さて、ダウンロードしたCSVデータの扱い方、運用方法は各自お好きなようにしていただくとして、この記事では、カレンダーのファイル内に「祝日」という名前でシートを準備して、データを一切加工せずに、項目行も含めてそのままA1始まりでコピペして使います。

この準備したデータベースから祝日を引っ張り出していくわけですが、、、まず先に表示する場所を整備しましょう。

 

前編で作ったカレンダーに、祝日名を表示するための行を挿入します。日付の計算式が崩れてしまいますが、すぐに直しますので気にせずにバンバン行きましょう。なんなら2週目の式をオートフィルして直せる3週目以降は先に消してしまってもいいです。行の高さ、罫線などは好みで調整してください。

こんな感じに挿入しました。
それでは、日付の式を直していきましょう。
まず、関数が崩れた原因を考えます。
これは言うまでもなく「行の挿入」によって「行数」が変わったことが原因ですね。前編で行数を使って日付を計算した事を覚えていますか?「行が増えると×7」と説明していたところです。おそらく、このあたりを修正できれば式は直りそうですね。
改めて前編で使った図解を見てみます。

これです。=ROW()を使って得られる行数の戻り値から「ー3」をして、7の倍数の乗数の1〜5までを準備していました。
この結果が、行の挿入によってどのように変わったのか見てみましょう。

このように、各行に1行ずつ挿入されているので、計算結果が「ちょうど2倍」になってしまっています。これでは日付が崩れて当然ですね。。。さあ、困りました。どう対処しましょうか。。。。

なんちゃって(笑)ノリツッコミみたいな流れですが、、、もう気づきましたか?!
前編からずっとやってきてますが、解決するためには、「得たい結果と同じになる法則を見つけて式にする」です!
今回の法則は、とてもわかりやすいですね〜。キレイに2倍になっているだけです。ならば、−3したあとに2で割れば解決じゃないですか!(笑)
早速、式を修正していきましょう。

前編でやった縦横の計算を盛り込んだ式がこれです。

=$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN())

このうちROW()-3を7で掛ける前に2で割ればいいわけです。

=$A$1-(WEEKDAY($A$1,1)-(ROW()-3)/2*7+COLUMN())

この赤枠で囲んだ箇所を入れるだけです。日付計算をしているすべての式にこの箇所はありますので、残らず修正します。
前編の最後に式を配置しましたが、同じように基準となる式だけ修正し、あとはコピーすれば間違いないと思います。図にすると下のようになるかと思います。

双眼鏡マークの置換を使っても早いと思いますが、今回は、行の挿入の仕方によっては参照先がズレたり無くなっていることもありますので、基準の3つの式はしっかり参照先を確認しながら修正したほうが無難かと思います。

 

それでは次は祝日を引っ張り出してきます。

Excelで、データ一覧から該当データを引っ張り出す時、まず思いつく方法って、VLOOKUP、HLOOKUP関数ではないでしょうか?さらに進化したXLOOKUPというやつもあります。
でもですね、、、私個人的な意見ですが、データの検索はINDEX+MATCHが最強なんじゃないかと思うんですよね〜。なので!ここではINDEX+MATCHを使った検索方法でやります。とりあえずこれだけ覚えておけば大抵の検索はできます。使い方によってですが、ソートを使わずに並び替えたり、検索結果をリストアップしたりも可能です。このへんは過去記事で書いてます。よかったら見てください!下にリンク作ってあります。

それでは、INDEX、MATCHの使い方です。過去にも何度も書いてますが、、、、ちょっと使い方によせて書いてみます。

まず、INDEXです。

=INDEX(データの範囲,行番号【縦軸】,列番号【横軸】)

これはデータの範囲で指定した表の、行と列を座標指定してやると、その場所のセル値が得られます。このデータ範囲の中で、行と列を別々に指定できる点が、広く使えるポイントです!

そしてMATCHです。

=MATCH(検査値,検索範囲【1列もしくは1行】,検索方法)

これは検索範囲の中で検索値が何番目にあるかを数値で教えてくれる関数です。検索範囲は行方向、列方向どちらでも構いませんが1行、もしくは1列でなければいけません。
そして、この検索方法は次の3パターンがありまして、

    3パターン(順不同)
  • 完全一致
  • 範囲の中で検索値と同じか次に小さいもの(以下)
  • 範囲の中で検索値と同じか次に大きいもの(以上)

となります。
使う時の注意としては、「以下」のときは昇順、「以上」のときは降順に並び替えておく必要があります。「完全一致」は順不同で構いません。
MATCHさんはデータ全体から「以上」、「以下」、「一致」を探しているわけではなく、データの初めから順番に見て、条件に一致した時点で、検索を終了する関数です。そのため、MATCHさんのために、探しやすく準備してあげないと、スネてしまってエラーになります(笑)こういうところ、人っぽくて好きです(笑)

この「以上」、「以下」は値が特定できなくても、検索値と比べて同じか、その次に小さいもの、もしくは、大きいものを探してくれるので、テストの合格判定や平均判定などに使われている例がよく見られます。
ですが、、、通常業務で運用するって考えると、データを検索するたびに並び替えって、、、ちょっと面倒ですよね。なので私は、あまり業務では使っていなかったです。
ただ、過去記事の「関数だけで24時間のシフト表をつくってみた」の中で、関数で並び替えしたデータに、MATCHの「以上」を使ってシフトデータをつなぎ、グリグリ動くシフト表を作りました。この時、検索した結果から、その次の値の検索をするという使い方は、やりたいことにピッタリでした。並び替えが自動化できれば、値が特定できなくても検索できる、というのはとても便利でしたね。(この詳しい説明はしてませんが(笑))

さて!この2つの関数を組み合わせて使うという話なのですが、INDEX関数の行と列を指定する箇所を、MATCH関数の検索結果にすることで、一覧表を検索してデータが抜き出せるという話になるわけです。

では、祝日一覧から該当祝日を検索して表示してみます。

データ一覧画像はいちいち載せませんが、このサンプルでは、「祝日シート」のA列に日付データ、B列に祝日名一覧があります。

それでは式の配置と解説は次のようになります。

ちょっと見にくくなってしまいましたかね。。。
式はこうなってます。

=INDEX(祝日!$A:$B,MATCH(A3,祝日$A:$A,0),2)

使い方で注意するのは、INDEXで指定したデータエリアのデータ総数とMATCHで指定する検索エリアのデータ総数が一致していなくてはいけません。それぞれが別関数なので、INDEXもMATCHもそれぞれが自分の指定されたエリアの順番という認識だからです。シートの行と列番号は関係ないのです。
ここでは、どちらも列全体を選択しているので一致してますね。

さて、この式の結果ですが、、、実はエラーになっています。。。
これは、VLOOKUP関数なども同じなのですが、検索して見つからなかった時はエラーとして表示されるのです。そもそもExcelは計算シートですからね。見つからない=エラーですわな。
しかし、これはカレンダーなので、この表示を回避します。IFERROR関数というエラーが出た時に処理を実行させる関数があります。これ便利で、よく使われる場面を見ますが、、、何かしらエラーが出たら、全部その処理になってしまうという困ったことになります。
「別にいいじゃん!」って思われるかもしれませんが、エラーの原因がわかっているときは、できる限り原因を潰しておくと、別の原因で出たエラーはちゃんと抽出できるようになるんですよ。
エラー表示は全部無くせば便利じゃないか?と思われるかもしれませんが、これだと意図しない不具合が、別にあったとき、何も気づかずに放置され動きます。人間ですから何か間違いはあるものなのですが、その人間ですから間違いがあっても見つけにくいんです。だからチェック機能はなるべく、極力、なんとか(笑)生かしておきましょうということです!ただ、わかっているエラーが2つも3つもあるときは関数式が長くなって編集できなくなるので、迷わず使っていいと思います。ただ、そういうときは、本当にその関数の使い方や方法があっているか考え直したほうがいいかもしれません。

はい!なので、ここでは原因を潰します。
見つからないということは、「データ一覧に同じ日付が無い」ということですね。では無いことを証明しましょう。
式はこうなります。

=IF(COUNTIF(祝日!$A:$A,A3)=0,"",INDEX(祝日!$A:$B,MATCH(A3,祝日$A:$A,0),2))

COUNTIF関数を使って、祝日データ一覧の日付の中から、A3と同じ日付をカウントさせます。見つからないということは、カウント結果は「0」です。なので、「0」のときは空白を表示します。

これで先程の原因は回避できました!
と、、、これで終わりかと思いきや、実はもう1つありまして(笑)
それは日付が空白の時です!
つい先程、祝日が無い時は空白にしてました。今度も空白にします。このような、同じ結果になる条件が2通りあるというときは、複数条件を設定できるANDとかORが便利です。式はこうなります。

=IF(OR(COUNTIF(祝日!$A:$A,A3)=0,A3=""),"",INDEX(祝日!$A:$B,MATCH(A3,祝日$A:$A,0),2)))

う〜ん、長いですな(笑)
「いやいや、、、もう長すぎて無理!(笑)」って方は、IFERRORでも問題ありません!そんな方は、

=IFERROR(INDEX(祝日!$A:$B,MATCH(A3,祝日$A:$A,0),2),"")

短いですね(笑)ただ、エラーの考え方だけ知っておいてください。不具合の抜け道は塞いでおくことをおすすめします。

さあ、これで祝日表示ができました!いつも長い記事書いちゃうな。。。これじゃ誰も読まないだろうな(笑)

はい、次は条件付き表示でカレンダーの色を指定して終わります!



セルA3を選択してから、下のようにウインドウを開きます。

これはセルの値や式の結果によって表示形式を変えることができるところです。ウインドウに初めからある、組み込みのものは今回使いません。上のように「新しいルール設定」を選択してください。そのあと、下のようにします。

Excel全般に言えますが、ウインドウに式を入力するのはとてもやりにくいです。入力補助機能が働くのでしょうが、入力を間違えたと思って、修正のためにカーソルキーを使って前後に移動させようとすると、セルが勝手に動いて、「クソッ!!」って叫びたくなります(笑)どこか関係ないセルに先に入力して、コピペしたほうがいいです。
さて、祝日のときにフォントを赤色にするための条件式は、

=COUNTIF(祝日!$A:$A,A3)>0

これになります。さっきのエラー回避のために追加した式と演算子が違うだけのものですね。今度は祝日である事を証明するので、カウントしたら1になるものを判定します。なので「=1」としてもよいですが、なんか2になることもあるかもしれない。。。ということで、ここは「>0」としてます。また、もっと簡単に判断する式がありまして、

=A4<>""

です。これは祝日名が表示されているかどうかで判断しています。すでに祝日判定を祝日名欄でやっていますので、それを有効利用しよう!ということですね。
この条件付き書式の式判定で気をつける事は、判定式の前にイコールが必要であるということ、あと、直入力せずに選択入力をするとセルが勝手に絶対参照になるので、コピーのときに動かしたい相対参照のセルについては$記号を外すことです。また、ちょっと条件付き書式の数式は動作に癖が強く、式が複雑だと相対参照がうまく効かないことがあります。なるべく簡単な式で判定することをおすすめします。なので今回なら祝日名があるか?の方が使いやすいと思います。

さて、このままセルA3に条件付き書式を2つ続けて設定します。それは、日付が表示月の前月の場合と翌月の場合は、グレーにするという条件をつけます。まず前月の場合を付け加えます。A3セルを選択したままでまた「新しいルール設定」を選んでください。
そのあとは、下のようにします。

これは簡単ですね。月初より小さいものは前月です。
ウインドウで$A$1を入力する時は、セル自体を選択してもできます。その際は、勝手に絶対参照になります。

さて、翌月もこのまま設定します。また同じくA3を選択したままで「新しいルール設定」にします。
先程の「次の値より小さい」を選んだところで「次の値以上」を選びます。
そして、「=$A$3」としたところに、次の式を入れます。

=DATE(YEAR($A$1),MONTH($A$1)+1,1)

こうですね。これは前編で使った式で、翌月月初を表します。翌月月初1日(ついたち)より大きいものは当然翌月ですね。
これで条件付き書式のルールが次のように並んだと思います。

実はこの並びに優先順位がありまして、上優先つまり、後で入力したものが優先されます。なのでこの並びですと、グレーが優先されるので、先月と翌月に祝日があっても日付はグレーになります。

それでは、このままA3から横方向に書式のみコピー。そのあと、下方向に書式のみコピーで全体に反映させます。
最後に、土曜日の日付は青色、日曜日は赤色にフォント設定します。これをしても条件付き書式が優先されますので、表示月から外れた時のグレー、祝日のときの赤が優先されます。

試しにセルA1に「5/1」と入力してみてください

ちゃんと反映されましたか?

 

さあ!これで完成です。もし条件付き書式がうまくいかないときはルールの修正で修正してみてください。ただ、相対参照の数式はうまくいかないときがあります。そのときは最初からやり直したほうが早いです。まあ、、、結局、うまくいくかどうかも含めて、慣れです(笑)たくさん失敗しましょう(笑)



これを書いていて、業務カレンダーのような日付固定バージョンや曜日別売上集計のような使い方もまとめてみたくなりました。そんなわけで、次は万年カレンダー(日付固定)と関数の業務転用なお題で書きます!長々とお付き合いありがとうございました!

【Excel】関数で万年カレンダー(曜日固定)をつくってみる(前編 その他の表示形式と日付の計算)

 

仕事の中にExcelがどっぷり浸透している割に、日付がテキストとして扱われていることが多いな、、、と感じる今日このごろ。

ああ、、、もったいないなぁ。。。。

日付はデータとして扱ったほうが、得られる情報が多くなりますし、計算もできるようになりますので、できることが増えます。
さらに!日付をデータとして扱う人が増えれば、社会全体の業務書類の汎用性は爆上がり間違いなし!!(ホントか?!(笑))
この記事を読んだら、ぜひ!日付はデータとして扱ってほしいです!

 

さて、日付の取り扱いを説明するなら、カレンダーがてっとり早い!
ということで、下のような万年カレンダーを関数と表示形式を使って作りながら、日付の扱い方を説明していきます。

日付は関数で計算、祝日はデータ一覧から関数で抽出。表示する色は条件付き書式で表現してます。

 

では、表示形式の説明から始めます。実は、これだけでかなり便利になります。
表示形式は知ってるので飛ばして読みたい方は
↓ここをクリック!↓
☆関数で日付計算するへ飛ぶ☆


この表示形式は、数字にカンマを振ったり、通貨単位を入れたりとよく使うと思いますが、日付も便利に使えるんです。今回説明するのは「その他の表示形式」です。具体的にどこの事を言っているのかといいますと、、、

これですね。
ここを選択すると、セルの書式設定というウインドウが開きます。フォントや罫線などを設定するところですね。
この表示形式を設定したいセルには、あらかじめ値を入れておくことをおすすめします。親切なExcelさんは、それにあった表示形式をコーディネートしてくれます。まあ、、、気にいるかどうかは別ですが(笑)。
その上で、今回は「日付」なので、先に、分類の「日付」を選択し、標準的な日付表示を選んでおいてから、「ユーザー定義」を選びます。

この「ユーザー定義」のウインドウの説明文に、
「基になる組み込み表示形式を選択し、新しい表示形式を入力してください。」
とあります。このとおり、新しい表示形式を自分で作れるんです!これ、結構自由度が高いです。
そして、「組み込み表示形式」というのが、ウインドウ内の分類リストの中にある数値や日付などになります。この組み込み形式には決まり事があり、数値を表す記号は#(シャープ)や0(ゼロ)だったり、日付の年はyyyyだったり、gggeだったりします。

「こんなのいちいち覚えてられっか!」

うんうん。全くだ(笑)。
・・・というわけなので、組み込み形式を覚えたり、自分で探したりしなくてもいいように、セルへ値を先に入れておいたり、先に日付表示を選んでおいたりするわけです。こうすることで基の組み込み形式や表示サンプルが表示されるので、そのままベースとして使ったり、表示サンプルをみながら編集したりと便利です。

ここで、万年カレンダー作成から少し脱線しますが、この表示形式でどんなことができるのかを少し書いておきます。

まず、上のスクショでは「yyyy/m/d」が選んであって、サンプルに「2023/3/1」と表示されています。組み込みの法則としては、yyyy が年、mが月、dが日付を表しているのがわかると思います。このアルファベットの並びを使って自由に設定できるわけです。
あと、PCって口下手なんで、代わりに書いておこうと思うんですが、、、PCさん、実はこの日付が何曜日か知っています。。。

じゃあ、教えてもらいましょう!
次のように「yyyy/m/d」と表示されているボックスを選択して、dのあとに続いて「aaa」と入力してみてください。

 

ほら!サンプルに曜日が現れましたね!
先に言ってよ〜って思いませんでした?まあ、WEEKDAY関数とかあるんだから当然って言えば当然なんですが、私は教えてもらうまで気づきませんでしたよ。
さらに!先程の「aaa」に続けて「a」を足し、4つ続けると、

ご丁寧に「曜日」が付きました!
では、このアルファベットの組み合わせを使っていくつか作ってみましょう。結果がサンプルに表示されるので確認しながらできるので便利です。
まずはオーソドックスに曜日をカッコでくくりたいですね。aaaをカッコでくくればいいです。

ほら簡単。
もう少し情報量を増やして、月と日付は2桁揃え、時間も一緒に表示できるようにしてみます。

このように簡単に表示を自分で変えることができます。
そして、情報を増やすことばかりではありません。逆に隠すこともできます。
例えば表のタイトルを「2023年3月 売上集計表」だとしましょう。そういうときは、こんな感じで、

どうでしょうか?データの一部が隠れてタイトルになってます。
これ、便利なところは、ごちゃごちゃと表示があっても、値は「日付データ」であるという点です。例えば、タイトルを3月から4月に変える時、テキストの場合、セルを選択して、3月の「3」に合わせて「4」を上書きして変えますが、表示だけ変えてある場合は、セルの日付を変えるだけなので、テキストは気にせずに、セルに直接「4/1」と入力すれば4月に変わります。その上、日付ですから関数計算も可能です。数字のカンマや通貨単位の 表示があっても計算ができるのと同じです。便利ですよね〜。
今回、このカレンダー日付は表示形式を「d」だけにしてます。



さて、万年カレンダーに戻ります。

まずは、説明しやすいように日付だけで作ります。祝日はその後で追加しますね。
下のようなカレンダー表を準備します。

もう表示形式はわかりますね?上の図のような組み込み表示形式がなければ自分で作りましょう!
このカレンダーの日付は、表示月がある週は前後の月の日付も表示することにします。よくあるカレンダーと同じです。
こうやって書くと難しそうに思えますが、実はとても単純で簡単です。日付データ用に特別な計算があるわけでもなく、1日経過させたいなら+1、戻したいなら−1するだけです。年とか月は考えなくても日数がズレれば一緒についてきます。なので、考え方としては、月初が何曜日で、そこから各日付のマスは何日ズレているのか?を表現すればカレンダー完成なわけです。

では、まずは月初めが何曜日か?を調べましょう。先程文中で出てきましたWEEKDAY関数使います。
WEEKDAY関数は調べたい日付の曜日が指定曜日から数えて何日目か?を教えてくれる関数です。
使い方は、

=WEEKDAY(調べたい日付データ,数え始める曜日の指定)

です。
ここでは調べたい日付データはセルA1に入っているので「A1」、数え始める曜日はカレンダーが日曜日始まりなので「1」になります。
この数え始める曜日は、当然7種類あり、それぞれに対応した数字がありますが、説明はカット!(笑)
WEEKDAY関数使うときにちゃんとExcelに説明がでますのでご安心を!!
そして、この調べたい日付のセルA1はカレンダーの基準としますので、絶対参照(フィルで動かさない)にしておきます。式にすると

=WEEKDAY($A$1,1)

です。
さて、この関数から得られる結果(以後、「戻り値」と呼びます)は、月初が日曜日なら1、月曜なら2、火曜なら3・・・と、曜日が日曜日から離れていくにつれて1ずつ増えていきます。
そして、カレンダーの一番初めの左上のマス(日曜日)に入る日付を考えてみます。セルA1の月初から何日ズレているか?を表現できればいいいわけですから、月初が日曜ならセルA1から0日のズレ、月曜なら−1日、火曜なら−2日・・・と1日ずつズレが増えていけばいいですね。
どうでしょうか??この2つ、共通点が見えませんか?
そうです。どちらも1つずつ数字が増えますね。しかも、戻り値から−1して、月初から引き算すれば、思っている結果と一緒になります!

では、これを式に表してみると、

=$A$1-(WEEKDAY($A$1,1)-1)

とできます。
同じように他の曜日を考えてみます。
月曜のマスに入る条件は、月初が日曜なら+1日、月曜なら0日、火曜なら−1日、、、となります。おっと、プラスが出てきてしまいました。
でも、日曜マスと同じように月曜マスの場合も1つずつ数字が増えてますね。戻り値から考えてみましょう。わかりやすい月初が月曜の時、戻り値は「2」でした。これをズレなし、つまり「0」にするわけですから、「ー2」すれば解決です。さて、日曜日の戻り値は「1」です。「ー2」すると結果は「−1」となります。これを月初から引く。。。。マイナスの引き算ということはプラス!!
もう文で書いてもわけわからん!(笑)
なので、このまますべての曜日を下の表にまとめますと、、、

説明している私が言うことではないのですが、、、、はたして、これはうまくまとまったのだろうか(笑)この表で伝わるのでしょうか?!(笑)
結局どうするの?って聞こえてきそうなので、式の配置はどうすればいいのかをダイレクトに図にすると、

とまあ、日曜日は−1、月曜日は−2・・・・と戻り値から引く数字が1つずつ増えていけばいいってことです。

では、全部のセルの式を手入力していく・・・・。まあ、1つ書いてそれをコピペしてからマイナスの数値だけ変えれば良さそうですが、1つずつ増えていくという法則があるものですから、できればオートフィルで完結したいですよね?

ここは、もうちょっと考えましょう!
「列が右に1つずつズレていくごとに1ずつ数値を増やす方法」。
はい!もうこれ答えです。「列が1つずつズレる」です。
自身のセルの列の位置を得る方法、COLUMN関数を使いましょう。セルに=COLUMN()と入れると、左から数えた列数を数値で返してくれる変数です。一番左は「1」になります。
なので、さっきまで頑張って説明していた戻り値からマイナスする箇所を

=$A$1-(WEEKDAY($A$1,1)-COLUMN())

このようにすれば1行目はすべて解決します。

やっと1行か、、、、やってることは単純、説明すると結構長い(笑)
さて、2行目以降です。

「え〜、まだやるの〜」って聞こえないでもない(笑)

はい、安心してください!!
すでに1行目の日付が決まっているということは、そこからどれだけズラしていくのかを考えればいいだけです。行が下にいくごとに1週間、つまり7日ずつズレが増えるだけですね!
図にまとめると、

こういうことですね。
では、1行増えるごとに7の倍数で増える数値を考えましょう。
さっきは「列が増えると+1」でした。今度は「行が増えると×7」です。
列ではCOLUMN関数を使いました。行にも同じことができるROW関数というものがあります。=ROW()と入力すると、一番上の1行目を「1」として、関数がある行が何行目か?を返してくる関数です。
では、この戻り値を使って、先程の得たい結果の図と合わせて考えてると、

こうなります。
これを式にすると、

=(ROW()-3)*7

こうなりますね。
この式が先程の-COLUMN()に足されれば解決します。合体しましょう。

=$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN())

これで2行目以降は解決しました!
そして、お気づきの方もいらっしゃるかもしれませんが、実はこの式、1週目に入れても大丈夫です。
どういうことかといいますと、1週目は3行目ですから戻り値は「3」です。ということは、「3−3=0」ですから、「7×0=0」となります。1週目は7倍は無効になってますね。
なので、日付表示はこの式1つで完成!ということになります。
ふ〜、長かった(笑)

しか〜し!!このままですと表示月をすぎて月が変わっても関数が入っているマスは日付が出続けてしまいます。
では、日付を表示しない状態を考えてみましょう。表示ルールとして、「日曜日始まりのカレンダーで、表示月がその週に1日でもあれば月が変わっても日付を表示させる」としてました。ということは、表示する週のはじめ、日曜日が翌月になったら、その日以降は全部翌月になります。逆に日曜日が表示月の場合、週の途中で月が変わっても日付を表示し続けなければならないわけです。なので、表示するかどうかの判定は、日曜日だけ見ればいいことがわかりますね。

早速判定を入れていきましょう。

さて、この判定方法ですが、2つ紹介します。実際にお題のようなカレンダーを作るだけなら1つでよいのですが、実際に日付を扱う場合、どうしても月末を扱うことがでてきます。例えば同じカレンダーでも、月末で表示を止めるとかですね。業務カレンダーや勤務表なんかは前後の月はでてないことのほうが多いんじゃないですかね。というわけで!まだまだお付き合いください(笑)

それでは判定方法1つ目。
各週の一番左、日曜日の日付の「月」が、セルA1の基準日の「月」と一緒かどうかで判断します。
どうするのかといいますと、先程の日付計算の式の結果と、セルA1の値、それぞれからMONTH関数で月を抜き出し比べて判定します。MONTH関数の使い方はとても簡単で、

=MONTH(調べたい日付データ)

と日付をMONTHでくくるだけで1から12までの数値で「月」を教えてくれます。

早速式を紹介といきたいところですが、ちょっと式が長くなるので、説明用に、先程の日付計算式を「日付計算」と置き換えて書いてみます。

=IF(MONTH(「日付計算」)<>MONTH($A$1),"",「日付計算」)

と、こうなります。実際に式に書くと

=IF(MONTH($A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN()))<>MONTH($A$1),"",$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN()))

長いですね〜。しかも同じ日付計算の式が2回あるのでスマートではない気がしますね。まあ、好みでしょうが、、、、。式に名前つけて整理してもいいですし、このへんはおまかせします(笑)



それでは、もう一つの判定方法です。
この方法は、日曜日の直前の日付、土曜日が月末以上なのか?で判定します。つまり、土曜日が月末以上であるなら、その次の日曜日は翌月以降なので表示しないという理屈です。
では、月末を求めましょう。
突然ですが、「西向く侍」ってご存知ですか?(笑)
ははは、、、まあ、別の話なので、詳しく書きませんが、これは、月の日数が31日ではない月を覚えるための、数字の語呂合わせです。このような語呂合わせが必要なぐらい各月の日数は揃っていません。その上、4年に一度、しかも2月だけ1日増えちゃいます(笑)
じゃあ、月末日付を抽出できる関数があるのか?と言えば、残念ながらMONTHやDAY関数のような月末日付をバシッ!と求める関数はありません。

お手上げのように思えますが、、、実は、月末を導き出すために、どの月でも絶対に変わらない基準があります。
それは、、、月初め、1日(ついたち)です!
えっ!?なんで??と、思っちゃいますか?でも、月初めの前日は必ず「月末」ですよね。。。こうなるともう、なぞなぞみたいですね(笑)
では、早速、表示月の月末を出してみましょう。
数値を日付に変換するDATE関数使います。使い方は、

=DATE(年の数値、月の数値、日付の数値)

です。
このように数字で指定してやると日付データで戻り値として返してくれます。今回は、表示月つまりセルA1に入っている日付の月末を調べればよいわけです。
セルA1は日付データになってしまってますので、DATE関数にそのまま入れることはできず、年と月の日付を数値として抜き出せねばなりません。
でも、大丈夫!これは簡単です。YEAR、MONTH、DAY関数で解決できます。YEAR、DAY関数は初めて出てきましたが、使い方はMONTH関数と一緒です。

まずは来月の月初を表します。数式にすると

=DATE(YEAR($A$1),MONTH($A$1)+1,1)

これで来月初めです。来月なので月だけ+1してます。日付は抜き出さなくても必ず月初にするので「1」ですね。
月に+1だけだと表示月が12月だと13月ってなっちゃうし、年が変わる対応できないんじゃない??と思われがちですが、DATE関数は結構融通が効きまして、月や日付を計算で進ませると、それに対応して日付として経過したものとしてくれます。これは逆もOKです。なので、実際ではありえない日付になっても、理屈があっていれば判断してくれるのです。
話がそれたようになっちゃいましたが、何が言いたいかといいますと、、、この「月末の日付を出す」を正攻法で行うとすると、上の式の戻り値から−1すれば解決です。でも、、、実は、下の式のように

=DATE(YEAR($A$1),MONTH($A$1)+1,0)

「翌月の0日」というありえない日付を設定すると、、、、これが「月末」になります。1日よりまえの0日です(笑)
まあ!便利なこと。
先程、「月末を出す関数は無い」と言いましたが、これが実質の月末を求める関数だと私は勝手に思ってます。

月末の日付だけ知りたいならこの式をDAY関数でくくればOKです。これ、結構使えますので業務で使ってみてください。
えっ?「何に使うか?」ですって?!例えば月の稼働割合を出す時なんか、毎月手入力で月の日数とか入れてないですか(笑)毎月1回だからいいやとか(笑)
私は以前、月間売上から1日あたりの売上を出す時に使ってましたよ(笑)人件費とか(笑)ね、結構使う機会ありますよ!

 

それではこれを使って2週目の日曜日に判定式を入れると、

=IF(G3>=DATE(YEAR($A$1),MONTH($A$1)+1,0),"",$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN()))

となります。
ですが、このままですとカレンダーの日付が2週(2行)空欄になるような場合、参照している土曜日のセルの値が空白になり、月末以上ではなくなるので、2行目だけ日付表示が復活してしまいます。まあ、カレンダーの表示が4週で止まることがほとんど無いので、このままでもいい気はしますが、運悪く2月が日曜始まりの28日おわりだと、可能性が無いわけではありません。なので、参照セルが空白だったときも表示しないようにガードします。
IF文のような条件分岐で複数条件をつけるとき、とても便利なOR、ANDがあります。ORは条件のうち、どれかが一致する場合、ANDは条件がすべて一致する場合です。IF文を複数組み合わせてもいいのですが、今回の条件は参照セルが「月末以上か空白かのどちらかの場合」という2つの条件で、わかりやすいものですのでORを使います。

=IF(OR(G3="",G3>=DATE(YEAR($A$1),MONTH($A$1)+1,0)),"",$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN()))

となりました。これでガードまでかけれたので大丈夫ですね。
先程の表示月を比べる方法と比べて、ガードまでかけたとしても、式自体が短くなり、少しスマートになりました。
私もこちらの月末判定の方をよく使います。むしろこちらしか使わないぐらいです。ですが、、、作成するもの、表示させる条件によっては、全く使えないこともあります。両方頭にいれておくとよいかと思います。

 

これで日曜日の判定はできました。あとは月曜から土曜だけです。

これはもう簡単ですね。その週の日曜日が空白かどうかで表示するかどうかを判定すればよいだけです。
2行目の月曜日の式に

IF($A4="","",$A$1-(WEEKDAY($A$1,1)-(ROW()-3)*7+COLUMN()))

と入れて、あとは横方向に土曜までオートフィル。そのまま下方向にオートフィルでOKです。日曜日が空白かどうかをどの曜日も見ればいいのでセルA4は列固定の参照にしてます。ただ、1週目も同じ式で問題なく動きますので、メンテナンスのことを考えると別の式をいれるより1週目から全部同じ方がわかりやすいでしょう。

それでは、ここまでのまとめとして、カレンダーの各マスにどの式を配置すればいいのかを図にしておきます。
式自体が短いので日曜日の判定は月末を使った方になっています。

これで日付の表示は完成しました。次は、、、と行きたいとことですが、、、

はてなブログは文字数が増えてくると編集時の動きが急に鈍くなります。。。。ブラウザのせいだろうか?
すでに現時点でとても編集しにくくなってきたので、残念ながら祝日と条件付き書式での日付の色変更は後編にします。。。ワンストップで全部書きたかったな。。。。無駄話が多いからだろうな(笑)

というわけで後編に続きます。

 

hamo440.hatenablog.com

【ExcelVBA】セルの値だけを消す「ClearContents」の結合セルでの使い方

これは失敗談からの記事なのですが、、、
バイト先で、取引先からExcelファイルで送られてくる入荷予定リストを、所定のフォルダから自動で読み取り、入荷する商品と同じ商品が倉庫のどこにあって、さらに、その場所に収まるかどうか?を判定して入荷一覧表に表示するものをExcelVBAで自動化しました。

これは、在庫状況を可視化するヒートマップがほしいと先に頼まれて作った後、「こんなのもあったら便利かね〜」ぐらいの思いつきで勝手にあとづけで作ったものでしたが、思った以上に需要が出てきたため、誰でも簡単に操作できるように整備したものでした。
この用途は限定されていたので、特にガードは気にせすにデータ出力させていたのですが、稀に想定外の使い方をされることも出てきて、異様に処理スピードがゆ〜〜〜っくりになる事が出てきました。
あまりにも遅くなるので、操作した人から「ごめん。なんか壊しちゃったみたい・・・・」と相談され、重くなる原因を調べてみたら、、、セルの値を消すときに、ClearContents を使わずに""を使っていたため、見た目以上にデータが入り込んでいるのが原因でした。

結局、「これ原因は私ですわ^^;」と謝罪しながら修正することになりました。



私はセルの値を消すときに""をよく使います。表の値をまとめて一気に消すときには、セルの結合配置も考えなくてよいので楽ですからね。それに、それほど大量のデータを出力させることもなかったので気にならなかったんです。多くてもA4用紙に収まる程度でしたから。

でも、""は、人間からみたら空白に見えますが、PCからすれば、「空白」というデータになってしまいます。消したことになりません。
今回のように表示するデータに上限がなければ、意図せずにファイル容量が増えてしまいます。良かれと思って作ったものが逆に迷惑になる。。。なんてことにならないように、みなさんも""の多用は気をつけましょう!(それは私か、、、ははは。)



で、、、ふと思ったのですが、ClearContentsって結合セルが対象だと少しだけ気を使いますよね。
「この操作は結合したセルには行えません」とかExcelから注意されたことありません?(笑)
このエラーメッセージ見て、「結合セルには使えないの??」って真に受けないでくださいね。エラーメッセージが言葉足らずなだけです。結合セルに対して使い方が違ってますよっていうエラーです。
では、何がOKで何がダメなのかを下にまとめていきます!



それでは次の表を例にして説明します。

この薄い黄色になっている部分に値を入れたり消したりするとします。この部分はそれぞれ結合セルになってます。
これ、セルに値を入れるようにVBAにベタ打ちすると、

With ActiveSheet
   .Range("C2")="東京都港区"
   .Range("C3")="山田太郎"
   .Range("C4")="0120-333-333"
   .Range("C5")="e_mail.com"
 End With

と、このようにできます。結合セルでも値を入れるだけなら、結合された端のセルだけ指定すればOKですよね。

えっ?ループ処理の場合も見ます??解説はしませんが(笑)

Dim buf As Variant,i As Integer
buf = Split("東京都港区,山田太郎,0120-333-333,e_mail.com",",")
For i=0 To Ubound(buf)
   ActiveSheet.Cells(i+2,3)=buf(i)
Next i

これで同じ動きになりますかね。試してないですが(笑)

 

で、これを消そうと思ったとき、失敗談であげた""を使うと、

ActiveSheet.Range("C2:C5")=""

この1行だけで解決しちゃいます。一気に端だけ指定すればOKです。
これは入力した値を全部消したわけではなく、すべて空白""に置き換えただけです。
このようにベタ打ちする程度のデータ量なら何も問題ありません。便利だから使えばいいと思います。
でも、ループ処理しなければならないようなデータ量になってくると、先程書いたように、知らない間にファイル容量が大きくなってしまう危険性があります。

それじゃあ、セルの値だけ消す命令、ClearContentsを使えばいいじゃない!ということで、上の指定方法のままで

ActiveSheet.Range("C2:C5").ClearContents

とすると、
「この操作は結合したセルには行えません」
とエラーになります。
では、どのように指定すればよいのかというと、結合セルを割らないように、欠けないように指定してやればいいのです。

今回なら、

ActiveSheet.Range("C2:F5").ClearContents

とします。いくつか例をあげて図解しますと

こんな感じになります。契約書や見積書など書式があって、消したくない場所がある場合は、上の図の上から2番めのように分割して指定することになりますね。分割のときは複数行にしたり、Rangeの中でカンマ区切りにしたりと工夫してください。コードを書くときに短くすればいいってもんでもないですよ。自分があとでわかりやすくて、直しやすい方法を取りましょう!

でも、まあ、、、データ量が少ないものは、""を使ってサクッと解決すればよいかと思います。

あと、ここからおまけです。
値入力でループ処理書きましたので、消すときもやってみますね。
結合範囲が不揃いなうえ、わからないとき、とても便利な命令があります。
この例の表ならば、

Dim i As Integer
i=2
With ActiveSheet
   Do while .Cells(i,2)<>""
      .Cells(i,3).MergeArea.ClearContents
      i=i+1
   Loop
End With

とできます。
このループ処理は、表の項目欄(B列のこと)が、空白を挟むこと無く、表の終わりまで続いているので使えるコードです。これなら結合範囲やデータ数を気にせずに処理できます。2行目以下であればデータ項目を追加してもコードは変えずに動きます。.MergeAreaは便利な命令で、結合しているかどうかの判断をさせたり、住所(座標)をしらべたり、今回のように選択したりできます。しかも!うれしいことに、結合して無いセルが対象になっても、エラーになりません!
こういう融通の効く命令は、一定のルールを表に持たせれば、仕組み化が割と簡単ですね〜。

 

久しぶりに記事書きました。まだ書類管理の記事に手を付けてませんが(笑)

それこそ需要無しなんでまあいいか〜(笑)

PHPのクラスでページ滞在時間計測をしようと考える事自体ダメだった

自分のWebページに訪問した人の滞在時間をざっとでもいいから図ってみようと、PHPのクラスのコンストラクタとデストラクタで表現しようと思って、下のようなコードを試しに書いたわけですよ。

//訪問者タイマー
class visitor_timer
{
  public $page_st;
  public $page_ed;

  public function __construct()
  {
    $this -> page_st = New DateTime();
  }

  public function __destruct()
  {
    $this -> page_ed = New DateTime();
    $result = $this -> page_st -> diff($this->page_ed);
    //echo $result -> s;
  }

}

これでインクルードしておいて、ページ内でNewでインスタンスと同時に現在時間がpage_stに入って、インスタンスされたオブジェクトが参照されなくなったらpage_edに時間をいれて、diffで差分をだしたらsで秒数がわかると、、、、ここにDataBaseとつなげて、、、、とあれ?

大きな勘違いでした。。。このオブジェクトが参照されなくなるタイミングがページを離脱する時ならこれでいいですが、、、
PHPはサーバーサイドで、ページ作成してブラウザに渡すので、ページが表示されたときにはすでにコードは終了してますよね(笑)だって差分は0秒でしたもん(笑)

やっぱりJavaScriptしかないかぁ。。。。まあ、私のページはクローラーしか来てないのでどっちでもいいですが(笑)

なので、これはとても意味のないコードということになりますね。

page_edは別にpublicにしなくてもいいですけど、後でつかうかなぁって思ったのでそうしてます。そもそも使えないのでそんなことはどうでもいいですけど。

クラスの勉強をはじめました

PHPで仕事したいなぁと考えてましたが、レベル不足で全然ダメだったので、あきらめて、趣味でごちゃごちゃやっておりますが、ある程度勉強をしていくと、
「クラス使えないのはダメ」とか「クラスなしの駄文」とか、、、いろいろ目にして気落ちする問題に当たります(笑)
「使えなくても動くからいいじゃないか!」と避けて来ましたが、最近、自作のコード規模が大きくなってきて、読みにくくて触れない状態になってきました。
最低限、関数化して見やすくするのですが、もっと何かないのか?と自分からそう思うように至りまして、、、、

結局クラスに手を出すことにしました。すると、、、あら、便利そう(笑)

勉強を始めた当初は全く意味がわからなかったのですが、ある程度勉強したおかげで少し内容がわかってきました。PHPで言えば、DateTimeを使うことと一緒なんですね!DateTimeを使うとき、私はNewで名前付きの時計を作ることをイメージしてたんですよ。一回変数に名前付きの時計を入れておけば、7日後とか2時間前とか指定するだけでわかるなんて便利〜って、思ってたんですよ。まさにこれでした。これは変数って言わずにオブジェクトって言うんですね。
なるほど、ExcelVBAでも同じですね。セルとかシートとかブックのオブジェクト(物体)に名前をつけて動かすイメージですもの。この名前をつけるときに使うSETが、 PHPでいうと、Newなんだ〜。

あと、やたら用語が難しくて当初困ってましたが、難しく考えすぎでしたわ。使っているのは変数と関数だけなんですね。

クラスは変数と関数の集合体で、クラスの中に入ると、変数がプロパティ、関数がメソッドになるわけだ。
で、クラスはあくまで金型+設計図でコレ自体を使うわけではなくて、Newで名前をつけてオブジェクトに実体化するわけですね。関数と違うのは、データを保持したまま量産が可能なのね。しかも量産したものを配列に入れたりもできると。。。

これを学校のクラスを考えると、いままでの変数データの集合体の配列はいわゆる名簿で、クラスで人間という設計図を作って量産したオブジェクトの集合体は。。。まさにクラスだ(笑)

これをまとめると、、、

こうなった!どうだ!?う〜ん、まだわかりにくいか(笑)