t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA 特定日付が、第何週目の何曜日なのかを求める関数

今回は特定日付が、第何週目の何曜日なのかを求める関数を紹介する。
といっても曜日はWeekday関数やFormat関数で簡単に求まるので、今回紹介するのは月のうちの何週目に相当するかを求める関数だ。

まず、特定月のうち何週目に当たるのかは、次の関数で求められる。

Function WeekOfTheMonth(d As Date) As Long
    WeekOfTheMonth = DatePart("ww", d) - DatePart("ww", DateSerial(Year(d), Month(d), 1)) + 1
End Function

ただ作ってみてこれは私が求めている結果と違うことに気付いた。
実務では第二火曜日とか、第三木曜日に特定の処理を行うことがある。

たとえば2019年3月21日は、3月の第四週目にあたる。
でも、2019年3月21日は第三木曜日である。

その月の何回目の木曜日かを数える必要があるのだ。

そこで、愚直に数え上げる関数を作った。
これが本記事のメインである。

Function WeekNumberByDayOfTheWeek(d As Date) As Long
    Dim ret As Long
    Dim target As Long: target = Weekday(d)
    Dim i As Date
    For i = DateSerial(Year(d), Month(d), 1) To d
        If Weekday(i) = target Then ret = ret + 1
    Next
    WeekNumberByDayOfTheWeek = ret
End Function

関数を試すコードが以下。最初に紹介したWeekOfTheMonthも使用している。

Sub hoge()
    Dim d As Date
    For d = #1/1/2019# To #4/1/2019#
        Debug.Print Format(d, "yyyy年m月d日"); "は、";
        Debug.Print Month(d); "月の第"; WeekOfTheMonth(d); "週目、";
        Debug.Print "第"; WeekNumberByDayOfTheWeek(d); Format(d, "aaa"); "曜日です。"
    Next
End Sub

結果はこんな感じ

2019年1月1日は、 1 月の第 1 週目、第 1 火曜日です。
2019年1月2日は、 1 月の第 1 週目、第 1 水曜日です。
2019年1月3日は、 1 月の第 1 週目、第 1 木曜日です。
2019年1月4日は、 1 月の第 1 週目、第 1 金曜日です。
2019年1月5日は、 1 月の第 1 週目、第 1 土曜日です。
2019年1月6日は、 1 月の第 2 週目、第 1 日曜日です。
2019年1月7日は、 1 月の第 2 週目、第 1 月曜日です。
2019年1月8日は、 1 月の第 2 週目、第 2 火曜日です。
2019年1月9日は、 1 月の第 2 週目、第 2 水曜日です。
2019年1月10日は、 1 月の第 2 週目、第 2 木曜日です。
2019年1月11日は、 1 月の第 2 週目、第 2 金曜日です。
2019年1月12日は、 1 月の第 2 週目、第 2 土曜日です。
2019年1月13日は、 1 月の第 3 週目、第 2 日曜日です。
2019年1月14日は、 1 月の第 3 週目、第 2 月曜日です。
…つづく

3/26 追記 もっとスマートなやり方

記事を引用していただいたのでお邪魔すると、更にスマートなやり方が紹介されていた。
z1000s.hatenablog.com

WeekNumberByDayOfTheWeek(d)は(Day(d) + 6) \ 7に置き換えできるようだ。
どういう理屈なのかは上記サイトを参照のこと。

以下で試してみたところ、中断されずに問題なく完了した。

Sub hoge()
    Dim d As Date
    For d = Date To Date + 100000
        Debug.Assert (Day(d) + 6) \ 7 = WeekNumberByDayOfTheWeek(d)
    Next
    Debug.Print "End"
End Sub

※Debug.AssertにFalseを与えると中断する。

おわりに

今回は英語の関数名を考えるのに苦労した。
もう日本語で「第X曜日」という関数名にしようかと思ったけど最近は英語の識別子に拘ってしまう。

日本語だと「曜日」の2文字で済むものが、英語だと「Day of the week」になる。
既存関数のようにWeekdayという単語を使っても良かったんだけど、平日限定の意味を含んでしまう気がしてやめた。

PowerPoint VBA スライドにプロジェクトのスケジュール図をプロットするマクロ

今回はパワポでよくあるこういうスケジュールスライドを作るVBAコードを紹介。
f:id:t-hom:20190319224303p:plain

と言っても現段階では完成度は低いのであまり期待されると困ってしまう。

