Archive for the 'NSCalendar' Category

2016/02/10 ISO8601ちっくな週カウント

Cocoaの機能を用いて指定日付が1年のうちの何週目にあたるか、一般常識的なカウントと、ISO8601で規定されている週カウントの両方で計算するAppleScriptです。

iso8601wc.png

ISO8601で規定している週番号では、1/1が木曜日から土曜日までの間にある場合には前年の12月の最終週の続きとして扱われます。aWNがiCalの画面上に表示される週番号、bWNがISO8601に則ってカウントした週番号です。

AppleScript名:ISO8601ちっくな週カウント
– Created 2016-02-10 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aNSDate to makeNSDate(2016, 1, 1) of me
set aCal to current application’s NSCalendar’s currentCalendar()
aCal’s setMinimumDaysInFirstWeek:1
set aWN to (aCal’s components:(current application’s NSWeekCalendarUnit) fromDate:aNSDate)’s week()

aCal’s setMinimumDaysInFirstWeek:4 –ISO8601 Week Count
set bWN to (aCal’s components:(current application’s NSWeekCalendarUnit) fromDate:aNSDate)’s week()

return {aWN, bWN}
–>  {1, 52}

–Y,M,Dを指定してNSDateを作成
on makeNSDate(aYear as integer, aMonth as integer, aDay as integer)
  set aComp to current application’s NSDateComponents’s alloc()’s init()
  
aComp’s setDay:aDay
  
aComp’s setMonth:aMonth
  
aComp’s setYear:aYear
  
set aGrego to current application’s NSCalendar’s calendarWithIdentifier:(current application’s NSGregorianCalendar)
  
set aDate to aGrego’s dateFromComponents:aComp
  
return aDate
end makeNSDate

★Click Here to Open This Script 

2015/08/27 100%クラッシュするNSCalendarの操作

AppleScriptObjCでNSCalendarをテストしだしたところ、実行するScript Editorが100%クラッシュする記述に遭遇。OS X 10.10.5でも、OS X 10.11 Betaでもクラッシュします(バグレポートずみ)。

# OS X 10.10.x/10.11.xでこれを実行してクラッシュしない環境があったら、逆におしえていただきたいです(2台でともに100%クラッシュ)

MacScriptersのBBSでもクラッシュの話が(ずいぶん前から)出ており、有名な話のようです。

*** CAUTION: THIS SCRIPT CAUSE CRASH ***

AppleScript名:ASOCでNSCalendarのじっけん(100%クラッシュ)
– Created 2015-08-27 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aCal to current application’s NSCalendar’s alloc()’s initWithCalendarIdentifier:(current application’s NSGregorianCalendar)

★Click Here to Open This Script 

Shane Stanleyから「こう書くとクラッシュを避けられるよ」というのを教えてもらいました。本当に重要ですよね、落とし穴がどこにあいているかの情報は、、、

AppleScript名:クラッシュしないNSCalendarのじっけん
– Created 2015-08-28 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aCal to current application’s NSCalendar’s calendarWithIdentifier:(current application’s NSGregorianCalendar)
–>  (_NSCopyOnWriteCalendarWrapper) <_nscopyonwritecalendarwrapper : 0x610000239e20>

★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 

2015/02/02 2月1日が日曜日でうるう年ではないかチェック(ASOC)

先日の「2月1日が日曜日でうるう年ではない(1日から28日までびっしり数字が入っている)」年を検出する(技術の無駄遣い的な ^ー^;)AppleScriptのASOC版です。Shane Stanleyが送ってくれました(こんなくだらないScriptに付き合わせて申し訳ない、、、)。

3,000年分計算して、だいたい0.8秒ぐらい。Pure AppleScript版より2倍以上高速ですが、3,000年程度の計算ではASOCの真の力が発揮できません。

AppleScript名:2月1日が日曜日でうるう年ではないかチェック(ASOC)
– Created 2015-02-02 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set resYList to {} –hit year

set theNSCalendar to current application’s NSCalendar’s currentCalendar() – do *not* use initWithCalendarIdentifier:

repeat with y from 0 to 3000
  set aMlen to getMlenAndVerifyFirstDaysNum(y, 2, theNSCalendar, 28, 1) of me
  
