@Override
    public byte[] toBytes(Object value) {
        Date date = (Date)value;

        Calendar calendar = new GregorianCalendar();
        calendar.setTime(date);

        long result;

        switch (precision) {
            case DATETIME:
                result = calendar.getTimeInMillis();
                break;
            case DATETIME_NOMILLIS:
                calendar.set(Calendar.MILLISECOND, 0);
                result = calendar.getTimeInMillis();
                break;
            case DATE:
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                result = calendar.getTimeInMillis();
                break;
            case TIME:
            case TIME_NOMILLIS:
                int hours = calendar.get(Calendar.HOUR_OF_DAY);
                int minutes = calendar.get(Calendar.MINUTE);
                int seconds = calendar.get(Calendar.SECOND);
                int millis = precision == Precision.TIME ? calendar.get(Calendar.MILLISECOND) : 0;
                result = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + millis;
                break;
            default:
                throw new RuntimeException("Unexpected precision: " + precision);
        }

        byte[] bytes = new byte[getLength()];

        switch (precision) {
            case TIME:
            case TIME_NOMILLIS:
                Bytes.putInt(bytes, 0, (int)result);
                break;
            default:
                Bytes.putLong(bytes, 0, result);
        }

        // To make the ints/longs sort correctly when comparing their binary
        // representations, we need to invert the sign bit
        bytes[0] = (byte)(bytes[0] ^ 0x80);
        return bytes;
    }