Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: Calendar

Numbersで選択範囲のdateの年を+1する

Posted on 3月 10 by Takaaki Naganoya

Numbersの書類上で選択中のセル内の日付形式データがあったら、yearを+1(インクリメント)するAppleScriptです。macOS 15.4beta+Numbers 14.3で動作確認していますが、OSバージョンおよびNumbersのバージョンに依存する部分はありません。

実行前

実行後

ものすごくつまらなくて、ものすごく用途が狭いScriptですが、実際に書いてみたら意外と手間取る感じでした。

当然、+1するだけではなくー1する機能も作ってあり、呼び出し部分を差し替えればー1するようになります。macOS標準搭載のスクリプトメニューに入れて実行することを想定しています。

AppleScript名:選択範囲のdateの年を+1する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/03/10
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

tell application "Numbers"
  tell front document
    tell active sheet
      try
        set theTable to first table whose class of selection range is range
        
set cellList to cell of selection range of theTable
        
set valList to value of cell of selection range of theTable
        
set aLen to length of cellList
      on error
        display notification "Numbers: There is no selection"
        
return
      end try
      
      
set bValList to {}
      
repeat with i in valList
        set j to contents of i
        
set aClass to class of j
        
        
if aClass = date then
          set j to incremenetYearOf(j) of me
        end if
        
        
set the end of bValList to j
      end repeat
      
      
      
repeat with i from 1 to aLen
        set aVal to contents of item i of bValList
        
set aCell to contents of item i of cellList
        
        
ignoring application responses
          set value of aCell to aVal
        end ignoring
      end repeat
      
    end tell
  end tell
end tell

on incremenetYearOf(aDate)
  set {bYear, bMonth, bDate} to {year of aDate, month of aDate, day of aDate}
  
set bYear to bYear + 1
  
set {year of aDate, month of aDate, day of aDate} to {bYear, bMonth, bDate}
  
return aDate
end incremenetYearOf

on decremenetYearOf(aDate)
  set {bYear, bMonth, bDate} to {year of aDate, month of aDate, day of aDate}
  
set bYear to bYear – 1
  
set {year of aDate, month of aDate, day of aDate} to {bYear, bMonth, bDate}
  
return aDate
end decremenetYearOf

★Click Here to Open This Script 

Posted in Calendar list | Tagged 13.0savvy 14.0savvy 15.0savvy Numbers | Leave a comment

iCalendarファイルの作成

Posted on 9月 20, 2024 by Takaaki Naganoya

オープンソースのプロジェクト「iCal4ObjC」をFramework化してAppleScriptから呼び出し、iCalendarファイル(.ics)をデスクトップに作成するテストコードです。実行には、iCalendarKit.framework(macOS 10.15以降用にUniversal Binaryでビルド)を必要とします。また、macOS標準搭載のスクリプトエディタ上では動作せず、Script DebuggerないしSDから書き出したEnhanced Appletとして動かす必要があります。

–> DownloadiCalendarKit.framework (To ~/Library/Frameworks)

本Scriptの実行結果です。

AppleScript名:iCal4ObjCのじっけん(iCalendarファイルの作成).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2024/09/20
—
–  Copyright © 2024 Piyomaru Software, All Rights Reserved
—

use AppleScript
use framework "Foundation"
use framework "iCalendarKit" –https://github.com/cybergarage/iCal4ObjC
use scripting additions

set ical to current application’s CGICalendar’s alloc()’s init()

–Add an object
set icalObj to current application’s CGICalendarObject’s alloc()’s initWithProdid:"//CyberGarage//iCal4ObjC//EN"

–Add a component
set icalComp to current application’s CGICalendarComponent’s alloc()’s initWithType:"VTODO"
icalObj’s addComponent:icalComp

ical’s addObject:icalObj

— Add a property
set icalProp to current application’s CGICalendarProperty’s alloc()’s init()
icalProp’s setName:"SUMMARY"
icalProp’s setValue:"Write report"
icalComp’s addComponent:icalProp

set outPath to POSIX path of (path to desktop) & (do shell script "uuidgen") & ".ics"

ical’s writeToFile:outPath

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.15savvy 11.0savvy 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

国民の祝日を求める v7

Posted on 7月 17, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2020〜2021年のイレギュラーな休日変更に対応してみました。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

こうした高度なカレンダー計算AppleScriptは、「Newt On Project」の直系の遺産です。自然言語で曜日や日数の指定を行う上で、休日の計算は欠かせないものでした。

休日に関しては政府がCSVファイルで配布しているものもありますが、来年や再来年、5年後のカレンダーを扱いたいといった場合に、自前で計算できないと話になりません。そうした処理に備える意味でも、自前で計算できる「意義」はあります。

AppleScript名:国民の祝日を求める v7.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
東京オリンピック(2020/2021年)関連の超イレギュラーな休日調整に対応

*)

set aList to retHolidayRec(2020) of me
–> {{date "2020年1月1日 水曜日 0:00:00", "元旦"}, {date "2020年1月13日 月曜日 0:00:00", "成人の日"}, {date "2020年2月11日 火曜日 0:00:00", "建国記念日"}, {date "2020年2月23日 日曜日 0:00:00", "天皇誕生日"}, {date "2020年2月24日 月曜日 0:00:00", "振替休日"}, {date "2020年3月20日 金曜日 0:00:00", "春分の日"}, {date "2020年5月3日 日曜日 0:00:00", "憲法記念日"}, {date "2020年5月4日 月曜日 0:00:00", "みどりの日"}, {date "2020年5月5日 火曜日 0:00:00", "こどもの日"}, {date "2020年5月6日 水曜日 0:00:00", "振替休日"}, {date "2020年7月22日 水曜日 0:00:00", "海の日"}, {date "2020年7月24日 金曜日 0:00:00", "スポーツの日"}, {date "2020年8月10日 月曜日 0:00:00", "山の日"}, {date "2020年9月21日 月曜日 0:00:00", "敬老の日"}, {date "2020年9月22日 火曜日 0:00:00", "秋分の日"}, {date "2020年11月3日 火曜日 0:00:00", "文化の日"}, {date "2020年11月23日 月曜日 0:00:00", "勤労感謝の日"}}

–国民の祝日を求める(属性つき)
on retHolidayRec(aYear as integer)
  –固定の祝日
  
if aYear < 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else if aYear = 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else if aYear = 2021 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
    
  else
    –2022年から
    
set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  end if
  
  
  
–天皇誕生日の計算
  
set curEmpBD to getCurEmpBitrthday(aYear) of me
  
if curEmpBD is not equal to false then
    set the end of holidayList to {curEmpBD, "天皇誕生日"}
  end if
  
  
set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"7/ 22", "海の日"}
  else if aYear = 2021 then
    set the end of holidayList to {"7/ 23", "海の日"}
  else
    set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日
  end if
  
  
  
set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"7/24", "スポーツの日"}
  else if aYear = 2021 then
    set the end of holidayList to {"7/23", "スポーツの日"}
    
  else if (aYear < 2000) then
    set the end of holidayList to {"10/10", "体育の日"} –体育の日–10月10日
  else if (aYear < 2020) then
    set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "体育の日"} –体育の日–10月の第2月曜日
  else
    set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –スポーツの日–10月の第2月曜日
  end if
  
  
–令和2年(2020年)及び3年(2021年)における「国民の祝日」の移動について
  
if aYear = 2020 then
    set the end of holidayList to {"8/10", "山の日"}
  else if aYear = 2021 then
    set the end of holidayList to {"8/8", "山の日"}
  else if aYear ≥ 2016 then
    set the end of holidayList to {"8/11", "山の日"} –山の日– 8月11日
  end if
  
  
  
set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日
  
set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日
  
  
  
  
set holiDate to {}
  
repeat with i in holidayList
    set holiD to date (aYear & "/" & (item 1 of i) as text)
    
set holiNum to weekday of holiD as number
    
    
–元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗)
    
–if ((item 1 of i) as text) is not "1/1" then
    
–振替休日付加処理
    
if holiNum = 1 then –祝祭日が日曜日だったら
      –日付を動かすのではなく、振替休日を追加する
      
set holiD_furikae to holiD + (1 * days)
      
set the end of holiDate to {holiD_furikae, "振替休日"}
    end if
    
–end if
    
set the end of holiDate to {holiD, item 2 of i}
    
  end repeat
  
  
  
–重複した休日が発生した場合の再振替処理  
  
–基本ルール:  振替休日を後に送る
  
–        「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う
  
set itemNum to 1
  
set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me
  
  
set huriList to {}
  
repeat with i in holiDateDup
    set iCount to length of i
    
repeat with ii in i
      set {aDate, aDateName} to ii
      
if aDateName = "振替休日" then
        set the end of huriList to contents of ii
      end if
    end repeat
  end repeat
  
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me
  
  
repeat with i in huriList
    set {aDate, aName} to i
    
set j to contents of i
    
set offsetDate to 1
    
repeat
      set bDate to aDate + (offsetDate * days)
      
if bDate is not in holiDateList then
        exit repeat
      end if
      
set offsetDate to offsetDate + 1
    end repeat
    
    
set iCount to 1
    
repeat with ii in holiDate
      set jj to contents of ii
      
if jj = j then
        –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない
        
set holiDate to itemsDelete(holiDate, {iCount}) of me
      end if
      
set iCount to iCount + 1
    end repeat
    
    
set the end of holiDate to {bDate, "振替休日"}
    
  end repeat
  
  
  
–秋分の日と敬老の日の「間の日」の休日判定処理
  
–参考文献:
  
–http://ja.wikipedia.org/wiki/秋分の日
  
–国民の祝日に関する法律第3条第3項に規定する休日(例)
  
set septDL to {}
  
set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日
  
set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日
  
set septDL to shellSort(septDL) of me
  
if septDL = {"9/21", "9/23"} then
    set kokuminShukujitu to (aYear as string) & "/9/22"
    
set kokuminShukujitu to date kokuminShukujitu
    
set the end of holiDate to {kokuminShukujitu, "国民の祝日"}
  end if
  
  
  
–重複を解消
  
set holiDate to removeDuplicates(holiDate) of me
  
  
–最後に、並べ替えを行って仕上げ
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
  
return holiDate
end retHolidayRec

–春分の日を求める
–2000年から2099年の間まで計算可能
on get_ShunbunNoHi(aYear)
  set a to 20.69115
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShunbunNoHi

–秋分の日を求める
–2000年から2099年の間まで計算可能
on get_ShuubunNoHi(aYear)
  set a to 23.09
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShuubunNoHi

–指定月の第x指定曜日に該当する日付を求める(mm/dd形式)
– 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。
– 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた
–パラメータ: 年, 月, 曜日番号, 順番
on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer)
  set sDat to date ((aYear & "/" & aMonth & "/1") as text)
  
set eDat to getMlenInternational(aYear, aMonth) of me
  
  
set countNum to 0
  
  
repeat with i from 1 to eDat
    set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text)
    
set aWeekDayNum to weekday of aCal as integer
    
if Youbi = aWeekDayNum then
      set countNum to countNum + 1
      
if countNum is orderNum then
        set aCalText to (aMonth & "/" & i as text)
        
return aCalText
      end if
    end if
  end repeat
end get_specifiedDay

–指定日の月のみ返す
on getMonth(aDat as date)
  set bDate to month of aDat
  
return bDate as integer
end getMonth

–指定日の日付のみ返す
on getDate(aDat as date)
  set bDate to day of aDat
  
return bDate as integer
end getDate

–指定日の年のみ返す
on getYear(aDat as date)
  set bDate to year of aDat
  
return bDate as integer
end getYear

–現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた)
on getMlenInternational(aYear, aMonth)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlenInternational

–リスト中から重複項目をリストアップする
on detectDuplicates(aList)
  set aCount to length of aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
if anItem is in aList then
      set the end of duplicationList to anItem
    end if
  end repeat
  
  
return duplicationList
end detectDuplicates

–リストから重複部分を除外
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–シェルソート
on shellSort(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap ≤ len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len – 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1)
          
set j to j – gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

–入れ子のリスト中から重複項目をアイテム番号つきでリストアップする
on detectDuplicatesFromNestedList(aList, itemNum)
  set aCount to length of aList
  
copy aList to orig_aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
    
–指定アイテムだけのリストを毎回再生成して存在確認を行う
    
set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me
    
if (contents of (item itemNum of anItem)) is in aaList then
      set the end of duplicationList to anItem
    end if
    
  end repeat
  
  
–検出した重複データを元に、該当するデータをリストアップ
  
set detectList to {}
  
repeat with i in duplicationList
    set j to contents of (item itemNum of i)
    
set detectItem to {}
    
repeat with ii in orig_aList
      set jj to contents of (item itemNum of ii)
      
      
if jj = j then
        set the end of detectItem to (contents of ii)
      end if
    end repeat
    
set the end of detectList to detectItem
  end repeat
  
  
return detectList
end detectDuplicatesFromNestedList

–入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す
on spritOrderedItemFromNestedList(aList, itemNum)
  set aaList to {}
  
repeat with i in aList
    set the end of aaList to contents of (item itemNum of i)
  end repeat
  
return aaList
end spritOrderedItemFromNestedList

–リスト中の指定要素を削除して返す
on itemsDelete(aList, delNumList)
  set delLen to length of delNumList
  
  
repeat with i from 1 to delLen
    
    
set newList to {}
    
set aLen to length of aList
    
    
set ii to item i of delNumList
    
    
if ii = 1 then
      set maeList to items 2 thru aLen of aList
      
set newList to maeList
      
    else if ii = aLen then
      set maeList to items 1 thru (aLen – 1) of aList
      
set newList to maeList
      
    else
      set maeList to items 1 thru (ii – 1) of aList
      
