text:
Concise case changing code
Create the shortest program that given an input string it outputs the opposite-cased string. For example if I pass the string "HeLlO" the program should output "hElLo".
Of course, neither aggregate functions can be used, nor framework functions like toupper() or tolower(). Any language is welcome.

Do I have to handle characters that aren't letters or spaces?

Those are irrelevant

@defmx what about uppercase numbers like ^?

Does the program have to handle letters other than A-Za-z? For example, what should the program output for ёЖик уКраЇнА?

Can you define "aggregate function" in the context of this question?

@PeterOlson Oh I only considered the latin alphabet for this exercise, I think this would be another puzzle

You still need to answer DigitalTrauma's question. (Does "aggregate function" mean fold?) And you still need to fully answer Peter Olson's question; the Latin alphabet covers over a thousand Unicode code points.

@PeterTaylor I presume defmx means the Basic Latin alphabet (U+0041 to U+005A and U+0061 to U+007A).

Can I assume that only A-Za-z will be input?

Bash shell, 16 characters
tr A-Za-z a-zA-Z

Sample runs:
llama@llama:~$ tr A-Za-z a-zA-Z
ThIs is a TeSt!
tHiS IS A tEsT!
llama@llama:~$ echo 'Programming Puzzles & Code Golf Stack Exchange' | tr A-Za-z a-zA-Z
pROGRAMMING pUZZLES & cODE gOLF sTACK eXCHANGE

It works in a file, too (of course):
llama@llama:~$ echo 'tr A-Za-z a-zA-Z' > swapcase.sh
llama@llama:~$ echo 'This is a test!' | bash swapcase.sh
tHIS IS A TEST!

I don't think anybody could possibly get their program any shorter than this

@ThreeFx you are right it is very short, however this question comes to my mind: is it really a program? I think this only invokes a program; I'm not disqualifying the idea, just help me get this clear.

@defmx It is indeed a Bash program; I suppose you could think of tr as a function in other languages. (You could even say that most small shell scripts are kind of just a lot of tiny scripts glued together to make a bigger script. :P) It runs as an executable if you add a #!/bin/bash to the beginning of the swapcase.sh file, so it is a program. In fact, I could even say that this is a 13-character program in tr, but that would be stretching it :)

@Doorknob Excellent, yes we could say it is a program in tr because it is the interpreter, cool!

echo ${1~~} for 11 chars, but I suppose this undocumented feature might counts as "framework functions"

CJam - 5
q32f^

Try it at http://cjam.aditsu.net/
Let me know if I missed something.

Wow, very nice! This seems to strip spaces and non-alphabetic characters, however.

@Doorknob well, it xor's everything with 32, space becomes NUL, but in my understanding, non-alphabetic characters are irrelevant

C, 67 chars
main(c,d){while(~(c=getchar()))d=c|32,putchar(d>96&&d<123?c^32:c);}

Input/output via STDIN/STDOUT:
$ ./swapcase <<< "HELLO world!"
hello WORLD!
$

C, 41 chars
main(){while(putchar(getchar()^32)<127);}

If we strictly only care about alphabetical letters.
$ printf %s {a..z} {A..Z} | ./swapcase
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz$
$

Perl, 16 (17 counting -p flag as 1)
y/A-Za-z/a-zA-Z/

Fundamentally the same as the sh solution. The only room for golfing is using the y synonym for tr.
I'd like to protest that all of these solutions are only correct for ASCII, though. I'd like to see someone produce a solution that's correct for Unicode 7.0, without using library functions or external data :-P

That's evil.  Post it as a challenege ;-)  If someone did that, you'd probably see everyone starting off with the same long Chinese-character filled string to cram the data in once someone makes it :)  Not all letters have a round-trip case conversion ( lc(uc(lc('ẞ'))) -> 'ss' (not ß), for instance).

@guifa I'll investigate. Going to see whether (IMO) the amount of information contained in the case mappings is "reasonable" for a challenge. If so, I'll write it.

To be honest, it will be too much. You'd need to know which characters have upper or lower versions which at two bits per character you're at ~16KB total just for masking whether the transformation can happen. Then you'll need 1-4 bytes to express the transformation per letter that can transform. I'd try limiting it to, say, just particular Latin code tables to make it feasible without coding in actual data compression

@guifa I'm optimistic that cleverness can get it down to perhaps 2kB without algorithms like gzip. Setting out to prove it :)

I've got some ideas on possible ways, given there's an uneven distribution. It'll be interesting to see people's tactics for sure.

This is a polyglot - it's also a valid sed script which does the same thing!

Vim, 2
~G

In Normal mode, with the cursor on the first line, and with 'tildeop' set, this command will swap the case of every character in the current buffer. This probably shouldn't count, but it's also very likely the fewest number of characters one need type to perform the operation in question, so I figured I'd share.

How about norm gg~G (9 chars) as a valid statement in VimL?

