mhc 1.2.4 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mhc/converter.rb CHANGED
@@ -131,15 +131,86 @@ module Mhc
131
131
  end
132
132
  end
133
133
 
134
+ # https://github.com/rubyredrick/ri_cal
135
+ def value_part(unit, diff) # :nodoc:
136
+ (diff == 0) ? nil : "#{diff}#{unit}"
137
+ end
138
+
139
+ # https://github.com/rubyredrick/ri_cal
140
+ def from_datetimes(start, finish, sign = '') # :nodoc:
141
+ if start > finish
142
+ from_datetimes(finish, start, '-')
143
+ else
144
+ diff = finish - start
145
+ days_diff = diff.to_i
146
+ hours = (diff - days_diff) * 24
147
+ hour_diff = hours.to_i
148
+ minutes = (hours - hour_diff) * 60
149
+ min_diff = minutes.to_i
150
+ seconds = (minutes - min_diff) * 60
151
+ sec_diff = seconds.to_i
152
+
153
+ day_part = value_part('D',days_diff)
154
+ hour_part = value_part('H', hour_diff)
155
+ min_part = value_part('M', min_diff)
156
+ sec_part = value_part('S', sec_diff)
157
+ t_part = (hour_diff.abs + min_diff.abs + sec_diff.abs) == 0 ? "" : "T"
158
+ "P#{day_part}#{t_part}#{hour_part}#{min_part}#{sec_part}"
159
+ end
160
+ end
161
+
162
+ # Convert ~X-SC-Days:~ to RFC5545 3.8.2 ~RDATE~
163
+ #
164
+ # RDATE allows three types of format: PERIOD, DATE, DATE-TIME.
165
+ # + DATE :: 20140301
166
+ # + DATE-TIME :: 20140301T123000
167
+ # + PERIOD :: 20140301T123000/20140301T143000 or 20140301T123000/PT2H
168
+ #
169
+ # NOTE: *Google calenadr does not accept PERIOD*
170
+ # That's why we cannot make X-SC-Day: have different duration-period for each occurrence.
171
+ #
134
172
  def rdates(event)
135
173
  return nil if event.dates.empty?
136
- ocs = Mhc::OccurrenceEnumerator.new(event, event.dates, empty_dates, empty_condition, empty_duration).map {|oc| oc.dtstart}
174
+ # Get all RDATE candidates from X-SC-Date
175
+ ocs = Mhc::OccurrenceEnumerator.new(event, event.dates, empty_dates, empty_condition, empty_duration).map do |oc|
176
+ if oc.dtstart.respond_to?(:hour)
177
+ # duration = from_datetimes(oc.dtstart, oc.dtend)
178
+
179
+ # 20140301T123000/20140301T143000
180
+ dtstart = IcalendarImporter.tz_convert(oc.dtstart, dst_tzid: 'UTC').strftime("%Y%m%dT%H%M00Z")
181
+ dtend = IcalendarImporter.tz_convert(oc.dtend, dst_tzid: 'UTC').strftime("%Y%m%dT%H%M00Z")
182
+
183
+ dtstart + "/" + dtend
184
+ # dtstart + "/" + duration
185
+ else
186
+ oc.dtstart
187
+ end
188
+ end
189
+
190
+ # Check if all in X-SC-Date should be converted into RDATE,
191
+ # because the first occurrence of X-SC-Date might be dedicated
192
+ # to DTSTART in some particular case.
193
+ #
137
194
  if event.recurring?
195
+ # Use all values of X-SC-Date as RDATE, because DTSTART
196
+ # should be calculated from recurrence-rule (X-SC-Cond and
197
+ # X-SC-Duration).
138
198
  ocs
139
199
  else
140
- ocs = ocs[1..-1]
141
- return nil if ocs.empty?
200
+ # This is Workaround for avoiding the serious bug in Android.
201
+ # Android badly ignores DTSTART-DTEND if RDATE is set.
202
+ # As a result, the first occurence is vanished.
203
+ # So, I set the first occurence twice; in RDATE and DTSTART.
204
+ return nil if ocs[1..-1].empty?
142
205
  return ocs
206
+
207
+ # This is what I wanted to do:
208
+ # Remove the first occurence of X-SC-Date, because the first
209
+ # value should be dedicated to DTSTART.
210
+ #
211
+ # ocs = ocs[1..-1]
212
+ # return nil if ocs.empty?
213
+ # return ocs
143
214
  end
