Introduction

In a standard* (e.g. not 2020) tax year, estimated payments are due quarterly according to the schedule outlined in Publication 505.

Payment Period Due Date
January 1 – March 31 April 15
April 1 – May 31 June 15
June 1 – August 31 September 15
September 1 – December 31 January 15

* Saturday, Sunday, holiday rule, fiscal year taxpayers, and January payment exceptions can be ignored for the purpose of this question; use the standard table provided.

Challenge

Input:

A date/timestamp, either provided as input to your solution (function parameter or stdin) as a string (at a minimum specifying a specific year, month, an day of month) or native Date object (or equivalent) or the current (dynamically updated) date/time based on the clock of the executing machine.

Output:

The most specific payment period, one of Q1, Q2, Q3, or Q4 as a string, either printed to stdout/console or programmatically returned from the function, to which a timely payment made on the input date would be applied.

Input Output
January 16 - April 15 Q1
April 16 - June 15 Q2
June 16 - September 15 Q3
September 16 - January 15 Q4

A day shall commence at 00:00 (midnight) and cover time periods up to but not including 00:00 (midnight) of the following day. The last moment of a given day, to a ms granularity is 23:59:59.999.

• can it take day of the year, as in the last day of the year is day 365? Jun 17 '21 at 0:26
• Publication 505 is 48 pages. Mind stating which page you got your table from so I don't have to pour over 48 pages to find the specific reference you're using for your tables? Jun 17 '21 at 0:53
• ALSO, this is for estimated taxes not general tax payments. Update your question to be specific about that, because estimated taxes don't actually fall within that fiscal quarter. I'm not sure you can golf this based on that. Jun 17 '21 at 0:55
• @ThomasWard Page 26, but the relevant bit is copied into the problem. It's an exercise in golfing, not tax code, hence ignoring the edge cases. The publication is just for flavor :) Jun 17 '21 at 2:59
• Coule we take three arguments day, month and year?
– user100690
Jun 17 '21 at 5:18

JavaScript (ES6), 53 bytes

Expects a string in "DD-MM-YYYY" format.

s=>"Q"+"4243131"[[d,m]=s.split-,(m*24|d>15)*9%23%7]


Try it online!

39 bytes

If we can just take the month and the day.

m=>d=>"Q"+"4243131"[(m*24|d>15)*9%23%7]


Try it online!

35 bytes

As suggested by @ovs, using a larger lookup table with a much simpler formula is actually shorter.

m=>d=>"Q"+"_4111223334444"[m+=d>15]


Try it online!

• Can you explain the thought process that goes around making formulas like these? Is it mainly brute force? what is the starting point? I am always amazed by how people find "magic formulas" for things
– user100752
Jun 17 '21 at 8:07
• @EliteDaMyth That's mainly brute force on several hard-coded templates and with hard-coded ranges for each variable. I was actually pretty sure that a more standard formula would work for that one, but I couldn't find any that was shorter than the hash. Jun 17 '21 at 10:18
• Does this work? It produces the same output for your extensive test-suite.
– ovs
Jun 17 '21 at 13:42
• @ovs I think that works indeed. Jun 17 '21 at 13:53

Retina 0.8.2, 72 63 bytes

\d+
$* 1+-(1{1,5}(1)*)-1{1,15}(1)* 11$3$2$1
(111)*1+
Q$#1 0|5 4  Try it online! Link includes test cases. Takes input in YYYY-MM-DD format. Explanation: \d+$*


Convert to unary.

1+-(1{1,5}(1)*)-1{1,15}(1)*
11$3$2$1  Add 2, 3 or 4 to the month depending on whether the month is greater than 5 or the day is greater than 15 or not. (111)*1+ Q$#1


Subtract 1 from the month, then integer divide it by 3 and prefix Q to the result. (This is to avoid zero width matches.)

0|5
4


Change Q0 and Q5 to Q4.

• @Arnauld Bah, I'd totally misread the table...
– Neil
Jun 17 '21 at 11:42

R, 65 62 bytes

function(d)paste0("Q",1+sum(c(3,5,8)<el(format(d-15,"%m"):1)))


Try it online!

Expects input in R's native Date class. Subtract 15 days from the input, extract the month (as a string such as "04"), and convert to numeric with el(...:1) (see this tip). We then get the result by adding 1 for each of the values 3, 5, and 8 which are smaller than the result.

• I like the lexicographic comparison, but I think numeric can be shorter. Jun 17 '21 at 14:16
• Ha! You edited while I was typing, and yours is better! Well done! Jun 17 '21 at 14:16

Charcoal, 28 bytes

≔Ｉ⪪Ｓ-θ§12344÷⁻§θ¹⁺›¹⁶⊟θ›⁶⊟θ³


Try it online! Link is to verbose version of code. Takes input in the form YYYY-MM-DD. Explanation:

≔Ｉ⪪Ｓ-θ


Input the date, split it on - and cast to integer.

§12344÷⁻§θ¹⁺›¹⁶⊟θ›⁶⊟θ³


Take the month, subtract 1 each if the day is less than 16 or the month is less than 6, divide by 3, then cyclically index into the string 12344, thus casting -1 and 4 to 4.

Python 3.8 (pre-release), 109 bytes

lambda n,i=int:'Q'+['?111223334444'[i(k:=(S:=n.split('-'))[1])],'1234'[i(k)//3-1]][k in'1469'and(i(S[0])<16)]


Try it online!

Way too long.

The OP has confirmed very kindly that this is the only solution of mine which is valid.

• You're allowed to ignore useless arguments -there's a meta post about it. Jun 17 '21 at 20:06