You need to compute the position of the underscore in the first 3 positions. The following works by successively CROSS APPLY'ing to get the CHARINDEX positions. Something like this
select datefromparts(ci2.dt_yr, ci4.dt_mo, ci3.dt_day) dt_from_parts
from (values ('2021_8_12_17_4_blahblahblahblah'),
('2021_28_6_42_blahblahblahblah')) string(dt)
cross apply (values (charindex('_', string.dt, 1))) ci1(loc)
cross apply (values (charindex('_', string.dt, ci1.loc+1),
left(string.dt, ci1.loc-1))) ci2(loc, dt_yr)
cross apply (values (charindex('_', string.dt, ci2.loc+1),
substring(string.dt, ci1.loc+1, (ci2.loc-ci1.loc)-1))) ci3(loc, dt_day)
cross apply (values (substring(string.dt, ci2.loc+1, (ci3.loc-ci2.loc)-1))) ci4(dt_mo);
dt_from_parts
2021-12-08
2021-06-28
Suppose this code is unsightly and too much to look at. It could be extracted into a SCHEMABOUND inline table valued function which you could use CROSS APPLY with to simplify the query.
drop function if exists dbo.test_fnExtractDate;
go
create function dbo.test_fnExtractDate(
@input_string varchar(64))
returns table with schemabinding as return
select datefromparts(ci2.dt_yr, ci4.dt_mo, ci3.dt_day) dt_from_parts
from (values (@input_string)) string(dt)
cross apply (values (charindex('_', string.dt, 1))) ci1(loc)
cross apply (values (charindex('_', string.dt, ci1.loc+1),
left(string.dt, ci1.loc-1))) ci2(loc, dt_yr)
cross apply (values (charindex('_', string.dt, ci2.loc+1),
substring(string.dt, ci1.loc+1, (ci2.loc-ci1.loc)-1))) ci3(loc, dt_day)
cross apply (values (substring(string.dt, ci2.loc+1, (ci3.loc-ci2.loc)-1))) ci4(dt_mo);
go
Then the query could look like this
select fex.*
from (values ('2021_8_12_17_4_blahblahblahblah'),
('2021_28_6_42_blahblahblahblah')) string(dt)
cross apply dbo.test_fnExtractDate(string.dt) fex
dt_from_parts
2021-12-08
2021-06-28