Marbelous - 249
00 @0 00 @1
&2 &0 // &0
.. /\ @0 \/
]] !! 0A
RR .. \\ &2
=0 \\ 00
&2 @1 /\
:R
}0 .. 00 @2
=A .. &1 +Z @3
@0 .. {0 +U //
.. /\ .. &0
>Z {0 >Z @4
-Z .. -Z
>T @2 >Z
-U .. -Z
<Q @3 >Q
+Z .. -S
+Z .. <Q &0
+R @4 +Z \/
\\ .. +S @0
.. &2 // &1
.. {0 .. \/

Don't forget to count newlines as characters, however, if you take out the spaces (which marbelous supports) you can get it down to 211 bytes, including newlines.

Python, 109 bytes
import sys
s=''
for c in sys.argv[1]:
  n=ord(c)
  if 64<n<91:n+=32
  elif 96<n<123:n-=32
  s+=chr(n)
print s

Would appending to a string and printing it at the end instead of all the messy sys stuff save any characters?

@Doorknob 2 chars is 2 chars!

Heh, well at least it's shorter! Couldn't you remove the ,sys on the first line now?

@Doorknob newp, need it for argv.

Oh, right. Actually, you could just use input() instead (in Python 3), which is shorter and also removes the need for sys. (you would have to add parentheses for print, but you would save many more characters)

@Doorknob By my estimation it would shave off another 2 chars, but I don't have etiher Python 3 installed or the gumption to do so. :P

Wouldn't it save 7? -4 for sys.argv[1] to input(), -4 for removing ,sys, and +1 for adding parentheses on the print s line.

Actually, now that I think about it, why do you even need to import string? You're not using anything in it (ord and chr are builtins).

I'm fairly new to python, and when I started I swear ord() didn't work until I imported string. :I

Cobra - 100 (or 81)
class P
    def main
        for c in Console.readLine,Console.write(if(c.isLetter,((c to int)^32)to char,c))

If only letters matter
class P
    def main
        for c as int in Console.readLine,Console.write((c^32)to char)

Befunge 93 - 45
>~:55+-#v_@>
_v#`g19:<] ^,-g29
 >92g+,    ^

Terminates when a NL is entered.

Python - 44
print''.join(chr(ord(c)^32)for c in input())

The program expects an input string quotes "HeLLo", which should be legit. Using Python 3 we can easily pass the string without quotes like HeLLo at the cost of two more characters for the print-brackets.

Two bytes shorter as a lambda

Python - 68
import sys
print''.join(chr((ord(l)-33)%64+65) for l in sys.argv[1])

Simple python script featuring no ifs or elses.

Rebol - 25
forall s[s/1: s/1 xor 32]

Usage example (in Rebol console):
>> ;; s is the input string

>> s: "HeLlO"
== "HeLlO"

>> forall s [s/1: s/1 xor 32]
== #"o"

>> s
== "hElLo"

>> forall s [s/1: s/1 xor 32]
== #"O"

>> s
== "HeLlO"

Japt, 3 bytes
c^H

Try it
XORs (^) the charcode (c) of each character with 32 (H).

Lua - 78
while""do a=io.read(1)b=a<']'and 32 or-32 io.write(string.char(a:byte()+b))end

Nothing special, and it ups any character smaller ']' and downs any one larger.

CoffeeScript - 85 (or 77 67)
Since non-alphabetic characters don't matter:
f=(x)->x.split('').map((c)->String.fromCharCode((c.charCodeAt(0)-33)%64+65)).join('')

Discarding whitespace and other non-alphabetic characters:
f=(x)->x.split('').map((c)->String.fromCharCode(c.charCodeAt(0)^32)).join('')

Even shorter:
f=(x)->(String.fromCharCode(c.charCodeAt(0)^32)for c in x).join('')

Clojure - 182 chars
I'm a Clojure newbie. Golfed:
(defn b[i x y](if(and(> i x)(< i y)) true false))(print(apply str(map #(let[i(int %)](if(b i 64 92)(char (+ i 32))(if(b i 96 123)(char (+ i -32)) %))) (first *command-line-args*))))

Ungolfed:
(defn b[i x y] (if (and(> i x)(< i y)) true false))
(print (apply str (map
        #(let [i (int %)] (if (b i 64 92)
                             (char (+ i 32))
                             (if (b i 96 123) (char (+ i -32)) %)))
        (first *command-line-args*))))

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/


text:
Plug it back in tonight or this weekend
This is my first code golf so please let me know if it's too broad or if I'm missing any information for a good puzzle!
Challenge
In Ontario and possibly other areas of the world, electricity is billed using Time-Of-Use (TOU) pricing, which varies the cost per kilowatt-hour according to when you use power.
Given a date and time, I want to know whether I'm in an on-peak (red), mid-peak (yellow), or off-peak (green) time period.
Input
Assume that input is provided in an acceptable timezone-less ISO 8601 date-time format with the minimum precision of hours: YYYY-MM-DDThh[:mm[:ss]] (the T is literal).
Examples

2014-09-01T14
2014-09-01T17:30
2014-09-01T17:30:02

Output
The output should be a string On, Mid, or Off.
Rules

Shortest code wins
For the purposes of this challenge, ignore statutory holidays
Assume the information found in this post. The actual rules of time-of-use pricing might change in the future by the Ontario Ministry of Energy.

Information
Summer weekdays (May 1st to October 31st)

Off-peak: 19h00 - 07h00
Mid-peak: 07h00 - 11h00 and 17h00 - 19h00
On-peak: 11h00 - 17h00

Winter weekdays (November 1st to April 30th)

Off-peak: 19h00 - 07h00
Mid-peak: 11h00 - 17h00
On-peak: 07h00 - 11h00 and 17h00 - 19h00

Weekends

Off-peak: All day

Are you sure the winter weekdays don't have mid-peak / on-peak swapped?

@JanDvorak, in winter people use lights and heating in the morning and evening; in summer they use air-con at midday.

This is borderline-duplicate of http://codegolf.stackexchange.com/q/7008/194 (parse a datetime and do a simple calculation based on whether it's a working day or not). I think the season dependency is just different enough, but others may disagree.

@PeterTaylor the rules seem much simpler here than in the linked question. This doesn't have to handle leap years, for example.

@JanDvorak you do need to consider leap years to correctly calculate the weekends in February and March. However it does seem simpler in its rules and output, and more rigid in its datetime format,  so I don't think it's a duplicate.

Should the general date format be YYYY-MM-DDThh[:mm[:ss]] since seconds can only be applied if minutes are applied?

Buy two of these, plug them in, program them, and then move the plug from one to the next twice a year. Problem solved: http://www.amazon.com/GE-Plug-In-Digital-Security-Feature/dp/B007BJUKVM unless you just want off-peak and don't care about mid peak.  Then you only need one.  If you don't care about weekends you can get a cheaper 24 hour timer.

Semi-on topic: I'm happy BC is fighting against those smart meters ;)

Ruby - 147 144 143 141 137 135
x=->s{y,m,d,h=s.scan(/\d+/).map &:to_i
g=Time.new(y,m,d).wday%6<1?0:[0..11,4..9].count{|r|r===h-7}
%W{Off Mid On}[m<5||m>10?(3-g)%3:g]}

This represents a function which takes a string as a parameter and returns a string.
Here's an online demo with some test cases: http://ideone.com/wyIydw

Python 2 - 164
from datetime import*
d,t=input().split('T')
y,m,d=map(int,d.split('-'))
t=int(t[:2])
print'OMOfinfd'[(1+((10<t<17)==(4<m<11)))*(date(y,m,d).weekday()<5<6<t<19)::3]

If needed, below is a explanation of the logic in the final line:
The final line prints a slice of 'OMOfinfd' depending on the evaluation of its conditionals.

First, evaluate the operation 1+((10<t<17)==(4<m<11)).
If the XNOR between the conditions 10<t<17 and 4<m<11 is False, this will evaluate to 1+False => 1+0 => 1. Otherwise, the operation will evaluate to 1+True => 1+1 => 2.
Finally, multiply that result of the above operation by whether the day is a weekday and whether the time is between 6am-7pm.
If this is False, either the day is a weekend or the time is between 7pm-6am, and the result will be (1|2)*0 => 0. Otherwise the result will be (1|2)*1 => 1|2.

A result of 0 will print Off, 1 will print Mid, and 2 will print On.

Isn't the golfed version actually longer because you use semicolons? Or are newlines counted?

Newlines are typically counted. In Notepad++ (where I typically get my counts) the ungolfed version is 4bytes longer at 168.

@BeetDemGuise Edit → EOL Conversion → UNIX/OSX Format.

Ruby - 135
Abuses the Time module. Input by command line argument.
d=Time.new(*$*[0].scan(/\d+/)[0..3])
o,m,f=%w{On Mid Off}
o,m=m,o if (d.mon-5)%10<6
p d.wday%6<1||(12>h=(d.hour+5)%24)?f:15<h&&h<22?m:o

Edit: Thanks w0lf for Time which helped shorten and solve a bug.

Your program is incorrect. For the input 2014-09-01T17:30 it correctly outputs "Mid", but for 2014-09-01T17 it outputs "Off".

C# - 240 220 chars
string x(string s){var d=DateTime.Parse((s+":00:00").Substring(0,19));int h=d.Hour,i=(int)d.DayOfWeek,x=d.Month;string o="off",m="mid",f="on";return i==6|i==0?o:x>=5&x<11?h>18|h<7?o:h>10&h<17?f:m:h>18|h<7?o:h>10&h<17?m:f;}

Nothing special. Straight forward coding.
Thanks to w0lf :)

I think you can shorten s.Length==13?s+":00:00":s.Length==16?s+":00":s to (s+":00:00").Substring(0,19)

Groovy - 621 534 524 491 chars
Some further golfing to do, but pretty simple when leveraging Joda-Time
@Grab(group='joda-time',module='joda-time',version='2.3')
f={p,a->org.joda.time.format.DateTimeFormat.forPattern(p).parseDateTime a}
g={p,a->def x=null;try{x=f p,a}catch(Exception e){}}
a=args[0]
d=["",":mm",":mm:ss"].collect{g "yyyy-MM-dd'T'HH$it",a}.find{it}
r="Off"
m="Mid"
j={d,a,b->d.hourOfDay>a&&d.hourOfDay<b}
k={j(it,6,11)||(j(it,16,19))}
if(d.dayOfWeek<6){x=d.monthOfYear;if(x>4&&x<12){if(j(d,10,17))r="On";if(k(d))r=m}else if(x<5||x>10){if(j(d,10,17))r=m;if(k(d))r="On"}}
println r

sample runs:
bash-3.2$ ./run.peak.sh
groovy Peak.groovy 2014-08-26T19
Off
groovy Peak.groovy 2014-08-26T07:00
Mid
groovy Peak.groovy 2014-08-26T18:00:00
Mid
groovy Peak.groovy 2014-08-26T12:30:30
On
groovy Peak.groovy 2014-11-01T00
Off
groovy Peak.groovy 2014-02-05T11:11:11
Mid
groovy Peak.groovy 2014-01-05T08:08
Off
groovy Peak.groovy 2014-12-18T18:59:59
On
groovy Peak.groovy 2014-08-31T14
Off

Ungolfed:
@Grab(group='joda-time',module='joda-time',version='2.3')

f = { p,a -> org.joda.time.format.DateTimeFormat.forPattern(p).parseDateTime a}
g = { p,a -> def x=null; try{x=f p,a} catch(Exception e) {} }
a=args[0]

d = ["",":mm",":mm:ss"].collect{g "yyyy-MM-dd'T'HH$it",a}.find{it}
r = "Off"
m = "Mid"

j = {d,a,b -> d.hourOfDay > a && d.hourOfDay < b}
k = { j(it,6,11) || (j(it,16,19)) }

if (d.dayOfWeek<6) {
    x = d.monthOfYear;
    if ( x>4 && x<12 ) {
        if (j(d,10,17)) r="On";
        if (k(d)) r=m
    } else if (x<5||x>10) {
        if (j(d,10,17)) r=m;
        if (k(d)) r="On"
    }
}
println r

Since Java 8, there's also java.time.*, which is very similar to Joda Time but is part of the JRE. Perhaps this could shorten the code a bit.

re: Java 8. excellent point

In your 3rd-to-last example '2014-01-05T08:08', January 5th, 2014 is a Sunday. Thus, it should be 'Off'

re: Jan 5, 2014. Quite right. The current code is correct, but the output run is wrong. Will fix.

Tried java.time.*. failed miserably. The parser is too damn strict. Use DateParser from nashorn (also part of the JRE8) which is lenient, and with a bit of abusive hacking, even lenient enough to skip minutes&seconds.

R, 243 204 characters
b=strptime(scan(,""),"%Y-%m-%dT%H");i=function(x)as.integer(format(b,x));h=i("%H");d=i("%m%d");w=d>1100|d<430;f=ifelse;cat(c("Off","Mid","On")[f(i("%u")%in%5:6|h<7|h>19,1,f(h>11&h<17,f(w,2,3),f(w,3,2)))])

Indented and commented:
b=strptime(scan(,""),"%Y-%m-%dT%H") #Takes stdin and converts into POSIXct
i=function(x)as.integer(format(b,x)) #Format the POSIXct and convert it to integer
h=i("%H")      #Format to hours
d=i("%m%d")    #Format to Month/Day
w=d>1100|d<430 #True if winter time, false if summer
f=ifelse
cat(c("Off","Mid","On")[f(i("%u")%in%5:6|h<7|h>19, #If weekend or night
                          1,                       #Case 1
                          f(h>11&h<17,            #Else if mid-day
                             f(w,3,2),             #Case 2 in winter, case 3 in summer
                             f(w,2,3)))])          #else vice versa

Examples:
> b=strptime(scan(,""),"%Y-%m-%dT%H");i=function(x)as.integer(format(b,x));h=i("%H");d=i("%m%d");w=d>1100|d<430;f=ifelse;cat(c("Off","Mid","On")[f(i("%u")%in%5:6|h<7|h>19,1,f(h>11&h<17,f(w,3,2),f(w,2,3)))])
1: 2014-08-26T15
2:
Read 1 item
On
> b=strptime(scan(,""),"%Y-%m-%dT%H");i=function(x)as.integer(format(b,x));h=i("%H");d=i("%m%d");w=d>1100|d<430;f=ifelse;cat(c("Off","Mid","On")[f(i("%u")%in%5:6|h<7|h>19,1,f(h>11&h<17,f(w,3,2),f(w,2,3)))])
1: 2014-12-10T15
2:
Read 1 item
Mid
> b=strptime(scan(,""),"%Y-%m-%dT%H");i=function(x)as.integer(format(b,x));h=i("%H");d=i("%m%d");w=d>1100|d<430;f=ifelse;cat(c("Off","Mid","On")[f(i("%u")%in%5:6|h<7|h>19,1,f(h>11&h<17,f(w,3,2),f(w,2,3)))])
1: 2014-08-26T23
2:
Read 1 item
Off

Bash, 286
this is a simple bash answer using the date program
d(){ date -d $1 +%$2; };D=$(echo $1|sed 's/\(T..\)$/\1:00/');H=$(d $D H);M=$(d $D m);if [ $(d $D u) -gt 5 ]||[ $H -lt 7 ]||[ $H -gt 18 ];then echo Off;exit;fi;if [ $M -gt 4 ]&&[ $M -lt 11 ];then I=On;O=Mid;else I=Mid;O=On;fi;if [ $H -gt 10 ]&&[ $H -lt 17 ];then echo $I;else echo $O;fi

Here goes another one!
JavaScript, 175 171
function f(x){d=new Date(x.slice(0,10));t=x.slice(11,13),m=(x.slice(5,7)+1)%12;return(t<8||t>18||!(d.getUTCDay()%6)?'off':((t<11||t>17)?(m<5?'on':'mid'):(m<5?'mid':'on'))}

Unminified:
function f(x) {
  d = new Date(x.slice(0, 10));
  t = x.slice(11, 13), m = (x.slice(5, 7) + 1) % 12;
  return (t < 8 || t > 18 || !(d.getUTCDay() % 6) ? 'off' : ((t < 11 || t > 17) ? (m < 5 ? 'on' : 'mid') : (m < 5 ? 'mid' : 'on'))
}

Only works on interpreters where an ISO8601 date string can be passed into the Date constructor.
CoffeeScript, 192 189
Surprisingly, it's longer in CoffeeScript because there's no ternary operator in that language (which as you can see from my JavaScript, I heavily relied on).
f=(x)->d=new Date(x.slice(0,10));t=x.slice(11,13);m=(x.slice(5,7)+1)%12;return'off'if(t<8||t>18||!(d.getUTCDay()%6));if(t<11||t>17)then(if m<5then'on'else'mid')else(if m<5then'mid'else'on')

Python 3 - 352 chars
import datetime as dt
t=input()
i=int
p=print
a='on'
b='mid'
c='off'
m=i(t[5:7])
h=i(t[11:13])
d=dt.date(i(t[0:4]),m,i(t[8:10])).weekday()
if 5==d or 6==d:p(c)
elif h>=19 and h<7:p(c)
elif m<=10 and m>=4:
 if h>=7 and h<11:p(b)
 if h>=11 and h<17:p(a)
 if h>=17 and h<19:p(b)
else:
 if h>=7 and h<11:p(a)
 if h>=11 and h<17:p(b)
 if h>=17 and h<19:p(a)

You should change s=['high','mid','off'] to s=['on','mid','off'] - not only does this save 2 chars, but the spec says to output "on".

Java - 426 309 / 301? (see comments)
String z(String a){
    DateParser b=new DateParser(a);
    boolean c=b.parseEcmaDate();
    Integer[]d=b.getDateFields();
    GregorianCalendar e=new GregorianCalendar(d[0],d[1]-(c?0:1),d[2]);
    e.setTimeZone(new SimpleTimeZone(0,""));
    return(124>>e.get(7)&1)>0&d[3]>6&d[3]<19?
           d[3]>10&d[3]<17^(1008>>e.get(2)&1)>0?"Mid":"On":"Off";
}

Example output:
2014-03-02T00   Off
2014-03-02T06   Off
2014-03-02T07   Off
2014-03-02T10   Off
2014-03-02T11   Off
2014-03-02T16   Off
2014-03-02T17   Off
2014-03-02T18   Off
2014-03-02T19   Off
2014-03-02T23   Off
2014-04-02T00   Off
2014-04-02T06   Off
2014-04-02T07   On
2014-04-02T10   On
2014-04-02T11   Mid
2014-04-02T16   Mid
2014-04-02T17   On
2014-04-02T18   On
2014-04-02T19   Off
2014-04-02T23   Off
2014-05-02T00   Off
2014-05-02T06   Off
2014-05-02T07   Mid
2014-05-02T10   Mid
2014-05-02T11   On
2014-05-02T16   On
2014-05-02T17   Mid
2014-05-02T18   Mid
2014-05-02T19   Off
2014-05-02T23   Off

I used the same EXOR trick as the Python submission. I also used a + as an OR function, for when it's weekend OR night.
My other big trick: bit masks.
For example, to see if a number is between 2 and 6 (Monday to Friday), first create a bit pattern where the interesting values are 1:
  6   2
0b1111100 = 124

Then, use bit shift to get the interesting bit to the LSB and extract it:
(124 >> day_of_week) & 1

Similarly, I made bit patterns for months and hours:
  9    4
0b1111110000 = 1008

      76          76
0b111110000000000001111111 = 16253055
0b000000011111100000000000 = 129024

Unfortunately, it turns out simply x>y&x<z is shorter in most cases, so I didn't use it in some places.
And finally, a bit of hackery (highly implementation dependent) with jdk.nashorn.internal.parser.DateParser:
When parseEcmaDate() doesn't completely parse a date (like when it reads hour and hits end of string), it returns false.

When it finishes properly, the date is stored in an Integer[] (auto-unboxing ftw), with the month fixed to be base-0 (like other Java classes).
When it returns false, it aborted half-way and didn't do this fixing. It however still put whatever it parsed into the array, which is readily available. Hence the -(c?0:1).

I think using just .parse() might also work (shaving off 8 characters), but I haven't tested it thoroughly with different inputs. parse internally calls parseEcmaDate and if it fails, calls parseLegacyDate. The latter may screw up the array, but this didn't happen with a few cases I tested.

ES6 - 146
This is in function form, uses a couple nasty hacks.
let y,m,d,h,c=s=>[y,m,d,h]=s.split(/[T:-]/g).map(i=>+i),new Date(y,m,d).getDay()in{0:1,6:1}||h<20||8>h?'Off':['Mid','On'][(10>h&&h>16)^(m>5||m<8)]

Explained:
// These variables are declared outside of the function scope to save
// characters.
let y, // year
    m, // month
    d, // day
    h, // hour
    c = s => // c for check, called as c(isoString)
      [y, m, d, h] = // Assign to fields
        s.split(/[T:-]/g) // Split at delimiters
         .map(i => +i), // Convert all to numbers
                        // Comma used to keep it as a single statement.
      new Date(y, m, d).getDay() // Create Date to get day of week
        in {0:1, 6:1} // Check if it is good (0 = Sunday, 6 = Saturday). It
                      // is shorter to use an object literal than to
                      // do individual checks.
      ||
      h < 20 || 8 > h ? // h is between 7 and 19 (7pm)
       'Off' : // Return 'Off'
       ['Mid','On'][ // Two other outputs
        (10 > h && h > 16) ^ // Check if it is 11-16 (5pm)
        (m > 5 || m < 8)] // Invert that if the month is in summer/fall. Note
                          // that this returns a number, either 1 or 0. This
                          // is an ugly hack using the bitwise xor operator.

Nothing stops me from entering my own, and there's other shorter ones here anyways, so…
PHP 5.4+, 194
<?function f($x){$t=substr($x,11,2);$m=(substr($x,5,2)+1)%12;if($t<8||$t>18||(new DateTime(substr($x,0,10)))->format('N')>5)return'off';return($t<11||$t>17)?($m<5?'on':'mid'):($m<5?'mid':'on');}

Unminified and commented:
<?
function f($x) {
  $t = substr($x,11,2); // hour
  $m = (substr($x,5,2) + 1) % 12; // month shifted up by 1

  if ($t < 8 || $t > 18 || (new DateTime(substr($x,0,10)))->format('N') > 5)
    return 'off'; // evenings and weekends

  return ($t < 11 || $t > 17)
    ? ($m < 5 ? 'on' : 'mid') // morning and mid-afternoon
    : ($m < 5 ? 'mid' : 'on'); // afternoon
}

Also note that the date.timezone directive in php.ini must be set, otherwise an Exception will be thrown.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/

sort: votes


text:
How strong are nonary numbers?
You are given a nonary (base 9) non-negative integer consisting of the digits 0 through 8 as usual. However the number of digits in this number (with no leading zeros) is a prefect square.
Because of this, the number can be arranged in a square grid (with the reading order still preserved).
Example with 1480 (1125 base 10):
14
80

Now let every digit in such a nonary grid indicate a motion to another grid space (with periodic boundary conditions):
432
501
678

This is saying that
0 = stay still
1 = move right
2 = move right and up
3 = move up
...
8 = move right and down

So, if in the 1480 grid you start at the 4, you then move up (remember pbc) and left to the 8, which means you move right and down back to the 4, starting a cycle with period 2.
In general this process is continued until you get to a 0 or a cycle is noticed. (A 0 is considered a cycle with period 1.)
In the case of 1480, the period eventually reached at each of the 4 starting digits is 2 2 2 1 respectively.
For a larger grid these numbers might be larger than 8, but we can still use them as "digits" in a new nonary number (simply the coefficients of 9^n as if they were digits):
2*9^3 + 2*9^2 + 2*9 + 1 = 1639 (base 10) = 2221 (base 9)

We will call this the strength of the original nonary number. So the strength of 1480 is 1639 (base 10) or, equivalently, 2221 (base 9).
Challenge
Write the shortest program that tells whether the strength of a nonary number is greater than, less than, or equal to the nonary number itself. (You do not necessarily need to compute the strength.)
The input will be a non-negative nonary number that contains a square number of digits (and no leading zeros besides the special case of 0 itself). It should come from the command line or stdin.
The output should go to stdout as:
G if the strength is larger than the original number (example: 1480 -> strength = 2221)
E if the strength is equal to the original number (example: 1 -> strength = 1)
L if the strength is less than the original number (example: 5 -> strength = 1)

Fun Bonus Challenge:
Whats the highest input you can find that is equal to its strength? (Is there a limit?)

As for the input, is it given as a decimal number the digits of which are the same as the nonary number or as the decimal (or binary) representation of the nonary number? i.e: for 1480(non) will the input be 1480 or 1125?

@overactor In nonary format.

I'm fairly confident that no-one will find a higher input that equals its strength than 10^71-1(non) that is, a 64 digit number that consists only of 8's

@overactor It might be possible with cycles of period greater than 8, I think.

@MartinBüttner i will be thoroughly impressed if you find any of those.

@AndoDaan Starting at the 1 gets you into the 4-8 period 2 cycle. The 1 is not part of the cycle.

@StandardToaster I confused count for cycle. Got it. Thanks.

Python 296
Not actually too inefficient, it only checks as many digits as it needs to.
n=raw_input();s=int(len(n)**.5);t=0
for i in range(s**2):
    l=[]
    while i not in l:l.append(i);c=n[i];i=(i%s+(-1 if c in'456'else 1 if c in'218'else 0))%s+((i/s+(-1 if c in'432'else 1 if c in'678'else 0))%s)*s
    t=t*9+len(l)-l.index(i)
print'EGL'[cmp(t,long(n,9))]

As for numbers equal to their strength, I think the only solutions are, for every N x N square up to N=8 a square containing N in every space. My thinking is that since every number in a loop must be the same number (the length of the loop) each loop would have to be all in one direction. This of course means that the loop's size must be N (and each element must be N). I'm pretty sure that this logic can be applied to squares and loops of any size, meaning there are no squares equal to their strength other than the first 8.

Though unlikely, it might be possible for loops larger than 8.

I think this gives the wrong result on 3117275531177455, because of loop sizes greater than 8. See my post.

@isaacg Oh I didn't see that, I changed it to make it work but I'm not going to try and golf it further because that would pretty much be just copy-pasting your answer. Oh and I think you can improve your last two lines by using cmp.

Python 2, 213 209 202
Edit: Removed shortcircuiting, which is sometimes incorrect. See below.
(Largely) The same algorithm as @KSab, but very heavily golfed.
n=`input()`
s=int(len(n)**.5)
c=0
for i in range(s*s):
 a=[]
 while(i in a)<1:a+=[i];x='432501678'.find(n[i]);i=(i+x%3-1)%s+((i/s+x/3-1)%s)*s
 c=c*9+len(a)-a.index(i)
d=long(n,9)
print'EGL'[(c>d)-(c<d)]

Golfs:

213: Short-circuiting, flawed solution.

209: First working solution.

202: Combined the two string lookups into one.

Edit: I just realised that this program, and thus KSab's too, were flawed in that they ignore multidigit cycle lengths. Example failure:
3117
2755
3117
7455

While the 3 has a cycle length of 2, and thus the above algorithm short circuits to 'L', this should in fact return 'G', because the cycle length of 14 on the second digit more than overcomes that. I have therefore changed the program. It also got shorter, funnily enough. To test your program, use 3117275531177455. It should return G.

Wow I thought I golfed it down a fair bit but you did some pretty clever stuff there.

@KSab Thanks - your algorithm was very clever to start with - I couldn't find any better way to do it.

CJam - 81
q:X,:L,{[L2*{_[_Lmqi:Smd@X=432501678s#3md]2/z{~+(S+S%}%Sb}*]L>_&,\X=~-}%9bg"EGL"=

Try it at http://cjam.aditsu.net/
It can probably be golfed some more.

Lua - Not golfed yet
Just putting in here for safekeeping. I'll golf it (and implement the "For a larger grid these numbers might be larger than 8, but we can still use them as "digits"") later. Works though.
d={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}
d[0]={0,0}ssd=''
n=arg[1]
q=math.sqrt(#n)t={}

    for y=1,q do
    table.insert(t,y,{})
    for x =1,q do
        v=(y-1)*q+x
        table.insert(t[y],x,n:sub(v,v)+0)
        io.write(t[y][x])
    end
end
for y=1,q do
    for x=1,q do
        cx=x cy=y pxy=''sd=0
        while pxy:match(cx..':%d*:'..cy..' ')==nil do
            pxy=pxy..cx..':'..sd..':'..cy.." "
            ccx=cx+d[t[cx][cy]][2]
            ccy=cy+d[t[cx][cy]][1]
            cx=ccx cy=ccy
            if cx<1 then cx=q elseif cx>q then cx=1 end
            if cy<1 then cy=q elseif cy>q then cy=1 end
            sd=sd+1
        end
        dds=(pxy:sub(pxy:find(cx..':%d+:'..cy)):match(':%d*'))
        ssd=ssd..(sd-dds:sub(2))
    end
end
print(ssd)
nn=tonumber(n,9) tn=tonumber(ssd,9)
if tn>nn then print("G") elseif tn==nn then print("E") else print("L") end


text:
Family Tree Solver
This is one of several challenges left for the community by Calvin's Hobbies.
Take a "family tree describing" file with lines of the form:
[ID] [mother ID] [father ID] [gender] [full name]

such as this which describes the first family tree at http://en.wikipedia.org/wiki/Cousin:
1 ? ? M Adam
2 ? ? F Agatha
3 ? ? M Bill
4 2 1 F Betty
5 2 1 M Charles
6 ? ? F Corinda
7 3 4 M David
8 6 5 F Emma

Write a program or function that takes in the file name and two IDs and outputs how those people are blood-related in simplest terms, using the common English names for relations. Input may be via STDIN, ARGV or function arguments but output should be to STDOUT.
Notes

IDs are positive integers.
? is used when parentage is not known.
Assume the graph will be connected and has no cycles.
You may not assume that each person's parents are listed before that person (so a person's parent ID could be greater than their own ID).
Assume everyone is either male or female and everyone has exactly one mother and exactly one father (of correct gender), although they might be unknown.
Assume names are unique.
Names can have spaces in them.

Blood relations
The following definitions of relationships R determine if person A is the R or person B. If two variants of R are listed, the first is for female A and the second for male A. All of these need to be implemented. If multiple definitions match, the earlier one is to be used. Terms in parentheses are gender-neutral terms, which do not need to be implemented but will be reused in further definitions. In definitions involving N and M, assume N > 1 and M > 0.

daughter/son: A lists B as either parent.
mother/father (parent): B lists A as either parent.
sister/brother (sibling): A and B list the same mother and father.
half-sister/half-brother (sibling): A and B list the same mother or the same father.
niece/nephew: A lists a parent who is the sibling of B.
aunt/uncle: B is A's niece or nephew.
granddaughter/grandson (grandchild): A lists a parent who lists B as their parent.
grandmother/grandfather (grandparent): B is A's grandchild.
great-niece/great-nephew: A is the grandchild of C who is the sibling of B.
great-aunt/great-uncle: B is A's great-niece or great-nephew.
great-granddaughter/son (1st great-grandchild): A is a grandchild of C who lists B as their parent.
great-grandmother/father (1st great-grandparent): B is A's 1st great-grandchild.
Nth great-granddaughter/son (Nth great-grandchild): A is an (N-1)th grandchild of C who lists B as their parent.
Nth great-grandmother/father (Nth great-grandparent): B is A's Nth great-grandchild.
Nth great-niece/nephew: A is the (N-1)th great-grandchild of C who is the sibling of B.
Nth great-aunt/uncle: B is A's Nth great-niece of Nth great-nephew.
cousin: A is the grandchild of C who is the grandparent of B.
Nth cousin: A is the (N-1)th grandchild of C who is the (N-1)th grandparent of B.
cousin, M times removed: A is the grandchild of C who is the Mth grandparent of B or A is the Mth grandchild of C who is the grandparent of B .
Nth cousin, M times removed: A is the Pth great-grandchild of C who is the Qth great-grandparent of B, where N = min(P,Q) + 1 and M = |P-Q|.

For Nth, write 2nd, 3rd, 4th, 5th etc.
For M times, write once, twice, thrice, 4 times, 5 times etc.
Examples
Assume the following file is used (you don't have to be able to deal with multiple spaces, but I added them for legibility):
 1  ?  ? F Agatha
 2  ?  ? M Adam
 3  ?  ? F Betty
 4  1  2 M Bertrand
 5  1  2 F Charlotte
 6  ?  ? M Carl
 7  ?  ? F Daisy
 8  3  4 M David
 9  5  6 F Emma
10  ?  ? M Edward
11  ?  ? F Freya
12  7  8 M Fred
13  9 10 F Grace
14  ?  ? M Gerald
15  ?  ? F Hillary
16 11 12 M Herbert
17 13 14 F Jane
18  ?  ? M James
19 15 16 F Kate
20 17 18 M Larry
21  ? 18 F Mary

Then input IDs should map to outputs as follows:
 1  2 --> Agatha is not a blood relative to Adam.
 8  3 --> David is the son of Betty.
 9 13 --> Emma is the mother of Grace.
 4  5 --> Bertrand is the brother of Charlotte.
 9  4 --> Emma is the niece of Bertrand.
 5  8 --> Charlotte is the aunt of David.
16  7 --> Herbert is the grandson of Daisy.
 1  9 --> Agatha is the grandmother Emma.
12  5 --> Fred is the great-nephew of Charlotte.
 4 13 --> Bertrand is the great-uncle of Grace.
16  3 --> Herbert is the great-grandson of Betty.
 6 17 --> Carl is the great-grandfather of Jane.
19  2 --> Kate is the 3rd great-granddaughter of Adam.
 1 17 --> Agatha is the 2nd great-grandmother of Jane.
20  4 --> Larry is the 3rd great-nephew of Bertrand.
 5 16 --> Charlotte is the 2nd great-aunt of Herbert.
 8  9 --> David is the cousin of Emma.
19 20 --> Kate is the 4th cousin of Larry.
16  9 --> Herbert is the cousin, twice removed, of Emma.
12 17 --> Fred is the 2nd cousin, once removed, of Jane.
21 20 --> Mary is the half-sister of Larry.

I wrote those up by hand so let me know if you spot any mistakes.
Another set of test data (provided by Scott Leadley, any errors are mine and not Martin's)
Ptolemy family tree

The picture is illustrative; the data below comes from the Wikipedia article "Ptolemaic dynasty".
 1  ?  ? F Berenice I of Egypt
 2  ?  ? M Ptolemy I Soter
41  1  2 F Arsinoe II of Egypt
 3  1  2 M Ptolemy II Philadelphus
 4  ?  ? F Arsinoe I of Egypt
 5  ?  ? M Philip
 6  4  3 M Ptolemy III Euergetes
 7  1  5 F Magas of Cyrene
 8  7  ? F Berenice II
 9  8  6 M Ptolemy IV Philopator
10  8  6 F Arsinoe III of Egypt
11 10  9 M Ptolemy V Epiphanes
12  ?  ? F Cleopatra I of Egypt
13 12 11 M Ptolemy VI Philometor
14 12 11 F Cleopatra II
15 12 11 M Ptolemy VIII Physcon
19  ?  ? F Eirene
16 14 13 M Ptolemy VII Neos Philopator
17 14 13 F Cleopatra III
18 14 15 M Ptolemy Memphites
20 19 15 M Ptolemy Apion
21 17 15 F Cleopatra IV
22 17 15 M Ptolemy IX Lathyros
23 17 15 F Cleopatra Selene I
24 17 15 M Ptolemy X Alexander I
25 23 22 F Berenice III of Egypt
26 23 24 M Ptolemy XI Alexander II
27 21 22 M Ptolemy XII Auletes
28 25 24 F Cleopatra V of Egypt
29 28 27 F Cleopatra VI of Egypt
30 28 27 F Berenice IV of Egypt
31 28 27 M Ptolemy XIII Theos Philopator
32 28 27 F Cleopatra VII Thea Philopator
33 28 27 M Ptolemy XIV
34 28 27 F Arsinoe IV of Egypt
35  ?  ? M Julius Caesar
37 32 35 M Ptolemy XV Caesarion
36  ?  ? M Mark Anthony
38 32 36 M Alexander Helios
39 32 36 M Ptolemy XVI Philadelphus
40 32 36 F Cleopatra Selene II

ECMAScript 6, 886
Division by zero is a wonderful thing.
This uses quasi-literals once (which are not implemented in Firefox 33 or node.js, but are available in the nightly builds of Firefox). The quasi literal used:

`
`

may be replaced with "\n" if whatever you are using lacks support for these.
This script constructs a tree from the list of people, storing both parents and children. Every path from person A to person B is tried, and the optimal path is saved. A path is considered valid if it only changes from going up to going down the tree once. The opposite change is not allowed - if one needs to go down to a children and back up to another parent in order to find a path, the two people are not blood relatives. (UUUUUDDD is valid, UUDUUU is not. U means go up (to a parent), D means go down (to a child)).
Sort of golfed:

R=(a,b)=>{F="forEach",C='';p=[],g=[],c={},n=[],e=m=1/0;y=i=>i+(k=i%10,k&&k<4&&~~(i%100/10)-1?[,'st ','nd ','rd '][k]:'th ');q=(a,b,s,$)=>!($=$.slice())|!a|~$.indexOf(a)||a-b&&$.push(a)|[p,c][F]((M,N)=>M[a][F](j=>q(j,b,s+N,$)))||(z=(s.match(/0/g)||[]).length,r=s.length-z,_=e+m-z-r,s.indexOf(10)<0&_>0|!_&m>r&&(e=z,m=r));I.split(`
`)[F](V=>{P=V.split(' ');D=+P[0];p[D]=[+P[1],+P[2]];g[D]=P[3]<'L';n[D]=P.slice(4).join(' ');c[D]=[]});p[F]((V,I)=>V[F](Y=>Y&&c[Y].push(I)));q(a,b,C,[]);U=e>m?m:e,V=e>m?e:m;alert(n[a]+' is '+(e/m+1?'the '+(U*V---1?U<2?(V<3?C:y(V-1))+(V<2?C:'great-')+(V*!U?'grand':C)+'son0father0nephew0uncle0daughter0mother0niece0aunt'.split(0)[g[a]*4+2*U+(U==e)]:(V-=--U,(U<2?C:y(U))+'cousin'+(V?', '+(V>3?V+' times':[,'on','twi','thri'][V]+'ce')+' removed,':C)):(p[a].join()==p[b].join()?C:'half-')+(g[a]?'sister':'brother'))+' of ':'not a blood relative to ')+n[b]+'.')}

Ungolfed (kind of):

// function for running.
R=(a,b)=>{
F="forEach",C='';
p=[], g=[], c={}, n=[], e=m=1/0;
// returns suffixed number (1->1st, 2->2nd, etc)
y= i=>i+(k=i%10,k&&k<4&&~~(i%100/10)-1?[,'st ','nd ','rd '][k]:'th ');
// this looks for the shortest path up/down the family tree between a and b.
q=(a,b,s,$)=>
  // copy the array of visited people
  !($=$.slice())
  // check if a is invalid
  | !a
  // check to make sure we are not visiting a for a second time
  | ~$.indexOf(a)
  // if a != b
  || a-b
  // add a to visited, and call q(...) on all parents and children
  && $.push(a) |
   [p,c][F]((M,N)=>M[a][F](j=>q(j,b,s+N,$)))
  || (
    // a == b
    // get number of ups and downs
    z=(s.match(/0/g)||[]).length,
    r=s.length-z,

    _=e+m-z-r,
    // if DU: path is invalid.
    // if _>0: path is shorter
    // if _==0: check m > r to see if new path should replace old
    s.indexOf(10)<0 & _>0|!_&m>r && (e=z,m=r));
// load list of people into arrays
I.split(`
`)[F](V=>{
  P=V.split(' ');
  // ID
  D=+P[0];
  // parents: NaN if not given
  p[D]=[+P[1],+P[2]];
  // gender: 1 if female, 0 if male
  g[D]=P[3]<'L';
  // merge the rest of the array to get name
  n[D]=P.slice(4).join(' ');
  // empty children array..for now
  c[D]=[]
});
// push current ID to parents' children array.
p[F]((V,I)=>V[F](Y=>Y&&c[Y].push(I)));

// get shortest path
q(a,b,C,[]);

U=e>m?m:e,V=e>m?e:m;
G=(a,b,c,d)=>(a<3?C:y(a-1))+(a<2?C:'great-')+(a*!b?'grand':C)+'son0father0nephew0uncle0daughter0mother0niece0aunt'.split(0)[g[d]*4+2*b+(b==c)];

// output
alert(n[a]+' is '+(e/m+1?'the '+(U*V---1?
    U<2?
        G(V,U,e,a)
    :(V-=--U,
     (U<2?C:y(U))+'cousin'+
     (V?
        ', '+(V>3?V+' times':[,'on','twi','thri'][V]+'ce')+' removed,'
     :C)
     )
:(p[a].join()==p[b].join()?C:'half-')+(g[a]?'sister':'brother'))+' of ':'not a blood relative to ')+n[b]+'.')
}

Notes:

List of people should be placed in a variable I (as a string, with single spaces and newlines).
To call: R(a,b), where a and b are the IDs of the two people being compared.

Cobra - 932
Out of all the challenges I've answered in Cobra, this is by far one of the best examples of what it can do.
EDIT: It's now a function, but must be prefixed by the signature for Z (included in char count).
sig Z(m,n=nil,r=nil)as String?
def f(f='',u='',v='')
    d={:}
    for l in File.readAllLines(f)
        w=l.trim.split
        i,j,k,p=w[:4]
        q=w[4:].join(' ')
        if i==u,x,g=q,if(p<'M',1,0)
        if i==v,y=q
        d.add(i,[j,k])
    o as Z=do(n,m,r)=if(n>1,"[n][if(0<n%10<4and not 10<n%100<14,'stndrd'[n%10-1:n%10+2],'th')] ",'')
    z as Z=do(m,n,r)
        h,a,b=n
        if m[0]==m[1]
            if if(b<1or 0<b<3and a>b,s=2,s=0),a,b=b,a
            r="the [if(a,if(a<2,if(b<2,if(not'?'in'[c=d[u]][e=d[v]]'and c==e,'','half-')+['brother','sister'][g],if(b<3,'',o(b-2)+'great-')+['uncle','aunt','nephew','neice'][s+g]),o(a-1)+'cousin'+if(b>a,', '+if((b-=a)<4,['on','twi','thri'][b-1]+'ce','[b] times')+' removed,','')),if(b,if(b<3,'',o(b-2)+'great-')+'grand','')+['father','mother','son','daughter'][s+g])] of"
        for t in d[m[h]],if'?'<>h,r?=if(h,z([m[0],t],[1,a,b+1]),z(m,[1,a,0])?z([t,v],[0,a+1,0]))
        return r to String?
    print x+" is [z([u,v],[0,0,0])?'not a blood relative to'] [y]."

Commented: (out of date, but still the same code-flow)
class F
    # Initilaise link dict
    var d={'?':@[''][:0]}
    # Gender bool
    var g
    def main
        # Initilaise name dict
        d={'?':@[''][:0]}
        # Take args
        f,a,b=CobraCore.commandLineArgs[1:]
        # For line in file
        for l in File.readAllLines(f)
            # Split line
            i=l.split
            # Add links to link dict
            .d.add(i[0],i[1:3])
            # Add names to name dict
            d.add(i[0],i[3:])
        # Get gender
        .g=if(d[a][0]=='F',1,0)
        # Print result
        print _
            '[d[a][1]] is '+ _ # Name A
                .r(@[1,0,0],@[a,a,b,b]) _ # If link found, link
                ? _ # Else
                'not a blood relative'+ _ # Not related
            ' of [d[b][1]].' # Name B
    def r(n as int[],m as String[])as String?
        # Recurse through the links at each level from A (A1), climbing when no links are found to B
        # For each level incremented for A, search upwards to the end of all nodes from B (B1), looking for A1
        r=nil
        # Check if we're done searching/climbing
        if m[1]==m[2]
            a,b=n[1:]
            s=if(b<1or b in[1,2]and a>b,1,0)
            if s,a,b=b,a
            # Take the A and B distance and translate them into a phrase
            return'the '+ _
                if(a, _
                    if(a<2, _
                        if(b<2, _
                            if('?'not in'[.d[m[0]]][.d[m[3]]]'and.d[m[0]]==.d[m[3]], _
                                '', _
                                'half-' _
                            )+['brother','sister'][.g], _
                            if(b<3, _
                                '', _
                                .o(b-2)+'great-' _
                            )+[['uncle','aunt'],['nephew','neice']][s][.g] _
                        ), _
                        .o(a-1)+'cousin'+if(b>a, _
                            ', '+if((b-=a)<4, _
                                ['once','twice','thrice'][b-1], _
                                '[b] times' _
                            )+' removed,', _
                            '' _
                        ) _
                    ), _
                    if(b, _
                        if(b<3, _
                            '', _
                            '[.o(b-2)]great-' _
                        )+'grand', _
                        '' _
                    )+[['father','mother'],['son','daughter']][s][.g] _
                )
        # Check if we're climbing
        if n[0]
            # For each link in the current A-level
            for x in.d[m[1]]
                r?= _
                    .r(@[0,n[1],0],m) _ # Start a search
                    ? _ # If the search failed
                    .r(@[1,n[1]+1,0],@[m[0],x,m[3],m[3]]) # Climb again
        # Check if we're searching
        else
            # For each link in the current B-level
            for x in.d[m[2]]
                # Search up one level from the current B-level
                r?=.r(@[0,n[1],n[2]+1],@[m[0],m[1],x,m[3]])
        return r
    def o(n as int)as String
        # Get ordinal string for the number
        return if(n>1,'[n][if(0<n%10<4and not 10<n%100<14,['st','nd','rd'][n%10-1],'th')] ','')

Ruby - 1892 1290 1247
Run as ruby relation.rb ID1 ID2 relationship_file.
P=Struct.new(:i,:m,:f,:s,:n,:c)
def f u,v,w,x,y,z
t=[y,z,v]
return t if v=='?'||x.include?(v)||v==w
r=x+[v];p=u[v]
p.c.each{|o|s=f(u,o,w,r,y,z+1);return s if s.last==w}
return t if z>0
[:m,:f].each{|i|s=f(u,p[i],w,r,y+1,z);return s if s.last==w}
t;end
def g j,a,r,b;puts"#{j[a].n} is the #{r} of #{j[b].n}.";end
def k n;n<2?'':n<3?'2nd':n<4?'3rd':"#{n}th";end
def h n;n<2?'':n<3?'great-':"#{k(n-1)} great-";end
def e n;s=n<2?'once':n<3?'twice':n<4?'thrice':"#{n} times";", #{s} removed,";end
def d u,a,b,x;y,z=x
if y==1&&z==1
w=u[a];v=u[b]
g(u,a,((w.f==v.f&&w.m==v.m)?'':'half-')+((w.s=='F')?'sister':'brother'),b)
elsif y<1||z<1
t=[y,z].max
g(u,a,h(t-1)+(t>=2?'grand':'')+(u[a].s=='F'?y>0?'daughter':'mother':y>0?'son':'father'),b)
elsif y==1||z==1
t=[y,z].max
g(u,a,h(t-1)+(u[a].s=='F'?y==1?'aunt':'niece':y==1?'uncle':'nephew'),b)
else
s=[y,z].min
g(u,a,(s-1>1?"#{k(s-1)} ":'')+'cousin'+((y==z)?'':e((z-y).abs)),b)
end;end
A,B=$*.shift(2);j={}
ARGF.each_line{|l|a=l.scan(/\s*(\d+)\s+(\d+|\?)\s+(\d+|\?)\s+([MF])\s+([\w\s]*\w+)\s*/).flatten;j[a[0]]=P.new(a[0],a[1],a[2],a[3],a[4],[])}
j.each{|k,i|[:f,:m].each{|l|j[i[l]].c<<k if i[l]!='?'}}
a=f(j,A,B,[],0,0)
if a.pop==B
d(j,A,B,a)
else
puts"#{j[A].n} is not a blood relative to #{j[B].n}."

Ungolfed version - 5251 3416 (same call tree, just did a lot of code folding)
Person = Struct.new( :id, :mother, :father, :sex, :name, :children )

#       Find a path between "start" and "finish". To reflect human consanguinity
# rules, either travel down through descendants or up through ancestors with a
# possible down leg through their descendants.
#
# Use depth-first search until forced to improve.
# If start up, path allowed one inflection point.
# Once start down, path must continue down.
# returns [stepsUp, stepsDown, trialResult],
#   shortest path found if trialResult == finish
def findRelationship(people, start, finish, pathSoFar, stepsUp, stepsDown)
  trialResult = [stepsUp, stepsDown, start]
  #     Return success or failure.
  return trialResult if start == '?' || pathSoFar.include?(start) || start == finish
  #     If success or failure not known, explore further.
  pathNext = pathSoFar + [start]
  person = people[start]
  #     Follow descendants.
  person[:children].each do |child|
    trial = findRelationship(people, child, finish, pathNext, stepsUp, stepsDown+1)
    return trial  if trial.last == finish
  end
  #     Already past inflection point?
  return trialResult  if stepsDown > 0
  #     Follow ancestry.
  [:mother, :father].each do |parent|
    trial = findRelationship(people, person[parent], finish, pathNext, stepsUp+1, stepsDown)
    return trial  if trial.last == finish
  end
  return trialResult
end

def printRelationship(people, a, relationship, b)
  puts "#{people[a][:name]} is the #{relationship} of #{people[b][:name]}."
end

def formatNth(n)
  return n<2?'':n<3?'2nd':n<4?'3rd':"#{n}th"
end

def formatGenerations(n)
  return n<2?'':n<3?'great-':"#{formatNth(n-1)} great-"
end

def formatRemoves(n)
  s=n<2?'once':n<3?'twice':n<4?'thrice':"#{n} times"
  return ", #{s} removed,"
end

def describeRelationship(people, a, b, legLengths)
  down = legLengths.pop
  up = legLengths.pop
  if up==1 && down==1
    who = people[a]
    what = people[b]
    printRelationship(people, a,
        (who[:father] == what[:father]  &&  who[:mother] == what[:mother] ? '' : 'half-') +
          ((who[:sex] == 'F') ? 'sister' : 'brother'),
        b)
  elsif up<1 || down<1
    pathLength = [up, down].max
    printRelationship(people, a,
        formatGenerations(pathLength-1) + ((pathLength>=2) ? 'grand' : '') +
          (up>0 ?
            people[a][:sex] == 'F' ? 'daughter' : 'son'  :
            people[a][:sex] == 'F' ? 'mother': 'father'
          ),
        b)
  elsif up==1 || down==1
    pathLength = [up, down].max
    printRelationship(people, a,
        formatGenerations(pathLength-1) +
          (up==1 ?
            people[a][:sex] == 'F' ? 'aunt': 'uncle'  :
            people[a][:sex] == 'F' ? 'niece': 'nephew'
          ),
        b)
  else
    shortestLeg = [up, down].min
    printRelationship(people, a,
        (shortestLeg-1>1 ? "#{formatNth(shortestLeg-1)} " : '') +
          'cousin' +
          (up==down ? '' : formatRemoves((down-up).abs)),
        b)
  end
end

A = $*.shift
B = $*.shift
#       Meet and greet.
people = {}
ARGF.each_line do |line|
  a = line.scan(/\s*(\d+)\s+(\d+|\?)\s+(\d+|\?)\s+([MF])\s+([\w\s]*\w+)\s*/).flatten
  people[a[0]] = Person.new( a[0], a[1], a[2], a[3], a[4], [] )
end
#       Build lineage.
people.each do |key, individual|
  [:father, :mother].each do |l|
      people[individual[l]][:children] << key  if individual[l] != '?'
  end
end
#       How are A and B related?
a = findRelationship(people, A, B, [], 0, 0)
if a.pop == B
  describeRelationship(people, A, B, a)
else
  puts "#{people[A][:name]} is not a blood relative to #{people[B][:name]}."
end

Passes the following test suite:
#!/usr/bin/env perl
#
use strict;
use warnings;
require File::Temp;
use File::Temp qw( tempfile tempdir );

use Test::More qw(no_plan);
# use Test::More tests => 38;

#       solution executable
my $solver='ruby relation.rb';

#       "happy" path
my $dir = tempdir( CLEANUP => 1 );
my ($fh, $filename) = tempfile( DIR => $dir );
my $testData = <<'END_TEST_DATA';
 1  ?  ? F Agatha
 2  ?  ? M Adam
 3  ?  ? F Betty
 4  1  2 M Bertrand
 5  1  2 F Charlotte
 6  ?  ? M Carl
 7  ?  ? F Daisy
 8  3  4 M David
 9  5  6 F Emma
10  ?  ? M Edward
11  ?  ? F Freya
12  7  8 M Fred
13  9 10 F Grace
14  ?  ? M Gerald
15  ?  ? F Hillary
16 11 12 M Herbert
17 13 14 F Jane
18  ?  ? M James
19 15 16 F Kate
20 17 18 M Larry
21  ? 18 F Mary
END_TEST_DATA
print $fh  $testData;
close($fh);

is( `$solver 1  2 $filename 2>&1`, "Agatha is not a blood relative to Adam.\n", 'OP example #1,  1  2');
is( `$solver 8 3 $filename 2>&1`, "David is the son of Betty.\n", 'OP example #2,  8  3');
is( `$solver 9 13 $filename 2>&1`, "Emma is the mother of Grace.\n", 'OP example #3,  9 13');
is( `$solver 4 5 $filename 2>&1`, "Bertrand is the brother of Charlotte.\n", 'OP example #4,  4  5');
is( `$solver 9 4 $filename 2>&1`, "Emma is the niece of Bertrand.\n", 'OP example #5,  9  5');
is( `$solver 5 8 $filename 2>&1`, "Charlotte is the aunt of David.\n", 'OP example #6,  5  8');
is( `$solver 16 7 $filename 2>&1`, "Herbert is the grandson of Daisy.\n", 'OP example #7, 16  7');
is( `$solver 1 9 $filename 2>&1`, "Agatha is the grandmother of Emma.\n", 'OP example #8,  1  9 (amended)');
is( `$solver 12 5 $filename 2>&1`, "Fred is the great-nephew of Charlotte.\n", 'OP example #9, 12  5');
is( `$solver 4 13 $filename 2>&1`, "Bertrand is the great-uncle of Grace.\n", 'OP example #10,  4 13');
is( `$solver 16 3 $filename 2>&1`, "Herbert is the great-grandson of Betty.\n", 'OP example #11, 16  3');
is( `$solver 6 17 $filename 2>&1`, "Carl is the great-grandfather of Jane.\n", 'OP example #12,  6 17');
is( `$solver 19 2 $filename 2>&1`, "Kate is the 3rd great-granddaughter of Adam.\n", 'OP example #13, 19  2 (amended)');
is( `$solver 1 17 $filename 2>&1`, "Agatha is the 2nd great-grandmother of Jane.\n", 'OP example #14,  1 17 (amended)');
is( `$solver 20 4 $filename 2>&1`, "Larry is the 3rd great-nephew of Bertrand.\n", 'OP example #15, 20  4');
is( `$solver 5 16 $filename 2>&1`, "Charlotte is the 2nd great-aunt of Herbert.\n", 'OP example #16,  5 16');
is( `$solver 8 9 $filename 2>&1`, "David is the cousin of Emma.\n", 'OP example #17,  8  9');
is( `$solver 19 20 $filename 2>&1`, "Kate is the 4th cousin of Larry.\n", 'OP example #18, 19 20');
is( `$solver 16 9 $filename 2>&1`, "Herbert is the cousin, twice removed, of Emma.\n", 'OP example #19, 16  9');
is( `$solver 12 17 $filename 2>&1`, "Fred is the 2nd cousin, once removed, of Jane.\n", 'OP example #20, 12 17');
is( `$solver 21 20 $filename 2>&1`, "Mary is the half-sister of Larry.\n", 'OP example #21, 21 20');

#       "sad" path
# none!

#       "bad" path
# none!

exit 0;

C - ungolfed
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef enum {
    MALE,
    FEMALE
} gender_t;

typedef enum {
    VISITED_A,
    VISITED_B,
    NOT_VISITED
} visited_t;

struct node {
    int id;
    int mother;
    int father;
    char *name;
    int height;
    gender_t gender;
    visited_t visited;
};

struct queue_item {
    void *item;
    struct queue_item *next;
    struct queue_item *previous;
};

struct queue {
    struct queue_item *first;
    struct queue_item *last;
};

void queue_push(struct queue *q, struct node *n)
{
    struct queue_item *i = malloc(sizeof(*i));
    i->item = (void *)n;
    i->next = q->last;
    i->previous = NULL;
    q->last = i;
    if(i->next != NULL) {
        i->next->previous = i;
    } else {
        q->first = i;
    }
}

void queue_pop(struct queue *q)
{
    struct queue_item *temp = q->first;
    if(temp) {
        q->first = q->first->previous;
        if(q->first == NULL) {
            q->last = NULL;
        } else {
            q->first->next = NULL;
        }
        free(temp);
    }
}

struct node *queue_front(struct queue *q)
{
    if(q->first) {
        return (struct node *)q->first->item;
    } else {
        return NULL;
    }
}

void queue_free(struct queue *q) {
    while(queue_front(q) != NULL) {
        queue_pop(q);
    }

    free(q);
}

struct node *find_shortest_path(struct node **nodes, struct node *a, struct node *b)
{

    struct queue *q = malloc(sizeof(*q));
    q->first = NULL;
    q->last = NULL;

    a->visited = VISITED_A;
    queue_push(q, a);
    b->visited = VISITED_B;
    queue_push(q, b);

    struct node *n, *father, *mother;

    while((n = queue_front(q)) != NULL) {
        if(n->visited == VISITED_A) {
            if(n->father != 0) {
                father = nodes[n->father-1];
                if(father->visited == VISITED_B) {
                    a->height = n->height + 1;
                    b->height = father->height;
                    n = father;
                    goto exit_queue_free;
                } else  if(father->visited == NOT_VISITED) {
                    father->visited = VISITED_A;
                    father->height = n->height+1;
                    queue_push(q, father);
                }
            }
            if(n->mother != 0) {
                mother = nodes[n->mother-1];
                if(mother->visited == VISITED_B) {
                    a->height = n->height + 1;
                    b->height = mother->height;
                    n = mother;
                    goto exit_queue_free;
                } else  if(mother->visited == NOT_VISITED) {
                    mother->visited = VISITED_A;
                    mother->height = n->height+1;
                    queue_push(q, mother);
                }
            }
        } else if (n->visited == VISITED_B) {
            if(n->father != 0) {
                father = nodes[n->father-1];
                if(father->visited == VISITED_A) {
                    b->height = n->height + 1;
                    a->height = father->height;
                    n = father;
                    goto exit_queue_free;
                } else  if(father->visited == NOT_VISITED) {
                    father->visited = VISITED_B;
                    father->height = n->height+1;
                    queue_push(q, father);
                }
            }
            if(n->mother != 0) {
                mother = nodes[n->mother-1];
                if(mother->visited == VISITED_A) {
                    b->height = n->height + 1;
                    a->height = mother->height;
                    n = mother;
                    goto exit_queue_free;
                } else  if(mother->visited == NOT_VISITED) {
                    mother->visited = VISITED_B;
                    mother->height = n->height+1;
                    queue_push(q, mother);
                }
            }
        }

        queue_pop(q);
    }

exit_queue_free:
    queue_free(q);
    return n;
}

int main(int argc, char *argv[]) {

    if(argc != 4) {
        return -1;
    }

    FILE *file = fopen(argv[1], "r");
    int id_1 = strtol(argv[2], NULL, 0);
    int id_2 = strtol(argv[3], NULL, 0);

    char name[128];
    char id[128];
    char id_father[128];
    char id_mother[128];
    char gender;

    struct queue *read_queue = malloc(sizeof(*read_queue));
    read_queue->first = NULL;
    read_queue->last = NULL;
    int nr_nodes = 0;

    while(fscanf(file, "%s %s %s %c %s",
        id, id_mother, id_father, &gender, name) == 5) {

        struct node *n = malloc(sizeof(*n));
        if(strcmp(id, "?") == 0) {
            n->id = 0;
        } else {
            n->id = strtol(id, NULL, 0);
        }

        if(strcmp(id_mother, "?") == 0) {
            n->mother = 0;
        } else {
            n->mother = strtol(id_mother, NULL, 0);
        }

        if(strcmp(id_father, "?") == 0) {
            n->father = 0;
        } else {
            n->father = strtol(id_father, NULL, 0);
        }

        if(gender == 'M') {
            n->gender = MALE;
        } else {
            n->gender = FEMALE;
        }

        n->name = malloc(strlen(name)+1);

        strcpy(n->name, name);

        n->visited = NOT_VISITED;
        n->height = 0;

        queue_push(read_queue, n);

        nr_nodes++;
    }

    struct node **nodes = malloc(sizeof(*nodes) * nr_nodes);
    struct node *temp;
    while((temp = queue_front(read_queue)) != NULL) {
        nodes[temp->id-1] = temp;
        queue_pop(read_queue);
    }

    queue_free(read_queue);

    struct node *a = nodes[id_1-1], *b = nodes[id_2-1];

    temp = find_shortest_path(nodes, a, b);

    if(temp) {
        if(a->height == b->height) {
            if(a->height == 1) {
                if((a->father == b->father) &&
                    (a->mother == b->mother)) {
                    printf("%s is the %s of %s.\n", a->name,
                        a->gender == MALE ?
                        "brother" : "sister",
                        b->name);
                } else {
                    printf("%s is the half-%s of %s.\n",
                        a->name,
                        a->gender == MALE ?
                        "brother" : "sister",
                        b->name);
                }
            } else if (a->height == 2) {
                printf("%s is the cousin of %s.\n", a->name,
                    b->name);
            } else if (a->height == 3){
                printf("%s is the 2nd cousin of %s.\n", a->name,
                    b->name);
            } else if (a->height == 4) {
                printf("%s is the 3rd cousin of %s.\n", a->name,
                    b->name);
            } else {
                printf("%s is the %dth cousin of %s.\n", a->name,
                    a->height-1,b->name);
            }
        } else if (a->height == 0) {
            if(b->height == 1) {
                printf("%s is the %s of %s.\n", a->name,
                    a->gender == MALE ? "father" :
                    "mother", b->name);
            } else if (b->height == 2) {
                printf("%s is the grand%s of %s.\n", a->name,
                    a->gender == MALE ? "father" :
                    "mother", b->name);
            } else if (b->height == 3) {
                printf("%s is the great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "father" : "mother", b->name);
            } else if (b->height == 4) {
                printf("%s is the 2nd great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "father" : "mother", b->name);
            } else if (b->height == 5) {
                printf("%s is the 3rd great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "father" : "mother", b->name);
            } else if (b->height == 6) {
                printf("%s is the %dth great-grand%s of %s.\n",
                    a->name, b->height-2,
                    a->gender == MALE ? "father" :
                    "mother", b->name);
            }
        } else if (b->height == 0) {
            if(a->height == 1) {
                printf("%s is the %s of %s.\n", a->name,
                    a->gender == MALE ? "son" :
                    "daughter", b->name);
            } else if (a->height == 2) {
                printf("%s is the grand%s of %s.\n", a->name,
                    a->gender == MALE ? "son" :
                    "daughter", b->name);
            } else if (a->height == 3) {
                printf("%s is the great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "son" : "daughter", b->name);
            } else if (a->height == 4) {
                printf("%s is the 2nd great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "son" : "daughter", b->name);
            } else if (a->height == 5) {
                printf("%s is the 3rd great-grand%s of %s.\n",
                    a->name, a->gender == MALE ?
                    "son" : "daughter", b->name);
            } else {
                printf("%s is the %dth great-grand%s of %s.\n",
                    a->name, a->height - 2,
                    a->gender == MALE ? "son" :
                    "daughter", b->name);
            }
        } else if (a->height == 1) {
            if(b->height == 2) {
                printf("%s is the %s of %s.\n", a->name,
                    a->gender == MALE ? "uncle" :
                    "aunt", b->name);
            } else if(b->height == 3) {
                printf("%s is the great-%s of %s.\n", a->name,
                    a->gender == MALE ? "uncle" :
                    "aunt", b->name);
            } else if(b->height == 4) {
                printf("%s is the 2nd great-%s of %s.\n", a->name,
                    a->gender == MALE ? "uncle" :
                    "aunt", b->name);
            } else if(b->height == 5) {
                printf("%s is the 3rd great-%s of %s.\n", a->name,
                    a->gender == MALE ? "uncle" :
                    "aunt", b->name);
            } else {
                printf("%s is the %dth great-%s of %s.\n",
                    a->name, b->height - 2,
                    a->gender == MALE ? "uncle" :
                    "aunt", b->name);
            }
        } else if (b->height == 1) {
            if(a->height == 2) {
                printf("%s is the %s of %s.\n", a->name,
                    a->gender == MALE ? "nephew" :
                    "niece", b->name);
            } else if(a->height == 3) {
                printf("%s is the great-%s of %s.\n", a->name,
                    a->gender == MALE ? "nephew" :
                    "niece", b->name);
            } else if(a->height == 4) {
                printf("%s is the 2nd great-%s of %s.\n", a->name,
                    a->gender == MALE ? "nephew" :
                    "niece", b->name);
            } else if(a->height == 5) {
                printf("%s is the 3rd great-%s of %s.\n", a->name,
                    a->gender == MALE ? "nephew" :
                    "niece", b->name);
            } else {
                printf("%s is the %dth great-%s of %s.\n",
                    a->name, a->height - 2,
                    a->gender == MALE ? "nephew" :
                    "niece", b->name);
            }
        } else {
            int m = a->height > b->height ? a->height - b->height :
                b->height - a->height;
            int n = a->height > b->height ? b->height - 1:
                a->height - 1;

            printf("%s is the ", a->name);
            if(n == 2) printf("2nd ");
            if(n == 3) printf("3rd ");
            if(n > 3) printf("%dth ", n);
            printf(" cousin, ");
            if (m == 1) printf("once");
            if (m == 2) printf("twice");
            if (m == 3) printf("thrice");
            if (m > 3) printf("%d times", m);
            printf(" removed, of %s.\n", b->name);
        }
    } else
        printf("%s is not a blood relative to %s.\n", a->name, b->name);

    int i;
    for(i = 0; i < nr_nodes; i++) {
        free(nodes[i]->name);
        free(nodes[i]);
    }

    free(nodes);

    fclose(file);

    return 0;
}

Is that an implementation of Dijkstra's Shortest Path Algorithm hiding in the middle?

Yes, it is Dijkstra's Shortest Path. It starts one Dijkstra instance at a and one at b and terminates when both searches meet.

Python 3: 1183
def D(i):
 if i==a:return 0
 r=[D(c)for c in t[i][4]]
 if r:return min(x for x in r if x is not None)+1
def A(i):
 if i=="?":return None
 r=D(i)
 if r is not None:return 0,r
 m,f=map(A,t[i][:2])
 return(f[0]+1,f[1])if not m or(f and sum(f)<sum(m))else(m[0]+1,m[1])if f else None
def P(r):print("%s is %s of %s"%(t[a][3],r,t[b][3]))
O=lambda n:"%d%s "%(n,{2:"nd",3:"rd"}.get(n,"th"))
G=lambda n:(O(n-2)if n>3 else"")+("great-"if n>2 else"")
GG=lambda n:G(n)+("grand"if n>1 else"")
f,a,b=input().split()
t={}
for l in open(f):
 i,m,f,g,n=l.strip().split(maxsplit=4)
 t[i]=(m,f,g,n,[])
for i,(m,f,g,n,c)in t.items():
 if m in t:t[m][4].append(i)
 if f in t:t[f][4].append(i)
g=t[a][2]=="M"
r=A(b)
if r:
 u,d=r
 if u==d==1:P("the "+("half-"if t[s][0]!=t[e][0]or t[s][1]!=t[s][1]else"")+["sister","brother"][g])
 elif u==0:P("the "+GG(d)+["daughter","son"][g])
 elif d==0:P("the "+GG(u)+["mother","father"][g])
 elif u==1:P("the "+G(d)+["niece","nephew"][g])
 elif d==1:P("the "+G(u)+["aunt","uncle"][g])
 else:
  n,m=min(u,d)-1,abs(u-d);P("the "+(O(n)if n>1 else"")+"cousin"+(" %s removed"%{1:"once",2:"twice",3:"thrice"}.get(m,"%d times"%m)if m else""))
else:
 P("not a blood relative")

The filename and the IDs of the people to be described are are read from standard input on a single line.
The top part of the code is function definitions. The script starts half-way down, and first works on processing the input (parsing the file, then assigning children to their parents in a second pass).
After the data is set up, we call the A function once to kick off a recursive search. The result defines the relationship.
The rest of the code is dedicated to describing that relationship in English. Siblings and cousins are complicated (and use long lines), the rest are pretty straight forward.
Example run (the second line is my input):
C:\>Python34\python.exe relations.py
relations.txt 20 4
Larry is the 3rd great-nephew of Bertrand

Function and variable name key:

f: The filename the family's data is read from.
a: The id of the person who's relationship we're naming.
b: The id of the person the relationship is defined relative to.
t: The family tree itself, as a dictionary mapping from an id to a 5-tuple of mother's id, father's id, sex, name and a list of children.
g: A Boolean value reflecting the gender of person a. It is True if they are a male.
u: The number of generations from b to the common ancestor of a and b (or 0 if b is a's ancestor).
d: The number of generations from a to the common ancestor of a and b (or 0 if a is b's ancestor).
D(i): Recursively search the descendents of person i for person a. Return the depth a was found at, or None if it was not found.
A(i): Recursively search i and i's descendents, but if its not found recursively search i's ancestors (and their descendents) too. Returns a 2-tuple, who's values are u and d described above. If a relationship is found through both parents, the one with the smallest number of generational steps (u+d) is preferred. If person a has no blood relationship to person i, A(i) returns None.
P(r): Print result string r bracketed by the names of persons a and b.
O(n): Return an ordinal string for the given number n. Only supports 1 < n < 21.
G(n): Return a prefix string equivalent to n-1 "greats" (e.g. "2nd great-" for n=2`). Will return an empty string for n <= 1.
GG(n): Returns a prefix string with "Nth great-" and "grand", as appropriate for n generations. Will return an empty string for n <= 1.

I took a few shortcuts in the name of shorter code which could be revised for better (or slightly more correct) performance on large genealogies. The A function doesn't make any attempt to avoid recursing down child trees that have already been searched, which makes it slower than necessary (though probably still fast enough for reasonable sized families). The O function doesn't correctly handle ordinals greater than 20 (it's a bit tricky to get all of 11th, 21st, and 101st right, but in one of my draft versions I did it in about 25 additional bytes). Unless you're dealing with very old and famous families (e.g. some of the royal families of Europe) I'm not sure I'd trust the accuracy of a genealogy that went back that far anyway.
On the other hand, I've also skipped over a few places where I could shave off a few bytes. For instance, I could save 3 bytes by renaming GG to a single character name, but basing the name off of great-grand seemed more worth while to me.
I believe that all white-space in the code is required, but it's possible that some can be skipped and I've just missed them (I kept finding stray spaces in argument lists as I was typing up this answer, but I think I've gotten them all now).
Because my recursive matching requires a relatively simple rule for which relationships to prefer if there is more than one, I don't give exactly the requested results in some obscure cases involving intergenerational incest. For instance, if person a is both person b's uncle and grandfather, my code will prefer the grandfather relationship despite the question saying that the uncle relationship should have higher precedence.
Here's an example dataset that exposes the issue:
1 ? ? F Alice
2 1 ? M Bob
3 1 2 F Claire
4 3 ? F Danielle

I suspect that for most programs, the relationships between Bob and Claire or between Bob and Danielle will cause trouble. They will either call the first pair half-siblings rather than father/daughter or they'll describe the latter pair as grandfather/granddaughter rather than uncle/niece. My code does the latter, and I don't see any reasonable way to change it to get the requested results without also getting the first pair wrong.

Javascript, 2292
for(var r=prompt().split("\n"),n=[{m:"",f:""}],t=1;t<r.length;t++){var e=r[t].split(" ");n[+e[0]]={m:"?"==e[1]?-1:+e[1],f:"?"==e[2]?-1:+e[2],s:e[3],n:e[4]}}var f=function(r,t){return r=n[r],t=n[t],~r.m&&r.m==t.m&&~r.f&&r.f==t.f?"M"==r.s?"brother":"sister":void 0},i=function(r,t){return r=n[r],t=n[t],~r.m&&r.m==t.m||~r.f&&r.f==t.f?"M"==r.s?"half-brother":"half-sister":void 0},o=function(r){var n=("0"+r).slice(-2),t=n[0];return n=n[1],r+(1==t?"th":1==n?"st":2==n?"nd":3==n?"rd":"th")+" "},a=function(r){return 1==r?"once":2==r?"twice":3==r?"thrice":r+" times"},h=function(r,t){var e,f,i=[t],a=[n[t].m,n[t].f];for(e=0;e<n.length&&!~a.indexOf(r);e++){i=a.slice(),a=[];for(var h=0;h<i.length;h++)i[h]>=0&&a.push(n[i[h]].m,n[i[h]].f)}if(!(e>=n.length))return f="M"==n[r].s?"father":"mother",e>0&&(f="grand"+f),e>1&&(f="great-"+f),e>2&&(f=o(e-1)+f),f},u=function(r,t){var e=h(t,r);return e?e.slice(0,-6)+("M"==n[r].s?"son":"daughter"):void 0},s=function(r){for(var t=[],e=1;e<n.length;e++)f(r,e)&&e!=r&&t.push(e);return t},l=function(r){return r=r.slice(0,-6),""==r?r:"grand"==r?"great ":"great-grand"==r?"2nd great ":o(+r.split(" ")[0].slice(0,-2)+1)+"great "},v=function(r,t){for(var e,f=s(r),i=0;i<f.length&&!(e=h(f[i],t));i++);return e?l(e)+("M"==n[r].s?"uncle":"aunt"):void 0},c=function(r,t){var e=v(t,r);return e?(e.split(" ").slice(0,-1).join(" ")+("M"==n[r].s?" nephew":" niece")).trim():void 0},g=function(r,n){for(var t=0;t<r.length;t++)if(~n.indexOf(r[t]))return!0},m=function(r,t){r=n[r],t=n[t];for(var e=[[r.m,r.f]],f=[[t.m,t.f]],i=0;i<n.length;i++){for(var h=e[i],u=f[i],s=[],l=0;l<h.length;l++){var v=0,c=0;-1!=h[l]&&(v=n[h[l]].m,c=n[h[l]].f),v>0&&s.push(v),c>0&&s.push(c)}for(var m=[],l=0;l<u.length;l++){var v=0,c=0;-1!=u[l]&&(v=n[u[l]].m,c=n[u[l]].f),v>0&&m.push(v),c>0&&m.push(c)}if(!s.length&&!m.length)break;e.push(s),f.push(m)}for(var i=1;i<Math.min(e.length,f.length);i++){var h=e[i],u=f[i];if(g(h,u))return(i>1?o(i):"")+"cousin"}for(var i=1;i<e.length;i++)for(var h=e[i],l=1;l<f.length;l++){var u=f[l];if(g(h,u)){var p=Math.min(i,l);return(p>1?o(p):"")+"cousin, "+a(Math.abs(i-l))+" removed,"}}},e=prompt().split(" "),p=+e[0],d=+e[1],M=u(p,d)||h(p,d)||f(p,d)||i(p,d)||c(p,d)||v(p,d)||m(p,d);alert(n[p].n+" is "+(M?"the "+M+" of ":"not a blood relative to ")+n[d].n+".\n"

I'm sure it can be golfed much further, all I did was put an ungolfed version through a minifier.
You can run the ungolfed version here on jsFiddle. Here is the output for the example data:
1 2 Agatha is not a blood relative to Adam.
8 3 David is the son of Betty.
9 13 Emma is the mother of Grace.
4 5 Bertrand is the brother of Charlotte.
9 4 Emma is the niece of Bertrand.
5 8 Charlotte is the aunt of David.
16 7 Herbert is the grandson of Daisy.
1 9 Agatha is the grandmother of Emma.
12 5 Fred is the great nephew of Charlotte.
4 13 Bertrand is the great uncle of Grace.
16 3 Herbert is the great-grandson of Betty.
6 17 Carl is the great-grandfather of Jane.
19 1 Kate is the 3rd great-granddaughter of Agatha.
2 17 Adam is the 2nd great-grandfather of Jane.
20 4 Larry is the 3rd great nephew of Bertrand.
5 16 Charlotte is the 2nd great aunt of Herbert.
8 9 David is the cousin of Emma.
19 20 Kate is the 4th cousin of Larry.
16 9 Herbert is the cousin, twice removed, of Emma.
12 17 Fred is the 2nd cousin, once removed, of Jane.
21 20 Mary is the half-sister of Larry.

A test suite. Stuff it into t/relation.t and run "prove" or "perl t/relation.t". It currently assumes the program file is "relation.rb".
It's community wiki, so feel free to add tests. If you change it, I think a timestamp (or some other obvious flag) would be in order. Wish list:

a "bad boy" test that will punish exhaustive search strategies

#
#       S. Leadley, Wed Aug 27 20:08:31 EDT 2014
use strict;
use warnings;
require File::Temp;
use File::Temp qw( tempfile tempdir );

use Test::More qw(no_plan);
# use Test::More tests => 38;

#       solution executable
my $solver='ruby relation.rb';

#       "happy" path
my $dir = tempdir( CLEANUP => 1 );
my ($fh, $filename) = tempfile( DIR => $dir );
my $testData = <<'END_TEST_DATA';
 1  ?  ? F Agatha
 2  ?  ? M Adam
 3  ?  ? F Betty
 4  1  2 M Bertrand
 5  1  2 F Charlotte
 6  ?  ? M Carl
 7  ?  ? F Daisy
 8  3  4 M David
 9  5  6 F Emma
10  ?  ? M Edward
11  ?  ? F Freya
12  7  8 M Fred
13  9 10 F Grace
14  ?  ? M Gerald
15  ?  ? F Hillary
16 11 12 M Herbert
17 13 14 F Jane
18  ?  ? M James
19 15 16 F Kate
20 17 18 M Larry
21  ? 18 F Mary
END_TEST_DATA
print $fh  $testData;
close($fh);

is( `$solver 1  2 $filename 2>&1`, "Agatha is not a blood relative to Adam.\n", 'OP example #1,  1  2');
is( `$solver 8 3 $filename 2>&1`, "David is the son of Betty.\n", 'OP example #2,  8  3');
is( `$solver 9 13 $filename 2>&1`, "Emma is the mother of Grace.\n", 'OP example #3,  9 13');
is( `$solver 4 5 $filename 2>&1`, "Bertrand is the brother of Charlotte.\n", 'OP example #4,  4  5');
is( `$solver 9 4 $filename 2>&1`, "Emma is the niece of Bertrand.\n", 'OP example #5,  9  5');
is( `$solver 5 8 $filename 2>&1`, "Charlotte is the aunt of David.\n", 'OP example #6,  5  8');
is( `$solver 16 7 $filename 2>&1`, "Herbert is the grandson of Daisy.\n", 'OP example #7, 16  7');
is( `$solver 1 9 $filename 2>&1`, "Agatha is the grandmother of Emma.\n", 'OP example #8,  1  9 (amended)');
is( `$solver 12 5 $filename 2>&1`, "Fred is the great-nephew of Charlotte.\n", 'OP example #9, 12  5');
is( `$solver 4 13 $filename 2>&1`, "Bertrand is the great-uncle of Grace.\n", 'OP example #10,  4 13');
is( `$solver 16 3 $filename 2>&1`, "Herbert is the great-grandson of Betty.\n", 'OP example #11, 16  3');
is( `$solver 6 17 $filename 2>&1`, "Carl is the great-grandfather of Jane.\n", 'OP example #12,  6 17');
is( `$solver 19 2 $filename 2>&1`, "Kate is the 3rd great-granddaughter of Adam.\n", 'OP example #13, 19  2 (amended)');
is( `$solver 1 17 $filename 2>&1`, "Agatha is the 2nd great-grandmother of Jane.\n", 'OP example #14,  1 17 (amended)');
is( `$solver 20 4 $filename 2>&1`, "Larry is the 3rd great-nephew of Bertrand.\n", 'OP example #15, 20  4');
is( `$solver 5 16 $filename 2>&1`, "Charlotte is the 2nd great-aunt of Herbert.\n", 'OP example #16,  5 16');
is( `$solver 8 9 $filename 2>&1`, "David is the cousin of Emma.\n", 'OP example #17,  8  9');
is( `$solver 19 20 $filename 2>&1`, "Kate is the 4th cousin of Larry.\n", 'OP example #18, 19 20');
is( `$solver 16 9 $filename 2>&1`, "Herbert is the cousin, twice removed, of Emma.\n", 'OP example #19, 16  9');
is( `$solver 12 17 $filename 2>&1`, "Fred is the 2nd cousin, once removed, of Jane.\n", 'OP example #20, 12 17');
is( `$solver 21 20 $filename 2>&1`, "Mary is the half-sister of Larry.\n", 'OP example #21, 21 20');

#       "sad" path
# none!

#       "bad" path
is( `$solver 1 32 $filename 2>&1`, "person with ID 32 does not exist\n", 'not required, not in the spec');

exit 0;