Archive for the 'NSDate' Category

2017/11/20 主要なタイムゾーンのカレンダーの開始曜日を取得して集計

macOS内に定義されているタイムゾーン(knownTimeZoneNames)のカレンダーの開始曜日を取得して、firstWeekdayを集計するAppleScriptです。

取得するScriptに集計部分を追加しただけのものです。

AppleScript名:主要なタイムゾーンのカレンダーの開始曜日を取得して集計
– Created 2017-11-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4990

property NSDate : a reference to current application’s NSDate
property NSCalendar : a reference to current application’s NSCalendar
property NSOrderedSet : a reference to current application’s NSOrderedSet
property NSLocale : a reference to current application’s NSLocale
property NSCountedSet : a reference to current application’s NSCountedSet
property NSMutableArray : a reference to current application’s NSMutableArray
property NSTimeZone : a reference to current application’s NSTimeZone

set currentCalendar to NSCalendar’s currentCalendar()
set tzList to (NSTimeZone’s knownTimeZoneNames()) as list

set fList to {}
repeat with i in tzList
  set j to contents of i
  (
currentCalendar’s setLocale:(NSLocale’s localeWithLocaleIdentifier:j))
  
  
set aTZ to (NSTimeZone’s timeZoneWithName:j)
  
set theComponents to (currentCalendar’s componentsInTimeZone:aTZ fromDate:(NSDate’s |date|()))
  
set tmpCalend to theComponents’s calendar()
  
  
set aFirstDay to (tmpCalend’s firstWeekday) as integer –firstWeekday: 1=Sunday, 2=Monday
  
set the end of fList to aFirstDay
end repeat

set aRes to calcFrequency(fList) of me
–> {{theKey:1, theCount:371}, {theKey:2, theCount:66}}

–1D Listのデータを各要素ごとに出現頻度集計
on calcFrequency(fList)
  set f2List to makeUniqueListFrom(fList) of me
  
  
set theCountedSet to NSCountedSet’s alloc()’s initWithArray:fList
  
set newArray to NSMutableArray’s new()
  
repeat with i in f2List
    (newArray’s addObject:{theKey:i, theCount:(theCountedSet’s countForObject:i)})
  end repeat
  
return newArray as list
end calcFrequency

–リスト内容のユニーク化
on makeUniqueListFrom(theList)
  set theSet to NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

★Click Here to Open This Script 

2017/06/21 日の出、日没時刻を計算するv2

オープンソースのプロジェクト「EDSunriseSet」(By Ernesto García)をFramework化した「EDSunriseSet.framework」を呼び出して日の出、日没時刻を計算するAppleScriptのアップデート版です。

本AppleScriptのテストのためには、EDSunriseSet.frameworkを~/Library/Frameworksフォルダに入れておく必要があります。バイナリを用意しておきましたので、自己責任でおためしください。

→ Download Binary (26KB)

EDSunriseSetが用意している各種の日の出、日没情報を取得するようにしてみました。

日没や日の出前後の「薄明」の情報についても取得します。

1024px-twilight_subcategoriessvg_resized.png

ただ、白夜などの現象が発生しない地域でこれらの薄明の情報はとくに必要はないように感じます(個人の見解です)。

AppleScript名:日の出、日没時刻を計算するv2
– Created 2017-06-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “EDSunriseSet” –erndev/EDSunriseSet
–https://github.com/erndev/EDSunriseSet
–http://piyocast.com/as/archives/4699

set cityRecList to {{kCityName:“Tokyo”, kCityLatitude:(35.6894875), kCityLongitude:(139.6917064), kCityTimeZone:“Asia/Tokyo”}}

set dList to {}

repeat with i in cityRecList
  set the end of dList to getSunriseSunset(i) of me
end repeat
return dList
–>  {{sunrise:date “2017年6月21日水曜日 4:25:37″, sunset:date “2017年6月21日水曜日 19:00:21″, civilTwilightStart:date “2017年6月21日水曜日 3:55:35″, civilTwilightEnd:date “2017年6月21日水曜日 19:30:23″, nauticalTwilightStart:date “2017年6月21日水曜日 3:18:15″, nauticalTwilightEnd:date “2017年6月21日水曜日 20:07:44″, astronomicalTwilightStart:date “2017年6月21日水曜日 2:36:45″, astronomicalTwilightEnd:date “2017年6月21日水曜日 20:49:13″, cityname:”Tokyo”}}

on getSunriseSunset(cityRec)
  set curLocale to current application’s NSLocale’s currentLocale()
  
set curDate to current application’s NSDate’s |date|()
  
  
set aTZ to current application’s NSTimeZone’s alloc()’s initWithName:(kCityName of cityRec)
  
set aSunrizeSunset to current application’s EDSunriseSet’s alloc()’s initWithDate:curDate timezone:aTZ latitude:(kCityLatitude of cityRec) longitude:(kCityLongitude of cityRec)
  
  
–日の出、日没  
  
set aSunRiseDate to (aSunrizeSunset’s sunrise) as date
  
set aSunSetDate to (aSunrizeSunset’s sunset) as date
  
  
–https://en.wikipedia.org/wiki/Twilight
  
–https://ja.wikipedia.org/wiki/薄明
  
  
–市民薄明(常用薄明、第三薄明)
  
set aCivilTwilightStart to (aSunrizeSunset’s civilTwilightStart) as date
  
set aCivilTwilightEnd to (aSunrizeSunset’s civilTwilightEnd) as date
  
  
–航海薄明(第二薄明)
  