set atoList to items (ii + 1) thru -1 of aList
      
set newList to maeList & atoList
    end if
    
    
–アイテム指定の補正
    
set delNumList to adjustItemNo(ii, delNumList) of me
    
    
set aList to newList
  end repeat
  
  
return newList
end itemsDelete

–itemsDeleteのサブルーチン
–リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が
–狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う
–paramNumとelemListの間でのパラメータの衝突は関知しない

–項目要素補正をリストに対して行う
on adjustItemNo(paramNum, elemList)
  –項目ゼロを指定してきた場合には、そのままelemListを戻す
  
if paramNum = 0 then return elemList
  
–プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない
  
set retList to {}
  
repeat with i in elemList
    set j to contents of i
    
if j > paramNum then
      set ansNum to j – 1
    else
      set ansNum to j
    end if
    
set the end of retList to ansNum
  end repeat
  
  
return retList
end adjustItemNo

–現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒)
on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternationalYMDhms

–現在のカレンダーで指定年月の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

–天皇誕生日の計算
on getCurEmpBitrthday(targYear)
  –昭和、平成、令和 の誕生日定義
  
set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}}
  
–浩宮氏が崩御の際には、崩御年を記入
  
  
set hitF to false
  
repeat with i in curEmperrorsBirthday
    copy i to {targDate, {beginYear, endYear}}
    
if targYear ≥ beginYear and targYear ≤ endYear then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then
    return false
  end if
  
  
return targDate
end getCurEmpBitrthday

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.15savvy 11.0savvy 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

国民の祝日を求める v6

Posted on 7月 5, 2024 by Takaaki Naganoya

日本のカレンダーにおける国民の祝日(休日)を、dateオブジェクトのリストで求めるAppleScriptです。

2018年に改元(平成→令和)、2020年に予定されていた東京オリンピックがCOVID-19の影響を受けて2021年に順延し、そのあたりで休日・祝日運用がイレギュラーになりまくって心が折れてアップデートしていませんでしたが、ようやく本ルーチンを最新の状況に合わせてアップデートしました。

2020〜2021年の計算については本ルーチンでは合わなくなる可能性がありますが、その点を理解したうえでご利用ください。一応、今年(2024年)の休日については、内閣府のホームページに掲載されているカレンダーと付け合わせを行なって、処理内容を確認してあります。

# 何か不具合や不足分があったらおしらせください
# 某・呪われた手帳メーカー系の仕事には不十分かもしれないので、不幸にもその案件に関わった方はメーカー支給の祝祭日データを利用してください

AppleScript名:国民の祝日を求める v6.scpt
use AppleScript
use scripting additions
use framework "Foundation"

(*

本バージョンのテーマ:
令和への改元にともない、天皇が交代。天皇誕生日を変更。祝日関連を最新アップデート
東京オリンピック(2021年)関連の超イレギュラーな休日調整は考慮していない

*)

set aList to retHolidayRec(2024) of me
–> {{date "2024年1月1日 月曜日 0:00:00", "元旦"}, {date "2024年1月8日 月曜日 0:00:00", "成人の日"}, {date "2024年2月11日 日曜日 0:00:00", "建国記念日"}, {date "2024年2月12日 月曜日 0:00:00", "振替休日"}, {date "2024年2月23日 金曜日 0:00:00", "天皇誕生日"}, {date "2024年3月20日 水曜日 0:00:00", "春分の日"}, {date "2024年5月3日 金曜日 0:00:00", "憲法記念日"}, {date "2024年5月4日 土曜日 0:00:00", "みどりの日"}, {date "2024年5月5日 日曜日 0:00:00", "こどもの日"}, {date "2024年5月6日 月曜日 0:00:00", "振替休日"}, {date "2024年7月15日 月曜日 0:00:00", "海の日"}, {date "2024年8月11日 日曜日 0:00:00", "山の日"}, {date "2024年8月12日 月曜日 0:00:00", "振替休日"}, {date "2024年9月16日 月曜日 0:00:00", "敬老の日"}, {date "2024年9月22日 日曜日 0:00:00", "秋分の日"}, {date "2024年9月23日 月曜日 0:00:00", "振替休日"}, {date "2024年10月14日 月曜日 0:00:00", "スポーツの日"}, {date "2024年11月3日 日曜日 0:00:00", "文化の日"}, {date "2024年11月4日 月曜日 0:00:00", "振替休日"}, {date "2024年11月23日 土曜日 0:00:00", "勤労感謝の日"}}

–国民の祝日を求める(属性つき)
on retHolidayRec(aYear as text)
  –固定の祝日
  
if aYear < 2020 then
    set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"4/29", "昭和の日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  else
    –2020年から
    
set holidayList to {{"1/1", "元旦"}, {"2/11", "建国記念日"}, {"5/3", "憲法記念日"}, {"5/4", "みどりの日"}, {"5/5", "こどもの日"}, {"8/11", "山の日"}, {"11/3", "文化の日"}, {"11/23", "勤労感謝の日"}}
  end if
  
  
  
–天皇誕生日の計算
  
set curEmpBD to getCurEmpBitrthday(aYear) of me
  
if curEmpBD is not equal to false then
    set the end of holidayList to {curEmpBD, "天皇誕生日"}
  end if
  
  
set the end of holidayList to {get_specifiedDay(aYear, 1, 2, 2), "成人の日"} –成人の日–1月の第2月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 7, 2, 3), "海の日"} –海の日–7月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 9, 2, 3), "敬老の日"} –敬老の日–9月の第3月曜日
  
set the end of holidayList to {get_specifiedDay(aYear, 10, 2, 2), "スポーツの日"} –体育の日–10月の第2月曜日
  
set the end of holidayList to {"3/" & get_ShunbunNoHi(aYear), "春分の日"} –春分の日
  
set the end of holidayList to {"9/" & get_ShuubunNoHi(aYear), "秋分の日"} –秋分の日
  
  
  
set holiDate to {}
  
repeat with i in holidayList
    set holiD to date (aYear & "/" & (item 1 of i) as text)
    
set holiNum to weekday of holiD as number
    
    
–元日以外を対象とする(元旦に振替休日なし)–> いや、ある(汗)
    
–if ((item 1 of i) as text) is not "1/1" then
    
–振替休日付加処理
    
if holiNum = 1 then –祝祭日が日曜日だったら
      –日付を動かすのではなく、振替休日を追加する
      
set holiD_furikae to holiD + (1 * days)
      
set the end of holiDate to {holiD_furikae, "振替休日"}
    end if
    
–end if
    
set the end of holiDate to {holiD, item 2 of i}
    
  end repeat
  
  
  
–重複した休日が発生した場合の再振替処理  
  
–基本ルール:  振替休日を後に送る
  
–        「振替休日」が重複リストに入っていないかどうかをチェックし、振替休日の再配置を行う
  
set itemNum to 1
  
set holiDateDup to detectDuplicatesFromNestedList(holiDate, itemNum) of me
  
  
set huriList to {}
  
repeat with i in holiDateDup
    set iCount to length of i
    
repeat with ii in i
      set {aDate, aDateName} to ii
      
if aDateName = "振替休日" then
        set the end of huriList to contents of ii
      end if
    end repeat
  end repeat
  
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
set holiDateList to spritOrderedItemFromNestedList(holiDate, 1) of me
  
  
repeat with i in huriList
    set {aDate, aName} to i
    
set j to contents of i
    
set offsetDate to 1
    
repeat
      set bDate to aDate + (offsetDate * days)
      
if bDate is not in holiDateList then
        exit repeat
      end if
      
set offsetDate to offsetDate + 1
    end repeat
    
    
set iCount to 1
    
repeat with ii in holiDate
      set jj to contents of ii
      
if jj = j then
        –「複数要素一括削除サブルーチン」などという高機能すぎるサブルーチンを使用。ちょっともったいない
        
set holiDate to itemsDelete(holiDate, {iCount}) of me
      end if
      
set iCount to iCount + 1
    end repeat
    
    
set the end of holiDate to {bDate, "振替休日"}
    
  end repeat
  
  
  
–秋分の日と敬老の日の「間の日」の休日判定処理
  
–参考文献:
  
–http://ja.wikipedia.org/wiki/秋分の日
  
set septDL to {}
  
set the end of septDL to "9/" & get_ShuubunNoHi(aYear) of me –秋分の日
  
set the end of septDL to get_specifiedDay(aYear, 9, 2, 3) –敬老の日 –9月の第3月曜日
  
set septDL to shellSort(septDL) of me
  
if septDL = {"9/21", "9/23"} then
    set kokuminShukujitu to (aYear as string) & "/9/22"
    
set kokuminShukujitu to date kokuminShukujitu
    
set the end of holiDate to {kokuminShukujitu, "国民の祝日"}
  end if
  
  
–最後に、並べ替えを行って仕上げ
  
set holiDate to shellSortListAscending(holiDate, 1) of me
  
  
return holiDate
end retHolidayRec

–春分の日を求める
–2000年から2099年の間まで計算可能
on get_ShunbunNoHi(aYear)
  set a to 20.69115
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShunbunNoHi

–秋分の日を求める
–2000年から2099年の間まで計算可能
on get_ShuubunNoHi(aYear)
  set a to 23.09
  
set b to (aYear – 2000) * 0.2421904
  
set c to round ((aYear – 2000) / 4) rounding toward zero
  
set d to round (a + b – c) rounding toward zero
  
return d
end get_ShuubunNoHi

–指定月の第x指定曜日に該当する日付を求める(mm/dd形式)
– 曜日の指定を数値(weekday of (current date) as number)で行えるようにした。
– 曜日を「日曜日」などの日本語ローカライズド文字列で指定するのをやめた
–パラメータ: 年, 月, 曜日番号, 順番
on get_specifiedDay(aYear as integer, aMonth as integer, Youbi as integer, orderNum as integer)
  set sDat to date ((aYear & "/" & aMonth & "/1") as text)
  
set eDat to getMlenInternational(aYear, aMonth) of me
  
  
set countNum to 0
  
  
repeat with i from 1 to eDat
    set aCal to date ((aYear & "/" & aMonth & "/" & (i as text)) as text)
    
set aWeekDayNum to weekday of aCal as integer
    
if Youbi = aWeekDayNum then
      set countNum to countNum + 1
      
if countNum is orderNum then
        set aCalText to (aMonth & "/" & i as text)
        
return aCalText
      end if
    end if
  end repeat
end get_specifiedDay

–指定日の月のみ返す
on getMonth(aDat as date)
  set bDate to month of aDat
  
return bDate as integer
end getMonth

–指定日の日付のみ返す
on getDate(aDat as date)
  set bDate to day of aDat
  
return bDate as integer
end getDate

–指定日の年のみ返す
on getYear(aDat as date)
  set bDate to year of aDat
  
return bDate as integer
end getYear

–現在のカレンダーで指定年月の日数を返す(getMlenから置き換えた)
on getMlenInternational(aYear, aMonth)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlenInternational

–リスト中から重複項目をリストアップする
on detectDuplicates(aList)
  set aCount to length of aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
if anItem is in aList then
      set the end of duplicationList to anItem
    end if
  end repeat
  
  
return duplicationList
end detectDuplicates

–リストから重複部分を除外
on removeDuplicates(aList)
  set newList to {}
  
repeat with i from 1 to (length of aList)
    set anItem to item 1 of aList
    
set aList to rest of aList
    
if {anItem} is not in aList then set end of newList to anItem
  end repeat
  
return newList
end removeDuplicates

–シェルソート
on shellSort(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap ≤ len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len – 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j ≥ gap) and (oBj’s list’s item (j – gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j – gap + 1)
          
set j to j – gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) > (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDecending(a, keyItem)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h ≤ (n – 1)) then
      repeat with i from h to (n – 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j ≥ h) and ((contents of item keyItem of item (j – h + 1) of a) < (item keyItem of v))
          set (item (j + 1) of a) to (item (j – h + 1) of a)
          
set j to j – h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortListDecending

–入れ子のリスト中から重複項目をアイテム番号つきでリストアップする
on detectDuplicatesFromNestedList(aList, itemNum)
  set aCount to length of aList
  
copy aList to orig_aList
  
  
set duplicationList to {}
  
repeat aCount times
    set anItem to contents of (first item of aList)
    
set aList to rest of aList
    
    
–指定アイテムだけのリストを毎回再生成して存在確認を行う
    
set aaList to spritOrderedItemFromNestedList(aList, itemNum) of me
    
if (contents of (item itemNum of anItem)) is in aaList then
      set the end of duplicationList to anItem
    end if
    
  end repeat
  
  
–検出した重複データを元に、該当するデータをリストアップ
  
set detectList to {}
  
repeat with i in duplicationList
    set j to contents of (item itemNum of i)
    
set detectItem to {}
    
repeat with ii in orig_aList
      set jj to contents of (item itemNum of ii)
      
      
if jj = j then
        set the end of detectItem to (contents of ii)
      end if
    end repeat
    
set the end of detectList to detectItem
  end repeat
  
  
return detectList
end detectDuplicatesFromNestedList

–入れ子のリストの全要素から指定アイテム目の要素だけを取り出してリストで返す
on spritOrderedItemFromNestedList(aList, itemNum)
  set aaList to {}
  
repeat with i in aList
    set the end of aaList to contents of (item itemNum of i)
  end repeat
  
return aaList
end spritOrderedItemFromNestedList

–リスト中の指定要素を削除して返す
on itemsDelete(aList, delNumList)
  set delLen to length of delNumList
  
  
repeat with i from 1 to delLen
    
    
set newList to {}
    
set aLen to length of aList
    
    
set ii to item i of delNumList
    
    
if ii = 1 then
      set maeList to items 2 thru aLen of aList
      
