Calculation code comments Monkey patched date class
On GitHub: config/initializers/monkey_patching.rb
2 module DateMonkeyPatch
See also: Parliamentary Time Period ontology
A Venn diagram of parliamentary time
A set of methods to determine the type of a given day across both Houses.
We want to determine if Parliament is dissolved on this day.
13 def is_dissolution_day?
14 DissolutionDay.all.where( 'date = ?', self ).first
15 end
We want to determine if Parliament is prorogued on this day.
18 def is_prorogation_day?
19 ProrogationDay.all.where( 'date = ?', self ).first
20 end
A set of methods to return the parliamentary time periods a calendar day forms part of.
A calendar day forms part of a dissolution period or a Parliament period.
Where a calendar day forms part of a Parliament period, it forms part of a session or a prorogation period.
We want to find which Parliament period a calendar day forms part of, if any.
27 def parliament_period
28 ParliamentPeriod.find_by_sql([
29 "
30 SELECT *
31 FROM parliament_periods
32 WHERE start_date <= :the_date
33 AND (
34 end_date >= :the_date
35 OR
36 end_date IS NULL
37 )
38 ",
39 the_date: self
40 ]).first
41 end
We want to find which dissolution period a calendar day forms part of, if any.
44 def dissolution_period
45 DissolutionPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
46 end
We want to find which session a calendar day forms part of, if any.
49 def session
50 Session.find_by_sql([
51 "
52 SELECT *
53 FROM sessions
54 WHERE start_date <= :the_date
55 AND (
56 end_date >= :the_date
57 OR
58 end_date IS NULL
59 )
60 ",
61 the_date: self
62 ]).first
63 end
We want to find which prorogation period a calendar day forms part of, if any.
66 def prorogation_period
67 ProrogationPeriod.all.where( "start_date <= ?", self ).where( "end_date >= ?", self).first
68 end
A set of methods to determine the type of a given day in a House.
Sitting days
Parliamentary clerks define a sitting day in a House as a day when the House started sitting. Should a House start sitting on a Monday, continue sitting through the night, rise on the Tuesday and not start sitting again on that Tuesday, that would count as one sitting day.
As far as we are aware, this definition made its first appearance in legislation as part of schedule 5, paragraph 44 (2) of the European Union (Withdrawal Agreement) Act 2020.
Clerks have suggested that this definition would apply across all calculations of scrutiny periods. Lawyers have suggested otherwise.
The Parliamentary Time application defines three types of sitting day:
- a parliamentary sitting day, being a day on which a House started sitting according to the definition given above.
- a calendar sitting day, being a day on which a House sat, regardless of whether it started sitting on that date.
- a continuation sitting day, being a day on which a House sat when it did not start sitting on that date.
We want to determine if this is a parliamentary sitting day in the Commons.
84 def is_commons_parliamentary_sitting_day?
85 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 1 ).first
86 end
We want to determine if this is a parliamentary sitting day in the Lords.
89 def is_lords_parliamentary_sitting_day?
90 SittingDay.all.where( 'start_date = ?', self ).where( house_id: 2 ).first
91 end
We want to determine if this is a calendar sitting day in the Commons.
This method is never called so is commented out here.
95 def is_commons_calendar_sitting_day?
96 SittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
97 end
We want to determine if this is a calendar sitting day in the Lords.
This method is never called so is commented out here.
101 def is_lords_calendar_sitting_day?
102 SittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
103 end
We want to determine if this is a continuation sitting day in the Commons.
106 def is_commons_continuation_sitting_day?
107 SittingDay.all.where( 'start_date < ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
108 end
We want to determine if this is a continuation sitting day in the Lords.
111 def is_lords_continuation_sitting_day?
112 SittingDay.all.where( 'start_date < ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
113 end
Virtual sitting days
During the COVID pandemic, the House of Lords sat virtually for 11 days.
As of February 2026, the House of Commons has never sat virtually.
In guidance issued on 16 April 2020 the Lords Procedure Committee stated:
"A Virtual Proceeding is not a sitting of the House. There is no Mace present and the Virtual Proceeding will not be empowered to make decisions. Virtual Proceedings can debate and sc[r]utinise an issue but when a decision is needed that must be taken by 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 determine if this is a virtual sitting day in the Commons.
126 def is_commons_virtual_sitting_day?
127 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 1 ).first
128 end
We want to determine if this is a virtual sitting day in the Lords.
131 def is_lords_virtual_sitting_day?
132 VirtualSittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self ).where( house_id: 2 ).first
133 end
Adjournment days
We want to determine if this is an adjournment day in the Commons.
139 def is_commons_adjournment_day?
140 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 1 ).first
141 end
We want to determine if this is an adjournment day in the Lords.
144 def is_lords_adjournment_day?
145 AdjournmentDay.all.where( 'date = ?', self ).where( house_id: 2 ).first
146 end
Adjourned days
Days on which a House is adjourned encompass more day types than those declared as adjournment days in the calendar.
According to the guidance set out by the Lords Procedure Committee - given above - days on which a House sat virtually also count as adjourned days.
According to the definition of a parliamentary sitting day - also given above - as understood by clerks and as set out in some legislation, continuation sitting days also count as adjourned days.
We want to determine if the day counts as the Commons being adjourned.
155 def counts_as_commons_adjourned_day?
156 self.is_commons_adjournment_day? \
157 or self.is_commons_virtual_sitting_day? \
158 or self.is_commons_continuation_sitting_day?
159 end
We want to determine if the day counts as the Lords being adjourned.
162 def counts_as_lords_adjourned_day?
163 self.is_lords_adjournment_day? \
164 or self.is_lords_virtual_sitting_day? \
165 or self.is_lords_continuation_sitting_day?
166 end
A set of methods to determine the type of a given day in either House.
We want to determine if this is a parliamentary sitting day in either House.
172 def is_either_house_parliamentary_sitting_day?
173 SittingDay.all.where( 'start_date = ?', self ).first
174 end
We want to determine if this is a calendar sitting day in either House.
177 def is_either_house_calendar_sitting_day?
178 SittingDay.all.where( 'start_date <= ?', self ).where( 'end_date >= ?', self).first
179 end
We want to determine if this is an adjournment day in either House.
182 def is_either_house_adjournment_day?
183 AdjournmentDay.all.where( 'date = ?', self ).first
184 end
A set of methods to determine if this is a joint sitting day.
We want to check if this is a parliamentary sitting day in both Houses.
190 def is_joint_parliamentary_sitting_day?
191 self.is_commons_parliamentary_sitting_day? and self.is_lords_parliamentary_sitting_day?
192 end
We want to determine if this is a calendar sitting day in both Houses.
195 def is_joint_calendar_sitting_day?
196 self.is_commons_calendar_sitting_day? and self.is_lords_calendar_sitting_day?
197 end
A set of methods to determine if the calendar is populated.
A method to determine if this is a day for which we have something in the calendar.
That may be:
- a day within dissolution
- a day within prorogation
- a parliamentary sitting day
- a calendar sitting day
- a virtual sitting day
- an adjournment day
210 def is_calendar_populated?
211 self.is_dissolution_day? \
212 or self.is_prorogation_day? \
213 or self.is_commons_calendar_sitting_day? \
214 or self.is_lords_calendar_sitting_day? \
215 or self.is_commons_virtual_sitting_day? \
216 or self.is_lords_virtual_sitting_day? \
217 or self.is_commons_adjournment_day? \
218 or self.is_lords_adjournment_day?
219 end
We want to determine if this is a day for which we do not have anything in the calendar.
This method is used to determine whether a calculation has enough calendar data to proceed.
If the calendar is not populated for a date, calculations cannot proceed past that date.
224 def is_calendar_not_populated?
225 !self.is_calendar_populated?
226 end
A set of methods to determine whether a date forms part of a break in sitting days of a determined length.
Under some legislation, periods where the Houses are adjourned for a defined number of days count as scrutiny days for instruments laid under powers in that legislation.
In the legislation we've encountered where this is set out, such periods are defined as being not more than four days.
These methods allow the calculation to be adjusted by passing in a maximum day count.
Methods to calculate non-sitting scrutiny days in both Houses.
We want to check if this is a non-sitting scrutiny day in the Commons.
237 def is_commons_non_sitting_scrutiny_day?( maximum_day_count )
If this counts as an adjourned day in the Commons ...
240 if self.counts_as_commons_adjourned_day?
... we start the non-sitting scrutiny day count at 1.
243 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.
246 date = self
For each number between 1 and the maximum day count ...
249 for i in ( 1 .. maximum_day_count )
... we go forward one day.
252 date = date.next_day
If this day counts as an adjourned day in the Commons ...
255 if date.counts_as_commons_adjourned_day?
... we add one to the non-sitting scrutiny day count.
258 non_sitting_scrutiny_day_count += 1
Otherwise, if this day does not count as an adjourned day in the Commons ...
261 else
... we stop cycling through following days.
264 break
265 end
266 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
269 date = self
For each number between 1 and the maximum day count ...
272 for i in ( 1 .. maximum_day_count )
... we go back one day.
275 date = date.prev_day
If this day counts as an adjourned day in the Commons ...
278 if date.counts_as_commons_adjourned_day?
... we add one to the non-sitting scrutiny day count.
281 non_sitting_scrutiny_day_count += 1
Otherwise, if this day does not count as an adjourned day in the Commons ...
284 else
... we stop cycling through preceding days.
287 break
288 end
289 end
If the total number of non-sitting scrutiny days is more than the maximum day count ...
292 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
295 is_commons_non_sitting_scrutiny_day = false
If the total number of non-sitting scrutiny days is less than or the same as the maximum day count ...
298 else
... then this day does count as a non-sitting scrutiny day.
301 is_commons_non_sitting_scrutiny_day = true
302 end
We return if this day is a Commons non-sitting scrutiny day
305 is_commons_non_sitting_scrutiny_day
306 end
307 end
We want to check if this is a non-sitting scrutiny day in the Lords.
310 def is_lords_non_sitting_scrutiny_day?( maximum_day_count )
If this counts as an adjourned day in the Lords ...
313 if self.counts_as_lords_adjourned_day?
... we start the non-sitting scrutiny day count at 1.
316 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.
319 date = self
For each number between 1 and the maximum day count ...
322 for i in ( 1 .. maximum_day_count )
... we go forward one day.
325 date = date.next_day
If this day counts as an adjourned day in the Lords ...
328 if date.counts_as_lords_adjourned_day?
... we add one to the non-sitting scrutiny day count.
331 non_sitting_scrutiny_day_count += 1
Otherwise, if this day does not count as an adjourned day in the Lords ...
334 else
... we stop cycling through following days.
337 break
338 end
339 end
We want to cycle through the preceding days until we reach the maximum day count passed into this function.
342 date = self
For each number between 1 and the maximum day count ...
345 for i in ( 1 .. maximum_day_count )
... we go back one day.
348 date = date.prev_day
If this day counts as an adjourned day in the Lords ...
351 if date.counts_as_lords_adjourned_day?
... we add one to the non-sitting scrutiny day count.
354 non_sitting_scrutiny_day_count += 1
Otherwise, if this day does not count as an adjourned day in the Lords ...
357 else
... we stop cycling through preceding days.
360 break
361 end
362 end
If the total number of non-sitting scrutiny days is more than the maximum day count ...
365 if non_sitting_scrutiny_day_count > maximum_day_count
... then this day does not count as a non-sitting scrutiny day.
368 is_lords_non_sitting_scrutiny_day = false
If the total number of non-sitting scrutiny days is less than or the same as the maximum day count ...
371 else
... then this day does count as a non-sitting scrutiny day.
374 is_lords_non_sitting_scrutiny_day = true
375 end
We return if this day is a Lord non-sitting scrutiny day
378 is_lords_non_sitting_scrutiny_day
379 end
380 end
A set of methods to determine if this is a scrutiny day in a House, in both Houses and in either House.
We want to determine if this is a scrutiny day in the Commons.
385 def is_commons_scrutiny_day?
We set the Commons is scrutiny day boolean to false.
388 is_commons_scrutiny_day = false
If this is a parliamentary sitting day in the Commons ...
... or this is a non-sitting scrutiny day in the Commons ...
392 if self.is_commons_parliamentary_sitting_day? or self.is_commons_non_sitting_scrutiny_day?( 4 ) # 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.
... we set the is Commons scrutiny day boolean to true.
395 is_commons_scrutiny_day = true
396 end
We return the is Commons scrutiny day boolean.
399 is_commons_scrutiny_day
400 end
We want to determine if this is a scrutiny day in the Lords.
403 def is_lords_scrutiny_day?
We set the Lords is scrutiny day boolean to false.
406 is_lords_scrutiny_day = false
If this is a parliamentary sitting day in the Lords ...
... or this is a non-sitting scrutiny day in the Lords ...
410 if self.is_lords_parliamentary_sitting_day? or self.is_lords_non_sitting_scrutiny_day?( 4 ) # 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.
... we set the Lords is scrutiny day boolean to true.
413 is_lords_scrutiny_day = true
414 end
We return the Lords is scrutiny day boolean.
417 is_lords_scrutiny_day
418 end
We want to check if this is a scrutiny day in both Houses.
421 def is_joint_scrutiny_day?
We set the is joint scrutiny day boolean to false.
424 is_joint_scrutiny_day = false
If this is a scrutiny day in the Commons and this is a scrutiny day in the Lords ...
427 if self.is_commons_scrutiny_day? and self.is_lords_scrutiny_day?
... we set the is joint scrutiny day boolean to true.
430 is_joint_scrutiny_day = true
431 end
We return the is joint scrutiny day boolean.
434 is_joint_scrutiny_day
435 end
We want to check if this is a scrutiny day in either House.
438 def is_either_house_scrutiny_day?
We set the is either house scrutiny day boolean to false.
441 is_either_house_scrutiny_day = false
If this is a scrutiny day in the Commons or this is a scrutiny day in the Lords ...
444 if self.is_commons_scrutiny_day? or self.is_lords_scrutiny_day?
... we set the is either house scrutiny day boolean to true.
447 is_either_house_scrutiny_day = true
448 end
We return the is either house scrutiny day boolean.
451 is_either_house_scrutiny_day
452 end
A set of methods to determine the relationship of a day to its containing or preceding session.
We want to determine if this is the final day of a session.
457 def is_final_day_of_session?
We set the is final day of session boolean to false.
460 is_final_day_of_session = false
We attempt to find a session ending on this date.
463 session = Session.all.where( "end_date = ?", self ).first
If we find a session ending on this data ...
466 if session
... we set the is final day of session boolean to true.
469 is_final_day_of_session = true
470 end
We return the is final day of session boolean.
473 is_final_day_of_session
474 end
We want to find the session immediately preceding this day.
477 def preceding_session
We attempt to find the first session starting before this day.
480 Session.all.where( "start_date < ?", self ).order( "start_date DESC" ).first
481 end
We want to find the session immediately following this day.
This method is used to determine which session papers laid in prorogation are recorded in.
485 def following_session
We attempt to find the next session starting after this day.
488 Session.all.where( "start_date > ?", self ).order( "start_date" ).first
489 end
A set of methods to find the first following and first preceding joint sitting days.
We attempt to find the first following joint sitting day.
This method is used in the forwards calculations for a Proposed Negative Statutory Instrument.
Even if a PNSI is laid on a joint sitting day, the clock does not start until the next joint sitting day.
496 def first_joint_parliamentary_sitting_day
If this is a day on which the calendar is not yet populated ...
499 if self.is_calendar_not_populated?
... we cannot find a following joint sitting day, so we stop looking.
502 return nil
Otherwise, if this is a day on which the calendar is populated ...
505 else
... if this is not a joint sitting day ...
508 unless self.is_joint_parliamentary_sitting_day?
... we go to the next day and check that.
511 self.next_day.first_joint_parliamentary_sitting_day
Otherwise, if this is a joint sitting day ...
514 else
... we return this day as the first following joint sitting day.
517 self
518 end
519 end
520 end
We want to find the first preceding joint sitting day.
This method is used in the backwards calculations for a Proposed Negative Statutory Instrument.
Even if a PNSI is laid on a joint sitting day, the clock does not start until the next joint sitting day.
525 def last_joint_parliamentary_sitting_day
If this is a day on which the calendar is not yet populated ...
528 if self.is_calendar_not_populated?
... we cannot find a preceding joint sitting day, so we stop looking.
531 return nil
Otherwise, if this is a day on which the calendar is populated ...
534 else
... if this is not a joint sitting day ...
537 unless self.is_joint_parliamentary_sitting_day?
... we go to the previous day and check that.
540 self.prev_day.last_joint_parliamentary_sitting_day
Otherwise, if this is a joint sitting day ...
543 else
... we return this day as the first preceding joint sitting day.
546 self
547 end
548 end
549 end
A set of methods to apply labels to a day in the calendar views.
A method to generate a label for the type of a day in the Commons in a session.
554 def commons_day_type
If the day is a parliamentary sitting day in the Commons ...
557 if self.is_commons_parliamentary_sitting_day?
... we set the day type label to 'Sitting day'.
560 day_type = 'Sitting day'
Otherwise, if the day is a continuation sitting day in the Commons ...
563 elsif self.is_commons_continuation_sitting_day?
... we set the day type label to 'Continuation sitting day'.
566 day_type = "Continuation sitting day"
Otherwise, if the day is a virtual sitting day in the Commons ...
569 elsif self.is_commons_virtual_sitting_day?
... we set the day type label to 'Virtual sitting day'.
572 day_type = 'Virtual sitting day'
Otherwise, if the day is a adjournment day in the Commons ...
575 elsif self.is_commons_adjournment_day?
... we want to display if the adjournment day forms part of a named recess ...
... so we call the Commons adjournment day label method.
579 day_type = self.commons_adjournment_day_label
Otherwise, if the day is in a session but has no defined type ...
582 elsif self.session
... we set the day type label to 'Session day of unknown type'.
585 day_type = 'Session day of unknown type'
Otherwise, if the day is a prorogation day ...
588 elsif self.is_prorogation_day?
... we set the day type label to 'Prorogation'.
591 day_type = 'Prorogation'
Otherwise, if the day is a dissolution day ...
594 elsif self.is_dissolution_day?
... we set the day type label to 'Dissolution'.
597 day_type = 'Dissolution'
598 end
We return the day type label.
601 day_type
602 end
A method to generate a label for the type of a day in the Lords in a session.
605 def lords_day_type
If the day is a parliamentary sitting day in the Lords ...
608 if self.is_lords_parliamentary_sitting_day?
... we set the day type label to 'Sitting day'.
611 day_type = 'Sitting day'
Otherwise, if the day is a continuation sitting day in the Lords ...
614 elsif self.is_lords_continuation_sitting_day?
... we set the day type label to 'Continuation sitting day'.
617 day_type = "Continuation sitting day"
Otherwise, if the day is a virtual sitting day in the Lords ...
620 elsif self.is_lords_virtual_sitting_day?
... we set the day type label to 'Virtual sitting day'.
623 day_type = 'Virtual sitting day'
Otherwise, if the day is a adjournment day in the Lords ...
626 elsif self.is_lords_adjournment_day?
... we want to display if the adjournment day forms part of a named recess ...
... so we call the Lords adjournment day label method.
630 day_type = self.lords_adjournment_day_label
Otherwise, if the day is in a session but has no defined type ...
633 elsif self.session
... we set the day type label to 'Session day of unknown type'.
636 day_type = 'Session day of unknown type'
Otherwise, if the day is a prorogation day ...
639 elsif self.is_prorogation_day?
... we set the day type label to 'Prorogation'.
642 day_type = 'Prorogation'
Otherwise, if the day is a dissolution day ...
645 elsif self.is_dissolution_day?
... we set the day type label to 'Dissolution'.
648 day_type = 'Dissolution'
649 end
We return the day type label.
652 day_type
653 end
A set of methods to decorate a day label for an adjournment day with the name of a recess where applicable.
A method to label a Commons adjournment day, with the name of a recess if applicable.
658 def commons_adjournment_day_label
We set the day type label to 'Adjournment day'
661 day_type = 'Adjournment day'
We attempt to find a recess on this date, in this House.
664 recess_date = RecessDate
665 .all
666 .where( "start_date <= ?", self )
667 .where( "end_date >= ?", self )
668 .where( house_id: 1 ) # 1 being the ID of the House of Commons.
669 .first
If we find a recess date on this day, in this House ...
672 if recess_date
... we append the description of the recess date to the day type label.
675 day_type += ' (' + recess_date.description + ')'
676 end
We return the day type label.
679 day_type
680 end
A method to label a Lords adjournment day, with the name of a recess if applicable.
683 def lords_adjournment_day_label
We set the day type label to 'Adjournment day'
686 day_type = 'Adjournment day'
We attempt to find a recess on this date, in this House.
689 recess_date = RecessDate
690 .all
691 .where( "start_date <= ?", self )
692 .where( "end_date >= ?", self )
693 .where( house_id: 2 ) # 2 being the ID of the House of Lords.
694 .first
If we find a recess date on this day, in this House ...
697 if recess_date
... we append the description of the recess date to the day type label.
700 day_type += ' (' + recess_date.description + ')'
701 end
We return the day type label.
704 day_type
705 end
A set of methods to generate a label for whether this day counts as a scrutiny day in a House, or not.
A method to generate a label for whether this day counts as a scrutiny day in the Commons, or not.
710 def is_commons_scrutiny_day_label
If this is a scrutiny day in the Commons ...
713 if self.is_commons_scrutiny_day?
... we set the label to 'True'.
716 label = 'True'
Otherwise, if this is not a scrutiny day in the Commons ...
719 else
... we set the label to 'False'.
722 label = 'False'
723 end
We return the label.
726 label
727 end
A method to generate a label for whether this day counts as a scrutiny day in the Lords, or not.
730 def is_lords_scrutiny_day_label
If this is a scrutiny day in the Lords ...
733 if self.is_lords_scrutiny_day?
... we set the label to 'True'.
736 label = 'True'
Otherwise, if this is not a scrutiny day in the Lords ...
739 else
... we set the label to 'False'.
742 label = 'False'
743 end
We return the label.
746 label
747 end
748 end
We include these methods in the Ruby Data class.
751 Date.include( DateMonkeyPatch )
