Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert ISO 8601 period to a string readable by humans [Android Studio]?

Any suggestions on how to convert the ISO 8601 duration format PnYnMnDTnHnMnS (ex: P1W, P5D, P3D) to number of days?

I'm trying to set the text of a button in a way that the days of free trial are displayed to the user.

Google provides the billing information in the ISO 8601 duration format through the key "freeTrialPeriod", but I need it in numbers the user can actually read.

The current minimum API level of the app is 18, so the Duration and Period classes from Java 8 won't help, since they are meant for APIs equal or greater than 26.

I have set the following method as a workaround, but it doesn't look like the best solution:

private String getTrialPeriodMessage() {
        String period = "";

        try {
            period = subsInfoObjects.get(SUBS_PRODUCT_ID).getString("freeTrialPeriod");
        } catch (Exception e) {
            e.printStackTrace();
        }

        switch (period) {
            case "P1W":
                period = "7";
                break;
            case "P2W":
                period = "14";
                break;
            case "P3W":
                period = "21";
                break;
            case "P4W":
                period = "28";
                break;
            case "P7D":
                period = "7";
                break;
            case "P6D":
                period = "6";
                break;
            case "P5D":
                period = "5";
                break;
            case "P4D":
                period = "4";
                break;
            case "P3D":
                period = "3";
                break;
            case "P2D":
                period = "2";
                break;
            case "P1D":
                period = "1";
        }

        return getString(R.string.continue_with_free_trial, period);
    }

Any suggestions on how to improve it?

Thanks!

like image 424
lhndrypltt Avatar asked Jan 14 '19 18:01

lhndrypltt


2 Answers

java.time and ThreeTenABP

This exists. Consider not reinventing the wheel.

import org.threeten.bp.Period;

public class FormatPeriod {

    public static void main(String[] args) {
        String freeTrialString = "P3W";
        Period freeTrial = Period.parse(freeTrialString);
        String formattedPeriod = "" + freeTrial.getDays() + " days";
        System.out.println(formattedPeriod);
    }
}

This program outputs

21 days

You will want to add a check that the years and months are 0, or print them out too if they aren’t. Use the getYears and getMonths methods of the Period class.

As you can see, weeks are automatically converted to days. The Period class doesn’t represent weeks internally, only years, months and days. All of your example strings are supported. You can parse P1Y (1 year), P1Y6M(1 year 6 months), P1Y2M14D (1 year 2 months 14 days), even P1Y5D, P2M, P1M15D, P35D and of course P3W, etc.

Question: Can I use java.time on Android?

Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in. In this case import from java.time rather than org.threeten.bp.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

  • Oracle tutorial: Date Time explaining how to use java.time. Section Period and Duration.
  • Java Specification Request (JSR) 310, where java.time was first described.
  • ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
  • ThreeTenABP, Android edition of ThreeTen Backport
  • Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
  • Wikipedia article: ISO 8601
like image 172
Ole V.V. Avatar answered Oct 17 '22 07:10

Ole V.V.


If you do want to re-invent the wheel here is a simple solution to parse ISO 8601 durations, it only supports year/week/day and not the time part but it can of course be added. Another limitation of this solution is that it expects the different types, if present, comes in the order Year->Week->Day

private static final String REGEX = "^P((\\d)*Y)?((\\d)*W)?((\\d)*D)?";

public static int parseDuration(String duration) {            
    int days = 0;

    Pattern pattern = Pattern.compile(REGEX);
    Matcher matcher = pattern.matcher(duration);

    while (matcher.find()) {

        if (matcher.group(1) != null ) {
            days += 365 * Integer.valueOf(matcher.group(2));
        }
        if (matcher.group(3) != null ) {
            days += 7 * Integer.valueOf(matcher.group(4));
        }
        if (matcher.group(5) != null ) {
            days +=  Integer.valueOf(matcher.group(6));
        }

    }
    return days;
}
like image 31
Joakim Danielson Avatar answered Oct 17 '22 06:10

Joakim Danielson