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.
33 def is_commons_parliamentary_sitting_day?
34 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 1 ).first
35 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.
41 def is_lords_parliamentary_sitting_day?
42 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 2 ).first
43 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.
53 def is_commons_virtual_sitting_day?
54 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
55 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.
62 def is_lords_virtual_sitting_day?
63 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
64 end
We want to check if this is an actual sitting day in either House.
68 def is_either_house_actual_sitting_day?
69 self.is_commons_actual_sitting_day? or self.is_lords_actual_sitting_day?
70 end
We want to check if this is an actual sitting day in both Houses.
74 def is_joint_actual_sitting_day?
75 self.is_commons_actual_sitting_day? and self.is_lords_actual_sitting_day?
76 end
We want to check if this is a parliamentary sitting day in either House.
80 def is_either_house_parliamentary_sitting_day?
81 self.is_commons_parliamentary_sitting_day? or self.is_lords_parliamentary_sitting_day?
82 end
We want to check if this is a parliamentary sitting day in both Houses.
86 def is_joint_parliamentary_sitting_day?
87 self.is_commons_parliamentary_sitting_day? and self.is_lords_parliamentary_sitting_day?
88 end
We want to check if this is an adjournment day in either House.
92 def is_adjournment_day?
93 AdjournmentDay.all.where( 'date = ?', self ).first
94 end
We want to check if this is an adjournment day in the Commons.
98 def is_commons_adjournment_day?
99 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 1 ).first
100 end
We want to check if this is an adjournment day in the Lords.
104 def is_lords_adjournment_day?
105 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 2 ).first
106 end
We want to check if Parliament is prorogued on this day.
110 def is_prorogation_day?
111 ProrogationDay.all.where( 'date = ?', self ).first
112 end
We want to check if Parliament is dissolved on this day.
116 def is_dissolution_day?
117 DissolutionDay.all.where( 'date = ?', self ).first
118 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.
124 def is_calendar_populated?
125 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?
126 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.
134 def is_calendar_not_populated?
135 !self.is_calendar_populated?
136 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.
156 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.
160 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.
164 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.
168 date = self
169 for i in ( 1..maximum_day_count )
Go forward one day.
173 date = date.next_day
If this is a Commons adjournnment day or a Commons virtual sitting day ...
176 if date.is_commons_adjournment_day? or date.is_commons_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
179 non_sitting_scrutiny_day_count +=1
If this is not a Commons adjournnment day or a Commons virtual sitting day ...
182 else
... stop cycling through following days.
185 break
186 end
187 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
190 date = self
191 for i in ( 1..maximum_day_count )
Go back one day.
194 date = date.prev_day
If this is a Commons adjournnment day or a Commons virtual sitting day ...
197 if date.is_commons_adjournment_day? or date.is_commons_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
200 non_sitting_scrutiny_day_count +=1
If this is not a Commons adjournnment day or a Commons virtual sitting day ...
203 else
... stop cycling through preceding days.
206 break
207 end
208 end
If the total number of continuous non-sitting scrutiny days is more than the maximum day count ...
211 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
214 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 ...
217 else
... then this day does count as a non-sitting scrutiny day.
220 is_commons_non_sitting_scrutiny_day = true
221 end
Returns if this day is a Commons non-sitting scrutiny day
224 is_commons_non_sitting_scrutiny_day
225 end
226 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.
238 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.
242 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.
246 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.
250 date = self
251 for i in ( 1..maximum_day_count )
Go forward one day.
255 date = date.next_day
If this is a Lords adjournnment day or a Lords virtual sitting day ...
258 if date.is_lords_adjournment_day? or date.is_lords_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
261 non_sitting_scrutiny_day_count +=1
If this is not a Lords adjournnment day or a Lords virtual sitting day ...
264 else
... stop cycling through following days.
267 break
268 end
269 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
272 date = self
273 for i in ( 1..maximum_day_count )
Go back one day.
276 date = date.prev_day
If this is a Lords adjournnment day or a Lords virtual sitting day ...
279 if date.is_lords_adjournment_day? or date.is_lords_virtual_sitting_day?
... add one to the non-sitting scrutiny day count.
282 non_sitting_scrutiny_day_count +=1
If this is not a Lords adjournnment day or a Lords virtual sitting day ...
285 else
... stop cycling through preceding days.
288 break
289 end
290 end
If the total number of continuous non-sitting scrutiny days is more than the maximum day count ...
293 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
296 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 ...
299 else
... then this day does count as a non-sitting scrutiny day.
302 is_lords_non_sitting_scrutiny_day = true
303 end
Returns if this day is a Lords non-sitting scrutiny day
306 is_lords_non_sitting_scrutiny_day
307 end
308 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.
318 def is_commons_scrutiny_day?
319 self.is_commons_actual_sitting_day? or self.is_commons_non_sitting_scrutiny_day?( 4 )
320 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.
328 def is_lords_scrutiny_day?
329 self.is_lords_actual_sitting_day? or self.is_lords_non_sitting_scrutiny_day?( 4 )
330 end
We want to check if this is a scrutiny day in either House.
334 def is_either_house_scrutiny_day?
335 self.is_commons_scrutiny_day? or self.is_lords_scrutiny_day?
336 end
We want to check if this is a scrutiny day in both Houses.
340 def is_joint_scrutiny_day?
341 self.is_commons_scrutiny_day? and self.is_lords_scrutiny_day?
342 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.
355 def first_scrutiny_day_in_either_house
If this is a day on which the calendar is not yet populated ...
358 if self.is_calendar_not_populated?
... then we cannot find a first scrutiny day so we stop looking.
361 return nil
If this is a day on which the calendar is populated ...
364 else
... then if this is not a scrutiny day in either House ...
367 unless self.is_either_house_scrutiny_day?
... then go to the next day and check that.
370 self.next_day.first_scrutiny_day_in_either_house
... then if this is a scrutiny day in either House ...
373 else
... then return this day as the first scrutiny day in either House.
376 self
377 end
378 end
379 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.
387 def first_joint_scrutiny_day
If this is a day on which the calendar is not yet populated ...
390 if self.is_calendar_not_populated?
... then we cannot find a first scrutiny day so we stop looking.
393 return nil
If this is a day on which the calendar is populated ...
396 else
... then if this is not a scrutiny day in both Houses ...
399 unless self.is_joint_scrutiny_day?
... then go to the next day and check that.
402 self.next_day.first_joint_scrutiny_day
... then if this is a scrutiny day in both Houses ...
405 else
... then return this day as the first scrutiny day in both Houses.
408 self
409 end
410 end
411 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.
419 def first_commons_scrutiny_day
If this is a day on which the calendar is not yet populated ...
422 if self.is_calendar_not_populated?
... then we cannot find a first Commons scrutiny day so we stop looking.
425 return nil
If this is a day on which the calendar is populated ...
428 else
... then if this is not a scrutiny day in the Commons ...
431 unless self.is_commons_scrutiny_day?
... then go to the next day and check that.
434 self.next_day.first_commons_scrutiny_day
... then if this is a scrutiny day in the Commons ...
437 else
... then return this day as the first scrutiny day in the Commons.
440 self
441 end
442 end
443 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.
451 def first_joint_parliamentary_sitting_day
If this is a day on which the calendar is not yet populated ...
454 if self.is_calendar_not_populated?
... then we cannot find a first parliamentary sitting day in both Houses so we stop looking.
457 return nil
If this is a day on which the calendar is populated ...
460 else
... then if this is not a parliamentary sitting day in both Houses ...
463 unless self.is_joint_parliamentary_sitting_day?
... then go to the next day and check that.
466 self.next_day.first_joint_parliamentary_sitting_day
... then if this is a parliamentary sitting day in both Houses ...
469 else
... then return this day as the first parliamentary sitting day in both Houses.
472 self
473 end
474 end
475 end
We want to find the last preceding 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.
483 def last_joint_parliamentary_sitting_day
If this is a day on which the calendar is not yet populated ...
486 if self.is_calendar_not_populated?
... then we cannot find a first parliamentary sitting day in both Houses so we stop looking.
489 return nil
If this is a day on which the calendar is populated ...
492 else
... then if this is not a parliamentary sitting day in both Houses ...
495 unless self.is_joint_parliamentary_sitting_day?
... then go to the previous day and check that.
498 self.prev_day.last_joint_parliamentary_sitting_day
... then if this is a parliamentary sitting day in both Houses ...
501 else
... then return this day as the first parliamentary sitting day in both Houses.
504 self
505 end
506 end
507 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.
515 def first_joint_actual_sitting_day
If this is a day on which the calendar is not yet populated ...
518 if self.is_calendar_not_populated?
... then we cannot find a first actual sitting day in both Houses so we stop looking.
521 return nil
If this is a day on which the calendar is populated ...
524 else
... then if this is not an actual sitting day in both Houses ...
527 unless self.is_joint_actual_sitting_day?
... then go to the next day and check that.
530 self.next_day.first_joint_actual_sitting_day
... then if this is an actual sitting day in both Houses ...
533 else
... then return this day as the first actual sitting day in both Houses.
536 self
537 end
538 end
539 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.
547 def first_commons_actual_sitting_day
If this is a day on which the calendar is not yet populated ...
550 if self.is_calendar_not_populated?
... then we cannot find a first actual sitting day in the House of Commons so we stop looking.
553 return nil
If this is a day on which the calendar is populated ...
556 else
... then if this is not an actual sitting day in the House of Commons ...
559 unless self.is_commons_actual_sitting_day?
... then go to the next day and check that.
562 self.next_day.first_commons_actual_sitting_day
... then if this is an actual sitting day in the House of Commons ...
565 else
... then return this day as the first actual sitting day in the House of Commons.
568 self
569 end
570 end
571 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.
585 def dissolution_period
586 DissolutionPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
587 end
We want to find which Parliament period a calendar day sits in, if any.
590 def parliament_period
591 ParliamentPeriod.find_by_sql([
592 "
593 SELECT *
594 FROM parliament_periods
595 WHERE start_date <= :the_date
596 AND (
597 end_date >= :the_date
598 OR
599 end_date IS NULL
600 )
601 ",
602 the_date: self
603 ]).first
604 end
We want to find which prorogoration period a calendar day sits in, if any.
607 def prorogation_period
608 ProrogationPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
609 end
We want to find which session a calendar day sits in, if any.
612 def session
613 Session.find_by_sql([
614 "
615 SELECT *
616 FROM sessions
617 WHERE start_date <= :the_date
618 AND (
619 end_date >= :the_date
620 OR
621 end_date IS NULL
622 )
623 ",
624 the_date: self
625 ]).first
626 end
We want to find out if this is the final day of a session.
629 def is_final_day_of_session?
630 is_final_day_of_session = false
631 session = Session.all.where( "end_date = ?", self )
632 is_final_day_of_session = true unless session.empty?
633 is_final_day_of_session
634 end
We want to find the session immediately preceding this date.
638 def preceding_session
639 Session.all.where( "start_date < ?", self ).order( "start_date DESC" ).first
640 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.
644 def following_session
645 Session.all.where( "start_date > ?", self ).order( "start_date" ).first
646 end
Generate label for the day type in the Commons in a session.
649 def commons_day_type
650 if self.is_commons_parliamentary_sitting_day?
651 day_type = 'Parliamentary sitting day'
652 elsif self.is_commons_actual_sitting_day?
653 day_type = "Continuation sitting day"
654 elsif self.is_commons_virtual_sitting_day?
655 day_type = 'Virtual sitting day'
656 elsif self.is_commons_non_sitting_scrutiny_day?( 4 )
657 day_type = 'Scrutiny non-sitting day'
658 elsif self.is_commons_adjournment_day?
659 day_type = self.commons_adjournment_day_label
660 elsif self.session
661 day_type = 'Session day of unknown type'
662 elsif self.is_prorogation_day?
663 day_type = 'Prorogation'
664 elsif self.is_dissolution_day?
665 day_type = 'Dissolution'
666 end
667 day_type
668 end
Generate label for the day type in the Lords in a session.
671 def lords_day_type
672 if self.is_lords_parliamentary_sitting_day?
673 day_type = 'Parliamentary sitting day'
674 elsif self.is_lords_actual_sitting_day?
675 day_type = "Continuation sitting day"
676 elsif self.is_lords_virtual_sitting_day?
677 day_type = 'Virtual sitting day'
678 elsif self.is_lords_non_sitting_scrutiny_day?( 4 )
679 day_type = 'Scrutiny non-sitting day'
680 elsif self.is_lords_adjournment_day?
681 day_type = self.lords_adjournment_day_label
682 elsif self.session
683 day_type = 'Session day of unknown type'
684 elsif self.is_prorogation_day?
685 day_type = 'Prorogation'
686 elsif self.is_dissolution_day?
687 day_type = 'Dissolution'
688 end
689 day_type
690 end
Generate a label to say whether it's a scrutiny day in the Commons or not.
693 def is_commons_scrutiny_day_label
694 if self.is_commons_scrutiny_day?
695 label = 'True'
696 else
697 label = 'False'
698 end
699 label
700 end
Generate a label to say whether it's a scrutiny day in the Lords or not.
703 def is_lords_scrutiny_day_label
704 if self.is_lords_scrutiny_day?
705 label = 'True'
706 else
707 label = 'False'
708 end
709 label
710 end
A method to label a Commons adjournment day, with recess if applicable.
713 def commons_adjournment_day_label
714 commons_adjournment_day_label = 'Adjournment day'
We attempt to find a recess on this date, in this House.
717 recess_date = RecessDate
718 .all
719 .where( "start_date <= ?", self )
720 .where( "end_date >= ?", self )
721 .where( house_id: 1 )
722 .first
If we find a recess date on this day, in this House ...
725 if recess_date
... we append the description of the recess date to the label
728 commons_adjournment_day_label += ' (' + recess_date.description + ')'
729 end
730 commons_adjournment_day_label
731 end
A method to label a Lords adjournment day, with recess if applicable.
734 def lords_adjournment_day_label
735 lords_adjournment_day_label = 'Adjournment day'
We attempt to find a recess on this date, in this House.
738 recess_date = RecessDate
739 .all
740 .where( "start_date <= ?", self )
741 .where( "end_date >= ?", self )
742 .where( house_id: 2 )
743 .first
If we find a recess date on this day, in this House ...
746 if recess_date
... we append the description of the recess date to the label
749 lords_adjournment_day_label += ' (' + recess_date.description + ')'
750 end
751 lords_adjournment_day_label
752 end
753 end
755 Date.include(DateMonkeyPatch)