set newList to maeList
      
    else if ii = aLen then
      set maeList to items 1 thru (aLen – 1) of aList
      
set newList to maeList
      
    else
      set maeList to items 1 thru (ii – 1) of aList
      
set atoList to items (ii + 1) thru -1 of aList
      
set newList to maeList & atoList
    end if
    
    
–アイテム指定の補正
    
set delNumList to adjustItemNo(ii, delNumList) of me
    
    
set aList to newList
  end repeat
  
  
return newList
end itemsDelete

–itemsDeleteのサブルーチン
–リストに対して複数アイテムの削除を行う場合に、1つ削除した後にはアイテム指定が
–狂ってしまうため、毎回削除するたびにアイテム指定の補正を行う
–paramNumとelemListの間でのパラメータの衝突は関知しない

–項目要素補正をリストに対して行う
on adjustItemNo(paramNum, elemList)
  –項目ゼロを指定してきた場合には、そのままelemListを戻す
  
if paramNum = 0 then return elemList
  
–プラス方向のレンジ外判定は行っていない。elemListのlengthよりも大きな値は関知しない
  
set retList to {}
  
repeat with i in elemList
    set j to contents of i
    
if j > paramNum then
      set ansNum to j – 1
    else
      set ansNum to j
    end if
    
set the end of retList to ansNum
  end repeat
  
  
return retList
end adjustItemNo

–現在のカレンダーで指定年月のdate objectを返す(年、月、日、時、分、秒)
on getDateInternationalYMDhms(aYear, aMonth, aDay, anHour, aMinute, aSecond)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternationalYMDhms

–現在のカレンダーで指定年月の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

–天皇誕生日の計算
on getCurEmpBitrthday(targYear)
  –昭和、平成、令和 の誕生日定義
  
set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}}
  
–浩宮氏が崩御の際には、崩御年を記入
  
  
set hitF to false
  
repeat with i in curEmperrorsBirthday
    copy i to {targDate, {beginYear, endYear}}
    
if targYear ≥ beginYear and targYear ≤ endYear then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then
    return false
  end if
  
  
return targDate
end getCurEmpBitrthday

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

相対年月を計算(月の相対指定)v4

Posted on 7月 5, 2024 by Takaaki Naganoya

現在日時からの相対月計算を行う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

★Click Here to Open This Script 

Posted in Calendar | Tagged 12.0savvy 13.0savvy 14.0savvy 15.0savvy | Leave a comment

Calendar.appのイベントにAppletを添付できる?

Posted on 9月 22, 2023 by Takaaki Naganoya

Calendar.appのイベント(予定)に、ファイルを添付できるようになっています。ここに、AppleScriptアプレットを添付して実行することで、タイマー実行アプリとしてCalendar.appを利用する、というスタイルが初期のMac OS Xで見られました。Mac OS X 10.6ぐらいまででしょうか。

その後、どうもCalendar.appのイベントにAppletを添付しても実行されなくなった、という話をよく聞くようになりました。

自分も、タイマー実行アプリとしてCalendar.appは「使えないもの」と判断し、自前でタイマー機能などを作り込んで使ってきました。確実さでいえば、これは実に確実ではあるものの、それを外部に提供するのかという話については、いい「落とし所」を見出せない感じではあります。

こちらもタダで出すつもりはないけれど、これどうせ国内外のScripter連中は買わないよね、というところで話が止まっているものでもあります。モノがモノなので、Mac App Storeにも出せないでしょう(あらかじめ、どういうアプリを操作する、という設定を行なって提出する必要があるので。事前にわかるわけがない)。

そんな中、X(旧称Twitter)上で「Calendar.appをAppleScript Appletのタイマー起動用に使っている」という話を聞きつけ、その内容について実際にやりとりをして、自分の手元でもできることを確認しました。

その時点での自分の理解では、「イベントにAppletを添付して、イベントのアクションで『添付ファイルをオープンする』という指定を行えばOK」だという話だと納得しました。なるほど、自分は使い方を誤解していたのか、と。

しかし、その翌日に「別のAppletも添付して試してみよう」ということで、別のイベントを作成して別のAppletを添付して「添付ファイルをオープン」させてみたものの、今度はイベントの指定時刻に通知は来るものの、添付ファイルであるAppletは起動されませんでした。

Calendar.app自体のバグなのか、それともイベントへのApplet添付・実行には何か気づかないパラメータが存在しているのか?(イベント登録時のアカウントであるとか、ローカルの通知だけにしておかないとダメとか) そのあたりがまったく不明です。

昨日、一昨日ぐらいの段階でCalendar.appへのAppletの添付・実行は2・3度成功していたので、なかなか謎なところがあるものです。

Posted in Bug Calendar | Tagged 13.0savvy Calendar | Leave a comment

AS関連データの取り扱いを容易にする(はずの)privateDataTypeLib

Posted on 8月 22, 2022 by Takaaki Naganoya

AS関連データの取り扱いを簡単にすることを目的に書き出した、privateDatatypeLibです。

プログラムとデータを分離して、データ記述部分を外部ファイル(設定ファイルなど)に追い出したときに、どのようなデータかを表現するための道具として試作してみました。

macOS上のデータ記述子は、

などのさまざまなデータ識別方法が存在していますが、どれも(自分の)用途には合わなかったので、検討・試作をはじめてみました。Predicatesが一番近いものの、不十分なのでいろんなデータ型や用途に拡張。あくまで自分用なので「public」などの宣言はとくに不必要と考え、縮小して処理を行なっています。

とくに、ファイルパスの処理なんて定型処理しかしないのに、わざわざ何かの表現を行う必要があるのはナンセンスですし、日付関連も割と余計な記述が多いように感じています。

また、緯度/経度のデータや座標データなども、もう少しなんとかならないかと思っています。

AppleScript名:privateDataTypeLib.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/08/22
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aData to "100"
set dType to "@number"
set bData to "" –不要なケース多数。不要な場合にはヌル文字を指定
set mePath to path to me –実際には、呼び出し側で取得して指定。ライブラリ自体のパスを求めているのはテスト実行時のみ
set aRes to getParameterAndCalc(aData, dType, bData, mePath) of me

on getParameterAndCalc(aData, dType, bData, mePath)
  if dType = "@string" then
    return normalizeByNFC(aData) of me
    
  else if dType = "@number" then
    set tmpA to zenToHan(aData) of charConvKit of me –全角→半角変換
    
set a to detectOutNumStr(tmpA) of me –数字+関連・意外の文字を除外
    
return normalizeByNFC(a) of me
    
  else if dType = "@filepath.sub.foldername" then
    set aClass to class of aData –aliasだったらPOSIX pathに変換。file…はどうなんだか
    
if aClass = alias then set aData to POSIX path of aData
    
return aData & bData & "/"
    
  else if dType = "@file.comment" then
    set aStr to getFinderComment(POSIX path of mePath) of me
    
return normalizeByNFC(aStr) of me
    
  else if dType = "@file.name" then
    set aClass to class of aData
    
if aClass = alias then set aData to POSIX path of aData
    
set aStr to (current application’s NSString’s stringWithString:aData)’s lastPathComponent()’s stringByDeletingPathExtension()
    
return normalizeByNFC(aStr as string) of me
    
  else if dType = "@date.month" then
    set curDate to current date
    
set curMonth to month of curDate as number
    
return curMonth as string
    
  else
    return aData
  end if
end getParameterAndCalc

on normalizeByNFC(aStr)
  set aNSStr to current application’s NSString’s stringWithString:aStr
  
set aNFC to aNSStr’s precomposedStringWithCanonicalMapping()
  
return aNFC as string
end normalizeByNFC

–ANK文字列以外のものをそぎ落とす
on detectOutNumStr(testStr)
  set sList to characters of testStr
  
set aStr to ""
  
  
repeat with i in sList
    if detectOutNumChar(i) of me then
      set aStr to aStr & (i as string)
    end if
  end repeat
  
  
return aStr
end detectOutNumStr

on detectOutNumChar(testText)
  –Numeric + Special char
  
set ankChar to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "-", "+", "E", ","}
  
  
set _testChar to testText as Unicode text
  
  
ignoring case
    repeat with i in _testChar
      set j to contents of i
      
if j is not in ankChar then
        return false
      end if
    end repeat
  end ignoring
  
  
return true
end detectOutNumChar

–Finderコメントを取得
on getFinderComment(aPOSIX)
  set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIX
  
set aMetaInfo to current application’s NSMetadataItem’s alloc()’s initWithURL:aURL
  
set metaDict to (aMetaInfo’s valuesForAttributes:{"kMDItemFinderComment"}) as record
  
if metaDict = {} then return ""
  
set aComment to kMDItemFinderComment of (metaDict)
  
return aComment
end getFinderComment

script charConvKit
  — Created 2017-09-06 by Shane Stanley
  
— Modified 2017-09-06 by Takaaki Naganoya
  
use AppleScript
  
use framework "Foundation"
  
property parent : AppleScript
  
  
property NSString : a reference to current application’s NSString
  
property NSStringTransformFullwidthToHalfwidth : a reference to current application’s NSStringTransformFullwidthToHalfwidth
  
property NSStringTransformHiraganaToKatakana : a reference to current application’s NSStringTransformHiraganaToKatakana
  
property NSStringTransformLatinToHiragana : a reference to current application’s NSStringTransformLatinToHiragana
  
property NSStringTransformLatinToKatakana : a reference to current application’s NSStringTransformLatinToKatakana
  
property NSStringTransformToUnicodeName : a reference to current application’s NSStringTransformToUnicodeName
  
property NSStringTransformToXMLHex : a reference to current application’s NSStringTransformToXMLHex
  
  
–半角→全角変換
  
on hanToZen(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:true) as string
  end hanToZen
  
  
–全角→半角変換
  
on zenToHan(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:false) as string
  end zenToHan
  
  
–ひらがな→カタカナ変換
  
on hiraganaToKatakana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:false) as string
  end hiraganaToKatakana
  
  
–カタカナ→ひらがな変換
  
on katakanaToHiraganaTo(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:true) as string
  end katakanaToHiraganaTo
  
  
–ローマ字→ひらがな変換
  
on alphabetToHiragana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:false) as string
  end alphabetToHiragana
  
  
–ひらがな→ローマ字変換
  
on hiraganaToalphabet(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:true) as string
  end hiraganaToalphabet
  
  
–ローマ字→カタカナ変換
  
on alphabetToKatakana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:false) as string
  end alphabetToKatakana
  
  
–カタカナ→ローマ字変換
  
on katakanaToAlphabet(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:true) as string
  end katakanaToAlphabet
  
  
–文字→Unicode Name変換
  
on characterToUnicodeName(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:false) as string
  end characterToUnicodeName
  
  
–Unicode Name→文字変換
  
on unicodeNameToCharacter(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:true) as string
  end unicodeNameToCharacter
  
  
–文字→XML Hex変換
  
on stringToXMLHex(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:false) as string
  end stringToXMLHex
  
  
–XML Hex→文字変換
  
on xmlHexTostring(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:true) as string
  end xmlHexTostring
end script

★Click Here to Open This Script 

Posted in Calendar file File path folder Library Number Text URL | Tagged 10.14savvy 10.15savvy 11.0savvy 12.0savvy | 1 Comment

経過時間文字列を秒数に変換する

Posted on 6月 7, 2021 by Takaaki Naganoya

Safariで表示中のダウンロード残り時間の文字列を数値に変換するために作成したAppleScriptです。

Safariで実行中のダウンロード残り時間の一覧情報を表インタフェースで表示する、ごくごく私的な用途のAppleScriptを書いていたときに必要になったものです。

Safariのダウンロード情報表示は、最近のバージョンではポップオーバーで行われるようになっています。そのポップオーバーの上に表インタフェースが配置されていて、その中の各行の中にあるUI Elementを追いかけるとダウンロード残り時間情報などが取得できます。


▲GUI Scriptingに欠かせない。PFiddlesoftのUI Browser。使わないのと使うのとでは、生産性が1億倍ぐらい違うツール

ただし、得られた「残り時間」の文字列をいったんdateオブジェクトに変換して、経過秒数を計算するのに少々骨が折れました。Safariのダウンロード一覧に表示される文字列はあくまでも「結果表示」で、0分とか0時間とか0秒といった桁は表示が省略されます。

当初、「HH時間mm分ss秒」という固定フォーマットだと勝手に思い込んで処理していました。ところが、各桁が省略されるパターンに遭遇。固定文字列でフォーマット指定をして評価しようとしても、いろいろと無理がありました。自然言語っぽい表記で経過時間の情報を与えられたときに、それを評価してdateオブジェクトに変換するのはけっこうな手間がかかります。

「時」「分」「秒」の各情報が存在するかどうかチェックし、各パターンに合わせた日付フォーマッター文字列を場合分けで書いてみたものの、前述のとおり途中の桁が省略されるパターンに遭遇。3つの桁情報の有無に対応するだけでも、3x2x1=6パターンに対応するif文が必要。さらに、「年」「月」「日」まで表示される可能性を考慮すると、桁要素が6個になるため、6x5x4x3x2x1=720パターンに対処するif文が必要に。この、if文で固定フォーマット文字列を場合分けで選択するというアプローチでは処理が破綻することがあきらかです(if文をそんなに書いたら見通しが悪くなって、矛盾した表記が混入してもわからないでしょう。そこはかとなく、頭が悪い感じもします)。

そこで、与えられた経過時間文字列を直接チェックして、「経過時間文字列に合うように」NSDateFormatter用の文字列を「動的に」組み立てるように方針を転換。これで、大幅に簡潔な記述で済むようになりました。