ところどころハードコーディングしているし、クラスモジュール内でSelectionを参照しちゃってるし、変数名がまだまだ適当なところあるし変数宣言もしてたりしてなかったりだしといった具合。
使い勝手という意味でも、スケジュールはコードにベタ打ちだし、微調整が効かずイマイチ。

でも良いアイデアだと思うので、使えそうなら部品取りして改造するなりこれをヒントにイケてるツールを作るなりしてもらえれば幸い。私は私で、ブラッシュアップしてみようと思う。

コード

書き殴りでもクラスモジュールは使う。むしろオートシェイプ系は座標計算が入ってくるのでクラスを使って操作性をシンプルに保たないと頭がこんがらがる。

クラスモジュール Task

Public StartDay As Date
Public EndDay As Date
Public Title As String
Public Property Get Self() As Object
    Set Self = Me
End Property

クラスモジュール Schedule

Private schedule_ As Collection
Public Width As Double
Public Height As Double
Public StartDay As Date
Public EndDay As Date
Property Get MonthCount() As Long
    MonthCount = DateDiff("m", StartDay, EndDay) + 1
End Property
Property Get MonthlyWidth() As Double
    MonthlyWidth = Width / MonthCount
End Property

Function CalcDateLocation(d As Date) As Double
    Dim ret As Double
    ret = DateDiff("m", StartDay, d) * MonthlyWidth
    ret = ret + (MonthlyWidth * CalcRatioOfDateInMonth(d))
    CalcDateLocation = ret
End Function

Sub Add(t As Task)
    schedule_.Add t
End Sub

Private Sub Class_Initialize()
    Set schedule_ = New Collection
End Sub

Private Function CalcRatioOfDateInMonth(d As Date)
    CalcRatioOfDateInMonth = Day(d) / Day(CalcEndOfMonth(d))
End Function
Private Function CalcEndOfMonth(d As Date)
  CalcEndOfMonth = DateSerial(Year(d), Month(d) + 1, 0)
End Function

Public Sub Plot()
    OFFSET_L = 50
    OFFSET_T = 50
    Margin = 50
    Dim calender As Table
    Set calender = ActiveWindow.Selection.SlideRange.Shapes.AddTable(NumRows:=2, NumColumns:=MonthCount, Left:=OFFSET_L, Top:=OFFSET_T, Width:=Width).Table
    calender.ApplyStyle "{5940675A-B579-460E-94D1-54222C63F5DA}"
    
    Dim m As Long: m = Month(StartDay)
    calender.Rows(2).Height = Height
    For i = 1 To MonthCount
        calender.Cell(1, i).Shape.TextFrame.TextRange.Text = Format(DateSerial(2019, m, 1), "mmm")
        m = m + 1
    Next
    
    Dim t As Task
    
    Dim targetSlide As Slide
    Set targetSlide = ActiveWindow.Selection.SlideRange(1)
    
    start_y = OFFSET_T + calender.Rows(1).Height + Margin / 2
    arrowWeight = calender.Rows(2).Height / schedule_.Count - Margin
    
    For Each t In schedule_
        L = CalcDateLocation(t.StartDay)
        W = CalcDateLocation(t.EndDay) - CalcDateLocation(t.StartDay)
        start_x = L + OFFSET_L
        arrowLength = W
        targetSlide.Shapes.AddShape(msoShapePentagon, start_x, start_y, arrowLength, arrowWeight).Select
        start_y = start_y + arrowWeight + Margin
    Next
End Sub

標準モジュール (任意名称)

Sub スケジュールのプロット()
    Dim s As Schedule
    Set s = New Schedule
    s.Width = ActivePresentation.PageSetup.SlideWidth * 0.8
    s.Height = ActivePresentation.PageSetup.SlideHeight * 0.8
    s.StartDay = Now
    s.EndDay = #7/10/2019#
    With New Task
        .StartDay = Now
        .EndDay = #3/31/2019#
        s.Add .Self
    End With
    With New Task
        .StartDay = #4/5/2019#
        .EndDay = #5/15/2019#
        s.Add .Self
    End With
    With New Task
        .StartDay = #5/17/2019#
        .EndDay = #6/20/2019#
        s.Add .Self
    End With
    With New Task
        .StartDay = #6/22/2019#
        .EndDay = #7/20/2019#
        s.Add .Self
    End With
    s.Plot
End Sub

使い方

