Calculation code comments Monkey patched date class
On GitHub: config/initializers/monkey_patching.rb
2 module DateMonkeyPatch
A set of methods to work out the type of a given day.
We want to check if this is an actual sitting day in the Commons.
We use a naive definition of a sitting day: this includes a calendar day when the Commons sits, together with following calendar days if the Commons sat through the night.
For example: if the Commons sat on a Tuesday and continued to sit overnight into Wednesday, both Tuesday and Wednesday would count as actual sitting days.
If the Tuesday sitting lasted long enough to overlap the starting time of the Wednesday sitting, the Tuesday would be a parliamentary sitting day, but the Wednesday would not.
14 def is_commons_actual_sitting_day?
15 SittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
16 end
We want to check if this is an actual sitting day in the Lords.
We use a naive definition of a sitting day: this includes a calendar day when the Lords sits, together with following calendar days if the Lords sat through the night.
For example: if the Lords sat on a Tuesday and continued to sit overnight into Wednesday, both Tuesday and Wednesday would count as actual sitting days.
If the Tuesday sitting lasted long enough to overlap the starting time of the Wednesday sitting, the Tuesday would be a parliamentary sitting day, but the Wednesday would not.
26 def is_lords_actual_sitting_day?
27 SittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
28 end
We want to check if this is a parliamentary sitting day in the Commons.
We use a more strict definition of a sitting day. We don’t include dates for which the Commons continued sitting from a previous day, where the preceding day’s sitting overlapped with the next day’s programmed sitting.
34 def is_commons_parliamentary_sitting_day?
35 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 1 ).first
36 end
We want to check if this is a parliamentary sitting day in the Lords.
We use a more strict definition of a sitting day. We don’t include dates for which the Lords continued sitting from a previous day, where the preceding day’s sitting overlapped with the next day’s programmed sitting.
42 def is_lords_parliamentary_sitting_day?
43 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 2 ).first
44 end
We want to check if this is a virtual sitting day in the Commons.
This is a day where all Members of the House sit ‘digitally’, rather than physically.
A virtual sitting may continue over more than one calendar day. We count any continuation, where the preceding day’s sitting overlapped with the next day’s programmed sitting, as also being a virtual sitting day.
As of the end of June 2020, the Commons has had no virtual sitting days.
54 def is_commons_virtual_sitting_day?
55 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
56 end
We want to check if this is a virtual sitting day in the Lords.
This is a day where all Members of the House sit ‘digitally’, rather than physically.
A virtual sitting may continue over more than one calendar day. We count any continuation, where the preceding day’s sitting overlapped with the next day’s programmed sitting, as also being a virtual sitting day.
64 def is_lords_virtual_sitting_day?
65 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
66 end
We want to check if this is an actual sitting day in either House.
70 def is_either_house_actual_sitting_day?
71 self.is_commons_actual_sitting_day? or self.is_lords_actual_sitting_day?
72 end
We want to check if this is an actual sitting day in both Houses.
76 def is_joint_actual_sitting_day?
77 self.is_commons_actual_sitting_day? and self.is_lords_actual_sitting_day?
78 end
We want to check if this is a parliamentary sitting day in either House.
82 def is_either_house_parliamentary_sitting_day?
83 self.is_commons_parliamentary_sitting_day? or self.is_lords_parliamentary_sitting_day?
84 end
We want to check if this is a parliamentary sitting day in both Houses.
88 def is_joint_parliamentary_sitting_day?
89 self.is_commons_parliamentary_sitting_day? and self.is_lords_parliamentary_sitting_day?
90 end
We want to check if this is an adjournment day in either House.
94 def is_adjournment_day?
95 AdjournmentDay.all.where( 'date = ?', self ).first
96 end
We want to check if this is an adjournment day in the Commons.
100 def is_commons_adjournment_day?
101 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 1 ).first
102 end
We want to check if this is an adjournment day in the Lords.
106 def is_lords_adjournment_day?
107 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 2 ).first
108 end
We want to check if Parliament is prorogued on this day.
112 def is_prorogation_day?
113 ProrogationDay.all.where( 'date = ?', self ).first
114 end
We want to check if Parliament is dissolved on this day.
118 def is_dissolution_day?
119 DissolutionDay.all.where( 'date = ?', self ).first
120 end
We want to check if this is a day for which we have something in the calendar.
That may be an actual sitting day - including parliamentary sitting days, a virtual sitting day, an adjournment day, a day within prorogation or a day within dissolution.
126 def is_calendar_populated?
127 self.is_commons_actual_sitting_day? or self.is_lords_actual_sitting_day? or self.is_commons_virtual_sitting_day? or self.is_lords_virtual_sitting_day? or self.is_adjournment_day? or self.is_prorogation_day? or self.is_dissolution_day?
128 end
We want to check if this is a day for which we do not have anything in the calendar.
We use this to check if we've "run out of calendar" so we don't keep cycling into future days and loop infinitely.
This is our event horizon.
136 def is_calendar_not_populated?
137 !self.is_calendar_populated?
138 end
Methods to calculate non-sitting scrutiny days in both Houses.
In guidance issued on 16-04-2020 the Lords Procedure Committee stated, "A Virtual Proceeding is not a sitting of the House."
Whilst clerks state that a virtual sitting day does not count as a sitting day for the purposes of calculating scrutiny periods, lawyers imply this would need to be tested in court.
Unless and until this is resolved, these methods use the clerks' definition of non-sitting scrutiny day.
We want to check if this is a non-sitting scrutiny day in the Commons.
During a short adjournment, adjournment days count toward the scrutiny period.
This method allows the definition of a “short” adjournment to be adjusted by passing in a maximum day count.
In all known cases “short” is defined as not more than four days.
For the purposes of calculating non-sitting scrutiny days, virtual sitting days also count.
158 def is_commons_non_sitting_scrutiny_day?( maximum_day_count )
We want to check if this is a Commons adjournnment day or a Commons virtual sitting day.
162 if self.is_commons_adjournment_day? or self.is_commons_virtual_sitting_day?
Having found that this is a Commons adjournnment day or a Commons virtual sitting day, we start the adjournment day count at 1.
166 non_sitting_scrutiny_day_count = 1
We want to cycle through the following days until we reach the maximum day count passed into this function.
170 date = self
171 for i in ( 1..maximum_day_count )
Go forward one day.
175 date = date.next_day
If this is a Commons adjournnment day or a Commons virtual sitting day ...
178 if date.is_commons_adjournment_day? or date.is_commons_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
181 non_sitting_scrutiny_day_count +=1
If this is not a Commons adjournnment day or a Commons virtual sitting day ...
184 else
... stop cycling through following days.
187 break
188 end
189 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
192 date = self
193 for i in ( 1..maximum_day_count )
Go back one day.
196 date = date.prev_day
If this is a Commons adjournnment day or a Commons virtual sitting day ...
199 if date.is_commons_adjournment_day? or date.is_commons_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
202 non_sitting_scrutiny_day_count +=1
If this is not a Commons adjournnment day or a Commons virtual sitting day ...
205 else
... stop cycling through preceding days.
208 break
209 end
210 end
If the total number of continuous non-sitting scrutiny days is more than the maximum day count ...
213 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
216 is_commons_non_sitting_scrutiny_day = false
If the total number of continuous non-sitting scrutiny days is less than or the same as the maximum day count ...
219 else
... then this day does count as a non-sitting scrutiny day.
222 is_commons_non_sitting_scrutiny_day = true
223 end
Returns if this day is a Commons non-sitting scrutiny day
226 is_commons_non_sitting_scrutiny_day
227 end
228 end
We want to check if this is a non-sitting scrutiny day in the Lords.
During a short adjournment, adjournment days count toward the scrutiny period.
This method allows the definition of a “short” adjournment to be adjusted by passing in a maximum day count.
In all known cases “short” is defined as not more than four days.
For the purposes of calculating non-sitting scrutiny days, virtual sitting days also count.
240 def is_lords_non_sitting_scrutiny_day?( maximum_day_count )
We want to check if this is a Lords adjournnment day or a Lords virtual sitting day.
244 if self.is_lords_adjournment_day? or self.is_lords_virtual_sitting_day?
Having found that this is a Lords adjournnment day or a Lords virtual sitting day, we start the adjournment day count at 1.
248 non_sitting_scrutiny_day_count = 1
We want to cycle through the following days until we reach the maximum day count passed into this function.
252 date = self
253 for i in ( 1..maximum_day_count )
Go forward one day.
257 date = date.next_day
If this is a Lords adjournnment day or a Lords virtual sitting day ...
260 if date.is_lords_adjournment_day? or date.is_lords_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
263 non_sitting_scrutiny_day_count +=1
If this is not a Lords adjournnment day or a Lords virtual sitting day ...
266 else
... stop cycling through following days.
269 break
270 end
271 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
274 date = self
275 for i in ( 1..maximum_day_count )
Go back one day.
278 date = date.prev_day
If this is a Lords adjournnment day or a Lords virtual sitting day ...
281 if date.is_lords_adjournment_day? or date.is_lords_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
284 non_sitting_scrutiny_day_count +=1
If this is not a Lords adjournnment day or a Lords virtual sitting day ...
287 else
... stop cycling through preceding days.
290 break
291 end
292 end
If the total number of continuous non-sitting scrutiny days is more than the maximum day count ...
295 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
298 is_lords_non_sitting_scrutiny_day = false
If the total number of continuous non-sitting scrutiny days is less than or the same as the maximum day count ...
301 else
... then this day does count as a non-sitting scrutiny day.
304 is_lords_non_sitting_scrutiny_day = true
305 end
Returns if this day is a Lords non-sitting scrutiny day
308 is_lords_non_sitting_scrutiny_day
309 end
310 end
(End of methods to calculate non-sitting scrutiny days in both Houses.)
We want to check if this is a scrutiny day in the Commons.
A scrutiny day in the Commons is either an actual sitting day in the Commons, or a non-sitting scrutiny day in the Commons.
We pass '4' as the maximum day count to the non-sitting scrutiny day calculation, because non-sitting scrutiny days are days within a series of not more than four non-sitting days.
320 def is_commons_scrutiny_day?
321 self.is_commons_actual_sitting_day? or self.is_commons_non_sitting_scrutiny_day?( 4 )
322 end
We want to check if this is a scrutiny day in the Lords.
A scrutiny day in the Lords is either an actual sitting day in the Lords, or a non-sitting scrutiny day in the Lords.
We pass '4' as the maximum day count to the non-sitting scrutiny day calculation, because non-sitting scrutiny days are days within a series of not more than four non-sitting days.
330 def is_lords_scrutiny_day?
331 self.is_lords_actual_sitting_day? or self.is_lords_non_sitting_scrutiny_day?( 4 )
332 end
We want to check if this is a scrutiny day in either House.
336 def is_either_house_scrutiny_day?
337 self.is_commons_scrutiny_day? or self.is_lords_scrutiny_day?
338 end
We want to check if this is a scrutiny day in both Houses.
342 def is_joint_scrutiny_day?
343 self.is_commons_scrutiny_day? and self.is_lords_scrutiny_day?
344 end
(End of set of methods to work out the type of a given day.)
A set of methods to find the first day of a given type.
We want to find the first scrutiny day in either House.
This method is used when a Statutory Instrument is laid outside of a scrutiny period, that is: during a non-sitting period of more than four days, or during a period in which Parliament is prorogued. In such cases, the clock starts from the first actual sitting day in either House following the laying.
This method is used for bicameral negative SIs - and bicameral made affirmatives where the enabling legislation specifies that either House can be sitting.
357 def first_scrutiny_day_in_either_house
If this is a day on which the calendar is not yet populated ...
360 if self.is_calendar_not_populated?
... then we cannot find a first scrutiny day so we stop looking.
363 return nil
If this is a day on which the calendar is populated ...
366 else
... then if this is not a scrutiny day in either House ...
369 unless self.is_either_house_scrutiny_day?
... then go to the next day and check that.
372 self.next_day.first_scrutiny_day_in_either_house
... then if this is a scrutiny day in either House ...
375 else
... then return this day as the first scrutiny day in either House.
378 self
379 end
380 end
381 end
We want to find the first scrutiny day in both Houses.
This method is used when a Statutory Instrument is laid outside of a scrutiny period, that is: during an adjournment of more than four days, or during a period in which Parliament is prorogued. The clock starts from the first actual sitting day in both Houses following the laying.
This method is used for bicameral made affirmatives where the enabling legislation specifies that both Houses must be sitting.
389 def first_joint_scrutiny_day
If this is a day on which the calendar is not yet populated ...
392 if self.is_calendar_not_populated?
... then we cannot find a first scrutiny day so we stop looking.
395 return nil
If this is a day on which the calendar is populated ...
398 else
... then if this is not a scrutiny day in both Houses ...
401 unless self.is_joint_scrutiny_day?
... then go to the next day and check that.
404 self.next_day.first_joint_scrutiny_day
... then if this is a scrutiny day in both Houses ...
407 else
... then return this day as the first scrutiny day in both Houses.
410 self
411 end
412 end
413 end
We want to find the first scrutiny day in the Commons.
This method is used when a Statutory Instrument is laid outside of a scrutiny period, that is: during an adjournment of more than four days, or during a period in which Parliament is prorogued. The clock starts from the first actual sitting day in the Commons following the laying.
This method is used for negative or made affirmative SIs - laid in the Commons and not laid in the Lords.
421 def first_commons_scrutiny_day
If this is a day on which the calendar is not yet populated ...
424 if self.is_calendar_not_populated?
... then we cannot find a first Commons scrutiny day so we stop looking.
427 return nil
If this is a day on which the calendar is populated ...
430 else
... then if this is not a scrutiny day in the Commons ...
433 unless self.is_commons_scrutiny_day?
... then go to the next day and check that.
436 self.next_day.first_commons_scrutiny_day
... then if this is a scrutiny day in the Commons ...
439 else
... then return this day as the first scrutiny day in the Commons.
442 self
443 end
444 end
445 end
We want to find the first parliamentary sitting day in both Houses.
This method is used when a Proposed Negative Statutory Instrument is laid.
Even if a PNSI is laid on a joint parliamentary sitting day, the clock does not start until the next joint parliamentary sitting day.
453 def first_joint_parliamentary_sitting_day
If this is a day on which the calendar is not yet populated ...
456 if self.is_calendar_not_populated?
... then we cannot find a first parliamentary sitting day in both Houses so we stop looking.
459 return nil
If this is a day on which the calendar is populated ...
462 else
... then if this is not a parliamentary sitting day in both Houses ...
465 unless self.is_joint_parliamentary_sitting_day?
... then go to the next day and check that.
468 self.next_day.first_joint_parliamentary_sitting_day
... then if this is a parliamentary sitting day in both Houses ...
471 else
... then return this day as the first parliamentary sitting day in both Houses.
474 self
475 end
476 end
477 end
We want to find the first actual sitting day in both Houses.
This method is used to calculate periods A and B for treaties.
Even if a treaty is laid or a ministerial statement is made on a joint actual sitting day, the clock does not start until the next joint actual sitting day.
485 def first_joint_actual_sitting_day
If this is a day on which the calendar is not yet populated ...
488 if self.is_calendar_not_populated?
... then we cannot find a first actual sitting day in both Houses so we stop looking.
491 return nil
If this is a day on which the calendar is populated ...
494 else
... then if this is not an actual sitting day in both Houses ...
497 unless self.is_joint_actual_sitting_day?
... then go to the next day and check that.
500 self.next_day.first_joint_actual_sitting_day
... then if this is an actual sitting day in both Houses ...
503 else
... then return this day as the first actual sitting day in both Houses.
506 self
507 end
508 end
509 end
We want to find the first actual sitting day in the House of Commons.
This method is used by the House of Commons only sitting day calculation.
Even if an instrument is laid on a House of Commons actual sitting day, the clock does not start until the next House of Commons actual sitting day.
517 def first_commons_actual_sitting_day
If this is a day on which the calendar is not yet populated ...
520 if self.is_calendar_not_populated?
... then we cannot find a first actual sitting day in the House of Commons so we stop looking.
523 return nil
If this is a day on which the calendar is populated ...
526 else
... then if this is not an actual sitting day in the House of Commons ...
529 unless self.is_commons_actual_sitting_day?
... then go to the next day and check that.
532 self.next_day.first_commons_actual_sitting_day
... then if this is an actual sitting day in the House of Commons ...
535 else
... then return this day as the first actual sitting day in the House of Commons.
538 self
539 end
540 end
541 end
(End of set of methods to find the first day of a given type.)
A set of methods to return which higher level parliamentary time periods a calendar day sits in.
A calendar day may sit in either a dissolution period or a Parliament period.
If a calendar day sits inside a Parliament period, it may sit inside either a session or a prorogation period.
We want to find which dissolution period a calendar day sits in, if any.
555 def dissolution_period
556 DissolutionPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
557 end
We want to find which Parliament period a calendar day sits in, if any.
560 def parliament_period
561 ParliamentPeriod.find_by_sql([
562 "
563 SELECT *
564 FROM parliament_periods
565 WHERE start_date <= :the_date
566 AND (
567 end_date >= :the_date
568 OR
569 end_date IS NULL
570 )
571 ",
572 the_date: self
573 ]).first
574 end
We want to find which prorogoration period a calendar day sits in, if any.
577 def prorogation_period
578 ProrogationPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
579 end
We want to find which session a calendar day sits in, if any.
582 def session
583 Session.find_by_sql([
584 "
585 SELECT *
586 FROM sessions
587 WHERE start_date <= :the_date
588 AND (
589 end_date >= :the_date
590 OR
591 end_date IS NULL
592 )
593 ",
594 the_date: self
595 ]).first
596 end
We want to find out if this is the final day of a session.
599 def is_final_day_of_session?
600 is_final_day_of_session = false
601 session = Session.all.where( "end_date = ?", self )
602 is_final_day_of_session = true unless session.empty?
603 is_final_day_of_session
604 end
We want to find the session immediately preceding this date.
608 def preceding_session
609 Session.all.where( "start_date < ?", self ).order( "start_date DESC" ).first
610 end
We want to find the session immediately following this date.
This method is used to determine which session papers laid in prorogation are recorded in.
614 def following_session
615 Session.all.where( "start_date > ?", self ).order( "start_date" ).first
616 end
Generate label for the day type in the Commons in a session.
619 def commons_day_type
620 if self.is_commons_parliamentary_sitting_day?
621 day_type = 'Parliamentary sitting day'
622 elsif self.is_commons_actual_sitting_day?
623 day_type = "Continuation sitting day"
624 elsif self.is_commons_virtual_sitting_day?
625 day_type = 'Virtual sitting day'
626 elsif self.is_commons_non_sitting_scrutiny_day?( 4 )
627 day_type = 'Scrutiny non-sitting day'
628 elsif self.is_commons_adjournment_day?
629 day_type = self.commons_adjournment_day_label
630 elsif self.session
631 day_type = 'Session day of unknown type'
632 elsif self.is_prorogation_day?
633 day_type = 'Prorogation'
634 elsif self.is_dissolution_day?
635 day_type = 'Dissolution'
636 end
637 day_type
638 end
Generate label for the day type in the Lords in a session.
641 def lords_day_type
642 if self.is_lords_parliamentary_sitting_day?
643 day_type = 'Parliamentary sitting day'
644 elsif self.is_lords_actual_sitting_day?
645 day_type = "Continuation sitting day"
646 elsif self.is_lords_virtual_sitting_day?
647 day_type = 'Virtual sitting day'
648 elsif self.is_lords_non_sitting_scrutiny_day?( 4 )
649 day_type = 'Scrutiny non-sitting day'
650 elsif self.is_lords_adjournment_day?
651 day_type = self.lords_adjournment_day_label
652 elsif self.session
653 day_type = 'Session day of unknown type'
654 elsif self.is_prorogation_day?
655 day_type = 'Prorogation'
656 elsif self.is_dissolution_day?
657 day_type = 'Dissolution'
658 end
659 day_type
660 end
Generate a label to say whether it's a scrutiny day in the Commons or not.
663 def is_commons_scrutiny_day_label
664 if self.is_commons_scrutiny_day?
665 label = 'True'
666 else
667 label = 'False'
668 end
669 label
670 end
Generate a label to say whether it's a scrutiny day in the Lords or not.
673 def is_lords_scrutiny_day_label
674 if self.is_lords_scrutiny_day?
675 label = 'True'
676 else
677 label = 'False'
678 end
679 label
680 end
A method to label a Commons adjournment day, with recess if applicable.
683 def commons_adjournment_day_label
684 commons_adjournment_day_label = 'Adjournment day'
We attempt to find a recess on this date, in this House.
687 recess_date = RecessDate
688 .all
689 .where( "start_date <= ?", self )
690 .where( "end_date >= ?", self )
691 .where( house_id: 1 )
692 .first
If we find a recess date on this day, in this House ...
695 if recess_date
... we append the description of the recess date to the label
698 commons_adjournment_day_label += ' (' + recess_date.description + ')'
699 end
700 commons_adjournment_day_label
701 end
A method to label a Lords adjournment day, with recess if applicable.
704 def lords_adjournment_day_label
705 lords_adjournment_day_label = 'Adjournment day'
We attempt to find a recess on this date, in this House.
708 recess_date = RecessDate
709 .all
710 .where( "start_date <= ?", self )
711 .where( "end_date >= ?", self )
712 .where( house_id: 2 )
713 .first
If we find a recess date on this day, in this House ...
716 if recess_date
... we append the description of the recess date to the label
719 lords_adjournment_day_label += ' (' + recess_date.description + ')'
720 end
721 lords_adjournment_day_label
722 end
723 end
725 Date.include(DateMonkeyPatch)