Googleカレンダーを使って祝日の情報を取得する †
「ある年の何月何日が祝日か」を知るのは、意外と難しいです。中でも春分の日と秋分の日は計算によって予測は付きますが、確定されるのは前年なので、2年後以降の正確な日付は分かりません。よって、祝日がいつかという情報が必要ならば、祝日が決定された後で手動で入力するというのが一番確実な方法でしょう。
しかし自動的に正確な情報を取得できるのであれば、それに越したことはありません。そこで、自動で祝日を取得する方法を考えてみます。
まず考えられるのが、計算で求める方法です。前述した通り、計算と実際の祝日が絶対に同じになるとは言い切れません。しかし、違うということもほぼないでしょう。また、法律が変わって祝日が変わる可能性もあります。
祝日を計算で求める方法は、Javaであれば、「Java祝日計算 プロジェクト」があります。また、.NETのライブラリとして、すらばさんが「Holiday_JP」を公開されています。また、VB.NET用のクラスとして、よっし〜さんが「祝日判定用クラス(平成19年度祝日法改正対応)」を公開されています。
別の方法は、Webサービスを利用するというものです。調べてみると、独立行政法人農業・食品産業技術総合研究機構のサイトに「曜日・祝日計算サービス」というWebサービスを見つけました。
また、Googleが提供している「Google Calendar APIs」を利用する方法もあります。ここではこちらの方法を調べていきます。
Google Calendar Data API Version2 †
実はこの記事はかなり前に書いたもので、その時はGoogle Calendar Data APIのバージョン2を使っていました。今年中に公開してしまおうと思い、ふとGoogle Calendar APIのページを見てみると、バージョン2に「Deprecated」と書かれているではありませんか。調べてみると、バージョン2は3年後(2014年11月17日)には使用できなくなるというのです。そのため、この期に及んで書き直さなければならなくなりました。
しかしせっかく書いたものですので、ここでバージョン2を使って祝日を取得するコードだけを紹介させていただきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
| |
Public Shared Function GetNationalHolidaysV2(ByVal year As Integer, _
ByVal userId As String) As NationalHoliday()
Const googleUrl As String = "http://www.google.com/calendar/feeds/"
Const visibility As String = "public"
Const projection As String = "full-noattendees"
Const maxResults As Integer = 100
Dim query As String = String.Format( _
"start-min={0}-01-01&start-max={1}-01-01&max-results={2}", _
year, year + 1, maxResults)
Dim url As String = _
googleUrl & userId & "/" & visibility & "/" & projection & "?" & query
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim st As Stream = res.GetResponseStream()
Dim sr As New StreamReader(st)
Dim resXml As String = sr.ReadToEnd()
sr.Close()
st.Close()
res.Close()
Dim xmlDoc As New XmlDocument()
xmlDoc.LoadXml(resXml)
Dim rootElm As XmlElement = xmlDoc.DocumentElement
Dim nsmgr As New XmlNamespaceManager(xmlDoc.NameTable)
nsmgr.AddNamespace("ns1", "http://www.w3.org/2005/Atom")
Dim nodeList As XmlNodeList = rootElm.SelectNodes("/ns1:feed/ns1:entry", nsmgr)
Dim holidays As NationalHoliday() = New NationalHoliday(nodeList.Count - 1) {}
For i As Integer = 0 To nodeList.Count - 1
Dim entryElm As XmlNode = nodeList(i)
Dim title As String = entryElm("title").InnerXml
Dim startTime As String = entryElm("gd:when").GetAttribute("startTime")
holidays(i) = New NationalHoliday(title, startTime)
Next
Return holidays
End Function
Public Shared Function GetNationalHolidaysV2(ByVal year As Integer) _
As NationalHoliday()
Const japaneseHolidaysId As String = _
"outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com"
Return GetNationalHolidaysV2(year, japaneseHolidaysId)
End Function
Public Class NationalHoliday
Private _name As String
Public ReadOnly Property Name() As String
Get
Return Me._name
End Get
End Property
Private _date As DateTime
Public ReadOnly Property [Date]() As DateTime
Get
Return Me._date
End Get
End Property
Public Sub New(ByVal holidayName As String, ByVal holidayDate As String)
Me._name = holidayName
Me._date = System.Xml.XmlConvert.ToDateTime(holidayDate, _
System.Xml.XmlDateTimeSerializationMode.Local)
End Sub
End Class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
| |
public static NationalHoliday[] GetNationalHolidaysV2(int year, string userId)
{
const string googleUrl = "http://www.google.com/calendar/feeds/";
const string visibility = "public";
const string projection = "full-noattendees";
const int maxResults = 100;
string query = string.Format(
"start-min={0}-01-01&start-max={1}-01-01&max-results={2}",
year, year + 1, maxResults);
string url = googleUrl + userId + "/" + visibility + "/" + projection + "?" + query;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
string resXml = sr.ReadToEnd();
sr.Close();
st.Close();
res.Close();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(resXml);
XmlElement rootElm = xmlDoc.DocumentElement;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("ns1", "http://www.w3.org/2005/Atom");
XmlNodeList nodeList = rootElm.SelectNodes("/ns1:feed/ns1:entry", nsmgr);
NationalHoliday[] holidays = new NationalHoliday[nodeList.Count];
for (int i = 0; i < nodeList.Count; i++)
{
XmlNode entryElm = nodeList[i];
string title = entryElm["title"].InnerXml;
string startTime = entryElm["gd:when"].GetAttribute("startTime");
holidays[i] = new NationalHoliday(title, startTime);
}
return holidays;
}
public static NationalHoliday[] GetNationalHolidaysV2(int year)
{
const string japaneseHolidaysId =
"outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com";
return GetNationalHolidaysV2(year, japaneseHolidaysId);
}
public class NationalHoliday
{
private string _name;
public string Name
{
get { return this._name; }
}
private DateTime _date;
public DateTime Date
{
get { return this._date; }
}
public NationalHoliday(string holidayName, string holidayDate)
{
this._name = holidayName;
this._date = XmlConvert.ToDateTime(holidayDate,
XmlDateTimeSerializationMode.Local);
}
}
|
Google Calendar Data API Version3 †
バージョン3は2よりハードルが上がっています。最低でもAPI key(または、OAuth 2.0 token)が必要です。もし持っていなければ、「Google APIs Console」で取得してください(Googleのアカウントが必要です)。
また、結果がJSONで返されます。XMLでは取得できません。JSONを解析する方法は.NETプログラミング研究第97号で紹介していますが、XMLのように簡単にはいきません。
難しいことは後回しにして、とりあえず試してみましょう。ウェブブラウザで
https://www.googleapis.com/calendar/v3/calendars/ja.japanese%23holiday@group.v.calendar.google.com/events?key={YOUR_API_KEY}&timeMin=2012-01-01T00:00:00Z&timeMax=2013-01-01T00:00:00Z&maxResults=100&orderBy=startTime&singleEvents=true
というURLを開いてみてください(「{YOUR_API_KEY}」の部分は、有効なAPI keyに置き換えてください)。ダウンロードしたデータを見ていただけると分かると思いますが、このようにしてJSONで2012年の日本の祝日のリストが返されます。
それでは、「https://www.googleapis.com/calendar/v3/calendars/ja.japanese%23holiday@group.v.calendar.google.com/events?key={YOUR_API_KEY}&timeMin=2012-01-01T00:00:00Z&timeMax=2013-01-01T00:00:00Z&maxResults=100&orderBy=startTime&singleEvents=true」というURLの中身を詳しく見ていきましょう。
calendarId †
「ja.japanese%23holiday@group.v.calendar.google.com」というのは「calendarId」で、Google公式の、日本の祝日を日本語で記述したカレンダーのIDです。
先頭の「ja.」は、日本語という意味です。「en.」とすると、英語になります。
日本以外に色々な国の祝日のカレンダーが用意されています。以下に幾つか紹介します(「%23」を「#」にデコードしています)。
「ja.japanese#holiday@group.v.calendar.google.com」で取得できる祝日の情報は間違いが多く、春分の日や秋分の日が抜けていたり、間違った日にちになっているという報告があります。そのため、非公式のmozilla.org版「outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com」の方がよく使われているようです。
events †
「events」というのは、calendar.events.listメソッドを呼び出すという意味です。このメソッドはカレンダーのイベントのコレクションを返します。祝日はイベントとして登録されています。
timeMinとtimeMax †
「timeMin=2012-01-01T00:00:00Z&timeMax=2013-01-01T00:00:00Z」は、2012年1月1日0時0分0秒から2013年1月1日0時0分0秒までのイベントを返してくれという意味です。timeMinの日時は期間に含まれますが、timeMaxの日時は含まれませんので、2012年の正月は含まれても、2013年の正月は含まれません。
日時はRFC3339形式で指定します。また、日にちだけ指定することはできず、時刻も指定しなければならないようです。
maxResults †
「max-results」には、返して欲しいエントリーの数を指定します。私が試した所では、デフォルトは(max-resultsを指定しないと)50のようです。1年の祝日を取得するにはこれで十分ですが、もっと多くのエントリーを取得したいのであれば大きな値を指定します。ただし制限があるようで、制限を超えた数字をmaxResultsに設定しても制限値分しか返さないようです。
orderBy †
「orderBy」を指定すると、エントリーを並び替えた状態で返してくれます。orderByに指定できる値は「startTime」と「updated」のどちらかで、「startTime」を指定すると「start date」(祝日の日にち)を昇順で並び替えてくれます。
singleEvents †
「singleEvents」は繰り返しのイベントを展開するかを指定するものらしいですが、「orderBy」を指定した時は「singleEvents=true」としないとエラーになるようです。
サンプルコード †
以上を踏まえて、Googleカレンダーから祝日の情報(名前と日付)を取得するサンプルコードを書いてみました。JSONの解析にはJavaScriptSerializerを使っていますので、.NET Framework 3.5以降で、System.Web.Extensionsを参照設定する必要があります。JSONの解析については、.NETプログラミング研究第97号で説明していますので、そちらをご覧ください。
また、「{YOUR_API_KEY}」は有効なAPI keyに書き換えてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
| | Imports System.IO
Imports System.Net
Imports System.Xml
Imports System.Collections.Generic
Class Module1
Shared Sub Main()
Dim holidays As NationalHoliday() = GetNationalHolidays(2012, _
"{YOUR_API_KEY}")
For Each holiday As NationalHoliday In holidays
Console.WriteLine("Name: {0}", holiday.Name)
Console.WriteLine("Date: {0}", holiday.Date)
Next
Console.ReadLine()
End Sub
Public Shared Function GetNationalHolidays(ByVal year As Integer, _
ByVal apiKey As String, ByVal calendarId As String) As NationalHoliday()
Const googleUrl As String = "https://www.googleapis.com/calendar/v3/calendars/"
Const methodString As String = "events"
Const maxResults As Integer = 100
Dim query As String = String.Format("key={0}&" & _
"timeMin={1}-01-01T00:00:00Z&" & _
"timeMax={2}-01-01T00:00:00Z&" & _
"maxResults={3}", _
apiKey, year, year + 1, maxResults)
Dim url As String = googleUrl & calendarId & "/" & methodString & "?" & query
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim st As Stream = res.GetResponseStream()
Dim sr As New StreamReader(st)
Dim jsonString As String = sr.ReadToEnd()
sr.Close()
st.Close()
res.Close()
Dim serializer As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim jsonDic As Dictionary(Of String, Object) = _
serializer.Deserialize(Of Dictionary(Of String, Object))(jsonString)
If Not jsonDic.ContainsKey("items") Then
Return New NationalHoliday(-1) {}
End If
Dim items As System.Collections.ArrayList = _
DirectCast(jsonDic("items"), System.Collections.ArrayList)
Dim holidays As NationalHoliday() = New NationalHoliday(items.Count - 1) {}
For i As Integer = 0 To items.Count - 1
Dim item As Dictionary(Of String, Object) = _
DirectCast(items(i), Dictionary(Of String, Object))
Dim title As String = DirectCast(item("summary"), String)
Dim startTime As String = DirectCast(DirectCast(item("start"), _
Dictionary(Of String, Object))("date"), String)
holidays(i) = New NationalHoliday(title, startTime)
Next
Return holidays
End Function
Public Shared Function GetNationalHolidays(ByVal year As Integer, _
ByVal apiKey As String) As NationalHoliday()
Const japaneseHolidaysId As String = _
"outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com"
Return GetNationalHolidays(year, apiKey, japaneseHolidaysId)
End Function
Public Class NationalHoliday
Private _name As String
Public ReadOnly Property Name() As String
Get
Return Me._name
End Get
End Property
Private _date As DateTime
Public ReadOnly Property [Date]() As DateTime
Get
Return Me._date
End Get
End Property
Public Sub New(ByVal holidayName As String, ByVal holidayDate As String)
Me._name = holidayName
Me._date = XmlConvert.ToDateTime(holidayDate, _
XmlDateTimeSerializationMode.Local)
End Sub
End Class
End Class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
| | using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Collections.Generic;
public class Program
{
static void Main(string[] args)
{
NationalHoliday[] holidays = GetNationalHolidays(
2012, "{YOUR_API_KEY}");
foreach (NationalHoliday holiday in holidays)
{
Console.WriteLine("Name: {0}", holiday.Name);
Console.WriteLine("Date: {0}", holiday.Date);
}
}
public static NationalHoliday[] GetNationalHolidays(int year,
string apiKey, string calendarId)
{
const string googleUrl = "https://www.googleapis.com/calendar/v3/calendars/";
const string methodString = "events";
const int maxResults = 100;
string query = string.Format(
"key={0}&" +
"timeMin={1}-01-01T00:00:00Z&" +
"timeMax={2}-01-01T00:00:00Z&" +
"maxResults={3}",
apiKey, year, year + 1, maxResults);
string url = googleUrl + calendarId + "/" + methodString + "?" + query;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
string jsonString = sr.ReadToEnd();
sr.Close();
st.Close();
res.Close();
System.Web.Script.Serialization.JavaScriptSerializer serializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
Dictionary<string, object> jsonDic =
serializer.Deserialize<Dictionary<string, object>>(jsonString);
if (!jsonDic.ContainsKey("items"))
{
return new NationalHoliday[0];
}
System.Collections.ArrayList items =
(System.Collections.ArrayList)jsonDic["items"];
NationalHoliday[] holidays = new NationalHoliday[items.Count];
for (int i = 0; i < items.Count; i++)
{
Dictionary<string, object> item = (Dictionary<string, object>)items[i];
string title = (string)item["summary"];
string startTime =
(string)((Dictionary<string, object>)item["start"])["date"];
holidays[i] = new NationalHoliday(title, startTime);
}
return holidays;
}
public static NationalHoliday[] GetNationalHolidays(int year, string apiKey)
{
const string japaneseHolidaysId =
"outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com";
return GetNationalHolidays(year, apiKey, japaneseHolidaysId);
}
public class NationalHoliday
{
private string _name;
public string Name
{
get { return this._name; }
}
private DateTime _date;
public DateTime Date
{
get { return this._date; }
}
public NationalHoliday(string holidayName, string holidayDate)
{
this._name = holidayName;
this._date = XmlConvert.ToDateTime(holidayDate,
XmlDateTimeSerializationMode.Local);
}
}
}
|
出力結果は以下のようになります。
Name: 振替休日 / Furikae ky?jitsu / Substitute Holiday
Date: 2012/01/02 0:00:00
Name: 海の日 / Umi no hi / Marine Day
Date: 2007/07/16 0:00:00
Name: 成人の日 / Seijin no hi / Coming-of-age Day
Date: 2007/01/08 0:00:00
Name: 敬老の日 / Keir? no hi / Respect for the Aged Day
Date: 2007/09/17 0:00:00
Name: 振替休日 / Furikae ky?jitsu / Substitute Holiday
Date: 2012/12/24 0:00:00
Name: 体育の日 / Taiiku no hi / Health-Sports Day
Date: 2007/10/08 0:00:00
Name: 振替休日 / Furikae ky?jitsu / Substitute Holiday
Date: 2012/04/30 0:00:00
Name: 天皇誕生日 / Tenn? tanj?bi / Emperor Akihito's Birthday
Date: 2007/12/23 0:00:00
Name: 元日 / Ganjitsu / New Year's Day
Date: 2007/01/01 0:00:00
Name: 勤労感謝の日 / Kinr? kansha no hi / Labour Thanksgiving Day
Date: 2007/11/23 0:00:00
Name: 春分の日 / Shunbun no hi / Vernal Equinox Day
Date: 2008/03/20 0:00:00
Name: 憲法記念日 / Kenp? kinenbi / Constitution Memorial Day *
Date: 2007/05/03 0:00:00
Name: 秋分の日 / Sh?bun no hi / Autumnal Equinox Day
Date: 2012/09/22 0:00:00
Name: 昭和の日 / Sh?wa no hi / Sh?wa Day *
Date: 2007/04/29 0:00:00
Name: 文化の日 / Bunka no hi / Culture Day
Date: 2007/11/03 0:00:00
Name: 建国記念の日 / Kenkoku kinen no hi / National Foundation Day
Date: 2007/02/11 0:00:00
Name: 子供の日 / Kodomo no hi / Children's Day
Date: 2007/05/05 0:00:00
Name: みどりの日 / Midori no hi / Greenery Day *
Date: 2007/05/04 0:00:00
最後に †
年末の忙しい時に取り急ぎ書きなおしたため、もしかしたら不備があるかもしれません(多分あるでしょう)。もしありましたら、申し訳ないです。
最後になりましたが、皆様良いお年をお迎えください。
コメント †