144
215
  end
145
216
 
data/lib/mhc/datastore.rb CHANGED
@@ -25,7 +25,7 @@ module Mhc
25
25
  Dir.foreach(".") do |ent|
26
26
  parse_mhcc(ent).each {|ev|
27
27
  next if category && !ev.in_category?(category)
28
- next if recurrence && !ev.in_recurrence?(recurrence)
28
+ next if recurrence && !(ev.recurrence_tag.to_s == recurrence)
29
29
  yielder << ev
30
30
  } if /\.mhcc$/ =~ ent
31
31
  next unless /\.mhc$/ =~ ent
@@ -52,22 +52,25 @@ module Mhc
52
52
  return Event.parse_file(path)
53
53
  end
54
54
 
55
- def create(event)
55
+ def create(event, draft = false)
56
56
  if find_by_uid(event.uid)
57
57
  raise "Already exist uid:#{uid} in #{@basedir}"
58
58
  end
59
+ path = uid_to_path(event.uid, draft)
59
60
  File.open(path, "w") do |f|
60
61
  f.write(event.dump)
61
62
  end
63
+ return path.to_s
62
64
  end
63
65
 
64
- def update(event)
65
- unless path = uid_to_path(event.uid)
66
+ def update(event, draft = false)
67
+ unless path = uid_to_path(event.uid, draft)
66
68
  raise "Not found uid:#{uid} in #{@basedir}"
67
69
  end
68
70
  File.open(path, "w") do |f|
69
71
  f.write(event.dump)
70
72
  end
73
+ return path.to_s
71
74
  end
72
75
 
73
76
  def delete(uid_or_event)
@@ -105,7 +108,8 @@ module Mhc
105
108
  return Dir.glob(glob).first
106
109
  end
107
110
 
108
- def uid_to_path(uid)
111
+ def uid_to_path(uid, draft = false)
112
+ return @basedir + ('draft/' + uid + '.mhc') if draft
109
113
  return @basedir + ('spool/' + uid + '.mhc')
110
114
  end
111
115
 
data/lib/mhc/etag.rb CHANGED
@@ -52,7 +52,7 @@ module Mhc
52
52
  end
53
53
 
54
54
  def unlink(key)
55
- if File.exists?(path = make_path(key))
55
+ if File.exist?(path = make_path(key))
56
56
  File.unlink(path)
57
57
  end
58
58
  end
@@ -0,0 +1,32 @@
1
+ module Mhc
2
+ class Formatter
3
+ class Html < Base
4
+ TEMPLATE_DIR = File.expand_path("../../templates", __FILE__)
5
+
6
+ def initialize(date_range:, options:)
7
+ super(date_range: date_range, options: options)
8
+ @json_formatter = Json.new(date_range: date_range, options: options)
9
+ end
10
+
11
+ def <<(occurrence)
12
+ @json_formatter << occurrence
13
+ end
14
+
15
+ private
16
+
17
+ def format_header(context)
18
+ require "erb"
19
+ template_path = File.expand_path("full-calendar.html.erb", TEMPLATE_DIR)
20
+ @template = ERB.new(File.open(template_path).read, nil, "-")
21
+ return ""
22
+ end
23
+
24
+ def format_body(context)
25
+ env = Struct.new(:json_event_array, :tzid)
26
+ mhc = env.new(@json_formatter.to_s, Mhc.default_tzid) # used in ERB template
27
+ @template.result(binding)
28
+ end
29
+
30
+ end # class Html
31
+ end # class Formatter
32
+ end # module Mhc
data/lib/mhc/formatter.rb CHANGED
@@ -21,6 +21,8 @@ module Mhc
21
21
  Howm.new(date_range: date_range, options:options)
22
22
  when :json
23
23
  Json.new(date_range: date_range, options:options)
24
+ when :html
25
+ Html.new(date_range: date_range, options:options)
24
26
  else
25
27
  raise Formatter::NameError.new("Unknown format: #{formatter} (#{formatter.class})")
26
28
  end
@@ -37,6 +39,7 @@ module Mhc
37
39
  autoload :OrgTable, "#{dir}/org_table.rb"
38
40
  autoload :SymbolicExpression, "#{dir}/symbolic_expression.rb"
39
41
  autoload :Text, "#{dir}/text.rb"
42
+ autoload :Html, "#{dir}/html.rb"
40
43
 
41
44
  end # class Formatter
42
45
  end # module Mhc