一番の誤算は、AppleScript上でNSDateのtimeIntervalSinceReferenceDate()の結果が正しく取得できなかったこと。このメソッドで2000年1月1日からの相対秒を取得するつもりだったのですが、期待したような結果は得られませんでした。そのため、2000年1月1日のdateオブジェクトを用意して引き算で経過秒数を求めました。このあたり、AFS(Apple File System)導入時にタイムスタンプの分解能が増やされたことに影響を受けているのだろうかと疑っています。

用途が果てしなく下らない割にてこずらされました。

AppleScript名:時分秒の文字列を数値に変換 v5
— Created 2021-06-06 by Takaaki Naganoya
— 2021 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set relSecNum to relativeSecondsFromStringWithDateFormat("22時間 21分 15秒") of me
–> 80475

set relSecNum to relativeSecondsFromStringWithDateFormat("22時間 15秒") of me
–> 79215

set relSecNum to relativeSecondsFromStringWithDateFormat("22時間") of me
–> 79200

on relativeSecondsFromStringWithDateFormat(postDateStr as string)
  set sepList to {"年", "月", "日", "時間", "分", "秒"}
  
set formList to {"yyyy", "MM", "dd", "HH", "mm", "ss"}
  
  
set formatStr to ""
  
repeat with i from 1 to 6
    set aSepStr to contents of item i of sepList
    
set aFormStr to contents of item i of formList
    
if postDateStr contains aSepStr then
      set formatStr to formatStr & (aFormStr) & (aSepStr)
    end if
  end repeat
  
  
set postDate to dateFromStringWithDateFormat(postDateStr, formatStr) of me
  
  
set origDate to dateFromStringWithDateFormat("0時間 0分 0秒", "HH時間mm分ss秒") of me
  
set aSeconds to postDate – origDate
  
return aSeconds
end relativeSecondsFromStringWithDateFormat

on dateFromStringWithDateFormat(dateString as string, dateFormat as string)
  set dStr to current application’s NSString’s stringWithString:dateString
  
set dateFormatStr to current application’s NSString’s stringWithString:dateFormat
  
set aDateFormatter to current application’s NSDateFormatter’s alloc()’s init()
  
aDateFormatter’s setDateFormat:dateFormatStr
  
aDateFormatter’s setLocale:(current application’s NSLocale’s alloc()’s initWithLocaleIdentifier:"en_US_POSIX")
  
set aDestDate to (aDateFormatter’s dateFromString:dStr)
  
return aDestDate as date
end dateFromStringWithDateFormat

★Click Here to Open This Script 

Posted in Calendar Text | Tagged 11.0savvy Safari | Leave a comment

1888年1月1日以前の日付が-1日になる問題の原因

Posted on 1月 23, 2020 by Takaaki Naganoya

Shane Stanleyにチャットで教えてもらっていました(お食事前に申し訳ない!) これは-1日になるほうが暦自体の仕様的に正しいようです。1888/1/1以前には「子午線」とかタイムゾーン(子午線ごとの標準時)という考え方自体が存在せず、1888/1/1に運用を開始した「日本標準時」に合わせてGMT +9になったため、この1888/1/1から前の日付はさかのぼって-1日される、と。

# 子午線のある現在の日付から子午線という概念がまだ存在しない日付をさかのぼった結果、日付が1日ズレた、と

現在施行されている「グレゴリオ暦」は、日本においても1873年(明治3年)に導入されました。この件については、よく知られていることです(自分の記憶があいまいで明治1年かと思っていました)。

ただ、この「グレゴリオ暦@Japan Ver.1」では子午線に関する決まりがなく、アメリカなどの国土が広い国では列車の時刻表などで問題になっていました。

その後、1884/10/11の国際子午線会議における子午線の制定を受け、日本標準時の運用が1888/1/1に開始になりました。この、1888/1/1の日本標準時の運用前にはタイムゾーンという考え方自体がなかったわけです。この、「グレゴリオ暦@Japan Ver.2」ともいえる暦法が1888/1/1に導入されたことにともない、時刻の変更とともに日付が変わったということである、と。

当時は子午線という概念そのものがなく、日本標準時が運用開始になったのが1888/1/1だということでようやく納得できました。

Classic Mac OSやMac OS X 10.6まではカレンダーの計算にこうした歴史的な経緯が加味されておらず、途中でポリシーが変更になったというのが真相のようです。累積誤差で日付が変わったんじゃなかったんですね、、、、

1582/10/15
ローマにてグレゴリオ暦 制定

1873/1/1(明治3年)
日本にてグレゴリオ暦に改暦。ただし、子午線の運用は各国間で規定がなかった。

1884/10/11
国際子午線会議

1888/1/1
日本標準時 運用開始

[おまけ] Cocoa自体のグレゴリオ暦の開始日を実際に取得してみました。

AppleScript名:gregorianStartDate.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/23
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set sDate to current application’s NSDateFormatter’s alloc()’s init()’s gregorianStartDate()
–> (NSDate) "1582-10-15 00:00:00 +0000"

set asDate to sDate as date
–> date "1582年10月15日 金曜日 9:18:59"

★Click Here to Open This Script 

Posted in Calendar History | 2 Comments

元号変換v42

Posted on 1月 23, 2020 by Takaaki Naganoya

西暦→和暦の元号を求めるAppleScriptの改修版です。

前バージョンでは、1868年から1887年までの計算が1日ズレるとか(dateオブジェクトの仕様)、明治の計算が合っていなかったので、その点を修正しました。

前バージョンまでは文字列で与えられた日付をいったんdateオブジェクトに変換していました。これは、”2020/11/31″といった正しくない日付が与えられた場合に、”2020/12/1″と妥当な解釈をし直すための処理でした。つまり、エラー対策のためだけにいったんdateオブジェクトに変換していたわけです。

これさえ行わなければ、とくにdateオブジェクトまわりの問題(before1887)は発生しません。dateオブジェクトとして解釈を行わず、単なる文字列を年、月、日に分解して、大小判定するだけの処理に置き換えました(11/31といったカレンダー的におかしな記述は無視)。

明治の計算についてもごにょごにょして修正してみました。

dateとしての年、月、日の妥当性チェックを行いたい場合には、月が1〜12、日が1〜31といった値の範囲チェックを追加で行うぐらいでしょうか。

AppleScript名:元号変換v42
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/23
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

set outList to {}
repeat with i from 1867 to 2021
  set aStr to (i as string) & "/1/25"
  
set {aGengoStr, aGengoNum} to retJapaneseGengo(aStr) of JGengoKit
  
set the end of outList to {i, aGengoStr, aGengoNum}
end repeat

return outList
–> {{1867, "(改暦前)", false}, {1868, "明治", 1}, {1869, "明治", 2}, {1870, "明治", 3}, {1871, "明治", 4}, {1872, "明治", 5}, {1873, "明治", 6}, {1874, "明治", 7}, {1875, "明治", 8}, {1876, "明治", 9}, {1877, "明治", 10}, {1878, "明治", 11}, {1879, "明治", 12}, {1880, "明治", 13}, {1881, "明治", 14}, {1882, "明治", 15}, {1883, "明治", 16}, {1884, "明治", 17}, {1885, "明治", 18}, {1886, "明治", 19}, {1887, "明治", 20}, {1888, "明治", 21}, {1889, "明治", 22}, {1890, "明治", 23}, {1891, "明治", 24}, {1892, "明治", 25}, {1893, "明治", 26}, {1894, "明治", 27}, {1895, "明治", 28}, {1896, "明治", 29}, {1897, "明治", 30}, {1898, "明治", 31}, {1899, "明治", 32}, {1900, "明治", 33}, {1901, "明治", 34}, {1902, "明治", 35}, {1903, "明治", 36}, {1904, "明治", 37}, {1905, "明治", 38}, {1906, "明治", 39}, {1907, "明治", 40}, {1908, "明治", 41}, {1909, "明治", 42}, {1910, "明治", 43}, {1911, "明治", 44}, {1912, "明治", 45}, {1913, "大正", 2}, {1914, "大正", 3}, {1915, "大正", 4}, {1916, "大正", 5}, {1917, "大正", 6}, {1918, "大正", 7}, {1919, "大正", 8}, {1920, "大正", 9}, {1921, "大正", 10}, {1922, "大正", 11}, {1923, "大正", 12}, {1924, "大正", 13}, {1925, "大正", 14}, {1926, "大正", 15}, {1927, "昭和", 2}, {1928, "昭和", 3}, {1929, "昭和", 4}, {1930, "昭和", 5}, {1931, "昭和", 6}, {1932, "昭和", 7}, {1933, "昭和", 8}, {1934, "昭和", 9}, {1935, "昭和", 10}, {1936, "昭和", 11}, {1937, "昭和", 12}, {1938, "昭和", 13}, {1939, "昭和", 14}, {1940, "昭和", 15}, {1941, "昭和", 16}, {1942, "昭和", 17}, {1943, "昭和", 18}, {1944, "昭和", 19}, {1945, "昭和", 20}, {1946, "昭和", 21}, {1947, "昭和", 22}, {1948, "昭和", 23}, {1949, "昭和", 24}, {1950, "昭和", 25}, {1951, "昭和", 26}, {1952, "昭和", 27}, {1953, "昭和", 28}, {1954, "昭和", 29}, {1955, "昭和", 30}, {1956, "昭和", 31}, {1957, "昭和", 32}, {1958, "昭和", 33}, {1959, "昭和", 34}, {1960, "昭和", 35}, {1961, "昭和", 36}, {1962, "昭和", 37}, {1963, "昭和", 38}, {1964, "昭和", 39}, {1965, "昭和", 40}, {1966, "昭和", 41}, {1967, "昭和", 42}, {1968, "昭和", 43}, {1969, "昭和", 44}, {1970, "昭和", 45}, {1971, "昭和", 46}, {1972, "昭和", 47}, {1973, "昭和", 48}, {1974, "昭和", 49}, {1975, "昭和", 50}, {1976, "昭和", 51}, {1977, "昭和", 52}, {1978, "昭和", 53}, {1979, "昭和", 54}, {1980, "昭和", 55}, {1981, "昭和", 56}, {1982, "昭和", 57}, {1983, "昭和", 58}, {1984, "昭和", 59}, {1985, "昭和", 60}, {1986, "昭和", 61}, {1987, "昭和", 62}, {1988, "昭和", 63}, {1989, "平成", 1}, {1990, "平成", 2}, {1991, "平成", 3}, {1992, "平成", 4}, {1993, "平成", 5}, {1994, "平成", 6}, {1995, "平成", 7}, {1996, "平成", 8}, {1997, "平成", 9}, {1998, "平成", 10}, {1999, "平成", 11}, {2000, "平成", 12}, {2001, "平成", 13}, {2002, "平成", 14}, {2003, "平成", 15}, {2004, "平成", 16}, {2005, "平成", 17}, {2006, "平成", 18}, {2007, "平成", 19}, {2008, "平成", 20}, {2009, "平成", 21}, {2010, "平成", 22}, {2011, "平成", 23}, {2012, "平成", 24}, {2013, "平成", 25}, {2014, "平成", 26}, {2015, "平成", 27}, {2016, "平成", 28}, {2017, "平成", 29}, {2018, "平成", 30}, {2019, "平成", 31}, {2020, "令和", 2}, {2021, "令和", 3}}

script JGengoKit
  on retJapaneseGengo(aDate as string)
    set dList to parseByDelim(aDate, "/") of me
    
if length of dList is not equal to 3 then error "Date Format Error"
    
    
copy dList to {aYear, aMonth, aDay}
    
    
tell current application
      
      
set aStr to retZeroPaddingText(aYear, 4) of me & retZeroPaddingText(aMonth, 2) of me & retZeroPaddingText(aDay, 2) of me
      
      
set aGengo to ""
      
if aStr ≥ "20190501" then
        set aGengo to "令和"
        
set aGengoNum to aYear – 2019 + 1
      else if aStr ≥ "19890108" then
        set aGengo to "平成"
        
set aGengoNum to aYear – 1989 + 1
      else if aStr ≥ "19261225" then
        set aGengo to "昭和"
        
set aGengoNum to aYear – 1926 + 1
      else if aStr ≥ "19120730" then
        set aGengo to "大正"
        
set aGengoNum to aYear – 1912 + 1
      else if aStr ≥ "18680125" then
        set aGengo to "明治"
        
if aYear = 1868 then
          set aGengoNum to 1
        else if (aYear ≥ 1869) or (aYear ≤ 1912) then
          set aGengoNum to aYear – 1867
        end if
      else
        –日本では明治以降に太陽暦を導入したのでそれ以前は意味がない?
        
set aGengo to "(改暦前)"
        
set aGengoNum to false
      end if
      
      
return {aGengo, aGengoNum}
    end tell
  end retJapaneseGengo
  
  
–数値にゼロパディングしたテキストを返す
  
on retZeroPaddingText(aNum, aLen)
    set tText to ("0000000000" & aNum as text)
    
set tCount to length of tText
    
set resText to text (tCount – aLen + 1) thru tCount of tText
    
return resText
  end retZeroPaddingText
  
  
on parseByDelim(aData, aDelim)
    set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to aDelim
    
set dList to text items of aData
    
set AppleScript’s text item delimiters to curDelim
    
return dList
  end parseByDelim
end script

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

1867/1/1〜1887/12/31までの範囲のdateオブジェクトからyear, month, dayを取り出すと-1日される?

Posted on 1月 22, 2020 by Takaaki Naganoya

過去のdateオブジェクトを検証する機会があり(明治の元号計算が合っていない件)、いろいろ検証していたところ、1867/1/1から1887/12/1までのdateオブジェクトからyear,month,dayを個別に取り出すと、dayが-1されるという現象が観測されました。

→ 本現象の発生メカニズムはこちら