set aNauticalTwilightStart to (aSunrizeSunset’s nauticalTwilightStart) as date
  
set aNauticalTwilightEnd to (aSunrizeSunset’s nauticalTwilightEnd) as date
  
  
–天文薄明(第一薄明)
  
set anAstronomicalTwilightStart to (aSunrizeSunset’s astronomicalTwilightStart) as date
  
set anAstronomicalTwilightEnd to (aSunrizeSunset’s astronomicalTwilightEnd) as date
  
  
return {sunrise:aSunRiseDate, sunset:aSunSetDate, civilTwilightStart:aCivilTwilightStart, civilTwilightEnd:aCivilTwilightEnd, nauticalTwilightStart:aNauticalTwilightStart, nauticalTwilightEnd:aNauticalTwilightEnd, astronomicalTwilightStart:anAstronomicalTwilightStart, astronomicalTwilightEnd:anAstronomicalTwilightEnd, cityname:kCityName of cityRec}
end getSunriseSunset

★Click Here to Open This Script 

2017/06/19 日の出、日没時刻を計算する

オープンソースのプロジェクト「EDSunriseSet」(By Ernesto García)をFramework化した「EDSunriseSet.framework」を呼び出して日の出、日没時刻を計算するAppleScriptです。

edsunriseset.png
▲もともとのGUI版サンプルプログラム

本AppleScriptのテストのためには、EDSunriseSet.frameworkを~/Library/Frameworksフォルダに入れておく必要があります。バイナリを用意しておきましたので、自己責任でおためしください。

→ Download Binary (26KB)

本Scriptは単にEDSunriseSetのサンプル中に書かれていたサンプルデータをそのまま利用したものであり、本来であればCoreLocationを呼び出して現在位置を取得し、システム設定からタイムゾーン情報を取得し位置情報から逆住所ジオコーダーで場所の住所情報を取得するといった処理になることでしょう。もちろん、任意の年月日の日付オブジェクトを作って指定するとか。

AppleScript名:日の出、日没時刻を計算する
– Created 2017-06-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “EDSunriseSet” –erndev/EDSunriseSet
–https://github.com/erndev/EDSunriseSet
–http://piyocast.com/as/archives/4696

set cityRecList to {{kCityName:“Madrid”, kCityLatitude:(40.4165), kCityLongitude:(-3.70256), kCityTimeZone:“Europe/Madrid”}, {kCityName:“Beijing”, kCityLatitude:(39.9075), kCityLongitude:(116.39723), kCityTimeZone:“Asia/Shanghai”}, {kCityName:“Cupertino”, kCityLatitude:(37.3229978), kCityLongitude:(-122.0321823), kCityTimeZone:“America/Los_Angeles”}, {kCityName:“New York”, kCityLatitude:(40.7127837), kCityLongitude:(-74.0059413), kCityTimeZone:“America/New_York”}, {kCityName:“Tokyo”, kCityLatitude:(35.6894875), kCityLongitude:(139.6917064), kCityTimeZone:“Asia/Tokyo”}, {kCityName:“Sydney”, kCityLatitude:(-33.8674869), kCityLongitude:(151.2069902), kCityTimeZone:“Australia/Sydney”}}

set dList to {}

repeat with i in cityRecList
  set the end of dList to getSunriseSunset(i) of me
end repeat
return dList
–>  {{sunrise:date “2017年6月19日月曜日 13:44:22″, sunset:date “2017年6月20日火曜日 4:48:04″, cityname:”Madrid”}, {sunrise:date “2017年6月19日月曜日 5:45:35″, sunset:date “2017年6月19日月曜日 20:45:54″, cityname:”Beijing”}, {sunrise:date “2017年6月19日月曜日 21:47:30″, sunset:date “2017年6月20日火曜日 12:31:43″, cityname:”Cupertino”}, {sunrise:date “2017年6月19日月曜日 18:24:37″, sunset:date “2017年6月20日火曜日 9:30:19″, cityname:”New York”}, {sunrise:date “2017年6月19日月曜日 4:25:14″, sunset:date “2017年6月19日月曜日 18:59:52″, cityname:”Tokyo”}, {sunrise:date “2017年6月19日月曜日 5:59:28″, sunset:date “2017年6月19日月曜日 15:53:30″, cityname:”Sydney”}}

on getSunriseSunset(cityRec)
  set curLocale to current application’s NSLocale’s currentLocale()
  
set curDate to current application’s NSDate’s |date|()
  
  
set aTZ to current application’s NSTimeZone’s alloc()’s initWithName:(kCityName of cityRec)
  
set aSunrizeSunset to current application’s EDSunriseSet’s alloc()’s initWithDate:curDate timezone:aTZ latitude:(kCityLatitude of cityRec) longitude:(kCityLongitude of cityRec)
  
  
set aSunRiseDate to (aSunrizeSunset’s sunrise) as date
  
–>  date “2017年6月19日月曜日 4:25:14″
  
  
set aSunSetDate to (aSunrizeSunset’s sunset) as date
  
–>  date “2017年6月19日月曜日 18:59:52″
  
  
return {sunrise:aSunRiseDate, sunset:aSunSetDate, cityname:kCityName of cityRec}
end getSunriseSunset

★Click Here to Open This Script 

2016/07/04 delay命令のパラメータが0.001秒まで対応