if aMlen = true then
    set the end of resYList to y
  end if
  
–set the end of resYList to aMlen
end repeat

return resYList

on getMlenAndVerifyFirstDaysNum(aYear, aMonth, theNSCalendar, dMax, fdayNum)
  
  
–現在のLocaleのCalendarで指定年月の日数をかぞえる
  
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
  
–>  {location:1, length:31}
  
set mLen to |length| of theResult
  
  
–指定年月の1日を取得
  
set theDay to theNSCalendar’s components:((current application’s NSWeekdayCalendarUnit) + (current application’s NSMonthCalendarUnit as integer)) fromDate:theDate
  
  
–指定年月の1日の曜日が指定曜日で、うるう月でなく、指定年月の日数が指定日数(dMax)であるか判定
  
if (theDay’s |weekday|() = fdayNum) and (theDay’s isLeapMonth() is false) and (mLen = dMax) then
    return true
  else
    return false
  end if
  
end getMlenAndVerifyFirstDaysNum

★Click Here to Open This Script 

2015/02/02 国際化対応Mlen ASOC版

Shane Stanleyが送ってくれた、国際化対応MlenのASOC版(を、いろいろ検証して修正したもの)です。

そもそも、「Mlen」とは何か? 指定月(例:2012年2月)の日数を求めるMonth Length計算ルーチンです。そして、日本語環境だけではなく他の言語環境でも動くように配慮したものを「国際化対応Mlen」と呼んでいます。

Cocoaの機能を呼び出すようにした本バージョンでは、スピードアップが図られてはいますが、さすがに極小データの処理だけなので数千回ループで回してようやく「速い」とわかるぐらいです。数万件とか数十万件のオーダーになってくると、3倍以上高速かな? という感じです。

AppleScript名:getMlenInternational_ASOC
– Created 2015-02-02 by Shane Stanley
– Modified 2015-02-02 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set mList to {}
repeat with m from 1 to 12
  set the end of mList to getMlenInternational(2012, m) of me –2012 is a Leap Year–2012年はうるう年
end repeat
mList
–>  {​​​​​31, ​​​​​29, ​​​​​31, ​​​​​30, ​​​​​31, ​​​​​30, ​​​​​31, ​​​​​31, ​​​​​30, ​​​​​31, ​​​​​30, ​​​​​31​​​}

–現在のカレンダーで指定年月の日数を返す
on getMlenInternational(aYear, aMonth)
  –From Shane’s getMlenInternational(ASOC) v1
  
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
  
–>  {location:1, length:31}
  
return |length| of theResult
end getMlenInternational

★Click Here to Open This Script 

2014/12/08 Remindersのイベントの期日を追加する

リマインダー(Reminders.app)の特定のreminderに対して、「期日」を追加するAppleScriptです。

Twitter上で、reminderに「場所」を追加できないだろうかという問いがあって・・・AppleScriptでは、リマインダー(Reminder.app)のAppleScript用語辞書には「場所」の予約語がないので、そのままでは手も足も出ません。ただ、ASOC経由でCocoaのEventKitにアクセスすれば、できそうな雰囲気がありました。

US AppleのAppleScript Users MLの過去ログをあさってみたら、きわめて近い内容のものがあったので、実際に試してみたら・・・OS X 10.10.1上ではエラーに。

ML上でShane Stanleyに聞いてみたら「10.10上では少々直す必要がある」とのことで、その場で教えてもらえました。ドキュメントを読めば分かる的な話ではあったのですが、Appleのオンラインドキュメントには定数の値が書かれていません(自分はここでスタックしていました)。

・・・どうやらShaneのいう「ドキュメント」とは「ヘッダーファイル」のことだったらしいことに後から気づきました。

# Script Editor上で編集中のAppleScriptのuse文をスキャンして、どのヘッダーファイルをオープンするかをユーザーに尋ねてオープンするAppleScriptを書いておく必要がありそう、、、

とりあえず、本Scriptでは「場所」ではなく「期限」を追加します。さらに調査を行えば、「場所」の追加もできそうですが・・・。

eventkit1.png

iCloud上の「ホーム」というリストにある(最新の)reminderに対して、「期限」を追加します。

eventkit2.png