白紙のスライドを選択し、スケジュールのプロットを実行するとコード内で定義されたタスクがスライドにプロットされる。

ScheduleオブジェクトのStartDayとEndDayは、背景に配置する表を何月から何月とするかを決定している。

たとえば2019/1/1から2019/12/1まで指定すると12分割される。
f:id:t-hom:20190319225721p:plain

タスクを表すシェイプの高さはタスクの数によって変動する。均等割りされるのでタスクが1個とか2個だとすごく太いシェイプになる。

今後の展望

  • フォームモジュールでタスク登録のUIを作る。
  • コードのリファクタリング
  • サイズの指定を比率と固定値で選択できるように
  • タスクの名称をシェイプに入れる(Titleフィールドだけ用意して放置中)

以上

VBA マクロのバージョン番号の付け方

ソフトウェアは一度作って終わりではなく、公開後もバグの修正や機能向上のため修正を繰り返すことになる。
マクロも同様で、通常はバージョン番号をつけて管理したりする。

バージョン番号の付け方は特に決まっているわけではないが、一般的には次のルールにしていてることが多いようだ。

メジャーバージョン番号.マイナーバージョン番号.リビジョン番号

番号名 意味(私の解釈)
メジャーバージョン番号 コンセプトはそのままに一から作り直した場合や、作り直しに相当するほどの大きな変更があった場合に更新。外観の大きな変更を伴うことが多い。
マイナーバージョン番号 機能の追加・削除、操作性の変更等があった場合に更新。
リビジョン番号 バグ修正・リファクタリング(動作に影響を与えないソースコードの整理)等があった場合に更新。

使い捨てマクロならともかく、ある程度使いまわす予定があるならどのみちバージョン管理する羽目になるので、初めからHistoryシートを用意していつどのような更新を行ったのかを書いておくと良い。

かくいう私もHistoryシートを用意し始めたのは今日の話なんだけど。

f:id:t-hom:20190310184441p:plain

リビジョン番号はまだ付けてないけど、このあとバグ修正とかあれば付けていこうかなと考えている。

同日追記

Twitterでimihitoさんが以下を紹介されていたのでこちらでも紹介。
semver.org

Semanticというのは「意味」を表す言葉。「意味付けされた~」と解釈すれば良い。

上記ではメジャー.マイナー.パッチと呼んでいるが、考え方は同じ。
※メジャーバージョンを上げる基準としてAPI互換性を基準に上げているけどVBAの場合はそこはあんまり関係ない。

ひとつ参考にしたいなと思ったのは、メジャーバージョンの0はリリース前の開発中バージョンを表すということ。ベータ版として開発中のものを先行公開する場合は、メジャーバージョン0を採用しようと思う。

VBA Win32API GetAsyncKeyStateを使ってマクロ実行時に特定キーが押されているかを検知する。

今回はWin32APIのGetAsyncKeyStateを使ってマクロ実行時に特定キーが押されているかを検知するコード。
この手の情報は既に沢山出ているが、検索したサイトはいずれも情報が完全ではなかったので少し苦労した。

では、早速完成コードを紹介する。
今回はシフトキーが押されているかどうかを検知した。

コード

#If Win64 Then
    Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As LongLong) As Integer
#Else
    #If VBA6 Or VBA5 Then
        Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer
    #Else
        Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer
    #End If
#End If
'GetAsyncKeyStateは、-32768, 1, -32767, 0 のうちいずれかの16ビット整数を返す。
'これは二進数に直したときのビットに意味がある。
'-32768: 1000 0000 0000 0000  最上位ビットが1なら、現在そのキーが押されていることを示す。
'1         : 0000 0000 0000 0001 最下位ビットが1なら、最後のGetAsyncKeyState呼び出しの後にそのキーが押されたことを示す。
'-32767: 1000 0000 0000 0001 従って、これは両方に該当することを示す。
'0         :0000 0000 0000 0000 これは、どちらでもないことを示す。
'つまり現在キーが押されているかどうかを知るには、GetAsyncKeyStateの結果を-32768のAndマスクに掛け、
'-32768になればOKということ。

Function IsShiftKeyPressed() As Boolean
    Const KEY_PRESSED = -32768      '1000 0000 0000 0000 最上位ビットが1であることを示す。
    IsShiftKeyPressed = (GetAsyncKeyState(vbKeyShift) And KEY_PRESSED) = KEY_PRESSED
End Function

Sub hoge()
    Debug.Print IsShiftKeyPressed