Mac OS X 10.3, Pantherで、delayコマンドのパラメータが0.1秒単位での指定に対応しました。10.3の頃はAppleScriptのバグがとても多かったので、「指定できても無視されるんでしょ?」ぐらいにしか受け取っていませんでした。

本サイトでもdelay (”0.001″ as real)などの記述もあり、「本当にそこまで厳密に時間待ちしないが、最小限の単位時間の時間待ちを意図している」などと説明していました。

AppleScript Language Guideでは、

『delayコマンドは時間待ちの時間の長さを保証しない。60分の1秒(0.016秒)以下では精度がさらに落ちる。delayコマンドはオーディオやビデオのシンクロなどの実時間を反映させた処理には不向きである。』

という説明が行われています。あれ? Appleの公式ドキュメントの記述は、裏を返せば0.016秒までは指定できるということを書いていますね。

そうはいっても、つい最近まではAppleScriptではまともに秒以下の単位の計測が行えなかったため、見向きもされていませんでした(本当)。

が!(大事なところです)

本当に現行の環境で実測したことはなかったのではないか? などと疑念が湧いて出てきたので、実際に計測してみました。

その結果、0.001秒までは指定したとおりに時間待ちしていることが判明。ここに訂正いたします。OS X 10.11のMacBook Pro Retina 2012では0.001まで指定してその通りに時間待ちしてくれました。

Mac mini Late 2014(2.6GHz, Core i5) OS X 10.10.5で実測したところ、こちらも0.001秒まで時間待ちしてくれました。

MacBook Air Mid 2011(1.6GHz, Core i5)OS X 10.11で実測したところ、CPUのパワーの低さもあるのか0.001秒を指定すると0.002秒ぐらいの値になることが確認されました。

delay命令による0.001秒オーダーの時間待ちについては、実行環境によって精度が左右されるようです。

AppleScript名:delay 0.1
– Created 2016-07-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
delay 0.1
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat) as real

–>  0.101064026356

★Click Here to Open This Script 

AppleScript名:delay 0.01
– Created 2016-07-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
delay 0.01
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat) as real

–>  0.011389970779

★Click Here to Open This Script 

AppleScript名:delay 0.001.scptd
– Created 2016-07-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
delay “0.001″ as real
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to (b1Dat - a1Dat) as real

–>  0.001370966434

★Click Here to Open This Script 

2016/04/13 ISO8601日付文字列を生成

NSDateからISO8601日付文字列を生成するAppleScriptです。

昨日のGHKitによるISO8601日付文字列ではいまひとつDropbox側と仕様が合わず、ちょっと困ってしまいました。

いくつものXcode Projectをgithub上で探してビルドしては捨て、捨ててはビルドしての繰り返しをしていました。ソースが古いとARC対応していなかったりして、かなり書き換える必要が生じてしまいます。