西暦から和暦に変換する処理で、明治の年が合わないという(自分のプログラム由来の)問題を検討していました。この問題の洗い出しのため、「日」単位で順次和暦変換していたら、そのちょうど(明治→大正など)改元の境目の日付で(原因不明の)計算ミスが起こっていました。

1868/1/25からは明治時代、という判定は行えても、その1868/1/25というdateオブジェクトからYear, Month, Dayの各要素を取り出すと1868, 1, 24という結果に。

以前から「大昔の日付を扱うと問題がありそう」だとは思っていましたが、このように具体的な現象として観測したのは(個人的には)初めてでした(単に覚えていないだけかも)。

確認したのはmacOS 10.14.6とmacOS 10.15.3beta、macOS 10.13.6上ですが、どれも同じ結果になるようです。

# 追試で、OS X 10.7.5でも試してみたところ、同じ結果になりました
# OS X 10.6.8では確認されませんでした

未来のdateオブジェクトは2050年ぐらいまでチェックしてみたものの、問題はありませんでした。

過去に遡ってうるう日が設定されたとかいう話なんでしょうか? そういう話は聞いたことがないのですが、、、

日本においては太陽暦(グレゴリオ暦)を導入したのが明治元年(1868/1/25〜)なので、それ以前の日付をグレゴリオ歴で求めてもいまひとつ実用性がない(?) ともいえますが、少なくとも1868年から1887年までの間のdateオブジェクトから各種の値の取り出しが期待どおりに行われないのは問題といえるでしょう。

多分、もっと昔の日付も同様にdateオブジェクトからの値取り出しを行うと問題が出ると思われますが、前述のような理由からそこまでの日付を計算させることもないだろうかと。

# ふだん使っているgetMLenInternationalに問題があるのかと考えて、昔使っていたgetMLenを引っ張り出してきましたが、こちらはずいぶんと記法がお可愛らしい感じで、、、、

ただ、この件は気がつかなかっただけで、ずいぶん昔から存在している話なのかも????


▲Classic Mac OS 8.6では確認できませんでした(SheepShaver上で動作)この時代にはmonthをas numberで数値にcastできませんでした

AppleScript名:dateTest.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/22
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

script spd
  property mList : {}
end script

set (mList of spd) to {}
repeat with y from 1867 to 1888
  repeat with m from 1 to 12
    set aLen to getMlen(y, m) of me
    
repeat with d from 1 to aLen
      set aTmpStr to (y as string) & "/" & (m as string) & "/" & (d as string)
      
set aTmpD to date aTmpStr
      
      
set tmpY to year of aTmpD
      
set tmpM to month of aTmpD as number
      
set tmpD to day of aTmpD
      
      
if (tmpY is not equal to y) or (tmpM is not equal to m) or (tmpD is not equal to d) then
        set the end of (mList of spd) to {aTmpD, tmpY, tmpM, tmpD}
      end if
    end repeat
  end repeat
end repeat

return (mList of spd)
–> {{date "1867年1月1日 火曜日 0:00:00", 1866, 12, 31}, …… {date "1887年12月31日 土曜日 0:00:00", 1887, 12, 30}}

–現在のカレンダーで指定年月の日数を返す
on getMlenInternational(aYear as integer, aMonth as integer)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlenInternational

–指定年の指定月の日数を求める
on getMlen(aYear, aMonth)
  set aYear to aYear as number
  
set aMonth to aMonth as number
  
  
set aDat to (aYear as text) & "/" & (aMonth as text) & "/1"
  
if aMonth is 12 then
    set eDat to ((aYear + 1) as text) & "/" & (1 as text) & "/1"
  else
    set eDat to ((aYear as text) & "/" & (aMonth + 1) as text) & "/1"
  end if
  
  
set eDat to date eDat
  
set eDat to eDat – 1
  
  
set mLen to day of eDat
  
return mLen
end getMlen

★Click Here to Open This Script 

Posted in Calendar History | Tagged 10.13savvy 10.14savvy 10.15savvy | 1 Comment

自然言語テキストから複数の日付情報を抽出

Posted on 1月 21, 2020 by Takaaki Naganoya

自然言語テキストから日付の情報(複数可)を抽出するAppleScriptです。

URLやメールアドレスの抽出では、複数のデータをNSDataDetectorで抽出するAppleScriptは書いてありましたが、日付情報の抽出を行うものはなかったので、書いておきました。

AppleScript名:自然言語テキストから複数の日付情報(複数)を抽出して日付のリストを返す.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/21
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theDate to getDatesIn("本テキストには次の火曜日という日付情報を含んでいる。明日かもしれない。次の木曜日もそうだ。") of me
–> {date "2020年1月28日 火曜日 12:00:00", date "2020年1月22日 水曜日 12:00:00", date "2020年1月23日 木曜日 12:00:00"}

set theDate to getDatesIn("This text contains next Tuesday. The date may be tomorrow. Next Wednesday happen.") of me
–> {date "2020年1月28日 火曜日 12:00:00", date "2020年1月22日 水曜日 12:00:00", date "2020年1月29日 水曜日 12:00:00"}

on getDatesIn(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
set theDetector to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeDate) |error|:(missing value)
  
set theMatchs to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
if theMatchs = missing value then error "No date found with String:" & aString
  
set dRes to theMatchs’s valueForKeyPath:"date"
  
return dRes as list
end getDatesIn

★Click Here to Open This Script 

AppleScript名:自然言語テキストから複数の日付情報(複数)を抽出して日付と当該箇所のリストを返す v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/21
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theDate to getDatesAndOrigStringsIn("本テキストには次の火曜日という日付情報を含んでいる。明日かもしれない。次の木曜日もそうだ。") of me
–> {{originalStr:"次の火曜日", detectDate:date "2020年1月28日 火曜日 12:00:00"}, {originalStr:"明日", detectDate:date "2020年1月22日 水曜日 12:00:00"}, {originalStr:"次の木曜日", detectDate:date "2020年1月23日 木曜日 12:00:00"}}

set theDate to getDatesAndOrigStringsIn("This text contains next Tuesday. The date may be tomorrow. Next Wednesday happen.") of me
–> {{originalStr:"next Tuesday", detectDate:date "2020年1月28日 火曜日 12:00:00"}, {originalStr:"tomorrow", detectDate:date "2020年1月22日 水曜日 12:00:00"}, {originalStr:"Next Wednesday", detectDate:date "2020年1月29日 水曜日 12:00:00"}}

on getDatesAndOrigStringsIn(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
set theDetector to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeDate) |error|:(missing value)
  
set theMatchs to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
if theMatchs = missing value then error "No date found with String:" & aString
  
set dRes to (theMatchs’s valueForKeyPath:"date") as list
  
set rRes to (theMatchs’s valueForKeyPath:"range") as list
  
  
set allRes to {}
  
set aLen to length of dRes
  
repeat with i from 1 to aLen
    set aSubStr to (anNSString’s substringWithRange:(item i of rRes))
    
set dDate to contents of item i of dRes
    
set the end of allRes to {originalStr:aSubStr as string, detectDate:dDate}
  end repeat
  
  
return allRes
end getDatesAndOrigStringsIn

★Click Here to Open This Script 

Posted in Calendar Natural Language Processing | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSDataDetector NSString | Leave a comment

元号変換v41

Posted on 12月 19, 2019 by Takaaki Naganoya

西暦→元号の変換を行うAppleScriptです。誰もがこんな感じのプログラムを作って(書き捨てて)いると思うのですが、不思議と誰も公開しないという、、、

Japanese Gengo is only for Japanese calendar. It is old-style age names in Japanese. I don’t care of it for daily use.

AppleScript名:元号変換v41
set a to "2019/7/21"
set {aGengoStr, aGengoNum} to retJapaneseGengo(a) of JGengoKit
–> {"令和", 1}

script JGengoKit
  on retJapaneseGengo(aDate as string)
    set aDateObj to date aDate
    
    
tell current application
      set aYear to year of aDateObj
      
set aMonth to month of aDateObj as number
      
set aDay to day of aDateObj
      
      
set aStr to retZeroPaddingText(aYear, 4) of me & retZeroPaddingText(aMonth, 2) of me & retZeroPaddingText(aDay, 2) of me
      
      
set aGengo to ""
      
if aStr ≥ "20190501" then
        set aGengo to "令和"
        
set aGengoNum to aYear – 2019 + 1
      else if aStr ≥ "19890108" then
        set aGengo to "平成"
        
set aGengoNum to aYear – 1989 + 1
      else if aStr ≥ "19261225" then
        set aGengo to "昭和"
        
set aGengoNum to aYear – 1926 + 1
      else if aStr ≥ "19120730" then
        set aGengo to "大正"
        
set aGengoNum to aYear – 1912 + 1
      else if aStr ≥ "18680125" then
        set aGengo to "明治"
        
set aGengoNum to aYear – 1868 + 1
      end if
      
      
return {aGengo, aGengoNum}
    end tell
  end retJapaneseGengo
  
  
–数値にゼロパディングしたテキストを返す
  
on retZeroPaddingText(aNum, aLen)
    set tText to ("0000000000" & aNum as text)
    
set tCount to length of tText
    
set resText to text (tCount – aLen + 1) thru tCount of tText
    
return resText
  end retZeroPaddingText
end script

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy | 2 Comments

アラートダイアログ上にカレンダー1か月分を表示して選択 有効日指定あり v3a

Posted on 11月 13, 2019 by Takaaki Naganoya

アラートダイアログ上に指定月のカレンダー1か月分を表示してユーザーの日付選択(複数可)をもとめるAppleScriptです。

NSDatePickerを用いて日付選択のダイアログを作っていたら、日付がとても小さくて現代のMacの画面解像度にとても合っておらず、もっと大きめのカレンダーで選択したいと考えて作ったものです。

(1)カレンダーの表示サイズを大型化

現代のMacの画面にNSDatePickerをそのまま表示すると、とても小さくて見ていられないので、表示サイズを自由に変えられるカレンダー選択部品を作ってみました。どれか1つの日付を選択するものではなく、複数の日付を選択して、リストで結果を返してきます。

▲画面上で見やすいようにカレンダー部品を大型化

(2)選択無効日指定

あらかじめ、日付選択の対象外とする日付を指定できるようにしました。

(3)過去日付の選択無効

過去の日付を表示・選択できないようにする指定です。

▲過去日付を無効に指定すると、現在日時よりも過去の日付は選択できない

(4)曜日を現在のLocale情報から変更

曜日に用いる文字列を現在のLocale情報から取得するようにしてみました。本来であれば、日曜日はじまり「ではない」カレンダーを用いている場所もあるので(香港とかフランスとか?)、どの曜日から表示開始するかを自由選択にしたいところですが、そこまでは作り込んでいません。

▲Localeに合わせた曜日が表示される(ただし、年/月は固定フォーマット)

実用性を考えれば、Calendar.appのように日付選択とスケジュール確認が行えるものが望ましいのですが、そこまで作り込む手間をかけるほどのものでもないですし、表示対象月の移動を行おうとしてclickイベントを拾いつつカレンダー再描画に失敗した「残骸」です。見た目はいいのに完成度がいまひとつというあたりが残念です。

AppleScript名:アラートダイアログ上にカレンダー1か月分を表示して選択 有効日指定あり v3a
— Created 2019-10-15 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSFont : a reference to current application’s NSFont
property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSColor : a reference to current application’s NSColor
property NSButton : a reference to current application’s NSButton
property NSOnState : a reference to current application’s NSOnState
property NSOffState : a reference to current application’s NSOffState
property NSTextField : a reference to current application’s NSTextField
property NSMutableArray : a reference to current application’s NSMutableArray
property NSButtonTypeOnOff : a reference to current application’s NSButtonTypeOnOff
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSKernAttributeName : a reference to current application’s NSKernAttributeName
property NSMutableParagraphStyle : a reference to current application’s NSMutableParagraphStyle
property NSLigatureAttributeName : a reference to current application’s NSLigatureAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSUnderlineStyleAttributeName : a reference to current application’s NSUnderlineStyleAttributeName
property NSParagraphStyleAttributeName : a reference to current application’s NSParagraphStyleAttributeName
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

property theResult : 0
property returnCode : 0
property bArray : {} –Checkbox button object array

property tYear : 0
property tMonth : 0
property theAlert : missing value
property theView : missing value

on run
  set tYear to 2019
  
set tMonth to 11
  
set ignoreP to true
  
set ndList to {2, 3, 4, 9, 10, 13, 16, 17, 23, 24, 25, 30} –選択表示させない日付
  
set paramObj to {myMessage:"", mySubMessage:"適切な日付を以下からえらんでください", targetYear:tYear, targetMonth:tMonth, nodisp:ndList, ignorePast:ignoreP}
  
  
–Detect Past Date Error
  
set curDate to current date
  
set dLimit to getMlen(tYear, tMonth) of me
  
set tmpDate to getDateInternational(tYear, tMonth, dLimit) of me
  
if (tmpDate < curDate) and (ignoreP = true) then error (("Target month is past (" & tYear as string) & "/" & tMonth as string) & "/1 is past. Today is " & date string of curDate & ")"
  
  
–my chooseItemByCheckBox:paramObj –for Debugging
  
my performSelectorOnMainThread:"chooseItemByCheckBox:" withObject:(paramObj) waitUntilDone:true
  
return my sort1DNumList:(my theResult) ascOrder:true
  
–> {1, 3, 5, 7, 9, 11}
end run

on chooseItemByCheckBox:(paramObj)
  set aMainMes to (myMessage of paramObj) as string
  
set aSubMes to (mySubMessage of paramObj) as string
  
set aMatList to (matrixTitleList of paramObj) as list
  
set tYear to (targetYear of paramObj) as integer
  
set tMonth to (targetMonth of paramObj) as integer
  
set noDispList to (nodisp of paramObj) as list
  
