Infiniwave
Infiniwave

Reputation: 547

How do I parse a string into milliseconds?

I have looked for other answers on StackOverflow with no luck.

I have a mute command in discord.py, that looks like this:

@client.command()
@commands.has_permissions(kick_members=True)
async def mute(ctx, member: discord.Member, time: typing.Optional[str], *, reason = None):
    guild = ctx.guild
    for role in guild.roles:
        if role.name == "Muted":
            await member.add_roles(role)
            await ctx.send("{} has has been muted because {}!" .format(member.username + "#" + member.discriminator, reason))

How do I make the time argument into milliseconds? Similar to the ms module in node.js.

For example I want the duration of >mute @user 1h some reason to be parsed into 3600000 milliseconds.

Upvotes: 0

Views: 116

Answers (2)

jdaz
jdaz

Reputation: 6053

Python has the built-in module timedelta that would work well for this.

If your string can have mixed time input like "1h", "1h10m", or "1h20m30s", you can use the following:

from datetime import timedelta
import re

def commandToMs(command):
    # Check for string of type 1h40m5s, any part optional, surrounding by whitespace.
    # Negative lookahead at beginning to prevent matching \s\s
    timeMatch = re.search(
        r"\s(?!\s)((?P<hrs>\d+)h)?((?P<mins>\d+)m)?((?P<secs>\d+)s)?\s", 
        command)  

    if not timeMatch:
        return 0

    timeDict = timeMatch.groupdict()

    # Convert empty matches from None to 0
    # keys() returns in arbitrary order. Sort to get "hrs", "mins", "secs"
    hrs, mins, secs = (
        int(timeDict[key]) if timeDict[key] else 0 for key in
        sorted(timeDict.keys())
    )
    delta = timedelta(hours=hrs, minutes=mins, seconds=secs)
    return delta.total_seconds() * 1000

commandToMs(">mute @user 1h some reason") # 3600000.0

Upvotes: 0

Michael Bianconi
Michael Bianconi

Reputation: 5242

I'm going to assume the formats are 1h, 1m and 1s.

We pull the third item from the string (this doesn't perform error checking to ensure it has 3 items)

raw_time = command.split()[2]  # Assuming the command is ">mute @user 1h..."
value = int(raw_time[0:-1])  # All but the last character
time_type = raw_time[-1]   # The last character

Then, we evaluate to see if it's an hour, minute, or second:

if time_type == 'h':
    return value * 3600000
elif time_type == 'm':
    return value * 60000
else:
    return value * 1000

You can expand this to include any period (e.g. milliseconds). However, it does not perform any error checking. To double check that the given command will work for this, you can run it through this regex:

if re.match('(\S+\s+){2}\d+(h|m|s).*', command) is not None:

    raw_time = command.split()[2]  # Assuming the command is ">mute @user 1h..."
    value = int(raw_time[0:-1])  # All but the last character
    time_type = raw_time[-1]   # The last character
    if time_type == 'h':
        return value * 3600000
    elif time_type == 'm':
        return value * 60000
    else:
        return value * 1000
else:
    # ...

        

Upvotes: 1

Related Questions