@@ -0,0 +1,417 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+
8
+ <title>Calendar</title>
9
+
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.css" />
11
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
12
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css" />
13
+
14
+ <style media="screen" type="text/css">
15
+ .container {
16
+ // max-width: 100%;
17
+ width: auto;
18
+ }
19
+
20
+ .ui-draggable {
21
+ cursor: grab;
22
+ }
23
+
24
+ a {
25
+ cursor: pointer;
26
+ }
27
+
28
+ .fc-sun {
29
+ color:#gray;
30
+ background-color: #fff0f0;
31
+ }
32
+
33
+ .fc-sat {
34
+ color:#gray;
35
+ background-color: #f0f0ff;
36
+ }
37
+
38
+
39
+
40
+ #calendar .mhc-allday {
41
+ background-color: lightblue;
42
+ border-color: lightblue;
43
+ color: black;
44
+ }
45
+
46
+ #calendar .mhc-time-range {
47
+ background-color: #f7f7ff;
48
+ border-color: #f7f7ff;
49
+ color: black;
50
+ }
51
+
52
+ #calendar .mhc-time-range:hover {
53
+ background-color: aliceblue;
54
+ border-color: lavender;
55
+ color: black;
56
+ }
57
+
58
+ #calendar .mhc-category-holiday {
59
+ background-color: orangered;
60
+ border-color: orangered;
61
+ color: white;
62
+ }
63
+
64
+ #calendar .mhc-category-birthday {
65
+ background-color: #ffffe8;
66
+ border-color: #ffffe8;
67
+ color: black;
68
+ }
69
+
70
+
71
+
72
+
73
+ #side-calendar .mhc-category-birthday {
74
+ display: none;
75
+ }
76
+
77
+
78
+
79
+
80
+ #stuck-events .fc-list-heading {
81
+ display: none;
82
+ }
83
+
84
+ </style>
85
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
86
+ <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
87
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.js"></script>
88
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js"></script>
89
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.js"></script>
90
+
91
+ </head>
92
+ <body>
93
+ <!--
94
+ <div class="navmenu navmenu-default navmenu-fixed-left offcanvas">
95
+ <a class="navmenu-brand" href="#">brand</a>
96
+ <ul class="nav navmenu-nav">
97
+ <li><a href="#">Menu text 1</a></li>
98
+ <li><a href="#">Menu text 2</a></li>
99
+ <li><a href="#">Menu text 3</a></li>
100
+ </ul>
101
+ </div>
102
+ <div class="navbar navbar-default navbar-fixed-top">
103
+ <button type="button" class="navbar-toggle" data-toggle="offcanvas" data-target=".navmenu" data-canvas="body">
104
+ <span class="icon-bar"></span>
105
+ <span class="icon-bar"></span>
106
+ <span class="icon-bar"></span>
107
+ </button>
108
+ </div>
109
+ -->
110
+
111
+ <div class="container">
112
+ <div class="row">
113
+
114
+ <div class="col-xs-3">
115
+ <ul class="nav nav-tabs">
116
+ <li class="active"><a data-toggle="tab" href="#side-calendar">Past Events</a></li>
117
+ <li><a data-toggle="tab" href="#stuck-events">Stuck Events</a></li>
118
+ </ul>
119
+ <div class="tab-content">
120
+ <div id="side-calendar" class="tab-pane fade in active"></div>
121
+ <div id="stuck-events" class="tab-pane fade"></div>
122
+ </div>
123
+ </div><!-- col-xs-3 -->
124
+
125
+ <div class="col-xs-9" id="calendar"></div> <!-- col-xs-9 -->
126
+ </div><!-- row -->
127
+ </div><!-- /.container -->
128
+
129
+ <script>
130
+ function create_uuid() {
131
+ var d = +new Date();
132
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
133
+ .replace(/[xy]/g, function(c) {
134
+ var r = (d + Math.random() * 16) % 16 | 0;
135
+ return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16).toUpperCase();
136
+ });
137
+ }
138
+
139
+ function max_height_for(element) {
140
+ var top = $(element).position().top;
141
+ return $(window).height() - top - 7;
142
+ }
143
+
144
+ function adjust_height(element) {
145
+ var height = max_height_for(element);
146
+ $(element).fullCalendar('option', 'height', height);
147
+ }
148
+
149
+ var json_event_array = [<%= mhc.json_event_array %>];
150
+
151
+ $(document).ready(function() {
152
+
153
+ $('#side-calendar').fullCalendar({
154
+ defaultView: 'listMonth',
155
+ header: {
156
+ left: '',
157
+ center: 'title',
158
+ right: ''
159
+ },
160
+ height: max_height_for('#side-calendar'),
161
+ editable: true,
162
+ eventResourceEditable: true,
163
+ eventLimit: true,
164
+ selectable: true,
165
+ droppable: true,
166
+ timezone: '<%= mhc.tzid %>',
167
+ timeFormat: "HH:mm",
168
+ displayEventEnd: false,
169
+ allDayText: "",
170
+ month: 'HH:mm',
171
+ week: 'HH:mm',
172
+ day: 'HH:mm',
173
+ // eventSources: [<%= $stuck_recurrences_json %>],
174
+ eventSources: json_event_array,
175
+ XeventDragStart: function(calEvent, jsEvent, ui, view) {
176
+ alert('Drag Event: ' + calEvent.title);
177
+ alert('Event ID: ' + calEvent.id);
178
+ alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
179
+ alert('View: ' + view.name);
180
+ // change the border color just for fun
181
+ $(this).css('border-color', 'red');
182
+ },
183
+ viewRender: function(view, element) {
184
+ $('tr.fc-list-item').each(function() {
185
+ var duration;
186
+ var seg = $(this).data('fc-seg');
187
+ var myevent = seg.footprint.getEventLegacy();
188
+ if (myevent.end) {
189
+ var minutes = moment.duration(myevent.end.diff(myevent.start)).asMinutes();
190
+ var days = Math.floor(minutes/(24*60));
191
+ minutes -= days * 24 * 60;
192
+ var hours = Math.floor(minutes/60);
193
+ minutes -= hours * 60;
194
+ duration = days + "." + hours + ":" + minutes;
195
+ } else {
196
+ duration = "0.00:00";
197
+ }
198
+ if (myevent.title == "母の日") {
199
+ // alert('My Event: ' + myevent.title);
200
+ // alert('Duration: ' + duration);
201
+ }
202
+ var uuid = create_uuid();
203
+ // alert('Seg: ' + seg);
204
+ $(this).data('event', {
205
+ title: myevent.title,
206
+ allDay: myevent.allDay,
207
+ start: myevent.start.format("HH:mm"),
208
+ duration: duration,
209
+ stick: true,
210
+ className: myevent.className
211
+ });
212
+ });
213
+
214
+ $('tr.fc-list-item').draggable({
215
+ appendTo: "#calendar",
216
+ cursorAt: {top: 5, left: 5},
217
+ iframeFix: true,
218
+ refreshPositions: true,
219
+ scroll: false,
220
+ cursor: "move",
221
+ revert: true, // will cause the event to go back to its
222
+ revertDuration: 0, // original position after the drag
223
+ helper: "clone",
224
+ zIndex: 100,
225
+ opacity: 0.35,
226
+
227
+ start: function(event, ui) {
228
+ }
229
+ });
230
+ if(view.name == 'listMonth') {
231
+ adjust_height('#side-calendar');
232
+ }
233
+ },
234
+ windowResize: function(view) {
235
+ if(view.name == 'listMonth') {
236
+ adjust_height('#side-calendar');
237
+ }
238
+ }
239
+ });
240
+
241
+ $('#stuck-events').fullCalendar({
242
+ defaultView: 'listall',
243
+ Xheader: {
244
+ left: '',
245
+ center: '',
246
+ right: ''
247
+ },
248
+ header: false,
249
+ views: {
250
+ listall: {
251
+ type: 'list',
252
+ duration: { days: 3650},
253
+ dayCount: 30
254
+ }
255
+ },
256
+ height: max_height_for('#side-calendar'), // XXX
257
+ editable: true,
258
+ eventResourceEditable: true,
259
+ eventLimit: true,
260
+ selectable: true,
261
+ droppable: true,
262
+ timezone: '<%= mhc.tzid %>',
263
+ timeFormat: " ",
264
+ allDayText: "",
265
+ month: 'HH:mm',
266
+ week: 'HH:mm',
267
+ day: 'HH:mm',
268
+ eventSources: [<%= $stuck_recurrences_json %>],
269
+ XeventDragStart: function(calEvent, jsEvent, ui, view) {
270
+ alert('Drag Event: ' + calEvent.title);
271
+ alert('Event ID: ' + calEvent.id);
272
+ alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
273
+ alert('View: ' + view.name);
274
+ // change the border color just for fun
275
+ $(this).css('border-color', 'red');
276
+ },
277
+ viewRender: function(view, element) {
278
+ $('tr.fc-list-item').each(function() {
279
+ var duration;
280
+ var seg = $(this).data('fc-seg');
281
+ var myevent = seg.footprint.getEventLegacy();
282
+ if (myevent.end) {
283
+ var minutes = moment.duration(myevent.end.diff(myevent.start)).asMinutes();
284
+ var days = Math.floor(minutes/(24*60));
285
+ minutes -= days * 24 * 60;
286
+ var hours = Math.floor(minutes/60);
287
+ minutes -= hours * 60;
288
+ duration = days + "." + hours + ":" + minutes;
289
+ } else {
290
+ duration = "0.00:00";
291
+ }
292
+ if (myevent.title == "母の日") {
293
+ // alert('My Event: ' + myevent.title);
294
+ // alert('Duration: ' + duration);
295
+ }
296
+ var uuid = create_uuid();
297
+ // alert('Seg: ' + seg);
298
+ $(this).data('event', {
299
+ title: myevent.title,
300
+ allDay: myevent.allDay,
301
+ start: myevent.start.format("HH:mm"),
302
+ duration: duration,
303
+ stick: true,
304
+ className: myevent.className
305
+ });
306
+ });
307
+
308
+ $('tr.fc-list-item').draggable({
309
+ appendTo: "#calendar",
310
+ cursorAt: {top: 5, left: 5},
311
+ iframeFix: true,
312
+ refreshPositions: true,
313
+ scroll: false,
314
+ cursor: "move",
315
+ revert: true, // will cause the event to go back to its
316
+ revertDuration: 0, // original position after the drag
317
+ helper: "clone",
318
+ zIndex: 100,
319
+ opacity: 0.35,
320
+
321
+ start: function(event, ui) {
322
+ }
323
+ });
324
+ if(view.name == 'listMonth') {
325
+ adjust_height('#stuck-events');
326
+ }
327
+ },
328
+ windowResize: function(view) {
329
+ if(view.name == 'listMonth') {
330
+ adjust_height('#stuck-events');
331
+ }
332
+ }
333
+ });
334
+
335
+ $('#calendar').fullCalendar({
336
+ header: {
337
+ left: 'prev,next today',
338
+ center: 'title',
339
+ right: 'month,agendaWeek,agendaDay,listMonth'
340
+ },
341
+ firstDay: 1,
342
+ height: max_height_for('#calendar'),
343
+ editable: true,
344
+ eventResourceEditable: true,
345
+ eventLimit: true,
346
+ selectable: true,
347
+ droppable: true,
348
+
349
+ // drop: Event ではない draggable からドロップされたとき.
350
+ // draggable が event として認識できないようなときでも発火する
351
+ drop: function(date) {
352
+ // alert("Dropped on " + date.format());
353
+ },
354
+
355
+ // eventReceive: Event ではない draggable からドロップされたとき
356
+ // draggable が event として認識可能な property を持っている場合だけ発火する
357
+ eventReceive: function(event) {
358
+ event.id = create_uuid(); // XXX! これがうまくいかない
359
+ $('#calendar').fullCalendar('updateEvent', event);
360
+ // alert("Drop event from ourter world! ID: " + event.id);
361
+ },
362
+
363
+ // eventDrop: 既に登録されている event を変更したとき
364
+ eventDrop: function(event, delta, revertFunc, jsEvent, ui, view) {
365
+ // alert("eventDrop:" + event.title);
366
+ // alert("eventDrop:" + event.id);
367
+ },
368
+
369
+ // return (this.eventInstance || this.eventDef).toLegacy()
370
+ eventClick: function(calEvent, jsEvent, view) {
371
+ var yesno = prompt('Do you delete ' + calEvent.title + '?');
372
+ // alert('Event ID: ' + calEvent.id);
373
+ if (yesno == "yes"){
374
+ $('#calendar').fullCalendar('removeEvents', [calEvent.id]);
375
+ }
376
+ // alert('This Event: ' + $(this).getEventLegacy().title);
377
+ //alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
378
+ //alert('View: ' + view.name);
379
+ // change the border color just for fun
380
+ // $(this).css('border-color', 'red');
381
+ },
382
+
383
+ timezone: '<%= mhc.tzid %>',
384
+ timeFormat: "HH:mm",
385
+ allDayText: "",
386
+ month: 'HH:mm',
387
+ week: 'HH:mm',
388
+ day: 'HH:mm',
389
+ eventSources: json_event_array,
390
+ // eventSources: [<%= mhc.json_event_array %>],
391
+ viewRender: function(view, element) {
392
+ if(view.name == 'month') {
393
+ adjust_height('#calendar');
394
+ adjust_height('#stuck-events');
395
+ }
396
+ var currentDate = $('#calendar').fullCalendar('getDate');
397
+ // alert("The current date of the calendar is " + currentDate.format());
398
+ $('#side-calendar').fullCalendar('gotoDate', currentDate.subtract(1, 'y'));
399
+ $('#stuck-events').fullCalendar('gotoDate', currentDate.subtract(5, 'y'));
400
+ },
401
+ windowResize: function(view) {
402
+ if(view.name == 'month') {
403
+ adjust_height('#calendar');
404
+ adjust_height('#stuck-events');
405
+ }
406
+ }
407
+ }) // $('#calendar')
408
+
409
+ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
410
+ var target = $(e.target).attr("href") // activated tab
411
+ adjust_height(target);
412
+ });
413
+
414
+ });
415
+ </script>
416
+ </body>
417
+ </html>
data/lib/mhc/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Mhc
2
- VERSION = "1.2.4"
2
+ VERSION = "1.2.6"
3
3
  PRODID = "-//Quickhack.net//MHC #{Mhc::VERSION}//EN"