が、そもそも別にそんなたいしたものでもないので(^ー^;; 「自分で書いた方が簡単なんじゃないの?」と気付き、さらっと書きました。

これで、REST API経由でAppleScriptからDropboxを小突きまわして、ファイル共有期限の日時指定を行えます。

AppleScript名:ISO8601日付文字列を生成
– Created 2016-04-13 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlust : script “BridgePlus” –for OS X 10.10.x

set aDate to (current date) + 3 * days
set bDate to Cocoaify aDate
set bStr to retISO8601DateTimeString(aDate) as string
–> “2016-04-16T09:23:11Z”

–NSDate -> ISO8601 Date & Time String for Dropbox
on retISO8601DateTimeString(targDate)
  set theNSDateFormatter to current application’s NSDateFormatter’s alloc()’s init()
  
theNSDateFormatter’s setDateFormat:“yyyy-MM-dd’T’HH:mm:ss’Z’”
  
theNSDateFormatter’s setTimeZone:(current application’s NSTimeZone’s alloc()’s initWithName:“UTC”)
  
return (theNSDateFormatter’s stringFromDate:targDate) as text
end retISO8601DateTimeString

★Click Here to Open This Script 

2016/04/12 GHKitで日付フォーマット変換

Gabriel Handford氏によるオープンソースのフレームワーク「GHkit」を利用して、さまざまなフォーマットの日付表現のNSDateとの相互変換(decode/encode)を行うAppleScriptです。

REST経由でさまざまなサービスを呼び出して使っていると、さまざまな形式のタイムスタンプの文字列を扱い、parseする必要に迫られるため、こういう種類のフレームワークがあるのではないかと考えて探したところ見つけました。

オリジナルでは、GHKitの各メソッド名が「gh_」という形式になっており、これ(アンダースコア)がAppleScriptにおけるObjective-Cのメソッド名変換時に問題になったため、Xcode上で自分ですべてリネームして使っています(汗)。

なお、OS X 10.10以降用にビルドしたFrameworkのバイナリを用意しましたので、使ってみたい方はアーカイブを展開したあと~/Library/Frameworksに入れてご利用ください(自己責任で)。

–> Download Framework Binary

AppleScript名:GHKitで日付フォーマット変換
– Created 2016-04-12 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “GHKit” –https://github.com/gabriel/GHKit

– AppleScriptObjC uses “_” as special character (equivalent to “:” in method names). So, I changed them in whole project.
–  Original Method Name: gh_parseISO8601:
–  Converted Method Name: GHparseISO8601:

–Decode RFC822, ISO8601& HTTP format date strings

set aStr to current application’s NSString’s stringWithString:“Sun, 06 Nov 1994 08:49:37 +0000″
set aDate to current application’s NSDate’s GHparseRFC822:aStr
–>  (NSDate) 1994-11-06 08:49:37 +0000

set bStr to current application’s NSString’s stringWithString:“1997-07-16T19:20:30.045Z” –For Dropbox file/folder sharing expiration format
set bDate to current application’s NSDate’s GHparseISO8601:bStr
–>  (NSDate) 1997-07-16 19:20:30 +0000

set cStr to current application’s NSString’s stringWithString:“Tue, 12 Apr 2016 06:28:45 GMT” –Dropbox REST Result header timestamp format
set cDate to current application’s NSDate’s GHparseHTTP:cStr
–>  (NSDate) 2016-04-12 06:28:45 +0000

–Encode NSDate to RFC822, ISO8601& HTTP format date strings

set eDateStr to bDate’s GHformatHTTP()
–>  (NSString) “Wed, 16 Jul 1997 19:20:30 GMT” –For Dropbox file/folder sharing expiration format

set fDateStr to bDate’s GHformatRFC822()
–>  (NSString) “Wed, 16 Jul 1997 19:20:30 +0000″

set gDateStr to bDate’s GHformatISO8601()
–>  (NSString) “1997-07-17T04:20:30.045+09:00″

★Click Here to Open This Script 

2015/09/05 秒以下の時間待ちを検証

Cocoaの機能を用いて、秒以下の時間待ちを行い、実際に指定時間だけ待っているかどうかを確認してみました。

比較のため、同一環境でdelay 0.001にあたる処理をコメントアウトして時間計測してみたところ、

–> 8.8036060333252E-5(指数表示)
–> 0.000088036060333252

–> 9.30428504943848E-5(指数表示)
–> 0.0000930428504943848

と、delayを指定していない場合にははるかに短い間隔で計測が呼び出され、delayがきちんと効力を発揮していることが見てとれました。

予想よりもちゃんと指定時間待っていて驚きました。積極的に使っていきたいところです。

AppleScript名:ASOCで秒以下の時間待ちを計測
– Created 2015-09-05 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

–>  0.001403987408
–>  0.001309990883
–>  0.001239955425

★Click Here to Open This Script 

2015/08/20 ASOCでdate pickerによる日付選択

Shane StanleyのScript Library「BridgePlus」の機能を用いた日付/時刻選択インタフェース「date picker」による日付/時刻選択を行うAppleScriptです。実行には、あらかじめBridgePlusをインストールしておく必要があります。

datepicker1.png
▲ASObjC Explorer 4での実行画面

datepicker2.png
▲Script Editorでの実行画面(Control-Command-R)

そうそう、こういうのが(OS標準で)欲しかったんですよね〜。あと、開始日と終了日の選択とか、休日を選択できないようにできるといいかも。

AppleScript名:ASOCでdate pickerによる日付選択
– Created 2015-08-20 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use script “BridgePlus” – for pre-10.11 compatibility

if not (current application’s NSThread’s isMainThread()) as boolean then
  display alert “This script must be run from the main thread.” buttons {“Cancel”} as critical
  
error number -128
end if

– create a view
set theView to current application’s NSView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 100, 200))

– create date picker
set datePicker to current application’s NSDatePicker’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, 100, 100))

– set style: choices are NSTextFieldAndStepperDatePickerStyle, NSClockAndCalendarDatePickerStyle, or NSTextFieldDatePickerStyle
datePicker’s setDatePickerStyle:(current application’s NSClockAndCalendarDatePickerStyle)

– set elements: choices include NSHourMinuteDatePickerElementFlag, NSHourMinuteSecondDatePickerElementFlag, NSTimeZoneDatePickerElementFlag, NSYearMonthDatePickerElementFlag, and NSEraDatePickerElementFlag
datePicker’s setDatePickerElements:((current application’s NSYearMonthDayDatePickerElementFlag) + (current application’s NSHourMinuteSecondDatePickerElementFlag as integer))

– set initial date
datePicker’s setDateValue:(current application’s NSDate’s |date|())

– get the size it needs
set theSize to datePicker’s fittingSize()

–resize the picker and view accordingly
theView’s setFrameSize:theSize
datePicker’s setFrameSize:theSize

– add the picker to the view
theView’s setSubviews:{datePicker}

– create an alert
set theAlert to current application’s NSAlert’s alloc()’s init()

– set up alert
tell theAlert
  its setMessageText:“Pick a date”
  
its setInformativeText:“Any date”
  
its addButtonWithTitle:“OK”
  
its addButtonWithTitle:“Cancel”
  
its setAccessoryView:theView
end tell

– show alert in modal loop
set returnCode to theAlert’s runModal()
if returnCode = (current application’s NSAlertSecondButtonReturn) then error number -128

– retrieve date
set theDate to ASify from (datePicker’s dateValue()) – or simply coerce to date in 10.11

–>  date “2015年8月20日木曜日 19:43:58″

★Click Here to Open This Script 

2015/07/23 ASOCでフォルダの連続作成1万個

ASOCでCocoaの機能を用いたフォルダ作成のテストを行いました。比較用にCocoaの機能を用いずにFinder経由で作成したものと、do shell script経由でシェルコマンドの機能を用いたものを用意して比較。

filegraph1.png

Cocoa経由で作成したものが非常に高速(Finder経由の7倍速ぐらい)ということが分かりました。ただし、テスト所要時間が実行するたびに変わり、ブレがあるのが特徴です(Finder経由で作成したときにはあまり見られない現象)。

