[Question]
  [
In his xkcd [about the ISO 8601 standard date format](https://xkcd.com/1179/) Randall snuck in a rather curious alternative notation:
[![enter image description here](https://i.stack.imgur.com/b1ZUO.png)](https://i.stack.imgur.com/b1ZUO.png)
The large numbers are all the digits that appear in the current date in their usual order, and the small numbers are 1-based indices of the occurrences of that digit. So the above example represents `2013-02-27`.
Let's define an ASCII representation for such a date. The first line contains the indices 1 to 4. The second line contains the "large" digits. The third line contains the indices 5 to 8. If there are multiple indices in a single slot, they are listed next to each other from smallest to largest. If there are at most `m` indices in a single slot (i.e. on the same digit, and in the same row), then each column should have be `m+1` characters wide and left-aligned:
```
2  3  1  4
0  1  2  3  7
5     67    8
```
*See also [the companion challenge](https://codegolf.stackexchange.com/q/67128/8478) for the opposite conversion.*
## The Challenge
Given an ISO 8601 date (`YYYY-MM-DD`), output the corresponding xkcd date notation.
You may write a program or function, taking input via STDIN (or closest alternative), command-line argument or function argument and outputting the result via STDOUT (or closest alternative), function return value or function (out) parameter.
Any year from `0000` to `9999` is valid input.
Trailing spaces are allowed, leading spaces are not. You may optionally output a single trailing newline.
Standard [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'") rules apply.
## Test Cases
```
2013-02-27
2  3  1  4
0  1  2  3  7
5     67    8
2015-12-24
2  3  1     4
0  1  2  4  5
   5  67 8
2222-11-11
     1234
1    2
5678
1878-02-08
   1     3  24
0  1  2  7  8
57    6     8
2061-02-22
2   4   1   3
0   1   2   6
5       678
3564-10-28
      1 4 2 3
0 1 2 3 4 5 6 8
6 5 7         8
1111-11-11
1234
1
5678
0123-12-30
1 2 3 4
0 1 2 3
8 5 6 7
```
  
[Answer]
# 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>
```
[Answer]
# 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
```
[Answer]
# 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.
[Answer]
# Pyth, 86 78 bytes
```
JS{K-z\-=GheS.nmmlkd=N,mf<T5d=ZmmhkxdcK1Jmf>T4dZjbmjkmj"".[|k\ \ Gd[hNm]dcJ1eN
```
I will try to golf this down as soon as my brain has recovered. I was happy just to see it work.
[Answer]
## C#, 456
Golfed:
```
string  x(string p){string s=p.Replace("-", ""),a="",d="",e="";var u=new Dictionary<char,List<int>>();for(int i=0;i<s.Length;i++)if(u.ContainsKey(s[i]))u[s[i]].Add(i+1);else u.Add(s[i],new List<int>{i+1});foreach (var c in u.Keys.OrderBy(k=>k)){var t=String.Join("",u[c].Where(i=>i<5));var b=String.Join("",u[c].Where(i=>i>4));var l=Math.Max(t.Length,b.Length);var m=c+"".PadRight(l);a+=t.PadRight(l)+" ";e+=m;d+=b.PadRight(l)+" ";}return a+"\n"+e+"\n"+d;}
```
Ungolfed:
```
string  x(string p)
    {
        string s = p.Replace("-", ""),a = "", d = "", e = "";;
        var u = new Dictionary<char, List<int>>();
        for (int i = 0; i < s.Length; i++) if (u.ContainsKey(s[i])) u[s[i]].Add(i + 1); else u.Add(s[i], new List<int>{ i + 1 });            
        foreach (var c in u.Keys.OrderBy(k => k))
        {
            var t = String.Join("", u[c].Where(i => i < 5));
            var b = String.Join("", u[c].Where(i => i > 4));
            var l = Math.Max(t.Length, b.Length);
            var m = c + "".PadRight(l);
            a += t.PadRight(l) + " ";
            e += m;
            d += b.PadRight(l) + " ";
        }
        return a + "\n" + e + "\n" + d;            
    }
```
[Answer]
# Perl6, 265 bytes
Golfed
```
my$i=get;$i~~s:g/\-//;my@b=$i.comb.unique.sort;my$f={$i.comb[$_[1]-1]eq$_[0]??$_[1]!!''};my$g={[~] .map: $f};my$h={(@b X @^a).rotor(4).map: $g}my@a=$h(1..4);my@c=$h(5..8);my$s=max(|@a».chars,|@c».chars)+1;my$x='%-'~$s~'s';for @a,@b,@c {say [~] @_.map: *.fmt($x)}
```
Ungolfed (slightly)
```
my $i = get;
$i ~~ s:g/\-//;
my @b = $i.comb.unique.sort;
my $f = { $i.comb[$_[1]-1] eq $_[0] ?? $_[1] !! '' };
my $g = { [~] .map: $f };
my $h = { (@b X @^a).rotor(4).map: $g }
my @a = $h(1..4);
my @c = $h(5..8);
my $s = max(|@a».chars, |@c».chars)+1;
my $x = '%-'~$s~'s';
for @a,@b,@c { say [~] @_.map: *.fmt($x) }
```
[Answer]
# Python 3, 306 bytes
I'm investigating ways to determine, before assembling the top and bottom lines, what the maximum width of any given column will be. Once I've got that, I should be able to build the spaces into the lines directly instead of using all those `join` functions.
```
j=''.join
def t(d):
 c,*l={},;i,*n=0,
 for e in d.replace('-',''):
  i+=1
  try:c[e]+=[i]
  except:c[e]=i,
 m=sorted(c)
 for x in m:
  l+=[j(str(p)for p in c[x]if p<5)]
  n+=[j(str(p)for p in c[x]if p>4)]
 f='<'+str(max(map(len,l+n)))
 return'\n'.join(map(lambda o:' '.join(format(i,f)for i in o),(l,m,n)))
```
[Answer]
# Powershell, ~~174~~ ~~170~~ ~~168~~ 167 bytes
```
$a=@{}
$args|% t*y|?{$_-45}|%{if(!$a.$_){$a.$_="","","$_"}$a.$_[++$i-gt4]+=$i}
0,2,1|%{$r=$_
-join($a.Keys|sort|%{$a.$_[$r]}|% p*ht(1+($a|% v*|%{$_|% l*h}|sort)[-1]))}
```
Less golfed test script:
```
$f = {
$a=@{}                              # a hash table for a result
$args|% toCharArray|?{$_-45}|%{     # for each digit from argument strings except a '-'
    if(!$a.$_){$a.$_="","","$_"}    #   create an array if there are no values for the current digit
    $a.$_[++$i-gt4]+=$i             #   append the character to the relative row (0 - top, 1 - bottom) and increment position number
}                                   # the result is a hash table in which the key is a char of a digit and the value is an array of string
                                    # for example, first lines for the first test case:
                                    # @{
                                    #     [char]48: ("2","5","0")
                                    #     [char]49: ("3","","1")
                                    #     [char]50: ("1","67","2")
                                    #     ...
                                    # }
$l=1+($a|% Values|%{$_|% Length}|sort)[-1]      # calc the maximum width of strings
0,2,1|%{                            # for each number 0,2,1
    $r=$_                           # store it as row number
    -join(
        $a.Keys|sort|               # for each keys (digit of the dates) in the sorted order
            %{$a.$_[$r]}|           # push to pipe the relative string
            % padRight $l           # for which to execute the 'padright' method to pad with spaces
    )                               # and finally join the row
}
}
@(
,("2013-02-27",
"2  3  1  4   ",
"0  1  2  3  7",
"5     67    8")
,("2015-12-24",
"2  3  1     4",
"0  1  2  4  5",
"   5  67 8   ")
,("2222-11-11",
"     1234 ",
"1    2    ",
"5678      ")
,("1878-02-08",
"   1     3  24 ",
"0  1  2  7  8  ",
"57    6     8  ")
,("2061-02-22",
"2   4   1   3 ",
"0   1   2   6 ",
"5       678   ")
,("3564-10-28",
"      1 4 2 3   ",
"0 1 2 3 4 5 6 8 ",
"6 5 7         8 ")
,("1111-11-11",
"1234 ",
"1    ",
"5678 ")
,("0123-12-30",
"1 2 3 4 ",
"0 1 2 3 ",
"8 5 6 7 ")
) | % {
    $d, $expected = $_
    $result = &$f $d
    $d
    $j=0
    $result|%{
        "$($_.trimEnd() -eq $expected[$j].TrimEnd()): |$_|"   # Vertical bars only to see trailing and leading spaces
        $j++
    }
}
```
Output (vertical bars only to see trailing and leading spaces):
```
2013-02-27
True: |2  3  1  4     |
True: |0  1  2  3  7  |
True: |5     67    8  |
2015-12-24
True: |2  3  1     4  |
True: |0  1  2  4  5  |
True: |   5  67 8     |
2222-11-11
True: |     1234 |
True: |1    2    |
True: |5678      |
1878-02-08
True: |   1     3  24 |
True: |0  1  2  7  8  |
True: |57    6     8  |
2061-02-22
True: |2   4   1   3   |
True: |0   1   2   6   |
True: |5       678     |
3564-10-28
True: |      1 4 2 3   |
True: |0 1 2 3 4 5 6 8 |
True: |6 5 7         8 |
1111-11-11
True: |1234 |
True: |1    |
True: |5678 |
0123-12-30
True: |1 2 3 4 |
True: |0 1 2 3 |
True: |8 5 6 7 |
```
]