set ignoreP to (ignorePast of paramObj) as boolean
  
  
if aMainMes = "" then
    set aMainMes to "日付を選択してください"
  end if
  
  
set theView to makeCalendarView(tYear, tMonth, noDispList, ignoreP) of me
  
  
–Select the first radio button item
  
set my theResult to {}
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:theView
    
    
–for Help Button
    
–its setShowsHelp:(true)
    
its setDelegate:(me)
    
set aWin to its |window|()
  end tell
  
  
aWin’s setLevel:(current application’s NSFloatingWindowLevel)
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
end chooseItemByCheckBox:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on clicked:aParam
  set aTag to (tag of aParam) as integer
  
  
–clicked
  
if aTag is not in (my theResult) then
    set the end of (my theResult) to aTag
  else
    set theResult to my deleteItem:aTag fromList:theResult
  end if
end clicked:

on deleteItem:anItem fromList:theList
  set theArray to NSMutableArray’s arrayWithArray:theList
  
theArray’s removeObject:anItem
  
return theArray as list
end deleteItem:fromList:

–1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DNumList:theList ascOrder:aBool
  tell current application’s NSSet to set theSet to setWithArray_(theList)
  
tell current application’s NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(missing value, true)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumList:ascOrder:

–Help Button Clicked Event Handler
on alertShowHelp:aNotification
  set aRes to display dialog "Do you change all checkbox state?" buttons {"All Off", "All On", "Cancel"} default button 3 with icon 1
  
set bRes to (button returned of aRes) as string
  
  
if bRes = "All Off" then
    set bLen to bArray’s |count|()
    
set theResult to {}
    
repeat with i from 0 to bLen
      ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOffState))
    end repeat
    
  else if bRes = "All On" then
    set bLen to bArray’s |count|()
    
set theResult to {}
    
repeat with i from 0 to bLen
      ((bArray’s objectAtIndex:i)’s setState:(current application’s NSOnState))
      
set the end of theResult to i + 1
    end repeat
  end if
  
  
return false –trueを返すと親ウィンドウ(アラートダイアログ)がクローズする
end alertShowHelp:

–書式つきテキストを組み立てる
on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aKerning as real, aLineSpacing as real)
  set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
  
set aKey1 to (NSFontAttributeName)
  
  
set aVal2 to NSColor’s blackColor()
  
set aKey2 to (NSForegroundColorAttributeName)
  
  
set aVal3 to aKerning
  
set akey3 to (NSKernAttributeName)
  
  
set aVal4 to 0
  
set akey4 to (NSUnderlineStyleAttributeName)
  
  
set aVal5 to 2 –all ligature ON
  
set akey5 to (NSLigatureAttributeName)
  
  
set aParagraphStyle to NSMutableParagraphStyle’s alloc()’s init()
  
aParagraphStyle’s setMinimumLineHeight:(aLineSpacing)
  
aParagraphStyle’s setMaximumLineHeight:(aLineSpacing)
  
set akey7 to (NSParagraphStyleAttributeName)
  
  
set keyList to {aKey1, aKey2, akey3, akey4, akey5, akey7}
  
set valList to {aVal1, aVal2, aVal3, aVal4, aVal5, aParagraphStyle}
  
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
  
  
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
  
return attrStr
end makeRTFfromParameters

on makeNSTextField(xPos as integer, yPos as integer, myWidth as integer, myHeight as integer, editableF as boolean, setVal as string, backgroundF as boolean, borderedF as boolean)
  set aNSString to NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(xPos, yPos, myWidth, myHeight))
  
aNSString’s setEditable:(editableF)
  
aNSString’s setStringValue:(setVal)
  
aNSString’s setDrawsBackground:(backgroundF)
  
aNSString’s setBordered:(borderedF)
  
return aNSString
end makeNSTextField

–指定月のカレンダーを1D List(7 days x 6 weeks) で返す
on retListCalendar(tYear, tMonth)
  set mLen to getMlen(tYear, tMonth) of me
  
set aList to {}
  
  
set fDat to getDateInternational(tYear, tMonth, 1) of me
  
tell current application
    set aOffset to (weekday of fDat) as number
  end tell
  
  
–header gap
  
repeat (aOffset – 1) times
    set the end of aList to ""
  end repeat
  
  
–calendar body
  
repeat with i from 1 to mLen
    set the end of aList to (i as string)
  end repeat
  
  
–footer gap
  
repeat (42 – aOffset – mLen + 1) times
    set the end of aList to ""
  end repeat
  
  
return aList
end retListCalendar

–現在のカレンダーで指定年月の日数を返す(国際化対応版)
on getMlen(aYear as integer, aMonth as integer)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
return |length| of theResult
end getMlen

–現在のカレンダーで指定年月の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

–ローカライズされた曜日名称を返す
on getLocalizedDaynames(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s standaloneWeekdaySymbols() as list
  
return dayNames
end getLocalizedDaynames

–ローカライズされた月名称を返す
on getLocalizedMonthnames(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set monthNames to df’s standaloneMonthSymbols() as list
  
return monthNames
end getLocalizedMonthnames

–ローカライズされた曜日の名称を返す
on getLocalizedWeekdaySymbol(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s weekdaySymbols()
  
return dayNames as list
end getLocalizedWeekdaySymbol

–ローカライズされた曜日の名称(短縮版)を返す
on getLocalizedShortWeekdaySymbol(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s shortWeekdaySymbols()
  
return dayNames as list
end getLocalizedShortWeekdaySymbol

–ローカライズされた曜日の名称(短縮記号)を返す
on getLocalizedVeryShortWeekdaySymbol(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s veryShortWeekdaySymbols()
  
return dayNames as list
end getLocalizedVeryShortWeekdaySymbol

–ローカライズされた曜日の名称を返す
on getLocalizedVeryStandaloneWeekdaySymbols(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s standaloneWeekdaySymbols()
  
return dayNames as list
end getLocalizedVeryStandaloneWeekdaySymbols

–ローカライズされた曜日の名称を返す
on getLocalizedShortStandaloneWeekdaySymbols(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s shortStandaloneWeekdaySymbols()
  
return dayNames as list
end getLocalizedShortStandaloneWeekdaySymbols

–ローカライズされた曜日の名称を返す
on getLocalizedVeryShortStandaloneWeekdaySymbols(aLoc)
  set df to current application’s NSDateFormatter’s alloc()’s init()
  
df’s setLocale:(current application’s NSLocale’s localeWithLocaleIdentifier:aLoc)
  
set dayNames to df’s veryShortStandaloneWeekdaySymbols()
  
return dayNames as list
end getLocalizedVeryShortStandaloneWeekdaySymbols

–カレンダー表示ビューを作成
on makeCalendarView(tYear as integer, tMonth as integer, noDispList as list, ignoreP as boolean)
  set colNum to 7
  
set rowNum to 8
  
set aLen to (colNum * rowNum)
  
  
set aButtonCellWidth to 70 –56
  
set aButtonCellHeight to 40
  
  
set viewWidth to aButtonCellWidth * colNum
  
set viewHeight to aButtonCellHeight * rowNum
  
  
–define the matrix size where you’ll put the radio buttons
  
set matrixRect to current application’s NSMakeRect(0.0, 0.0, viewWidth, viewHeight)
  
set aView to NSView’s alloc()’s initWithFrame:(matrixRect)
  
  
set aCount to 1
  
set aFontSize to 30
  
set bArray to current application’s NSMutableArray’s new()
  
  
–Make Header (Month, Year)
  
set x to 1
  
set tmpB to (NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum) * (aButtonCellHeight), aButtonCellWidth * 4, aButtonCellHeight)))
  (
tmpB’s setEditable:false)
  (
tmpB’s setBordered:false)
  (
tmpB’s setDrawsBackground:false)
  (
tmpB’s setAlignment:(current application’s NSLeftTextAlignment))
  (
tmpB’s setStringValue:((tYear as string) & " / " & tMonth as string))
  (
tmpB’s setFont:(NSFont’s fontWithName:"Times-Roman" |size|:40))
  (
bArray’s addObject:tmpB)
  
  
set aCount to aCount + 1
  
  
  
–Make Header (Weekday)
  
set curLocale to current application’s NSLocale’s currentLocale()
  
set aDS10 to (curLocale’s objectForKey:(current application’s NSLocaleIdentifier)) as string
  
set wdList to getLocalizedShortStandaloneWeekdaySymbols(aDS10) of me
  
set y to 2
  
  
repeat with x from 1 to 7
    set j to contents of item x of wdList
    
set tmpB to (NSTextField’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum – 1) * (aButtonCellHeight), aButtonCellWidth, aButtonCellHeight / 2)))
    (
tmpB’s setEditable:false)
    (
tmpB’s setAlignment:(current application’s NSCenterTextAlignment))
    (
tmpB’s setStringValue:(j))
    (
bArray’s addObject:tmpB)
  end repeat
  
  
set aCalList to retListCalendar(tYear, tMonth) of me
  
set aCount to 1
  
  
  
–Make Calendar (Calendar Body)
  
set curDat to current date
  
  
repeat with y from 3 to rowNum
    repeat with x from 1 to colNum
      set j to contents of item aCount of aCalList
      
set tmpB to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum – 2) * aButtonCellHeight, aButtonCellWidth, aButtonCellHeight)))
      
      
set tmpDate to getDateInternational(tYear, tMonth, j as integer) of me
      
set tmpF to (tmpDate < curDat) as boolean
      
      
if ignoreP = false then
        set dispF to true
      else if ignoreP = true and tmpF = false then
        set dispF to true
      else
        set dispF to false
      end if
      
      
if (j as integer) is not in noDispList then
        if (dispF = true) then
          (tmpB’s setTitle:(j as string))
          (
tmpB’s setFont:(NSFont’s fontWithName:"ArialMT" |size|:aFontSize))
          
–set attrTitle to makeRTFfromParameters((aCount as string), "ArialMT", aFontSize, 0, (aFontSize * 1.2)) of me
          
–(tmpB’s setAttributedTitle:(attrTitle))
          (
tmpB’s setShowsBorderOnlyWhileMouseInside:true)
          (
tmpB’s setAlignment:(current application’s NSCenterTextAlignment))
          (
tmpB’s setEnabled:(j ≠ ""))
          (
tmpB’s setTarget:me)
          (
tmpB’s setAction:("clicked:"))
          (
tmpB’s setButtonType:(NSButtonTypeOnOff))
          (
tmpB’s setHidden:(j = ""))
          
if j is not equal to "" then
            (tmpB’s setTag:(j))
            (
bArray’s addObject:tmpB)
          end if
        end if
      end if
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  (
aView’s setSubviews:bArray)
  
return aView
end makeCalendarView

★Click Here to Open This Script 

Posted in Calendar dialog GUI | Tagged 10.14savvy 10.15savvy NSAlert NSButton NSButtonTypeOnOff NSColor NSFont NSFontAttributeName NSForegroundColorAttributeName NSKernAttributeName NSLigatureAttributeName NSMutableArray NSMutableAttributedString NSMutableDictionary NSMutableParagraphStyle NSOffState NSOnState NSParagraphStyleAttributeName NSRunningApplication NSTextField NSUnderlineStyleAttributeName NSView | Leave a comment

日付ダイアログで開始時と終了時を選択する

Posted on 10月 23, 2019 by Takaaki Naganoya

edama2さんからの投稿です。いつもながら、(同じAppleScriptを書いているのに)異種格闘技のようなフレーバーの違いを感じるものですが、ソースコードを美しく書くことへのこだわりと、実際に実現されている機能については折り紙つきです。

—- >8 投稿ここから

タイトルは「日付ダイアログで開始時と終了時を選択する」です。
「アラートダイアログ上のDate Pickerで日付選択」をベースに日付をふたつ選択できるようにしました
結果はレコードで帰って返ってきます

--> {|開始日|:date "2019年10月23日 水曜日 21:23:04", |終了日|:date "2019年10月24日 木曜日 21:23:04"}

NSDatePickerのコメントアウトしている部分を変更するとカレンダー以外にもできます

スプリクトオブジェクトの使い方が奇怪(?)だと思うかもしれませんが、
script-factory さんのクラスオブジェクトのやり方を参考に結果のプロパティ値を本体と分離するためにこうしています。

制作環境:macOS10.14.6
—– >8 投稿ここまで

まず「property parent : AppleScript」で腰を抜かしてしまいました。なるほど、こういう逃げ道があったんですか。Script文の中にCocoaの機能を呼び出すAppleScriptObjCを書く方法がなかなか見つからずに、個人的にそういう書き方はしてこなかったところですが、これは実際に試してみないと。

AppleScriptで作った部品を積み上げて新しい概念や機能を実現することには関心がありますが、記法の限界を探るという方向にはほとんど興味がないので、参考になります。

自分も、NSDatePickerにNSBoxをかぶせてアラートダイアログに表示させるところまでは同様に作っていたんですが、サイズの調整に手間取って、思い通りの表示を行えていませんでした。はい、まさにこういう形のものが作りたかった(利用したかった)ので、これを見て勉強させていただきましょう。

AppleScript名:日付ダイアログで開始時と終了時を選択する.scpt
on run
  set dateObj to my chooseDate("期間を選択", "開始日と終了日を選択してください。", "開始日", "終了日")
end run

#カレンダー作成対象の年、月を選択(ただし、日付をクリックして選択しないと値を取得できないので注意)
on chooseDate(aMainMes, aSubMes, aLabel1, aLabel2)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _the_date : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on chooseDate(aMainMes, aSubMes, aLabel1, aLabel2)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myLabel1:aLabel1, myLabel2:aLabel2}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
return (my _the_date)
    end chooseDate
    
## ダイアログの生成
    
on raize:paramObj
      set aMainMes to paramObj’s myMessage
      
