When is their birthday?

6

1

Donald Knuth was born in 1938, on the 20th day of Capricorn.
The first day of Capricorn is the 22nd December.
Donald Knuth was born on the 10th of January (1938).


The challenge

Write a program or function which takes three inputs about a person (any other specified order is fine):

  • Their (calendar) year of birth (an integer)
  • Their sign of the zodiac (a string)
    - may be whichever you choose from the following, so long as it is consistent:
    • all lower case except the first character;
    • all lower case; or
    • all upper case.
  • The (1-based) day of the zodiac period on which they were born (an integer)

...and provides two outputs (the other order is fine if specified):

  • The day of the month on which they were born (an integer)
  • Their month of birth (an integer)

More details

If your answer were a function, F, then for Donald Knuth we would run F(1938, 'Capricorn', 20) and receive the output (10, 1)

For the purposes of this challenge you do not need to shift the zodiac due to the shift between the Gregorian calendar and the tropical year, but you do need to account correctly for leap years.

February has 29 days rather than 28 on leap years.
A year is usually a leap year if it is divisible by 4 - the exceptions are when it is divisible by 100 but not 400 (so, 1600 and 2000 were, whereas 1700, 1800, and 1900 were not).

For reference the names of the signs of the zodiac and their inclusive ranges are:

Aries       21 March     – 20 April
Taurus      21 April     – 21 May
Gemini      22 May       – 21 June
Cancer      22 June      – 22 July
Leo         23 July      – 22 August
Virgo       23 August    – 23 September
Libra       24 September – 23 October
Scorpio     24 October   – 22 November
Sagittarius 23 November  – 21 December
Capricorn   22 December  – 20 January
Aquarius    21 January   – 19 February
Pisces      20 February  – 20 March

This is , so the shortest code in bytes is the aim.
No loopholes, yada yada; you know the drill.


Test cases

Charles Babbage   F(1791, 'Capricorn',  5) -->  (26, 12)
George Boole      F(1815, 'Scorpio',   10) -->  ( 2, 11)
Herman Hollerith  F(1860, 'Pisces',    10) -->  (29,  2)
Vannevar Bush     F(1890, 'Pisces',    20) -->  (11,  3)
Howard H. Aiken   F(1900, 'Pisces',    17) -->  ( 8,  3)  - note 1900 was not a leap year
Donald Knuth      F(1938, 'Capricorn', 20) -->  (10,  1)
<Future Famed>    F(2000, 'Pisces',    17) -->  ( 7,  3)  - note 2000 was a leap year

Jonathan Allan

Posted 2016-09-05T20:53:12.143

Reputation: 67 804

Would it be OK to output the birth date as a string (such as "1791-12-26") rather than the birthday? Or the birthday as a string (such as "12-26")? – Arnauld – 2016-09-05T21:52:43.393

@Arnauld The output is strictly specified as two integers, so if you utilise any date utilities you'll have to pull them out. (I just saw the deleted comment, so just to clarify: the input is the calendar year of birth) – Jonathan Allan – 2016-09-05T21:56:47.650

Answers

3

Javascript (ES6), 121 120 119 bytes

(y,s,d)=>[(d=new Date(y,m=s[2]=='s'?1:'r.sune0oapti'.search(s[4]||0),23+d-'343322110012'[m])).getDate(),d.getMonth()+1]

Format for the sign of the zodiac is either:

  • all lower case except the first character
  • all lower case

Test cases

let f =
(y,s,d)=>[(d=new Date(y,m=s[2]=='s'?1:'r.sune0oapti'.search(s[4]||0),23+d-'343322110012'[m])).getDate(),d.getMonth()+1]

console.log(f(1791, 'Capricorn',  5)); // -->  (26, 12)
console.log(f(1815, 'Scorpio',   10)); // -->  ( 2, 11)
console.log(f(1860, 'Pisces',    10)); // -->  (29,  2)
console.log(f(1890, 'Pisces',    20)); // -->  (11,  3)
console.log(f(1900, 'Pisces',    17)); // -->  ( 8,  3)
console.log(f(1938, 'Capricorn', 20)); // -->  (10,  1)
console.log(f(2000, 'Pisces',    17)); // -->  ( 7,  3)

Arnauld

Posted 2016-09-05T20:53:12.143

Reputation: 111 334

2

TSQL, 147 bytes

Golfed:

DECLARE @ DATETIME='2000',@s char(3)='Pisces',@d INT=17

SELECT @s=CHARINDEX(@s,'  PisAriTauGemCanLeoVirLibScoSagCap')/3,@=DATEADD(m,@s*1,@)+18+substring('101122334432',@s+1,1)*1+@d SELECT day(@),month(@)

Ungolfed:

DECLARE @ DATETIME='2000',@s char(3)='Pisces',@d INT=17

SELECT 
  @s=CHARINDEX(@s,'  PisAriTauGemCanLeoVirLibScoSagCap')/3,
  @=DATEADD(m,@s*1,@)+18+substring('101122334432',@s+1,1)*1+@d
SELECT day(@),month(@)

Fiddle

t-clausen.dk

Posted 2016-09-05T20:53:12.143

Reputation: 2 874

1

Python 3 - 333 309 299 240 238 bytes

from datetime import*
def f(y,s,d):y=datetime(y,1,1)+timedelta((([[[21,80,111,142,173,235,267,297,327,356]['rsuneoapti'.index(s[4])],51][s[2]=='s'],204][len(s)<4])+d)%(365+[[[0,1][y%4==0],0][y%100==0],1][y%400==0])-2);return y.day,y.month

Saved 24 26 bytes thanks to @Jonathan Allan

Andrew Epstein

Posted 2016-09-05T20:53:12.143

Reputation: 341

You can shave off 20 bytes by moving the contents of the lambda into the only place you call it and replacing it's n with y and shortening mod. – Jonathan Allan – 2016-09-05T22:24:45.630

You could also save another 4 by moving S into place and then another 3 by putting the first three lines of the function on one line using ; to separate. – Jonathan Allan – 2016-09-05T22:44:02.827

Running on Python 3.3 gives me a syntax error for len(s)<4else with no space after the 4, but you could still save by switching the statements around and using if s[3:]; i.e. [[21,...=='s']if s[3:]else 204. Edit: in fact you could do s[3:]and[[21,...=='s']or 204 – Jonathan Allan – 2016-09-06T04:36:02.283