テストはMacBook Pro Retina 2012 (Core i7 2.6GHz)、OS X 10.10.5上で実施。もちろん、HDDではなくSSDを使っていればこその速度です。

ASOCのテスト1は常識的なフォルダ作成のルーチンを作って呼び出したもの、テスト2は速度をかせぐために「連番のフォルダを作成する」という機能のかたまりを作成して、サブルーチンにはしなかったものです。ねらいどおり、若干後者の方が高速ですが・・・逆に、NSFileManager’s defaultManager() を1万回呼び出して無駄にオブジェクトを生成しても(テスト1)たいしたオーバーヘッドにならないんだなーということが体感的によく分かりました。

念のため、do shell scirpt経由でのフォルダ作成をためしてみたところ、予想外に時間がかかりました。do shell scirptコマンドによるシェルコマンド呼び出しのオーバーヘッドが大きいことが分かります(シェルコマンドが遅いのではなく、呼び出しのためのオーバーヘッドが大きい。シェル側からosascriptコマンドでAppleScriptを呼び出すと同様の現象が起こる)。

あまりにdo shell script使用バージョンが遅かったので、「mkdir -p」ではなく「mkdir」で実行してみたものの、大差はありませんでした。

Shane Stanleyが「do shell scriptコマンドなんか使うのやめようよ」(Cocoaの機能を呼び出したほうがいいぞ)と言っていた理由がよ〜くわかりました。

AppleScript名:ASOCでフォルダ作成テスト1
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

repeat with i from 1 to 10000
  set aDirStr to “Desktop/test/” & (i as string)
  
set aRes to makeDirUnderHome(aDirStr) of me
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 5.814414978027 (MacBook Pro Retina 2012, Core i7 2.6GHz)

on makeDirUnderHome(dirStr)
  
  
set homeDir to current application’s NSHomeDirectory()
  
set dirPath to homeDir’s stringByAppendingPathComponent:dirStr
  
set fileManager to current application’s NSFileManager’s defaultManager()
  
  
  
–Sample with continuation mark (¬) , similar to Objective-C format
  
set aRes to fileManager’s createDirectoryAtPath:dirPath ¬
    withIntermediateDirectories:true ¬
    
attributes:(missing value) ¬
    
|error|:(reference)
  
  
–Sample without continuation mark (¬)
  
–set aRes to fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
  
  
copy aRes to {aFlag, aError}
  
return aFlag as boolean
  
end makeDirUnderHome

★Click Here to Open This Script 

AppleScript名:ASOCでフォルダ作成テスト2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set homeDir to current application’s NSHomeDirectory()
set fileManager to current application’s NSFileManager’s defaultManager()

repeat with i from 1 to 10000
  
  
set aDirText to “Desktop/test/” & (i as string)
  
set dirPath to (homeDir’s stringByAppendingPathComponent:aDirText)
  
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 4.124059021473 (MacBook Pro Retina 2012, Core i7 2.6GHz)

★Click Here to Open This Script 

AppleScript名:ASで(Finder経由で)フォルダ作成テスト(比較用)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”

tell application “Finder”
  
  
tell folder dtPath
    if (exists of folder “test”) = false then
      (make new folder with properties {name:“test”})
    end if
  end tell
  
  
repeat with i from 1 to 10000
    make new folder with properties {name:(i as string)} at folder tFolStr
  end repeat
  
end tell

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 37.555700004101

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用2)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir -p “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 272.857131958008

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用3)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

do shell script “mkdir -p “ & tFolPosix

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 283.687083005905

★Click Here to Open This Script 

2015/03/04 現在地点の緯度経度情報を取得する

This AppleScript get current location of your Mac by using CoreLocation. There is need to enable WiFi to get location using this AppleScript.

You can get altitude, timestamp, speed and course easily. But latitude and longitude were difficult to get.

I asked to Shane how to do this. His answer is “get description”.

You must execute this AppleScript in foreground. Original Script is made for a part of ASOC project on Xcode.

loc1.png
▲システム環境設定の「セキュリティとプライバシー」でScript EditorもしくはASObjC Explorer 4に位置情報の取得を許可しておく必要があります

loc2.png
▲許可しないとエラーが表示されます

本AppleScriptはお使いのMacの現在位置の緯度経度情報を取得するものです。CoreLocationを用いて、高度、タイムスタンプ、スピード、進行方向については容易に取得できました(位置情報を取得するためにWiFiをオンにしておく必要あり)。ただ、肝心の緯度経度情報だけはなかなか取得できず、Shaneに聞いてみたところ・・・「descriptionを取得しろ」とのこと(サンプルつきで)。

本AppleScriptの実行時には、foregroundで明示的に実行させる必要があります。オリジナルのScriptはASOC on Xcodeの一部として書いたもので、位置情報の取得/許可については本Scriptの方がうまくいっていない感じです(ASObjC Explorerがコケたりする)。

AppleScript名:GetCurrentLocation_Yosemite v3
– Created 2015-03-04 by Takaaki Naganoya, Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “CoreLocation”
use framework “AppKit”

property curLatitude : 0
property curLongitude : 0

on run
  startStandardUpdates()
end run

on startStandardUpdates()
  –Listing 1-1 Starting the standard location service
  
set locationManager to current application’s CLLocationManager’s alloc()’s init()
  
  
set locE to locationManager’s locationServicesEnabled()
  
if (locE as boolean) = true then
    locationManager’s setDelegate:me
    