End Sub

用途

私が公開しているフローチャート作成ツール BreadChart に機能搭載するのが目的。
Connectorモードのとき、条件分岐するシチュエーションにおいて、新しいコネクタから開始するのにわざわざモードをOff・OnしないといけないのをShift+Clickで出来るようにした。
thom.hateblo.jp

というワケで、昨日1.2を出したところだけど、1.3をしれっと公開済み。

参考サイトと苦労したポイント

最初に検索にヒットしたのがこちら。
officetanaka.net
ただ中身をみてみるとコマンドボタン限定なので今回の用途にはマッチしなかった。

次にこちら。
excel-excel.com
キーコード付きでサンプルが掲載されていてとてもわかりやすい。

そしてWin32APIを使う以上、64bit対応も考慮したかったので、こちらも参考にした。
ameblo.jp

それで単純なサンプルを書いてみた。

#If Win64 Then
    Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As LongLong) As Long
#Else
    #If VBA6 Or VBA5 Then
        Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
    #Else
        Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
    #End If
#End If

Sub hoge()
    Debug.Print IIf(GetAsyncKeyState(vbKeyControl), "Pressed", "Not Pressed")
End Sub

しかし、なぜかCtrlを押してない時までPressedになることがあり、次のコードで値を見てみた。

Sub hoge()
    Debug.Print GetAsyncKeyState(vbKeyControl)
End Sub

結果は、Ctrlが押されているときが32768か-32767、押されていない時が1か0になる。
なんじゃこれと思って調べてみると、以下のサイトに解説があった。
tokovalue.jp

この関数は、最上位ビットと最下位ビットに意味があるのだが、そもそもこれ、戻り値はInteger(16ビット整数)だそうだ。
最上位ビットは符号を表すので、IntegerとLongでは解釈が異なってしまう。

ということで修正。

#If Win64 Then
    Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As LongLong) As Integer
#Else
    #If VBA6 Or VBA5 Then
        Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer
    #Else
        Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Integer
    #End If
#End If

これで戻り値はCtrlが押されているときが-32768か-32767、押されていない時が1か0になった。

Ctrlが押されているとき、

  • 32768は1000 0000 0000 0000
  • 32767は1000 0000 0000 0001

Ctrlが押されていないとき、
1は0000 0000 0000 0001
0は0000 0000 0000 0000

つまり、最上位ビットが1かどうかを知りたければ、戻り値と-32768をAndしてやれば良い。
それで完成したのが冒頭のコードだ。

なんでShiftに変えたかというと、BreadChartに組み込む際に、Ctrl+クリックではマクロが発動する以前にオートシェイプの選択として機能してしまい、使えなかったので。

以上。

VBA フローチャート作成ツールBread Chartに挿入機能検討

先ほど新版を公開したところだけど、以前から悩んでいた機能がなんとか実現できそうな感じがしてきたので、取り急ぎコードだけここに書いておく。
thom.hateblo.jp

実現したい機能

たとえば下図のようなことをやりたいとする。
f:id:t-hom:20190309235109p:plain

しかし、単純に列ごと切り取って挿入すると、コネクターが切れてぐちゃぐちゃに。
f:id:t-hom:20190309235433p:plain

また、単純に行挿入したけれど、プレースホルダーが無くてプロセスを配置できない。
f:id:t-hom:20190309235637p:plain

現状のバージョン1.2では、ひな形から作り直すしかない。

今回はこれを何とかするコードをとりあえず書いてみた。
とりあえずなので、ユーザーインターフェースはなく、F8で実行する仕様。
もちろん、中途半端なのでGitHubには反映させていない。

コード

標準モジュールを挿入してUtilとでもしておくと良い。

Sub FixConnectorBug()
    Dim sh As Shape
    For Each sh In ActiveSheet.Shapes
        Select Case sh.AutoShapeType
        Case _
            msoShapeFlowchartProcess, _
            msoShapeFlowchartDecision

            '切れたように見えて論理的につながっているコネクタの表示を直すには、
            'コネクタがつながっているシェイプを少し移動すれば良いことに気付いた。
            '移動してから戻すというコードを考えたけれど、
            'マクロで対応するときは移動しなくても位置を再設定さえすれば直った。
            '以下の一行は一見無意味に見えて、表示バグを修正しているコード。
            sh.Left = sh.Left
        End Select
    Next
End Sub

