2

I am trying to strip every Time Unit of a String, e.g.

The String "4w10d50m39s" would return a TimeUnit of 4 weeks, a TimeUnit of 10 days, a TimeUnit of 50 minutes, and a TimeUnit of 39 seconds.

How can I achieve this?

Context: I need them to sum all the Time unit converted to millis and use it as a time stamp, it will be used inside a Minecraft server, in a command made to add a rank to a user for an specific amount of time, example: /addrank iLalox Vip 4w5d, that would set the expiration date to: System.currentMillis() + timeInMillis.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
iLalox
  • 43
  • 5
  • 1
    Apparently a follow-up question to [Convert String containing multiple time segments to millis \[closed\]](https://stackoverflow.com/questions/68789980/convert-string-containing-multiple-time-segments-to-millis). – Ole V.V. Aug 20 '21 at 04:56
  • 1
    I think we can help you more precisely is you tell us what you need those time units for. My first approach would be to parse into a `Period` and a `Duration` (or a ThreeTen Extra `PeriodDuration`). `Period` does not distinguish between 1 week and 7 days. Would it be a requirement to do that? `Duration` does not distinguish between 0 hours and hours not being present at all. Do you need such a distinction? – Ole V.V. Aug 20 '21 at 05:01
  • 2
    Can we exclude the possibility of any months being in the string? Asking because otherwise I wouldn’t know whether `3m` would mean 3 months or 3 minutes. – Ole V.V. Aug 20 '21 at 05:03
  • I suggest you educate the publisher of your data about the [ISO 8601 standard format for durations](https://en.m.wikipedia.org/wiki/ISO_8601#Durations). – Basil Bourque Aug 20 '21 at 06:43
  • 1
    I need them to sum all the Time unit converted to millis and use it as a time stamp, it will be used inside a Minecraft server, in a command made to add a rank to a user for an specific amount of time, example: /addrank iLalox Vip 4w5d, that would set the expiration date to: System.currentMillis() + timeInMillis – iLalox Aug 20 '21 at 07:00

2 Answers2

2

To extract unit you can use regex

\\d+[wdms]

Demo

Then you can use matcher to extract matches from string and create TimeUnits. There is no Week constant in TimeUnit, so i would present week as amount of weeks * 7 in days.

public static void main(String[] args) {
    String test = new String("4w10d50m39s");
    Pattern pattern = Pattern.compile("\\d+[wdms]");
    Matcher matcher = pattern.matcher(test);

    while(matcher.find()) {
        String unit = matcher.group();
        char timeType = unit.charAt(unit.length() - 1);
        int timeAmount = Integer.parseInt(unit.substring(0, unit.length() - 1));
        if(timeType == 'd') {
            System.out.println(TimeUnit.DAYS.toDays(timeAmount) + " DAYS");
        }

        if(timeType == 'm') {
            System.out.println(TimeUnit.MINUTES.toMinutes(timeAmount) + " MINUTES");
        }

        if(timeType == 's') {
            System.out.println(TimeUnit.SECONDS.toSeconds(timeAmount) + " SECONDS");
        }

        if(timeType == 'w') {
            System.out.println(TimeUnit.DAYS.toDays(timeAmount * 7L) + " DAYS IN WEEK");
        }
    }
}

Output:

28 DAYS IN WEEK
10 DAYS
50 MINUTES
39 SECONDS
vszholobov
  • 2,133
  • 1
  • 8
  • 23
  • 1
    If using Java 12 or later, a [switch expression](https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html) wold probably be useful here. Just guessing, one would also be interested in recognizing `h` for hours. – Ole V.V. Aug 20 '21 at 05:07
  • 1
    I think adding hours recognition is not that difficult over a template. By the way, do you think it would be appropriate here to create an enum for the time types. – vszholobov Aug 20 '21 at 05:11
  • So what you are saying, @user16320675, is that `ChronoUnit` is a better choice for enum than both `TimeUnit` and one we create ourselves? Correct? I think that it’s a good point. – Ole V.V. Aug 20 '21 at 19:35
1

Period, Duration and Instant from java.time

I recommend that you use java.time, the modern Java date and time API, for your date and time work. Assuming your expiration is in real time (not Minecraft time):

    String string = "4w10d50m39s";
     
    String dayBasedString
            = string.replaceFirst("(^(?:\\d+w)?(?:\\d+d)?).*$", "$1");
    String timeBasedString = string.substring(dayBasedString.length());

    Period period = dayBasedString.isEmpty()
            ? Period.ZERO
            : Period.parse("P" + dayBasedString);
    assert period.getYears() == 0 : period;
    assert period.getMonths() == 0 : period;

    Duration dur = timeBasedString.isEmpty()
            ? Duration.ZERO
            : Duration.parse("PT" + timeBasedString); 
    dur = Duration.ofDays(period.getDays()).plus(dur);
    
    Instant now = Instant.now();
    Instant expiration = now.plus(dur);
    
    System.out.format("Now: %s; expiration: %s%n", now, expiration);

Output when I ran the snippet just now:

Now: 2021-08-20T18:24:35.701136Z; expiration: 2021-09-27T19:15:14.701136Z

Java has classes Period for day-based periods and Duration for time-based ones. Since your string is both, I am using both classes. And the Instant class for a point in time like the expiration time of your rank.

The Period class can natively parse a String like P4w10d, and the Duration class one like PT50m39s. The challenge is to split your string after the weeks and days and before the hours, minutes and seconds. My regular expression matches any (possibly empty) part of the string containing weeks and/or days and copies this part to a separate variable. Then copying the rest to another variable is not so complicated. A Period may contain years, months and days (weeks are automatically converted to days). For my following calculation to work I need to assume that there were no years or months since I don’t know how many days those are. I am also assuming that a day is 24 hours even though this is not always the case in real life.

As a variation you may use a ZonedDateTime for the current time. The advantage will be that you can directly add first a Period and then a Duration to it without the need to convert the weeks and days to a Duration first. And as a further variation you may use the PeriodDuration class from the ThreeTen Extra project. I haven’t checked, but I would expect that it would be possible to add a PeriodDuration to a ZonedDateTime since it would make good sense.

I need them to sum all the Time unit converted to millis and use it as a time stamp, …

For that you neither need to sum the units yourself nor to convert to milliseconds. Period and Duration parse larger chunks of your string, and Instant.plus() directly accepts the Duration object that we end up with.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161