Compañero, no es un problema de formato o de que no pueda mostrar caracteres acentuados si no un problema en el entendimiento de un string en si y el metodo substr. Tienes que ver los strings como si se trataran de un array, porque lo son internamente.
Y como te digo un caracter especial ocupa 2 posiciones de dicho array, y no una.
No obstante el código que te puse funciona bien, si no entiendes algo de ahí podemos comentarlo.
Como te dije un caracter con tilde ocupa dos posiciones, no una. Si mi cadena es simplemente.
cadena = "ó"
Y quiero mostrarla por el metodo substr haré, substr(0,2) no substr(0,1).
Si entiendes los metodos de codificación de caracteres te explico que tiene que ver en el tamaño de bytes que utiliza para almacenarl. Si un caracter normal ocupa 4 bytes un caracter acentuado ocupa 8 bytes, por ejemplo. Por eso te muestra el signo de interrogacion porque tomarias solo 4 bytes de tu letra con tilde que ocupa 8 y eso decodificado no sabe lo que es.
Saludos