Sub InsertPlaceholderToSelection()
    Const ProcessWidth As Double = 100
    Const ProcessHeight As Double = 40
    Dim r As Range
    For Each r In Selection
        Dim sh As Shape
        Set sh = ChartSheet.Shapes.AddShape( _
            Type:=msoShapeFlowchartProcess, _
            Left:=r.Left + (r.Width - ProcessWidth) / 2, _
            Top:=r.Top + (r.Height - ProcessHeight) / 2, _
            Width:=ProcessWidth, _
            Height:=ProcessHeight)
        Call DeactivateProcess(sh)
        sh.OnAction = "Click"
    Next
End Sub

FixConnectorBugについて

列ごと入れ替えるとコネクターが切れると書いたけど、実は切れているわけではない。
コネクターを選択すると接続されていることを示す緑の端点が表示される。
実は下図でいう赤丸同士は、論理的にはつながっていて、表示がおかしいだけなのだ。
f:id:t-hom:20190310000235p:plain

右クリックで接続されているプロセスシェイプのどちらかを選択し(左だとマクロが発動するので)、その状態で左クリックでドラッグすると接続が元に戻ることを発見した。
f:id:t-hom:20190310000717g:plain

FixConnectorBugを実行するとすべてのシェイプを探索し、プロセスか判断だった場合(つまりコネクタ以外)はLeftプロパティで位置を再設定する。これでコネクターの表示が正常に戻る。

InsertPlaceholderToSelection

これは、プロセスのプレースホルダーが無いセルを選択して実行するとそこにプレースホルダーを作成するマクロ。
ひな型作成コードの一部を取り出しただけなので、なにも苦労は無かった。さっさと作っておけばよかった。

課題

今のところ手作業で行列の挿入・セルの挿入等を行った後にマクロで補完する方式なので、機能としてマクロに搭載するならまるっと自動でやりたい。でも欲張るとなかなか公開できないのが悩みどころ。

先行で使いたい方いたら今回の記事からコピペしていただくと良いかと。

あと、セルの挿入機能を使う以上、書式ズレの問題が避けられないのも課題。これもきちんとやるのは面倒くさそうだなぁ。

いずれにしても、光明が見えたので良かった。従来はプロセスを一つずつズラす方式を頭の中でシミュレーションして破綻してたので、セル挿入機能をベースにする方針を思いついたのは大きな収穫。むしろなぜ気付かなかったのか謎だ。

VBA フローチャート作成ツール(BreadChart)の更新 Version 1.2

GitHubで公開しているフローチャート作成ツールをアップデートしたので変更点のご案内。

入手場所

こちらからアクセスしていただき、
github.com

以下のDownloadボタンで入手できます。
f:id:t-hom:20190309220543p:plain

更新内容

Configシートでシェイプの色を指定できるように変更

Process・Judge・Connectorの色をそれぞれ指定できます。
ただしそんなに頻繁に変えるものではないので、GUIは実装してません。気が向いたら実装します。
f:id:t-hom:20190309222921p:plain

色は0~16777215までのLong値で指定します。
色から値を調べる方法はいくつかあります。
たとえばセルに色を塗り、そのセルを選択した状態でイミディエイトウインドウで「?selection.interior.color」を実行すると良いでしょう。
色名(XlRgbColor定数名)が分かっていれば、イミディエイトウインドウですぐに値を調べられます。例「?rgbWhiteSmoke」

コマンドボタンをリボンUIに変更

実はver 1.1としてしれっと変更してましたがTwitterでしか言ってなかったので改めて紹介。

Before

f:id:t-hom:20190309221226p:plain

After

f:id:t-hom:20190309221249p:plain

「チャート完成」を「チャート発行」に変更し、別ファイル出力に変更。

実はver 1.1としてしれっと変更してましたがTwitterでしか言ってなかったので改めて紹介。
といっても内容は見出しの通りなので特に書くことなし。

以前の更新

前回の更新

thom.hateblo.jp

忙しすぎる人に確実に読んでもらう為のメールの送り方について考察

今回は忙しすぎる人に確実にメールを読んでもらうためのテクニックについて自分の考察を紹介する。

ここでいう忙しすぎる人とは、「忙しすぎて諦めの境地にいる人」のことだ。諦めの境地なので、基本的に人の頼み事なんて知らんぷり。電話しても日中に繋がることは稀なので、基本コミュニケーションはメールになる。

にもかかわらず、メールはメールで返事が来ないのだ。

