現在日時からの相対月計算を行うAppleScriptです。
# 実戦投入してみたらマイナス月で不具合が出たので修正しました
カレンダー計算を行うのは、たいてい「日」単位です。
相対日では、今日(day ±0)、昨日(day -1)、一昨日(day -2)、翌一昨日(day -3)、明日(day +1)、明後日(day +2)、明々後日(day +3)など英語に翻訳できない、日本語特有の相対日指定の予約語を処理すべく、「日」ベースのカレンダー計算についてはやたらとAppleScriptで強力なルーチンを作りためてきました。
たまたま、「月」単位の相対計算を行う必要があった(ストック分に存在していなかった)ので、作ることになりました。たいがいの基礎ルーチンは作り置きがあるのですが、本当にこうして基礎ルーチンを作ったのは久しぶりです。
本当は固定で「現在月-2」の計算だけができればよかったのですが、±100か月ぐらいの範囲で動作確認したルーチンを作り置きしておきたいところです。気乗りしなかったので、ChatGPTに問い合わせてみたら、monthを秒で表現するタイプのルーチンを提案してきて、しかも内容が間違っていたので(構文確認を通過しないような内容を提案してくださります)、しぶしぶ作ってみました。ChatGPTが提案してきた作り方だと、おおよそ概算で500か月ぐらいの計算を行うと月単位の誤差が出てしまいます。
ChatGPTが「AppleScriptでrepeatループに0は指定できないよ?」とかの嘘を山のように出力してくるので、本当に初心者が使うと目が回るはずです(配列の要素が1から始まるのを「repeatで指定できない」という謎の過学習を発生させたもよう。ユーザーが間違ったフィードバックをやらかしている???)。
幾度かのアップデートを経て、完成しました。完成したといってよいのか? なんか無駄な処理が異様に多いような気もするのですが、結果が正しいのでよしとしましょう。
Cocoaの自然言語系の処理を用いて楽をして相対日付を処理しようかと試してみたものの、「本日」をtodayとしてピックアップしてしまうので、「本日から2か月前」といった認識ができませんでした。「現在のカレンダー」云々というのはCocoaの機能を使って処理しようとしていた頃のなごりです。
AppleScript名:相対年月を計算(月の相対指定)v4.scpt |
use AppleScript use scripting additions use framework "Foundation" repeat with i from 1 to 12 set curDate to getDateInternational(2024, i, 1) of me log curDate set {yNum1, mNum1} to retYMbyRelativeMonth(curDate, -2) of me log {yNum1, mNum1} end repeat on retYMbyRelativeMonth(curDate, relativeMonth) set yNum to (year of curDate) as number set mNum to (month of curDate) as integer set dNum to day of curDate if relativeMonth = 0 then return {yNum, mNum} set mDiff to (mNum + relativeMonth) if mDiff ≤ 0 then –log {"case 1"} set yDiv to (mDiff) div 12 set mNum to 12 + ((mDiff) mod 12) set yNum to yNum + (yDiv) – 1 else if mDiff > 12 then –log {"case 2"} set yDiv to (mDiff – 1) div 12 set mNum to ((mDiff – 1) mod 12) + 1 set yNum to yNum + yDiv else –log {"case 3"} set mNum to mDiff end if return {yNum, mNum} end retYMbyRelativeMonth –現在のカレンダーで指定年月のdate objectを返す on getDateInternational(aYear, aMonth, aDay) set theNSCalendar to current application’s NSCalendar’s currentCalendar() set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:0 minute:0 |second|:0 nanosecond:0 return theDate as date end getDateInternational |