set aSubMes to paramObj’s mySubMessage
      
set aLabel1 to paramObj’s myLabel1
      
set aLabel2 to paramObj’s myLabel2
      
set setTime1 to current date
      
set setTime2 to setTime1 + days
      
      
set aList to {}
      
set aList’s end to {boxLabel:aLabel1, aTime:setTime1}
      
set aList’s end to {boxLabel:aLabel2, aTime:setTime2}
      
      
set viewList to {}
      
set datePickerList to {}
      
set parentViewWidth to 0
      
set aMargin to 8
      
      
set countItem to count aList
      
repeat with num from 1 to countItem
        set anItem to (aList)’s item num
        
        
set setLabel to anItem’s boxLabel
        
set setTime to anItem’s aTime
        
        
## create a view
        
set opt to current application’s NSYearMonthDayDatePickerElementFlag
        
–set opt to opt + (current application’s NSHourMinuteSecondDatePickerElementFlag as integer)
        
tell current application’s NSDatePicker’s new()
          –setDatePickerStyle_(current application’s NSClockAndCalendarDatePickerStyle) –> 10.13以下
          
setDatePickerStyle_(current application’s NSDatePickerStyleClockAndCalendar) –> 10.14以上 カレンダー
          
–setDatePickerStyle_(current application’s NSDatePickerStyleTextField) –> 10.14以上 テキスト入力
          
–setDatePickerStyle_(current application’s NSDatePickerStyleTextFieldAndStepper) –> 10.14以上 ステップ入力
          
setDatePickerElements_(opt)
          
setDateValue_(setTime)
          
set thisSize to fittingSize()
          
setFrameSize_(thisSize)
          
          
### 余白の大きさを指定
          
set aWidth to (thisSize’s width) + aMargin * 3.75
          
set aHeight to (thisSize’s height) + aMargin * 5.25
          
set boxFrameSize to current application’s NSMakeSize(aWidth, aHeight)
          
setFrameOrigin_(current application’s NSMakePoint(aMargin, aMargin))
          
log frame()
          
set theDatePicker to it
        end tell
        
        
tell current application’s NSBox’s new()
          setTitle_(setLabel)
          
addSubview_(theDatePicker)
          
setFrameSize_(boxFrameSize)
          
setFrameOrigin_(current application’s NSMakePoint(parentViewWidth, 0))
          
set viewList’s end to it
        end tell
        
        
### 親NSViewの大きさを指定
        
set countItem2 to count viewList
        
repeat with num2 from num to countItem2
          set anItem2 to viewList’s item num2
          
set tmpFrame to anItem2’s frame()
          
set aBoxWidt to current application’s NSWidth(tmpFrame)
          
set parentViewHeight to current application’s NSHeight(tmpFrame)
          
set parentViewWidth to parentViewWidth + aMargin + aBoxWidt
          
log result
        end repeat
        
        
###
        
set datePickerList’s end to {pickerObj:theDatePicker, keyLabel:setLabel}
      end repeat
      
      
tell current application’s NSView’s new()
        –setAutoresizingMask_(0)
        
–setAutoresizesSubviews_(true)
        
setFrameSize_(current application’s NSMakeSize(parentViewWidth, parentViewHeight))
        
setSubviews_(viewList)
        
set theView to it
      end tell
      
      
## set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(aMainMes)
        
setInformativeText_(aSubMes)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
set returnCode to runModal() — show alert in modal loop
      end tell
      
      
if returnCode = (current application’s NSAlertSecondButtonReturn) then error number -128
      
      
## retrieve date
      
set keyList to {}
      
set dateList to {}
      
repeat with anItem in datePickerList
        set keyList’s end to anItem’s keyLabel
        
set dateList’s end to (anItem’s pickerObj)’s dateValue() as date
      end repeat
      
set my _the_date to (current application’s NSDictionary’s dictionaryWithObjects:dateList forKeys:keyList) as record
      
log result
    end raize:
  end script
  
  
##
  
tell (make MyDialog)
    return chooseDate(aMainMes, aSubMes, aLabel1, aLabel2)
  end tell
end chooseDate

★Click Here to Open This Script 

Posted in Calendar dialog GUI | Tagged 10.14savvy 10.15savvy | Leave a comment

choose date Lib

Posted on 10月 19, 2019 by Takaaki Naganoya

date pickerで日付選択ダイアログを表示するAppleScript Library「choose date Lib」です。

–> Download choose date Lib.scptd (To ~/Library/Script Libraries/)

date pickerで日付選択するScriptは割とありふれていて、難易度も低く、あえてライブラリ化する必要をそれほど感じていなかったのですが、たまに(このような部品として)使いたくなります。ライブラリ化することで、ブラックボックス化されるため大きなプログラムに突っ込んだときのシステム負荷が少ない(Script Debugger上で巨大なプログラムを編集していると応答速度が遅くなってくるので、こういうUI部品系のコードまで構文確認の対象にしたくない)、ということは確実にいえます。

外部フレームワークなしで呼び出せる部品を整備しておくとメリットが大きいため、技術的には何も見るべきものはありませんが、用意しておきました。

デフォルトの表示日時を指定できるあたりが自分的に必要な機能であり、さほどたいしたものでもありません。CotEditorのPowerPack作成時に、

「アプリケーション内蔵のスクリプトメニューから呼び出したときにウィンドウ(ダイアログ)の表示レベルがメインのアプリケーションよりも低くなる(背面に回される)」

という現象を経験していたので(CotEditor側がosascriptコマンドでScriptを呼び出していることが原因?)、Window levelについては強制的に操作しています。

本当はこれを2つ横に並べて、開始日と終了日を取得するライブラリを作りたいのですが、NSDatePickerが意外と難物で、予想外に時間がかかって放り投げてしまいました。

本ライブラリは、こんな感じ(↓)の用途に使っています。

–> Download Code-Signed AppleScript executable Applet with Library in its bundle

AppleScript名:cre & mod date modifier.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/10/19
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
use cDate : script "choose date Lib"

set aFile to choose file

tell application "Finder window"
  set aDate to creation date of aFile
end tell

set dRes1 to choose date main message "ファイルの作成&修正日変更" sub message "変更したい日付を選択してください。" default date aDate
copy dRes1 to {yNum, mNum, dNum}

set targDate to date ((yNum as string) & "/" & (mNum as string) & "/" & (dNum as string))

changeFileCreationDate(targDate, aFile) of me
changeFileModDate(targDate, aFile) of me

–指定パスのファイルの作成日時を変更する
on changeFileCreationDate(aDate, aFile)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithObject:aDate forKey:(current application’s NSFileCreationDate)
  
set aFM to current application’s NSFileManager’s defaultManager()’s setAttributes:aDic ofItemAtPath:(POSIX path of aFile) |error|:(missing value)
end changeFileCreationDate

–指定パスのファイルの修正日時を変更する
on changeFileModDate(aDate, aFile)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithObject:aDate forKey:(current application’s NSFileModificationDate)
  
set aFM to current application’s NSFileManager’s defaultManager()’s setAttributes:aDic ofItemAtPath:(POSIX path of aFile) |error|:(missing value)
end changeFileModDate

★Click Here to Open This Script 

Posted in Calendar dialog Script Libraries | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy | Leave a comment

天皇誕生日の計算

Posted on 8月 29, 2019 by Takaaki Naganoya

天皇誕生日を計算するAppleScriptです。

# 日本ローカルなカレンダー計算の話なので、他の国では関係のない話題です。

AppleScriptによるデータ処理テーマにおいて、各種スケジュールや日数計算(締め切りまでの営業日計算)は割とポピュラーなものです。つまり、誰でもやりたいと思うし、実際に全世界的にやった人が多いことでしょう。このレベルのものを「カレンダー計算」と呼びます。

一方、手帳やカレンダー(印刷物)の制作を行う仕事(案件)が世の中に存在しており、それを行うことを「カレンダー制作」と呼んでいます。日数計算だけでなく、その日がどのような日であるかを明確にデータ処理する必要があります。

そのうえ、デザイン優先で山のように体裁を整える必要があるのと明文化されていない作成ルールを過去の制作物から読み取れ(とくに、RFC関連規約)というご無体な指示が後出しジャンケン的に出てくる、とても関わりたくない案件の数々です。

さて、天皇誕生日は現在在位中の天皇(current emperor)の誕生日を祝日(=休日)とするものです。カレンダー計算でもカレンダー制作でも、割と無視できない休日のうちのひとつでしょう。

祝日の中でも「天皇誕生日」の仕様がかなり込み入っており、頭痛のタネでもあります。とくに改元(=天皇交代)時の天皇誕生日の変更タイミングが要注意点でもあります。

昭和天皇誕生日:「天皇誕生日」→「みどりの日」→「昭和の日」
平成天皇誕生日:「天皇誕生日」→(2019年には設定されない)
令和天皇誕生日:(2020/2/23より)

とくに、2019年に天皇誕生日が設定されないこと(要注意)。「昭和の日」があっても「平成の日」がないことなど、カレンダー計算および制作時に落とし穴がいっぱいです。

国民の祝日は、カレンダー計算を行ううえでなかなか仕様が込み入っており、仕事(案件)によっては自前で計算するものの、あらかじめ数年分の祝日データを客先から支給されるケースの方が一般的でしょうか。

ただ、あらかじめ計算しておいたデータをもとに処理するという処理方法だけではデータ支給範囲でしかカレンダー計算が行えないため、自前で計算できるようにしておいたほうがよいと考えるものです。

AppleScript名:天皇誕生日の計算.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/08/28
—
–  Copyright © 2019 jp.piyomarusoft, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set dRes to getCurEmpBitrthday(1988) of me
–> 1988/4/29

set dRes to getCurEmpBitrthday(1989) of me
–> 1989/12/23

set dRes to getCurEmpBitrthday(2018) of me
–> 2018/12/23

set dRes to getCurEmpBitrthday(2019) of me
–> false

set dRes to getCurEmpBitrthday(2020) of me
–> 2020/2/23

–天皇誕生日
on getCurEmpBitrthday(targYear)
  –昭和、平成、令和 の誕生日定義
  
set curEmperrorsBirthday to {{"4/29", {1926, 1988}}, {"12/23", {1989, 2018}}, {"2/23", {2020, 9999}}}
  
  
set hitF to false
  
repeat with i in curEmperrorsBirthday
    copy i to {targDate, {beginYear, endYear}}
    
if targYear ≥ beginYear and targYear ≤ endYear then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
  
if hitF = false then
    return false
  end if
  
  
set thisDate to (targYear as string) & "/" & targDate
  
return thisDate
end getCurEmpBitrthday

★Click Here to Open This Script 

Posted in Calendar | Tagged 10.12savvy 10.13savvy 10.14savvy | Leave a comment

アラートダイアログ上にRadio Buttonを表示 v3

Posted on 8月 7, 2019 by Takaaki Naganoya

アラートダイアログ上にラジオボタン的なUIを表示して選択するAppleScriptです。

–> Downdload calImageDialog (with Library within its bundle)

本当はRadio Button的なものを作りたかったのですが、NSMatrixがmacOS 10.8で非推奨というか事実上の廃止になったことを受けて、Radio Button的なものを作ってみたのですが、割と中途半端です(見た目はスゲーいいのに)。


▲macOS 10.14+Script Editor上の動き


▲macOS 10.14+Script Debugger上の動き

一応、ボタンの画像に1月分のカレンダー画像を割り当てており、機能と見た目のバランスを取ろうとしたのですが、Radio Buttonっぽい動きにはなっていません(残念!)。

あと、1年分のカレンダー画像を作成するのに不思議なぐらい時間がかかっています。

あんな素朴なcalコマンドにmacOS 10.13から当日のハイライト表示を消すための「-h」オプションが追加されていたとは、気づきませんでした。10.14で作って10.12上で動かなかったのでcalコマンドのオプションを条件分岐で変更するように初版から修正しました。

AppleScript名:アラートダイアログ上にRadio Buttonを表示 v3
— Created 2019-08-07 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use calImage : script "calImageKit"

property NSView : a reference to current application’s NSView
property NSAlert : a reference to current application’s NSAlert
property NSMatrix : a reference to current application’s NSMatrix
property NSButton : a reference to current application’s NSButton
property NSButtonCell : a reference to current application’s NSButtonCell
property NSRadioButton : a reference to current application’s NSRadioButton
property NSRadioModeMatrix : a reference to current application’s NSRadioModeMatrix
property NSRoundedBezelStyle : a reference to current application’s NSRoundedBezelStyle
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSAlertSecondButtonReturn : a reference to current application’s NSAlertSecondButtonReturn

property theResult : 0
property returnCode : 0

set paramObj to {myMessage:"Select target month", mySubMessage:"適切なものを以下からえらんでください", matrixTitleList:{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}}

–my chooseItemByRadioButton:paramObj–for Debugging

my performSelectorOnMainThread:"chooseItemByRadioButton:" withObject:paramObj waitUntilDone:true
return theResult

on chooseItemByRadioButton:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aMatList to (matrixTitleList of paramObj) as list
  
set aLen to length of aMatList
  
  
  
set aButtonCellWidth to 104
  
set aButtonCellHeight to 104
  
  
set colNum to 4
  
set rowNum to 3
  
  
set targYear to 2019
  
  
set viewWidth to aButtonCellWidth * colNum
  
set viewHeight to aButtonCellHeight * rowNum
  
  
–create the radio button prototype
  
set aProto to NSButtonCell’s alloc()’s init()
  
aProto’s setTitle:"Options"
  
aProto’s setButtonType:(NSRadioButton)
  
  
–define the matrix size where you’ll put the radio buttons
  
set matrixRect to current application’s NSMakeRect(0.0, 0.0, viewWidth, viewHeight)
  
