Using Spring Batch to parse date from file into LocalDateTime

Please use standard approach for data conversion:

  1. Data conversion is the responsibility of ConversionService.
  2. It's better to exposure FieldSetMapper as stand-alone bean due to assertions in afterPropertiesSet() method (it saves time to analyze misconfiguration).
  3. It's better to use FlatFileItemReaderBuilder.

You can easily extend and customize supported data types

@Configuration
@EnableBatchProcessing
public class SpringBatchDateParseConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public ConversionService testConversionService() {
        DefaultConversionService testConversionService = new DefaultConversionService();
        DefaultConversionService.addDefaultConverters(testConversionService);
        testConversionService.addConverter(new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String text) {
                return LocalDateTime.parse(text, DateTimeFormatter.ISO_DATE_TIME);
            }
        });

        return testConversionService;
    }

    @Bean
    public FieldSetMapper<TestClass> testClassRowMapper(ConversionService testConversionService) {
        BeanWrapperFieldSetMapper<TestClass> mapper = new BeanWrapperFieldSetMapper<>();
        mapper.setConversionService(testConversionService);
        mapper.setTargetType(TestClass.class);
        return mapper;
    }

    @Bean
    FlatFileItemReader<TestClass> testClassItemReader(FieldSetMapper<TestClass> testClassRowMapper) {
        return new FlatFileItemReaderBuilder<TestClass>().delimited()
                .delimiter(",")
                .names(new String[]{"foo", "bar", "date"})
                .linesToSkip(1)
                .resource(new ClassPathResource("test.csv"))
                .fieldSetMapper(testClassRowMapper)
                .build();
    }

    @Bean
    public Step step1(FieldSetMapper<TestClass> testClassRowMapper) {
        return stepBuilderFactory.get("step1")
                .<TestClass, TestClass>chunk(2)
                .reader(testClassItemReader(testClassRowMapper))
                .writer(testClassItemWriter())
                .build();
    }

    @Bean
    public Job job(Step step1) {
        return jobBuilderFactory.get("job")
                .start(step1)
                .build();
    }

    @Bean
    ItemWriter<TestClass> testClassItemWriter() {
        return new ItemWriter<TestClass>() {
            @Override
            public void write(List<? extends TestClass> items) throws Exception {
                for (TestClass TestClass : items) {
                    System.out.println(TestClass.toString());
                }
            }
        };
    }
}

I used the solution from Pavel because giving the Power to the ConversionService looks like a much cleaner solution to me.

The Date in my CSV is formatted like this : 10.01.20 so the pattern is : "dd.MM.yy" and i need to create LocalDate Objects from it.

Here is my custom ConversionService:

public ConversionService createConversionService() {
    DefaultConversionService conversionService = new DefaultConversionService();
    DefaultConversionService.addDefaultConverters(conversionService);
    conversionService.addConverter(new Converter<String, LocalDate>() {
        @Override
        public LocalDate convert(String text) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yy");
            return LocalDate.parse(text, formatter);
        }
    });
    return conversionService;
}

Then i added this custom ConversionService to my BeanWrapperFieldSetMapper which is used in the ItemReader.

fieldSetMapper.setConversionService(createConversionService());

You can override method initBinder of BeanWrapperFieldSetMapper<T>:

public class BeanWrapperFieldSetMapperCustom<T> extends BeanWrapperFieldSetMapper<T> {

    @Override
    protected void initBinder(DataBinder binder) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                if (StringUtils.isNotEmpty(text)) {
                    setValue(LocalDate.parse(text, formatter));
                } else {
                    setValue(null);
                }
            }

            @Override
            public String getAsText() throws IllegalArgumentException {
                Object date = getValue();
                if (date != null) {
                    return formatter.format((LocalDate) date);
                } else {
                    return "";
                }
            }
        });
    }
}