しつこく電話をかけ続けてやっと捕まえると、だいたい以下のような反応。

「あれ?そんなメール来てたかな?てへぺろ(・ω<)」
「あれ?何かしないといけないんでしたっけ?てへぺろ(・ω<)」
「あ、私関係ないと思ってました。てへぺろ(・ω<)」

彼らは、「本当に重要なことは、伝える側に責任がある」と考えている。だから、基本的によく読まないと分からないような案件は完全無視を決め込む。さもなくば自分が過労で倒れてしまうからだ。

大事なことなのでもういちどいう。
さもなくば自分が過労で倒れてしまうからだ。


。。。とはいえ、絶対に無視してもらっては困る情報もあるわけで。

今回はなぜ重要なメールが無視されてしまうのかを明らかにし、無視できなくなるメールの送り方について考察してみたい。

忙しすぎる人の情報フィルタリングフロー

メールに限らず、忙しすぎる人は情報を瞬時にフィルタリングする。なんのためかって?なるべく多くの情報を無視するためだ。

送り手は、自分が送った情報がこのように処理されることを想定している。
f:id:t-hom:20190306214613p:plain

しかし、私の経験上、忙しすぎる人のフローは以下のようになっている。
f:id:t-hom:20190306211905p:plain

どうしても読んでもらう必要がある場合

どうしても読んでもらわないと困るメールの場合、忙しすぎる人の情報フィルタリングフローを踏まえて、特に強調すべきメッセージが4つある。

それは、
「アクションが必要です!」
「あなたが対象者です!」
「デッドラインは〇日〇時です!」
「無視すると後で面倒なことになります!」

ということ。

この4点を示唆する文は、冒頭の数行で瞬時に把握できるようにしたい。

簡単なことなんだけど、送り手はつい「やってほしいこと」を強調してしまう。
極端な話、「重要さ」さえ伝われば、後は強調するまでもなく隅々まで読んでもらえるので、そんなところは強調しなくて良い。

アクションが必要であることを示す方法

私の場合、アクションが必要であることは、件名のタグで明示するケースが多い。
たとえば、【要返信】・【要対応】など。

多くの人が、「で、結局何かするのこれ?」っていう、ぼやっとしてよく分からないメールを受け取った経験があると思う。
件名に要対応とあれば、「とりあえず、何かアクションを求めているな」ということが分かるので、メールを読む優先度が上がる。

あなたが対象者ですと示す方法

相手が少数なら宛名で十分だけど、複数名に送る場合、「このメールは〇〇で××な方宛にお送りしています。」と冒頭に一文入れ、そのあと続けて「宛先各位」という宛名にしてることが多い。

なぜ自分に送られたのか分からないメールは、関係ないと判断される可能性が高いからだ。

デッドラインを示す方法

普通に日付を書く場合もあるけれど、基本的に社内ではHTMLメールなので、全体を赤字かつ数字をサイズアップして強調ことが多い。
201936日 (水) 17:00まで

このとき気を付けたいのが、日付と曜日のアンマッチあるあるだ。
特に、日付固定で何かをお願いする場合、絶対に外してはならない。真面目に確認してくれる人ばかりならまだいいんだけど、忙しい人は「やらない言い訳」を渇望しているので、誤解や勘違いを装った確信犯テクで依頼をかわされる危険がある。

また、時刻は基本的に入れる。なぜなら、時刻を入れることで、「何か根拠があってその時間になっているのでは?→バッファなしの本物のデッドライン」感が出るから。(ぶっちゃけてしまうと、もちろんバッファは積んでるので17時をデッドラインに設定する根拠はどこにもなかったりする。あー、言っちゃった。)

定時とかにせずに中途半端な時間にしておくと、より一層、何か事情がある感が増す。

無視すると後で面倒である旨を示す方法

これはケースバイケースだけど、基本的に面倒くささを想起させる文節を単体で強調する。

こんな感じ。
もし、今回の期日までにご対応いただけない場合、後程ご自身で〇〇していただく必要がございます。」

まとめ

今回は忙しすぎる人を対象にどうすれば読んでもらえるのかを考察した。
でもこれは相手がそれほど忙しくなくても使えるテクニックだと思う。基本的に、だらだら書かれたメールをきちんと読むのって面倒くさい。「無視して良いか」を速やかに判定できるメールは誰にとっても好ましいことである。

私のやり方が参考になるかどうかは分からないけど、各自工夫されると良いと思う。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。