AppleScript名:Remindersのイベントの期日を追加する
– Created 2014-03-08 by Shane Stanley
– Fixed 2014-12-08 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “EventKit”

–とりあえず、1つのreminderのIDを取得
tell application “Reminders”
  tell account “iCloud”
    tell list “ホーム”
      set theID to id of (last reminder whose completed is false)
    end tell
  end tell
end tell

set theID to text 20 thru -1 of theID
–> “x-apple-reminder://B63F627B-A177-4CA7-9811-2AEB4875B407″
setDueDate_forReminderID_(((current date) + (2 * days)), theID)

on setDueDate:ASDate forReminderID:theID
  
  
– ASのdateをNSDateに変換
  
set theNSDate to my makeNSDateFrom:ASDate
  
  
– Cocoaのカレンダーを取得
  
set theCalendar to current application’s NSCalendar’s currentCalendar()
  
  
– Components of dateを取得
  
set theComponents to theCalendar’s componentsInTimeZone:(current application’s NSTimeZone’s localTimeZone()) fromDate:theNSDate
  
  
– get event store; 1 means reminders –> 2
  
set eventStore to current application’s EKEventStore’s alloc()’s initWithAccessToEntityTypes:2
  
  
– get the reminder
  
set theReminder to eventStore’s calendarItemWithIdentifier:theID
  
  
– 「期限」の日付を設定する
  
theReminder’s setDueDateComponents:theComponents
  
  
– 保存
  
eventStore’s saveReminder:theReminder commit:true |error|:(missing value)
  
end setDueDate:forReminderID:

on makeNSDateFrom:theASDate
  
  
set {theYear, theMonth, theDay, theSeconds} to theASDate’s {year, month, day, time}
  
  
if theYear < 0 then
    set theYear to -theYear
    
set theEra to 0
  else
    set theEra to 1
  end if
  
  
set theCalendar to current application’s NSCalendar’s currentCalendar()
  
set newDate to theCalendar’s dateWithEra:theEra |year|:theYear |month|:(theMonth as integer) ¬
    |day|:theDay hour:0 minute:0 |second|:theSeconds nanosecond:0
  
  
return newDate
  
end makeNSDateFrom:

★Click Here to Open This Script 

2014/11/06 現在のLocaleのカレンダーの最初の曜日を取得する

現在のログイン中のユーザーアカウントで(システム環境設定の「言語と地域」で)設定しているカレンダーにおける、どの曜日で始まるかの設定を取得するAppleScriptです。

cal1.png

cal2.png

AppleScript名:asoc_現在のLocateのカレンダーの最初の曜日を取得する v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aCalendar to current application’s NSCalendar’s currentCalendar()
set aFirstDay to (aCalendar’s firstWeekday)
set c to aFirstDay as integer
–> 1 (Sunday Start Calendar)

–フランス、香港などは月曜日がカレンダーの先頭に来るため結果が異なる
–> 2 (Monday Start Calendar)

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

2014/11/04 現在のLocaleのカレンダーのTime Zoneを取得する

現在ログイン中のカレンダー(設定)を取得し、Time Zone名や時差の情報を取得するAppleScriptです。

sys1.png

sys2.png

言語設定によっては、OSレベルで管理しているカレンダーが「日曜日で始まる」か「月曜日で始まる」かといった違いがあります。こうした情報にアクセスするための習作として作成したものです。

GMTとの時差であれば、通常のAppleScriptでも(大昔から)取得できるようになっていますが、どの曜日からカレンダーが始まるかという情報は、Cocoaにアクセスしないとわからないため外せません。

AppleScript名:asoc_現在のLocaleのカレンダーのTime Zoneを取得する
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aCalendar to current application’s NSCalendar’s currentCalendar()
set aTimeZone to (aCalendar’s timeZone)
set tzName to aTimeZone’s |name|() as string
–> “Asia/Tokyo”

set tDiff to (aTimeZone’s secondsFromGMT()) / 3600
–> 9.0

set aDesc to aTimeZone’s |description|() as string
–> “Asia/Tokyo (JST) offset 32400″

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

AppleScript名:普通のASでGMTとの時差を求める
set tDIff to (time to GMT) / 3600
–> 9.0

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

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

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