AT TIME ZONE direction changes based on column having a timezone
Yes! it does!

The syntax to convert from timestamp with timezone (AKA timestamptz) to timestamp without timezone (AKA timestamp) is identical to the syntax to do the opposite transformation.

So, depending on which way you are converting the value you will see the printed value change in the corresponding direction.

You have a column called admission_at of type timestamp 
when you say admission_at AT TIME ZONE 'GMT+2' postgres interprets that to mean treat the values in  admission_at as-if they are in the zone 'GMT+2' and produce a result of type timestamptz

If you actually want to treat the admission_at values as GMT timestamps and display the apropriate Johannesburg wall time, convert them first to 
timestamptz using the GMT rules and then back to timestamp using the Johannesburg` rules.

SELECT
   admission_at AT TIME ZONE 'gmt' AT TIME ZONE 'gmt+2' AS gmt_plus_2, 
   admission_at AT TIME ZONE 'gmt' AT TIME ZONE 'Africa/Johannesburg' AS africa_jnb, 
   admission_at AT TIME ZONE 'gmt' AS gmt 
   admission_at AS naieve 
 FROM admissions WHERE admission_at::date = '2018-06-11'
 ORDER BY admission_at LIMIT 3;