4
4
  end
data/mhc.gemspec CHANGED
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_runtime_dependency "thor", ">= 0.19.1"
26
+ spec.add_runtime_dependency "thor", ">= 1.2.0"
27
+ spec.add_runtime_dependency "rexml", ">= 3.2.4"
27
28
  spec.add_runtime_dependency "ri_cal", ">= 0.8.8"
28
29
  spec.add_runtime_dependency "tzinfo", ">= 1.2.2"
29
30
  spec.add_runtime_dependency "tzinfo-data", ">= 1.2015.4"
@@ -31,14 +31,14 @@ X-SC-Record-Id: AC1B378C-BAFE-4B6E-AF9C-78C2377E8AA4
31
31
  X-SC-Subject: 天皇誕生日
32
32
  X-SC-Category: Holiday Japanese
33
33
  X-SC-Cond: 29 Apr
34
- X-SC-Duration: 19480429-19980429
34
+ X-SC-Duration: 19490429-19880429
35
35
  X-SC-Record-Id: B78EAAEE-9963-4573-9EC7-0879F8940AEE
36
36
 
37
37
  X-SC-Subject: みどりの日
38
38
  X-SC-Category: Holiday Japanese
39
39
  X-SC-Cond: 29 Apr
40
40
  X-SC-Duration: 19890429-20060429
