Introduction to the

Android makes some changes to some of the OpenJDK code, and this article takes a look at some of the potholes that have been made due to these changes.

Problem description

A Date utility class that works well online keeps reporting ParseException while writing unit tests as follows:

public static String utc2Local(String utcTime) {
    String utcTimePatten = "yyyy-MM-dd'T'HH:mm:ssZZZZZ";
    String localTimePatten = "yyyy.MM.dd";
    SimpleDateFormat utcFormater = new SimpleDateFormat(utcTimePatten);
    utcFormater.setTimeZone(TimeZone.getTimeZone("UTC"));// Time zone is defined and time is obtained
    Date gpsUTCDate = null;
    try {
        gpsUTCDate = utcFormater.parse(formatTimeStr(utcTime));
    } catch (Exception e) {
        e.printStackTrace();
        return utcTime;
    }
    SimpleDateFormat localFormater = new SimpleDateFormat(localTimePatten);
    localFormater.setTimeZone(TimeZone.getDefault());
    String localTime = localFormater.format(gpsUTCDate.getTime());
    return localTime;
}
Copy the code

The utcTime passed here is “2020-01-01 08:00:00+08:00”

This code works fine in the Android environment, but it keeps reporting errors in unit tests

why

The SimpleDateFormat in OpenJDK has been modified in the subParseNumericZone method:

As you can see, the OpenJDK originally did not support colon notation, but the subParseNumericZone method has been modified in Android to parse colon notation.

To solve

The solution is as simple as deleting the colon from the input parameter when testing:

try (MockedStatic<DateUtils> mockedDateUtils = Mockito.mockStatic(DateUtils.class, new CallsRealMethods())) {
    mockedDateUtils.when(() -> {
       	DateUtils.utc2Local(argThat((argument) -> {
            int index = argument.length() - 3;
            return argument.charAt(index) == ':';
        ));
    }).then((invocation) -> {
        String utcTime = invocation.getArgument(0, String.class);
        String fixedDate = formatDate;
        int index = formatDate.length() - 3;
        if (formatDate.charAt(index) == ':') {
            fixedDate = formatDate.substring(0, index) + formatDate.substring(index + 1);
        }
        return DateUtils.utc2Local(fixedDate);
    });
}
Copy the code