set aView to NSView’s alloc()’s initWithFrame:(matrixRect)
  
  
set aCount to 1
  
set tmpArray to current application’s NSMutableArray’s new()
  
repeat with y from 1 to rowNum
    repeat with x from 1 to colNum
      set j to contents of item aCount of aMatList
      
set tmpB to (NSButton’s alloc()’s initWithFrame:(current application’s NSMakeRect(((x – 1) * aButtonCellWidth), ((aLen – aCount) div colNum) * aButtonCellHeight, aButtonCellWidth, aButtonCellHeight)))
      
–(tmpB’s setTitle:j)
      
      
set tmpImage to makeSmallCalendarImage(targYear, aCount) of calImage
      (
tmpB’s setImage:(tmpImage))
      (
tmpB’s setShowsBorderOnlyWhileMouseInside:true)
      (
tmpB’s setTag:(aCount))
      (
tmpB’s setTarget:me)
      (
tmpB’s setAction:("clicked:"))
      (
tmpB’s setButtonType:(current application’s NSButtonTypeMomentaryPushIn))
      
      (
tmpArray’s addObject:tmpB)
      
set aCount to aCount + 1
    end repeat
  end repeat
  
  (
aView’s setSubviews:tmpArray)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aView
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
end chooseItemByRadioButton:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

on clicked:aParam
  set aTag to tag of aParam
  
set theResult to aTag
end clicked:

★Click Here to Open This Script 

Posted in Calendar dialog GUI Image | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSAlert NSAlertSecondButtonReturn NSButton NSRoundedBezelStyle NSRunningApplication NSView | Leave a comment

Photosで選択中の写真をKeynoteの現在の書類の現在のスライド以降に配置 v2

Posted on 5月 27, 2019 by Takaaki Naganoya

Photosで選択中の写真をKeynoteで現在オープン中の書類の現在のスライド(ページ)以降に配置していくAppleScriptの改良版です。

作業の過程をiPhoneで写真撮影。撮影した写真を写真.app(Photos.app)経由でKeynote書類にまとめる作業を行なっていました。作業工程を時間こみで表示できないとわかりにくかったので、Keynoteの各スライドに時刻を入れるようにAppleScriptを書いておきました。

初版では写真.app(Photos)上の選択項目を一切ソートなどしなかったため、exifから取得した撮影日付で昇順ソートしています。


▲写真.app(Photos)上でKeynoteに配置したい写真を選択


▲あらかじめKeynote上でマスタースライド「画像(横長)」上のアイテムを編集しておくといいかも


▲AppleScriptによって写真.app上の写真をKeynote書類上に順次割り付け。タイトル部分にexifに記録されていた撮影時刻が入る


▲実行結果。写真.appから割り付けた写真は撮影日時でソートされている

AppleScript名:Photosで選択中の写真をKeynoteの現在の書類の現在のスライド以降に配置 v2
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/05/26
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use BPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html
use mdLib : script "Metadata Lib" version "2.0.0" –https://www.macosxautomation.com/applescript/apps/Script_Libs.html#Metadata_Lib

property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property SMSForder : a reference to current application’s SMSForder
property NSFileManager : a reference to current application’s NSFileManager

tell application "Keynote"
  if (count every document) = 0 then return
end tell

–Photosで選択中の写真をすべて書き出してalias listを取得する
set aList to exportSelectedPhotoOnPhotos() of me
if aList = {} or aList = false then return

set aLen to length of aList

–書き出した画像の親フォルダを求めておく(あとで削除するため)
tell application "Finder"
  set parentFol to parent of (item 1 of aList)
end tell

–exifから撮影日時を取得して2D List化
set aaList to {}
repeat with i in aList
  set j to contents of i
  
set exifDate to readExifDateTimeOriginal(j) of me
  
set the end of aaList to {j, exifDate, (time string of exifDate)}
end repeat

–撮影日時で昇順ソート
set aaaList to sortList2DAscending(aaList, {2}) of me

tell application "Keynote"
  tell front document
    tell current slide
      set curNum to slide number –現在表示中のスライドの番号(ページ数)を取得する
    end tell
    
    
–Photosから取得した写真のアイテム数でループ
    
repeat with i from 1 to aLen
      copy (item i of aaaList) to {aPhoto, aDateObj, creTime}
      
      
set newSlide to make new slide at slide (curNum + i) with properties {base slide:master slide "画像(横長)"} –This item is localized!! Maybe a "Photo"
      
      
tell newSlide
        set object text of default title item to creTime
        
set file name of image 1 to aPhoto –place an image to image placeholder
      end tell
      
      
–配置画像を削除
      
tell application "Finder"
        delete aPhoto –配置した写真を削除
      end tell
    end repeat
  end tell
end tell

–あとしまつ
tell application "Finder"
  delete parentFol –画像のダウンロードフォルダを削除
end tell

–Photosで選択中のファイルをtmporaryフォルダに書き出してalias listで返す
on exportSelectedPhotoOnPhotos()
  set dtPath to (path to temporary items) as text
  
set aUUID to NSUUID’s UUID()’s UUIDString() as text
  
  
set dirPath to ((POSIX path of dtPath) & aUUID)
  
set fileManager to NSFileManager’s defaultManager()
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
set dtPath to dtPath & aUUID
  
  
tell application "Photos"
    set a to selection
    
if a = {} then return {}
    
set aRes to (export a to file dtPath)
  end tell
  
  
tell application "Finder"
    tell folder dtPath
      set fList to (every file) as alias list
    end tell
  end tell
  
  
if fList = {} then return false
  
return fList
end exportSelectedPhotoOnPhotos

–指定JPEG画像のExif情報からDateTimeOriginalを取得してAS dateオブジェクトに変換して返す
on readExifDateTimeOriginal(aTargFileAlias)
  set theMetadata to readMetadataFrom(aTargFileAlias) of me
  
if theMetadata = false then return false
  
  
set keysList to theMetadata’s allKeys()
  
  
if "{Exif}" is not in (keysList as list) then return false
  
  
set exifDate to theMetadata’s valueForKeyPath:"{Exif}.DateTimeOriginal"
  
if exifDate = missing value then return false
  
  
set a to NSString’s stringWithString:exifDate
  
set {aDateStr, aTimeStr} to (a’s componentsSeparatedByString:" ") as list
  
set bDateStr to repChar(aDateStr, ":", "/") of me
  
set fullDate to date (bDateStr & " " & aTimeStr)
  
  
return fullDate
end readExifDateTimeOriginal

–指定のファイルからメタデータを取得する
on readMetadataFrom(imageFile)
  load framework
  
set {theRecord, theError} to SMSForder’s metadataFromImage:imageFile |error|:(reference)
  
if theRecord = missing value then — there was a problem, so extract the error description
    error false
  else
    return theRecord
  end if
end readMetadataFrom

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

–入れ子のリストを昇順ソート
on sortList2DAscending(a, keyItem)
  return sort2DList(a, keyItem, {true}) of me
end sortList2DAscending

–入れ子のリストを降順ソート
on sortList2DDecending(a, keyItem)
  return sort2DList(a, keyItem, {false}) of me
end sortList2DDecending

–2D Listをソート
on sort2DList(aList as list, sortIndexes as list, sortOrders as list)
  load framework
  
  
–index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換
  
set newIndex to {}
  
repeat with i in sortIndexes
    set j to contents of i
    
set j to j – 1
    
set the end of newIndex to j
  end repeat
  
  
set sortTypes to {}
  
repeat (length of sortIndexes) times
    set the end of sortTypes to "compare:"
  end repeat
  
  
set resList to (SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as {missing value, list}
  
  
return resList
end sort2DList

★Click Here to Open This Script 

Posted in Calendar exif file Image list | Tagged 10.12savvy 10.13savvy 10.14savvy Finder Keynote NSFileManager NSString NSUUID Photos | Leave a comment

Photosで選択中の写真をKeynoteの現在の書類の現在のスライド以降に配置

Posted on 5月 27, 2019 by Takaaki Naganoya

Photosで選択中の写真をKeynoteで現在オープン中の書類の現在のスライド(ページ)以降に配置していくAppleScriptです。

作業の過程をiPhoneで写真撮影。撮影した写真を写真.app(Photos.app)経由でKeynote書類にまとめる作業を行なっていました。作業工程を時間こみで表示できないとわかりにくかったので、Keynoteの各スライドに時刻を入れるようにAppleScriptを書いておきました(同じ作業が二度あるかは不明ですが、二度目からは明らかに時間の節約。つまり時間の貯金ができることになります)。

写真.app(Photos)上でKeynoteに配置したい写真を選択しておき、本Scriptを実行します。

Keynote書類上に新規スライドを追加し、撮影時刻と写真を配置していきます。

実行直後にPhotosで選択中の写真をファイルに書き出すため、若干待たされます。

# 複数のマシンでiCloudを介して写真を同期した場合に、写真.appのライブラリの写真の並び順は撮影日時どおりにならないようなので、明示的にソートしておく必要があるようです

ちなみに、写真の自作キーボードキットはこの後フットペダルにつなぎなおして、PPSSPP用の足踏みコントローラー化する予定です(「戦場の絆ポータブル」用)。

AppleScript名:Photosで選択中の写真をKeynoteの現在の書類の現在のスライド以降に配置
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/05/26
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use BPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html
use mdLib : script "Metadata Lib" version "2.0.0" –https://www.macosxautomation.com/applescript/apps/Script_Libs.html#Metadata_Lib

property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property NSFileManager : a reference to current application’s NSFileManager

tell application "Keynote"
  if (count every document) = 0 then return
end tell

–Photosで選択中の写真をすべて書き出してalias listを取得する
set aList to exportSelectedPhotoOnPhotos() of me
if aList = {} or aList = false then return

set aLen to length of aList

–書き出した画像の親フォルダを求めておく(あとで削除するため)
tell application "Finder"
  set parentFol to parent of (item 1 of aList)
end tell

tell application "Keynote"
  tell front document
    tell current slide
      set curNum to slide number –現在表示中のスライドの番号(ページ数)を取得する
    end tell
    
    
–Photosから取得した写真のアイテム数でループ
    
repeat with i from 1 to aLen
      set aPhoto to contents of item i of aList
      
set newSlide to make new slide at slide (curNum + i) with properties {base slide:master slide "画像(横長)"} –This item is localized!! Maybe a "Photo"
      
set creTime to getCreationTime(aPhoto) of me
      
tell newSlide
        set object text of default title item to creTime
        
set file name of image 1 to aPhoto –place an image to image placeholder
      end tell
      
      
–配置画像を削除
      
tell application "Finder"
        delete aPhoto –配置した写真を削除
      end tell
    end repeat
  end tell
end tell

–あとしまつ
tell application "Finder"
  delete parentFol –画像のダウンロードフォルダを削除
end tell

–指定のaliasからExif情報の作成日の時刻情報を返す
on getCreationTime(anAlias)
  set exifDate to readExifDateTimeOriginal(anAlias) of me
  
return time string of exifDate
end getCreationTime

–Photosで選択中のファイルをtmporaryフォルダに書き出してalias listで返す
on exportSelectedPhotoOnPhotos()
  set dtPath to (path to temporary items) as text
  
set aUUID to NSUUID’s UUID()’s UUIDString() as text
  
  
set dirPath to ((POSIX path of dtPath) & aUUID)
  
set fileManager to NSFileManager’s defaultManager()
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
set dtPath to dtPath & aUUID
  
  
tell application "Photos"
    set a to selection
    
if a = {} then return {}
    
set aRes to (export a to file dtPath)
  end tell
  
  
tell application "Finder"
    tell folder dtPath
      set fList to (every file) as alias list
    end tell
  end tell
  
  
if fList = {} then return false
  
return fList
end exportSelectedPhotoOnPhotos

–指定JPEG画像のExif情報からDateTimeOriginalを取得してAS dateオブジェクトに変換して返す
on readExifDateTimeOriginal(aTargFileAlias)
  set theMetadata to readMetadataFrom(aTargFileAlias) of me
  
if theMetadata = false then return false
  
  
set keysList to theMetadata’s allKeys()
  
  
if "{Exif}" is not in (keysList as list) then return false
  
  
set exifDate to theMetadata’s valueForKeyPath:"{Exif}.DateTimeOriginal"
  
if exifDate = missing value then return false
  
  
set a to NSString’s stringWithString:exifDate
  
set {aDateStr, aTimeStr} to (a’s componentsSeparatedByString:" ") as list
  
set bDateStr to repChar(aDateStr, ":", "/") of me
  
set fullDate to date (bDateStr & " " & aTimeStr)
  
  
return fullDate
end readExifDateTimeOriginal

–指定のファイルからメタデータを取得する
on readMetadataFrom(imageFile)
  load framework
  
set {theRecord, theError} to current application’s SMSForder’s metadataFromImage:imageFile |error|:(reference)
  
if theRecord = missing value then — there was a problem, so extract the error description
    error false
  else
    return theRecord
  end if
end readMetadataFrom

–文字置換
on repChar(origText as string, targChar as string, repChar as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

Posted in Calendar exif file Image list | Tagged 10.12savvy 10.13savvy 10.14savvy Finder Keynote NSFileManager NSString NSUUID Photos | 1 Comment

Post navigation

  • Older posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Adobe AcrobatをAppleScriptから操作してPDF圧縮
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • AppleScriptによる並列処理
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • Keynote、Pages、Numbers Ver.14.0が登場
  • macOS 15 リモートApple Eventsにバグ?
  • デフォルトインストールされたフォント名を取得するAppleScript

Tags

10.11savvy (1102) 10.12savvy (1243) 10.13savvy (1392) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (190) 14.0savvy (142) 15.0savvy (120) CotEditor (66) Finder (51) iTunes (19) Keynote (116) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC