0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RRULE文字列とEKRecurrenceRuleを変換するAppleScriptハンドラ

Last updated at Posted at 2016-02-05
  • カレンダーやリマインダーを扱うEventKitフレームワーク
    • アプリケーションを起動しなくても操作できる
    • AppleScriptに対応していない項目を取得、設定できる
  • EverntKitフレームワークにあるイベントの繰り返しを管理するEKRecurrenceRuleクラス
    • initRecurrenceWithFrequency:interval:end:メソッドで作成
  • 一方で、一般的なカレンダーで繰り返しルールを表す文字列としてよく使われるRRULE (Recurrence Rule)
    • 例:FREQ=DAILY;INTERVAL=2は「隔日の繰り返し」を意味する
    • iCalendar RFCの一部
  • 両者を相互変換するAppleScriptハンドラを作成
    • EKRecurrenceRuleでは1日未満の繰り返しに対応していないため、FREQ=MINUTELYなどは無視
    • 週の開始曜日を決めるWKSTもEKRucurrenceRuleでは設定できない模様
use scripting additions
use framework "EventKit"
use framework "Foundation"

--Every other day
set recurrenceRule to my recurrenceRuleFromString("FREQ=DAILY;INTERVAL=2")
--> «class ocid» id «data optr00000000200393FFB97F0000»

my stringFromRecurrenceRule(recurrenceRule)
--> "FREQ=DAILY;INTERVAL=2"