locationManager’s setDesiredAccuracy:(current application’s kCLLocationAccuracyNearestTenMeters)
    
locationManager’s setDistanceFilter:500
    
locationManager’s startUpdatingLocation()
  end if
  
end startStandardUpdates

on locationManager:manager didUpdateLocations:locations
  
  
–Listing 1-3 Processing an incoming location event
  
set location to (locations’s lastObject())
  
set eventDate to (location’s timestamp())
  
set howRecent to (eventDate’s timeIntervalSinceNow())
  
  
–Calc ABS
  
set howRecent to howRecent as real
  
set howRecent to absNum(howRecent)
  
  
if howRecent < 15.0 then
    
    
set alt to location’s altitude
    
–>  (NSNumber) 46.356517791748
    
    
set aSpeed to location’s speed
    
–>  (NSNumber) -1.0
    
    
set aCourse to location’s course –North:0, East:90, South:180, West:270
    
–>  (NSNumber) -1.0
    
    
–By Shane Stanley
    
set theDescription to location’s |description|()
    
–> (NSString) “< +35.xxxxx,+139.xxxxxx> +/- 65.00m (speed -1.00 mps / course -1.00) @ 2015/03/04 8時56分41秒 日本標準時”
    
    
set anNSScanner to current application’s NSScanner’s scannerWithString:theDescription
    
