C#, 630 611 604 572 570 bytes, 120 years
(add ~2⅔ bytes per additional year if you know the offset)
This is good for people born 31-Jan-1900 through 24-Jan-2020 and will likely crash outside of that range. Are there bonus points for the number of years covered?
string Z(DateTime date)
{
int[] days = new int[] { 3, 22, 11, 1, 19, 7, -3, 16, 5, -6, 13, 2,
21, 9, -2, 17, 6, -5, 14, 4, 23, 11, 0, 19,
8, -3, 16, 5, -5, 13, 2, 20, 9, -2, 17, 7,
-4, 14, 3, 22, 11, -1, 18, 8, -3, 16, 5, -6,
13, 1, 20, 9, -1, 17, 6, -4, 15, 3, 21, 11,
0, 18, 8, -3, 16, 5, -7, 12, 2, 20, 9, -1,
18, 6, -5, 14, 3, 21, 10, 0, 19, 8, -3, 16,
5, 23, 12, 1, 20, 9, -1, 18, 7, -5, 13, 3,
22, 10, 0, 19, 8, -4, 15, 4, -6, 12, 1, 21,
10, -2, 17, 6, -5, 13, 3, 22, 11, 0, 19, 8 };
string[] signs = "Rat,Ox,Tiger,Rabbit,Dragon,Snake,Horse,Goat,Monkey,Rooster,Dog,Pig".Split(',');
string[] elements = "Metal,Water,Wood,Fire,Earth".Split(',');
string[] polarities = new string[] { "Yang", "Yin" };
int year = date.Year - 1900;
int x = year - (date.DayOfYear < days[year] + 28 ? 1 : 0);
return signs[x % 12] + " - " + elements[x / 2 % 5] + " - " + polarities[x % 2];
}
Or condensed (with added line breaks):
string Z(DateTime d){
int y=d.Year-1900,
x=y-(d.DayOfYear<new[]{3,22,11,1,19,7,-3,16,5,-6,13,2,21,9,-2,17,6,-5,14,4,23,11,0,19,8,-3,16,5,-5,13,2,20,9,-2,17,7,-4,14,3,22,11,-1,18,8,-3,16,5,-6,13,1,20,9,-1,17,6,-4,15,3,21,11,0,18,8,-3,16,5,-7,12,2,20,9,-1,18,6,-5,14,3,21,10,0,19,8,-3,16,5,23,12,1,20,9,-1,18,7,-5,13,3,22,10,0,19,8,-4,15,4,-6,12,1,21,10,-2,17,6,-5,13,3,22,11,0,19,8}[y]+28?1:0);
return "Rat,Ox,Tiger,Rabbit,Dragon,Snake,Horse,Goat,Monkey,Rooster,Dog,Pig".Split(',')[x%12]+" - "+"Metal,Water,Wood,Fire,Earth".Split(',')[x/2%5]+" - "+new[]{"Yang","Yin"}[x%2];
}
One of the tricks was to have the offset table's origin at 28-Jan. This proved to have the lowest character count.
If by chance it is insisted the input is a string, add 22 characters to change the method signature to:
string Z(string i){
and add the line:
var d=DateTime.Parse(i);
EDITS:
- Put all strings in one array and added offsets to the output selectors.
- Changed the day offset to 28-Jan.
string.Split()
Inspiration taken from Eduard Florinescu's answer.
- Inlined all arrays. It only saved me 2 characters. :-/
3Do you have a convenient link to how to calculate the start of a zodiac year? – Hand-E-Food – 2014-01-21T23:46:07.077
@Hand-E-Food Unfortunately no, I am trying to figure out and come with details, but other than that it seems pretty simple. – Eduard Florinescu – 2014-01-21T23:52:06.290
3You have no idea how to calculate the zodaic year but it "seems pretty simple". Perhaps we should close this question for now, and reopen it when you have those little details ironed out? – None – 2014-01-22T00:07:03.177
@LegoStormtroopr "other than that seems pretty simple" i.e. the beginning of the new Chinese year. I provided the link to Wikipedia an there is the table which is to big to bring it up here – Eduard Florinescu – 2014-01-22T00:14:54.887
1Another thing to note is that it is always between Jan 21 and Feb 21 inclusive. – Justin – 2014-01-22T00:32:11.260
@Quincunx It has to do with the new moon http://en.wikipedia.org/wiki/New_moon#Determining_new_moons:_an_approximate_formula
– Eduard Florinescu – 2014-01-22T00:42:34.537This and this might be useful. – plannapus – 2014-01-22T08:41:42.470
3The table you link is of no use unless you add a restriction that it is unnecessary to give the correct output for dates outside that range. – Peter Taylor – 2014-01-22T09:57:26.330
2Eduard Florinescu, two things you should clarify in the question: 1) input date range as @PeterTaylor points out, and 2) does the output format have to be exactly as you specify in your example? Every character counts so please make this objective. Thanks. – Darren Stone – 2014-01-22T18:32:50.007
I missed the latest edit; it fixes the previous problems, but introduces one new ambiguity: given that the requirement is now to take a date as input, can people opt to take dates in a sensible (e.g. ISO 8601) format as opposed to U.S. middle-endian? – Peter Taylor – 2014-02-01T19:23:45.220
@PeterTaylor you can opt to ISO 8601 see edit, but the question is closed – Eduard Florinescu – 2014-02-01T20:34:19.293