41
- X-SC-Record-Id: B78EAAEE-9963-4573-9EC7-0879F8940AEE
41
+ X-SC-Record-Id: 8C05BF80-006C-4FA7-8CF1-5F1C18CB8641
42
42
 
43
43
  X-SC-Subject: 昭和の日
44
44
  X-SC-Category: Holiday Japanese
@@ -186,12 +186,12 @@ X-SC-Day: 19730430 19730924 19740506 19740916 19741104 19751124
186
186
  20180430 20180924 20181224 20190506 20190812 20191104 20200224 20200506
187
187
  20210809
188
188
  20230102 20240212 20240506 20240812 20240923 20241104 20250224 20250506
189
- 20251124 20260506 20270322 20290212 20290430 20290924 20291224
189
+ 20251124 20260506 20270322 20290212 20290430 20290924
190
190
  20300506 20300812 20301104 20310224 20310506 20311124 20330321 20340102
191
- 20350212 20350430 20350924 20351224 20360506 20361124 20370506
192
- 20400102 20400430 20401224 20410506 20410812 20411104 20420224 20420506
191
+ 20350212 20350430 20350924 20360506 20361124 20370506
192
+ 20400102 20400430 20410506 20410812 20411104 20420224 20420506
193
193
  20421124 20430506 20440321 20450102 20460212 20460430 20460924
194
- 20461224 20470506 20470812 20471104 20480224 20480506 20500321
194
+ 20470506 20470812 20471104 20480224 20480506 20500321
195
195
  X-SC-Category: Holiday Japanese
196
196
  X-SC-Record-Id: E2D696DF-EB49-44D7-B06C-812B10BD99A2
197
197