on recurrenceRuleFromString(recur as text)
	--require framework: EventKit, Foundation
	
	--recurをパースしてdictionaryに変換
	set RRULE to current application's NSMutableDictionary's dictionaryWithObject:1 forKey:"INTERVAL"
	set scanner to current application's NSScanner's scannerWithString:(recur & ";")
	repeat until scanner's atEnd as boolean
		set {scanResult, |name|} to scanner's scanUpToString:"=" intoString:(reference)
		set scanResult to scanner's scanString:"=" intoString:(missing value)
		set {scanResult, value} to scanner's scanUpToString:";" intoString:(reference)
		set scanResult to scanner's scanString:";" intoString:(missing value)
		try
			
			if |name|'s isEqualToString:"FREQ" then
				set freqDictionary to (current application's NSDictionary's dictionaryWithObjects:{current application's EKRecurrenceFrequencyDaily, current application's EKRecurrenceFrequencyWeekly, current application's EKRecurrenceFrequencyMonthly, current application's EKRecurrenceFrequencyYearly} forKeys:{"DAILY", "WEEKLY", "MONTHLY", "YEARLY"})
				set value to (freqDictionary's objectForKey:value)
				
			else if |name|'s isEqualToString:"UNTIL" then
				if (value's |length| as integer) = 8 then
					set endDate to my dateFromStringUsingFormat(value, "yyyyMMdd")
				else if (value's |length| as integer) = 15 then
					set endDate to my dateFromStringUsingFormat(value, "yyyyMMdd'T'HHmmss")
				else if (value's |length| as integer) = 16 then
					set endDate to my dateFromStringUsingFormat(value, "yyyyMMdd'T'HHmmss'Z'")
					set endDate to my shiftDateFromGMT(endDate)
				end if
				set value to (current application's EKRecurrenceEnd's recurrenceEndWithEndDate:endDate)
				set |name| to "END"
				
			else if |name|'s isEqualToString:"COUNT" then
				set value to (current application's EKRecurrenceEnd's recurrenceEndWithOccurrenceCount:(value's integerValue))
				set |name| to "END"
				
			else if |name|'s isEqualToString:"INTERVAL" then
				set value to value's integerValue
				
			else if |name|'s isEqualToString:"BYDAY" then
				set weekdayDictionary to (current application's NSDictionary's dictionaryWithObjects:{current application's EKSunday, current application's EKMonday, current application's EKTuesday, current application's EKWednesday, current application's EKThursday, current application's EKFriday, current application's EKSaturday} forKeys:{"SU", "MO", "TU", "WE", "TH", "FR", "SA"})
				set recurrenceDayOfWeekArray to current application's NSMutableArray's array()
				repeat with weekdaynum in (value's componentsSeparatedByString:",") as list
					set |weekday| to (weekdayDictionary's objectForKey:(text -2 thru -1 of weekdaynum))
					if (count weekdaynum) = 2 then
						set recurrenceDayOfWeek to (current application's EKRecurrenceDayOfWeek's dayOfWeek:|weekday|)
					else
						set recurrenceDayOfWeek to (current application's EKRecurrenceDayOfWeek's dayOfWeek:|weekday| weekNumber:(text 1 thru -3 of weekdaynum as integer))
					end if
					(recurrenceDayOfWeekArray's addObject:recurrenceDayOfWeek)
				end repeat
				set value to recurrenceDayOfWeekArray
				
			else if (current application's NSArray's arrayWithArray:{"BYMONTHDAY", "BYYEARDAY", "BYWEEKNO", "BYMONTH", "BYSETPOS"})'s containsObject:|name| then
				set value to ((value's componentsSeparatedByString:",")'s valueForKeyPath:"self.integerValue")
				
			else
				set value to |name| & "=" & value
				set |name| to "UNRECOGNIZED NAME"
			end if
			
			--RRULEに追加
			(RRULE's setObject:value forKey:|name|)
			
			--エラー
		on error
			error "Invalid " & |name| & " in Recurrence Rule: " & |name| & "=" & value
		end try
	end repeat
	
	--EKRecurrenceRule作成
	set nameSet to current application's NSSet's setWithArray:(RRULE's allKeys())
	if nameSet's isSubsetOfSet:(current application's NSSet's setWithArray:{"FREQ", "INTERVAL", "END", "UNRECOGNIZED NAME"}) then
		return current application's EKRecurrenceRule's alloc()'s initRecurrenceWithFrequency:(RRULE's objectForKey:"FREQ") interval:(RRULE's objectForKey:"INTERVAL") |end|:(RRULE's objectForKey:"END")
	else
		return current application's EKRecurrenceRule's alloc()'s initRecurrenceWithFrequency:(RRULE's objectForKey:"FREQ") interval:(RRULE's objectForKey:"INTERVAL") daysOfTheWeek:(RRULE's objectForKey:"BYDAY") daysOfTheMonth:(RRULE's objectForKey:"BYMONTHDAY") monthsOfTheYear:(RRULE's objectForKey:"BYMONTH") weeksOfTheYear:(RRULE's objectForKey:"BYWEEKNO") daysOfTheYear:(RRULE's objectForKey:"BYYEARDAY") setPositions:(RRULE's objectForKey:"BYSETPOS") |end|:(RRULE's objectForKey:"END")
	end if
end recurrenceRuleFromString

on stringFromRecurrenceRule(recurrenceRule)
	--require framework: Foundation
	return (recurrenceRule's |description|'s componentsSeparatedByString:space)'s lastObject() as text
end stringFromRecurrenceRule

on dateFromStringUsingFormat(dateString as text, dateFormat as text)
	--reference: UTS #35: Unicode Locale Data Markup Language - http://www.unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns
	set dateFormatter to my currentDateFormatter(dateFormat)
	return (dateFormatter's dateFromString:dateString) as date
end dateFromStringUsingFormat

on shiftDateFromGMT(aDate as date)
	global timeToGMT
	try
		timeToGMT
	on error number -2753
		set timeToGMT to time to GMT
	end try
	return aDate + timeToGMT
end shiftDateFromGMT

on currentDateFormatter(dateFormat as text)
	--require framework: Foundation
	set dateFormatter to current application's NSDateFormatter's alloc()'s init()
	set dateFormatter's locale to current application's NSLocale's currentLocale()
	set dateFormatter's timeZone to current application's NSTimeZone's localTimeZone()
	set dateFormatter's dateFormat to dateFormat
	return dateFormatter
end currentDateFormatter

更新履歴

  • 2016-02-03: EventKitフレームワークを使ってstringFromRecurrenceRuleハンドラを作成
  • 2016-02-05: EKRecurrenceRuleをRRULE文字列に変換するstringFromRecurrenceRuleハンドラを作成
  • 2016-03-04: recurrenceRuleFromStringハンドラでrecurのパースにNSScannerを使用
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?