Convert a date to xkcd notation
JavaScript (ES6), 168 173
As an anonymous function. Using template strings, there is a newline near the end that is significant and included in the byte count.
d=>d.replace(/\d/g,c=>m=(l=((o[c]=o[c]||[b,c,b])[p/2&~1]+=++p).length)>m?l:m,t=[m=p=b='',b,b],o=[])&o.map(x=>x&&x.map((x,i)=>t[i]+=(x+' ').slice(0,m+1)))||t.join`
`
Less golfed
f=d=>(
// get the indices in o and the columns width in m
m=0,
p=0,
o=[],
d.replace(/\d/g,c=>(
o[c] = o[c]||['',c,''], // for each found digit :array with top indices, digit, bottom indices
o[c][p/2 & ~1] += ++p, // (p/2 and not 1) maps 0..3 to 0, 4..7 to 2
l = o[c].length,
m = l>m ? l : m // max indices string length in m
)),
// build the output in t
t=['','',''],
o.map(x=> x && x.map(
(x,i) => t[i]+=(x+' ').slice(0,m+1)) // left justify, max value of m is 4
),
t.join`\n` // return output as a newline separated string
)
Test snippet
f=d=>
d.replace(/\d/g,c=>m=(l=((o[c]=o[c]||[b,c,b])[p/2&~1]+=++p).length)>m?l:m,t=[m=p=b='',b,b],o=[])&
o.map(x=>x&&x.map((x,i)=>t[i]+=(x+' ').slice(0,m+1)))
||t.join`\n`
console.log=x=>O.textContent+=x+'\n'
;['2013-02-27','2015-12-24','2222-11-11','1878-02-08','2061-02-22','3564-10-28','1111-11-11']
.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
<pre id=O></pre>
Ruby, 200 195 189 178 162 157 characters
(156 characters code + 1 character command line option)
o={}
i=0
$_.gsub(/\d/){o[$&]||=['','']
o[$&][i/4]+="#{i+=1}"}
o=o.sort.map &:flatten
puts [1,0,2].map{|i|o.map{|c|c[i].ljust o.flatten.map(&:size).max}*' '}
Sample run:
bash-4.3$ ruby -n xkcd-date.rb <<< '2013-02-27'
2 3 1 4
0 1 2 3 7
5 67 8
bash-4.3$ ruby -n xkcd-date.rb <<< '2222-11-11'
1234
1 2
5678
bash-4.3$ ruby -n xkcd-date.rb <<< '3564-10-28'
1 4 2 3
0 1 2 3 4 5 6 8
6 5 7 8
Python 2.7, 308 310 bytes
i=raw_input().replace("-","")
s,w=sorted(set(i)),len
x,m={},0
for c in s:
q,v=i,[""]*2
while c in q:a=str(-~q.index(c)+(w(i)-w(q)));v[int(a)>4]+=a;q=q[q.index(c)+1:]
m,x[c]=max(m,max(map(w,v))),v
for l in[0,1]:print"".join((lambda x:x+(-~m-w(x))*" ")("".join(x[n][l]))for n in s)+"\n"+(" "*m).join(s)*(-l+1)
Wow, fixing it only costed 2 bytes!
The date doesn't have to be separated, the date can be any length, it doesn't have to be a date, it could be any string (but dashes are removed). The middle part looks pretty golfable to me.