プログラミング/CoffeeScript の履歴(No.3)
更新CoffeeScript†
もっと簡単に JavaScript を書きたい、という人向けに作られた言語だそうです。
Play Framework には自動的に JavaScript に変換してくれる機構が備わっているので シームレスに簡単に使うことができます
メリット†
- JavaScript より簡潔に書ける
- JavaScript で気をつけなければならない点のいくつかを自動的にカバーしてくれる
- まずまず書いてて気持ちいい
デメリット or 注意点†
- 普通の JavaScript に戻れなくなる
- 普通の for( ; ; ) が書けないのが痛いときがある
for ... in ... と for ... of ... の見分けが付きにくい†
JavaScript では for ... in ... は配列要素を列挙するための文法ではなく、 連想配列のキーを列挙するための文法なのですが、
http://d.hatena.ne.jp/amachang/20070202/1170386546
CoffeeScript では配列の列挙に使えるようになっています。
http://coffeescript.org/#loops
じゃ、連想配列のキーを列挙するにはどうするかというと、 for ... of ... なんだそうです。
わかりづらすぎる・・・
書いてみた†
日本の休日を求めるスクリプト†
まあ、車輪の再発明っぽいところもあるわけですが、 祝日名が取れて、なおかつメンテナンスしやすい形の ライブラリが見付からなかったので。
一応、https://gist.github.com/Songmu/703311 と突き合わせて 齟齬がないことを確認しましたが、運用は個々の責任でお願いします。
法律改正などで休日が変更になっても、よほどのことがない限り definition のところをいじるだけで対応できるはず???
ソース†
LANG:coffeescript(linenumber) ### 日本の休日を JavaScript で計算するためのライブラリ Osamu Takeuchi <osamu@big.jp> ChangeLog 2013.04.17 初出 以下の関数を追加する **** Date.prototype.isHoliday(furikae = true) 指定された日が休日かどうかを判定して、休日なら名前を返す 休日でなければ null を返す furikae に false を指定すると振替休日を除く 内部ではキャッシュした値を使って計算するためかなり高速 JavaScript: today = new Date(); holiday = today.isHoliday(); if(holiday) { alert("今日は " + holiday + " です<br/>"); } else { alert("今日は祝日ではありません<br/>"); } **** Date.getHolidaysOf(year, furikae = true) 指定された年の休日を連想配列にして返す 連想配列は "month,day" -> name_of_holiday の形になる furikae に false を指定すると、振替休日および国民の休日を除く 例(JavaScript): today = new Date(); holidays = Date.getHolidaysOf( today.getFullYear() ); for(holiday in holidays) { document.write( holiday.month + "月" + holiday.date + "日は " + holiday.name + " です<br/>" ); } **** Date.prototype.getShifted(year, mon, day, hour, min, sec, msec ) 元の時刻から指定時間だけずらした時刻を生成して返す 負の数も指定できる d = new Date(); d.getShifted(1); # 1年後の時刻 d.getShifted(0, -10); # 10ヶ月前の時刻 d.getShifted(0,0,0,1); # 1時間後の時刻 ### Date.prototype.getShifted = (year, mon, day, hour, min, sec, msec) -> # まずは日付以下の部分を msec に直して処理する res = new Date() res.setTime( @getTime() + (((( day || 0 ) * 24 + ( hour || 0 )) * 60 + ( min || 0 )) * 60 + ( sec || 0 )) * 1000 + (msec||0) ) # 年と月はちょっと面倒な処理になる res.setFullYear res.getFullYear() + ( year || 0 ) + Math.floor( ( res.getMonth() + ( mon || 0 ) ) / 12 ) res.setMonth ( ( res.getMonth() + ( mon || 0 ) ) % 12 + 12 ) % 12 return res ### ヘルパ関数 ### # 年を与えると指定の祝日を返す関数を作成 simpleHoliday = (month, day) -> (year) -> new Date(year, month-1, day) # 年を与えると指定の月の nth 月曜を返す関数を作成 happyMonday = (month, nth) -> (year) -> monday = 1 first = new Date(year, month-1, 1) first.getShifted( 0, 0, ( 7 - ( first.getDay() - monday ) ) % 7 + ( nth - 1 ) * 7 ) # 年を与えると春分の日を返す shunbun = (year) -> date = new Date() date.setTime( -655910271894.040039 + 31556943676.430065 * (year-1949) + 24*3600*1000/2 ) new Date(year, date.getMonth(), date.getDate()) # 年を与えると秋分の日を返す shubun = (year) -> date = new Date() date.setTime( -671361740118.508301 + 31556929338.445450 * (year-1948) + 24.3*3600*1000/2 ) new Date(year, date.getMonth(), date.getDate()) ### 休日データ https://ja.wikipedia.org/wiki/%E5%9B%BD%E6%B0%91%E3%81%AE%E7%A5%9D%E6%97%A5 ### definition = [ [ "元旦", simpleHoliday( 1, 1), 1949 ], [ "成人の日", simpleHoliday( 1, 15), 1949, 1999 ], [ "成人の日", happyMonday( 1, 2), 2000 ], [ "建国記念の日", simpleHoliday( 2, 11), 1967 ], [ "昭和天皇の大喪の礼", simpleHoliday( 2, 24), 1989, 1989 ], [ "春分の日", shunbun, 1949 ], [ "明仁親王の結婚の儀", simpleHoliday( 4, 10), 1959, 1959 ], [ "天皇誕生日", simpleHoliday( 4, 29), 1949, 1988 ], [ "みどりの日", simpleHoliday( 4, 29), 1989, 2006 ], [ "昭和の日", simpleHoliday( 4, 29), 2007 ], [ "憲法記念日", simpleHoliday( 5, 3), 1949 ], [ "みどりの日", simpleHoliday( 5, 4), 2007 ], [ "こどもの日", simpleHoliday( 5, 5), 1949 ], [ "こどもの日", simpleHoliday( 5, 5), 1949 ], [ "徳仁親王の結婚の儀", simpleHoliday( 6, 9), 1993, 1993 ], [ "海の日", simpleHoliday( 7, 20), 1996, 2002 ], [ "海の日", happyMonday( 7, 3), 2003 ], [ "敬老の日", simpleHoliday( 9, 15), 1966, 2002 ], [ "敬老の日", happyMonday( 9, 3), 2003 ], [ "秋分の日", shubun, 1948 ], [ "体育の日", simpleHoliday(10, 10), 1966, 1999 ], [ "体育の日", happyMonday( 10, 2), 2000 ], [ "文化の日", simpleHoliday(11, 3), 1948 ], [ "即位の礼正殿の儀", simpleHoliday(11, 12), 1990, 1990 ], [ "勤労感謝の日", simpleHoliday(11, 23), 1948 ], [ "天皇誕生日", simpleHoliday(12, 23), 1989 ], ] # 休日を与えるとその振替休日を返す # 振り替え休日がなければ null を返す furikaeHoliday = (holiday) -> # 振替休日制度制定前 または 日曜日でない場合 振り替え無し sunday = 0 if holiday < new Date(1973, 4-1, 30-1) or holiday.getDay() != sunday return null # 日曜日なので一日ずらす furikae = holiday.getShifted(0, 0, 1) # ずらした月曜日が休日でなければ振替休日 if !furikae.isHoliday(false) return furikae # 旧振り替え制度では1日以上ずらさない if holiday < new Date(2007, 1-1, 1) return null # たぶんこれに該当する日はないはず? loop # 振り替えた結果が休日だったら1日ずつずらす furikae = furikae.getShifted(0, 0, 1) if !furikae.isHoliday(false) return furikae # 休日を与えると、翌日が国民の休日かどうかを判定して、 # 国民の休日であればその日を返す kokuminHoliday = (holiday) -> if holiday.getFullYear() < 1988 # 制定前 return null # 2日後が振り替え以外の祝日か if !holiday.getShifted(0, 0, 2).isHoliday(false) return null sunday = 0 monday = 1 kokumin = holiday.getShifted(0, 0, 1) if kokumin.isHoliday(false) or # 次の日が祝日 kokumin.getDay()==sunday or # 次の日が日曜 kokumin.getDay()==monday # 次の日が月曜(振替休日になる) return null return kokumin # # holidays[furikae] = { # 1999: # "1,1": "元旦" # "1,15": "成人の日" # ... # } # holidays = { true: {}, false: {} } getHolidaysOf = (y, furikae) -> # キャッシュされていればそれを返す furikae = if !furikae? or furikae then true else false cache = holidays[furikae][y] return cache if cache? # されてなければ計算してキャッシュ # 振替休日を計算するには振替休日以外の休日が計算されて # いないとダメなので、先に計算する wo_furikae = {} for entry in definition continue if entry[2] && y < entry[2] # 制定年以前 continue if entry[3] && entry[3] < y # 廃止年以降 holiday = entry[1](y) # 休日を計算 continue unless holiday? # 無効であれば無視 m = holiday.getMonth()+1 # 結果を登録 d = holiday.getDate() wo_furikae[ [m,d] ] = entry[0] holidays[false][y] = wo_furikae # 国民の休日を追加する kokuminHolidays = [] for month_day of wo_furikae month_day = month_day.split(",") holiday = kokuminHoliday( new Date(y, month_day[0]-1, month_day[1] ) ) if holiday? m = holiday.getMonth()+1 # 結果を登録 d = holiday.getDate() kokuminHolidays.push([m,d]) for holiday in kokuminHolidays wo_furikae[holiday] = "国民の休日" # 振替休日を追加する w_furikae = {} for month_day, name of wo_furikae w_furikae[month_day] = name month_day = month_day.split(",") holiday = furikaeHoliday( new Date(y, month_day[0]-1, month_day[1] ) ) if holiday? m = holiday.getMonth()+1 # 結果を登録 d = holiday.getDate() w_furikae[ [m,d] ] = "振替休日" holidays[true][y] = w_furikae # 結果を登録 return holidays[furikae][y] Date.getHolidaysOf = (y, furikae) -> # データを整形する result = [] for month_day, name of getHolidaysOf(y, furikae) result.push( month : parseInt(month_day.split(",")[0]) date : parseInt(month_day.split(",")[1]) name : name ) # 日付順に並べ直す result.sort( (a,b)-> (a.month-b.month) or (a.date-b.date) ) result Date.prototype.isHoliday = (furikae) -> return getHolidaysOf(@getFullYear(), furikae)[ [@getMonth()+1, @getDate()] ]
JavaScript に直して minify したもの
LANG:javascript_dom (function(){var definition;var furikaeHoliday;var getHolidaysOf;var happyMonday;var holidays;var kokuminHoliday;var shubun;var shunbun;var simpleHoliday;Date.prototype.getShifted=function(year,mon,day,hour,min,sec,msec){var res;res=new Date;res.setTime(this.getTime()+((((day||0)*24+(hour||0))*60+(min||0))*60+(sec||0))*1E3+(msec||0));res.setFullYear(res.getFullYear()+(year||0)+Math.floor((res.getMonth()+(mon||0))/12));res.setMonth(((res.getMonth()+(mon||0))%12+12)%12);return res};simpleHoliday=function(month, day){return function(year){return new Date(year,month-1,day)}};happyMonday=function(month,nth){return function(year){var first;var monday;monday=1;first=new Date(year,month-1,1);return first.getShifted(0,0,(7-(first.getDay()-monday))%7+(nth-1)*7)}};shunbun=function(year){var date;date=new Date;date.setTime(-6.5591027189404E11+3.1556943676430065E10*(year-1949)+24*3600*1E3/2);return new Date(year,date.getMonth(),date.getDate())};shubun=function(year){var date;date=new Date;date.setTime(-6.713617401185083E11+ 3.155692933844545E10*(year-1948)+24.3*3600*1E3/2);return new Date(year,date.getMonth(),date.getDate())};definition=[["元旦",simpleHoliday(1,1),1949],["成人の日",simpleHoliday(1,15),1949,1999],["成人の日",happyMonday(1,2),2000],["建国記念の日",simpleHoliday(2,11),1967],["昭和天皇の大喪の礼",simpleHoliday(2,24),1989,1989],["春分の日",shunbun,1949],["明仁親王の結婚の儀",simpleHoliday(4,10),1959,1959],["天皇誕生日",simpleHoliday(4,29),1949,1988],["みどりの日",simpleHoliday(4,29),1989,2006],["昭和の日",simpleHoliday( 4,29),2007],["憲法記念日",simpleHoliday(5,3),1949],["みどりの日",simpleHoliday(5,4),2007],["こどもの日",simpleHoliday(5,5),1949],["こどもの日",simpleHoliday(5,5),1949],["徳仁親王の結婚の儀",simpleHoliday(6,9),1993,1993],["海の日",simpleHoliday(7,20),1996,2002],["海の日",happyMonday(7,3),2003],["敬老の日",simpleHoliday(9,15),1966,2002],["敬老の日",happyMonday(9,3),2003],["秋分の日",shubun,1948],["体育の日",simpleHoliday(10,10),1966,1999],["体育の日",happyMonday(10,2),2000],["文化の日",simpleHoliday(11,3),1948], ["即位の礼正殿の儀",simpleHoliday(11,12),1990,1990],["勤労感謝の日",simpleHoliday(11,23),1948],["天皇誕生日",simpleHoliday(12,23),1989]];furikaeHoliday=function(holiday){var furikae;var sunday;var _results;sunday=0;if(holiday<new Date(1973,4-1,30-1)||holiday.getDay()!==sunday)return null;furikae=holiday.getShifted(0,0,1);if(!furikae.isHoliday(false))return furikae;if(holiday<new Date(2007,1-1,1))return null;for(_results=[];true;){furikae=furikae.getShifted(0,0,1);if(!furikae.isHoliday(false))return furikae}return _results}; kokuminHoliday=function(holiday){var kokumin;var monday;var sunday;if(holiday.getFullYear()<1988)return null;if(!holiday.getShifted(0,0,2).isHoliday(false))return null;sunday=0;monday=1;kokumin=holiday.getShifted(0,0,1);if(kokumin.isHoliday(false)||kokumin.getDay()===sunday||kokumin.getDay()===monday)return null;return kokumin};holidays={"true":{},"false":{}};getHolidaysOf=function(y,furikae){var cache;var d;var entry;var holiday;var kokuminHolidays;var m;var month_day;var name;var w_furikae;var wo_furikae; var _i;var _j;var _len;var _len2;furikae=!(furikae!=null)||furikae?true:false;cache=holidays[furikae][y];if(cache!=null)return cache;wo_furikae={};for(_i=0,_len=definition.length;_i<_len;_i++){entry=definition[_i];if(entry[2]&&y<entry[2])continue;if(entry[3]&&entry[3]<y)continue;holiday=entry[1](y);if(holiday==null)continue;m=holiday.getMonth()+1;d=holiday.getDate();wo_furikae[[m,d]]=entry[0]}holidays[false][y]=wo_furikae;kokuminHolidays=[];for(month_day in wo_furikae){month_day=month_day.split(","); holiday=kokuminHoliday(new Date(y,month_day[0]-1,month_day[1]));if(holiday!=null){m=holiday.getMonth()+1;d=holiday.getDate();kokuminHolidays.push([m,d])}}for(_j=0,_len2=kokuminHolidays.length;_j<_len2;_j++){holiday=kokuminHolidays[_j];wo_furikae[holiday]="国民の休日"}w_furikae={};for(month_day in wo_furikae){name=wo_furikae[month_day];w_furikae[month_day]=name;month_day=month_day.split(",");holiday=furikaeHoliday(new Date(y,month_day[0]-1,month_day[1]));if(holiday!=null){m=holiday.getMonth()+ 1;d=holiday.getDate();w_furikae[[m,d]]="振替休日"}}holidays[true][y]=w_furikae;return holidays[furikae][y]};Date.getHolidaysOf=function(y,furikae){var month_day;var name;var result;var _ref;result=[];_ref=getHolidaysOf(y,furikae);for(month_day in _ref){name=_ref[month_day];result.push({month:parseInt(month_day.split(",")[0]),date:parseInt(month_day.split(",")[1]),name:name})}result.sort(function(a,b){return a.month-b.month||a.date-b.date});return result};Date.prototype.isHoliday=function(furikae){return getHolidaysOf(this.getFullYear(), furikae)[[this.getMonth()+1,this.getDate()]]}}).call(this);
これを使ってカレンダーを表示†
休日セルにマウスを重ねると休日名がポップアップします。
使うときは、
LANG:coffeescript target = document.getElementById("target_id"); target.appendChild( createCalendar(2013, 3) ); target.appendChild( createCalendar(2013, 4) ); target.appendChild( createCalendar(2013, 5) );
などとします。
LANG:coffeescript(linenumber) createElement = (tag, parent, innerHTML) -> child = document.createElement(tag) parent.appendChild(child) if parent? child.innerHTML = innerHTML if innerHTML? child createCalendar = (year, month) -> calendar = createElement("div"); calendar.className = "calendar" title = createElement("div", calendar); title.className = "title" a = createElement("a", title, year+"/"+month) a.name = "#"+year+"/"+month tab = createElement("table", calendar); d = new Date(year+"/"+month+"/1") i = 0 while(i < d.getDay()) tr = createElement("tr", tab) if (i==0) createElement("td", tr).className = "empty" i++ while(month == d.getMonth() + 1) tr = createElement("tr", tab) if (d.getDay()==0) td = createElement("td", tr, d.getDate()) holiday = d.isHoliday() if holiday td.className = "holiday" td.setAttribute("holiday", holiday) d = d.getShifted(0,0,1) return calendar
土曜日と日曜日は css の nth-child で色を付けられる。
LANG:css div.calendar { float: left; padding: 5px; } div.calendar td { font-family: Arial, Helvetica, Sans-serif; } div.calendar div.title { text-align: center; font-size: large; font-weight: bold; background: #666; color: #eee; } div.calendar table { margin:auto; } div.calendar table tr td { text-align: center; color: grey; } div.calendar table tr td:nth-child(7):not(.empty):not(.holiday) { background: #cce; } div.calendar table tr td.holiday, div.calendar table tr td:first-child:not(.empty) { background: #ecc; } div.calendar table tr td.holiday:hover:after { content: attr(holiday); position: absolute; margin-top: -2em; background: #ecc; padding: 2px; box-shadow: 1px 1px 3px grey; -moz-box-shadow: 1px 1px 3px grey; -webkit-box-shadow: 1px 1px 3px grey; }
コメント・質問†
Counter: 10937 (from 2010/06/03),
today: 1,
yesterday: 2