anNSScanner’s setCharactersToBeSkipped:(current application’s NSCharacterSet’s characterSetWithCharactersInString:“< ,")
    
set {theResult, aLat} to anNSScanner’s scanDouble:(reference)
    
set {theResult, aLng} to anNSScanner’s scanDouble:(reference)
    
    
copy {aLat, aLng} to {my curLatitude, my curLongitude}
    
  else
    stopUpdatingLocation()
  end if
  
end locationManager:didUpdateLocations:

on locationManager:anCLLocationManager didFailWithError:anNSError
  display dialog (anNSError’s localizedDescription() as text)
end locationManager:didFailWithError:

on absNum(aNum)
  if aNum > 0 then
    return aNum
  else
    return (aNum * -1)
  end if
end absNum

★Click Here to Open This Script 

2015/02/25 Conversion between UTCTime String and NSDate

Conversion ASOC script between UTCTime String (with milli-seconds) and NSDate.

In US Apple’s AppleScript Users ML, I saw the thread “UTC time with milliseconds”.

They talk about only current date -> UTCTime string (with milli-seconds) . And there is no care of each time zone.

So, I added time zone consideration and reverse conversion handler.

Reverse conversion (UTCTime string -> NSDate) does not need to get users time zone, I think.

Ole Begemann’s Working with Date and Time in Cocoa was very useful and helpful for me.

Shane Stanley taught me this is not right. Oh! my Buddha! (something like God..)

I misunderstood the definition of the word “UTCTime”. I meant it as a time string. So, I’ll fix this later. Hmm..I don’t need to get “UTCTime” string….Is there any need to get it?

AppleScript名:UTCTime StringとNSDateの相互変換
– Created 2015-02-24 by Shane Stanley
– Changed 2015-02-25 By Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aStr to retUTCTimeString()
–>   “2015-02-25T13:49:55.713″

set aNSDate to retNSDateFromUTCString(aStr)
–>  (NSDate) 2015-02-25 13:49:55 +0000 –ASObjC Explorer
–> «class ocid» id «data optr0000000030F7400000600000» –Apple’s Script Editor

–Current Date -> UTCTime String
on retUTCTimeString()
  –There is need to get Current Calendar in my Time Zone
  
set aCalendar to current application’s NSCalendar’s currentCalendar()
  
set aTimeZone to (aCalendar’s timeZone)
  
set tDiff to (aTimeZone’s secondsFromGMT())
  
  
set theNSDateFormatter to current application’s NSDateFormatter’s alloc()’s init()
  
  
theNSDateFormatter’s setDateFormat:“yyyy-MM-dd’T’HH:mm:ss.SSS”
  
theNSDateFormatter’s setTimeZone:(current application’s NSTimeZone’s timeZoneForSecondsFromGMT:tDiff)
  
  
return (theNSDateFormatter’s stringFromDate:(current application’s NSDate’s |date|())) as text
end retUTCTimeString

–UTCTime String -> NSDate
on retNSDateFromUTCString(aText)
  set aStr to current application’s NSString’s stringWithString:aText
  
  
set theNSDateFormatter to current application’s NSDateFormatter’s alloc()’s init()
  
theNSDateFormatter’s setDateFormat:“yyyy-MM-dd’T’HH:mm:ss.SSS”
  
theNSDateFormatter’s setTimeZone:(current application’s NSTimeZone’s timeZoneForSecondsFromGMT:0)
  
  
return theNSDateFormatter’s dateFromString:aStr
end retNSDateFromUTCString

★Click Here to Open This Script 

2014/11/08 ASObjCExtras.frameworkのつかいかた〜2D Listのソート

ASObjCExtras.frameworkで2DのListをソートする機能を利用してみました。

AppleScriptの世界でよく利用される、ネストしたリスト、2次元配列とか2D Listとかいろいろ呼び方はありますが・・・これをソートするのにCocoaの機能を呼び出したい。

・・・のですが、なかなか同じようなものが見つからない。むしろ、Recordのソートなんかは手軽に行えるんですが、2D Listのソートがなかなか。

というニーズを受けて(?)、ASObjCExtras.frameworkの中に当たり前のように2D Listのソート機能が用意されています。

AppleScriptだけで組んだ場合と比較して(10万レコード時で)、6倍ぐらい高速に処理できます(MacBook Pro Retina Mid 2012にて計測)。

念のために、ベンチマークに用いたASOCのプログラムとAppleScriptのプログラムの両方を掲載しておきます。

graph1.png

10万レコードの2D Listの場合、AppleScriptだけでは40秒近くかかるところが7秒。そこまで大量のデータを処理するケースは少ないですが、高速なソートルーチンが使えるのであれば、使えたほうがいいに決まっています。

AppleScript名:ASObjCExtrasのじっけん_2Dリストのソート (asoc)
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

script orig
  property aList : {}
end script

set aList of orig to {}

set aList to {}
repeat 10000 times
  set the end of aList of orig to {“a”, random number from 1 to 10, random number from 1 to 10, random number from 1 to 100}
end repeat

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

–2D Listのソート
set sortIndexes to {1, 2, 3} –Key Item id: begin from 0
set sortOrders to {true, true, true}
set sortTypes to {“compare:”, “compare:”, “compare:”}
set resList to (current application’s SMSFord’s subarraysIn:(aList of orig) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

return c1Dat

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

AppleScript名:2dlist_sort_as
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

script orig
  property aList : {}
end script

set aList of orig to {}

–複数キーによるソートテスト3(複数キーを許容)
set aList to {}
repeat 10000 times
  set the end of aList of orig to {“a”, random number from 1 to 10, random number from 1 to 10, random number from 1 to 100}
end repeat

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set resList to multiKeySortDescending(aList of orig, {2, 3, 4}) of multiKeySort

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

return c1Dat

–複数キーによるソート
script multiKeySort
  
  
script spd
    property aList : {} –ソート対象の2D Listが入る
    
property bList : {} –1次キーでソートした結果が入る
    
property cList : {} –1次キーでソートした結果のうち、1次キーで同じ値が連続する範囲が入る {{1,3},{10,20}}
    
    
property dList : {} –2次キーでソートする対象の一部のリストが入る(ワーク用)
    
property eList : {} –2次キーでソートした結果のリストが入る(ワーク用)
  end script
  
  
  
–複数キー(Primary, Secondary……)による降順ソート。キーは指定用のリストに入れる
  
on multiKeySortDescending(aList, keyList)
    
    
–Initialize
    
set aList of spd to {}
    
set bList of spd to {}
    
    
    
–Param Check
    
set aLen to length of keyList
    
if class of keyList is not equal to list then
      return false –キー値のリストがlistではなかった場合
    end if
    
    
–ソート対象の2D Listの要素数を取得
    
set tmpLen to length of first item of aList
    
repeat with i in keyList
      if i > tmpLen then
        return false –キー値として指定した内容が、ソート対象のリストの要素数よりも大きかった場合
      end if
    end repeat
    
    
    
set firstKeyNo to first item of keyList
    
    
–1次キーで2D Listをソート(降順)
    
set bList of spd to shellSortListDescending(aList, firstKeyNo) of me
    
    
–複数キーによるソート検証および実行ループ
    
repeat with iii from 1 to aLen - 1
      
      
set cList of spd to {}
      
set dList of spd to {}
      
set eList of spd to {}
      
      
–n次キーの値が連続する箇所を探す
      
set curData to missing value
      
      
set sucF to false –データ連続箇所検出中フラグ(false=非連続、true=連続中)
      
set biginItem to 0
      
set endItem to 0
      
      
set itemC to 0
      
      
repeat with i in bList of spd
        set thisData to item (item iii of keyList) of i –n次キー
        
        
–現在の値と前の値が等しい(連続箇所を検出した、あるいは連続箇所の中にいる)
        
if curData = thisData then
          
          
if sucF = false then
            set biginItem to itemC
            
set sucF to true
          else if sucF = true then
            –連続箇所の検索継続中、何もしない
          end if
          
        else
          –現在の値と前の値が等しくない(連続していない、あるいは連続箇所の末尾を検出した)
          
if sucF = true then
            set the end of cList of spd to {biginItem, itemC}
            
set sucF to false
          end if
          
          
set curData to thisData
          
        end if
        
        
set itemC to itemC + 1
        
      end repeat
      
      
–n次キーの連続状態の検出中のままリスト末尾に来た場合には、最終データを出力する
      
if sucF = true and curData = thisData then
        set the end of cList of spd to {biginItem, itemC}
      end if
      
      
      
–n次キーによる重複箇所がない場合には、n次キーによるソート結果をそのまま返す
      
if cList of spd = {} then
        return bList of spd
      end if
      
      
–n+1次キーによる部分ソートし直し
      
repeat with i in cList of spd
        set {tmpB, tmpE} to i
        
        
copy items tmpB thru tmpE of (bList of spd) to (dList of spd)
        
set (eList of spd) to shellSortListDescending((dList of spd), (item (iii + 1) of keyList)) of me
        
        
set tmpCounter to 1
        
repeat with ii from tmpB to tmpE
          copy item tmpCounter of (eList of spd) to item ii of (bList of spd)
          
set tmpCounter to tmpCounter + 1
        end repeat
      end repeat
      
    end repeat
    
    
return (bList of spd)
    
  end multiKeySortDescending
  
  
–シェルソートで入れ子のリストを降順ソート
  
on shellSortListDescending(aSortList, aKeyItem)
    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 (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of 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 shellSortListDescending
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/11/05 秒以下の単位で時間計測

Cocoaの機能を利用して、秒以下の単位で時間計測を行うAppleScriptです。

US AppleのAppleScriptObjC Users MLでShane Stanleyから「ベンチマーク的な時間計測ならこっちの方が」と教えてもらったものです。

NSDateのtimeIntervalSinceReferenceDateメソッドを利用し、システムの絶対基準日(2001年1月1日 00:00:00 GMT)と現在の日時との間隔を取得、AppleScriptで秒以下の単位での計測が可能になります。

Macの新製品が出ると、Apple StoreなどでAppleScriptを打ち込んで時間計測していますが(なんか、1980年代のノリみたいだ。家電屋に置かれていた8ビットのパソコンにBASICのプログラムを入力して時間を計っていた、、)、秒単位以下の計測がすぐにできれば、10倍とか100倍のループを回して細かい時間を取得するといったことも不要に、、、

AppleScript名:asoc_秒以下の単位で時間計測
–By Shane Stanley

use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

delay 5

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

–> 5.016112983227–マシンの実行速度によって違うはず

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/11/17 AppleScriptObjCでNSDatePickerに値を設定、取得

AppleScriptObjCで、ウィンドウ上に設置したNSDatePickerに日付の値を設定したり、NSDatePickerから値を取得するサンプルです。

NSDatePicker(以下、DatePicker)は、日付情報を入力するための標準GUI部品。最近は、カレンダー.appで使われているようなカレンダー表示付きのDatePickerなどもあり、これも普通に利用できるとよいでしょう(最悪、自前でカレンダー表示インタフェースを実装してカレンダーから日付を選択してもいいですし)。

DatePickerから値を取得するサンプルは海外のBBSにもあったのですが、DatePickerに日付情報を設定する処理が見つからなかったので、そこを補ってみました。

date1.png
▲起動時にDatePickerに現在の日付を設定

date2.png
▲ボタンをクリックすると、DatePickerから各種日付情報を取り出してダイアログにテスト表示

本サンプルでは、GMT(グリニッジ標準時)との時差を+0000で指定していますが、本来の意味においては正しくないような気がします。日本標準時(JST)はGMT+9.0なので、そのあたりを正しく指定して大丈夫なようにタイムゾーンを考慮してNSDateを指定できるとよいだろうか、と(ぼんやり)考えています。

AppleScriptのdateオブジェクトにはタイムゾーンの要素はないので、AppleScript的な処理の方法とCocoa的な価値観の間で揺れている「どちらから見ても中途半端」な感じのコードになっていますが、AppleScriptObjCのサンプルコードがAppleから出ているわけでもないので、とりあえずは「こうやれば動く」的なコードを出してみて、「ここはこうすべき」的な意見が出れば変えていく、というところでしょうか。

このサンプルが「とりあえず動く」レベルであることを認識する必要があり、同時に複数のタイムゾーン上の日付を扱うと(このレベルの実装だと)混乱するよ、ということを参考までに書いておきます(滅多にないんですけれども)。

本掲載リストは、Xcodeから選択中のAppleScriptファイル(テキストファイル)を取得して、AppleScriptObjC Explorer 2をコントロールしてテキストエディットに書式つきデータでコピペして、テキストエディットからHTML書き出し(AppleScriptで自動処理)しているのですが……Xcodeの外部エディタのAppleScriptObjC Explorer 2を経由するために、ハンドラの書式が「setString_(aVar)」スタイルから、「setString:aVar」スタイルに書き換えられてしまっています。

添付のXcodeプロジェクト中では「setString_(aVar)」スタイルで記述していますので、OS X 10.8上でも編集、実行できるはずです。

→ Xcodeプロジェクトのダウンロード(50KBytes)

AppleScriptObjCファイル名:datePickAppDelegate.applescript

– datePickAppDelegate.applescript
– datePick

– Created by Takaaki Naganoya on 09/11/22.
– Copyright 2009 Takaaki Naganoya. All rights reserved.


script datePickAppDelegate
  property parent : class “NSObject”
  
property datePicker : missing value
  
  
  
  on applicationWillFinishLaunching:aNotification
    
    tell current application
      set aCurDate to current date
      
set aYear to (year of aCurDate) as string
      
set aMonth to (month of aCurDate as number) as string
      
set aDay to (day of aCurDate) as string
      
    end tell
    
    set dStr to aYear & “-” & aMonth & “-” & aDay & ” 00:00:00 +0000″
    
log dStr
    
    set aDate to current application’s NSDate’s dateWithString:dStr
    
log aDate
    
    datePicker’s setDateValue:aDate
    
  end applicationWillFinishLaunching:
  
  on applicationShouldTerminate:sender
    return true
  end applicationShouldTerminate:
  
  
  
  
on clicked:sender
    
    set theDate to my datePicker’s dateValue()
    
set theCal to current application’s class “NSCalendar”’s currentCalendar()
    
set theComponents to theCal’s components:254 fromDate:theDate
    
    –Get Date properties
    
set theYear to theComponents’s |year|()
    
set theMonth to theComponents’s |month|()
    
set theDay to theComponents’s |day|()
    
set theHour to theComponents’s hour()
    
set theMinute to theComponents’s minute()
    
set theSecond to theComponents’s |second|()
    
    display dialog ((theYear as text) & return & theMonth as text) & return & theDay as text
    
  end clicked:
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に