Complicated issue with string.gsub()

Hello!

I am currently trying to display the days, hours, mins and secs of the day on a TextLabel (counting backwards).

The issue is that I store it like this:

1 day, 1 hour, 1 min, 1 sec

Whenever days, hours, mins or secs is 0, I remove them from the string but the comma and the space is a bit complicated to deal with.

The only solution I can think of without it bugging is:

1 day 1 hour 1 min 1 sec / 1day 1hour 1min 1sec

What I’m currently doing:

Cal = Cal:gsub('0 days', '')
Cal = Cal:gsub('0 hours', '')
Cal = Cal:gsub('0 mins', '')
Cal = Cal:gsub('0 secs', '')

I’ve been trying to think of a solution that lets the commas and space be, but can’t find any sadly.

1 Like

It seems like I have found a solution to my problem.

local Cal = (Days > 0 and Days .. ' ' .. CheckDays .. ', ' or '') .. (Hours > 0 and Hours .. ' ' .. CheckHours .. ', ' or '') .. (Minutes > 0 and Minutes .. ' ' .. CheckMinutes .. ', ' or '') .. (Seconds > 0 and Seconds .. ' ' .. CheckSeconds or '')

It’s ugly, yet valid.
I’ll clean up this later.

If anyone has any other ideas, I’m open for them as well!

2 Likes

Here, this works taking from a Time variable you set in seconds:

local Days = math.floor(Time/86400)
local Hours = math.floor((Time-(86400*Days))/3600)
local Minutes = math.floor((Time-(86400*Days)-(3600*Hours))/60)
local Seconds = math.floor(Time-(86400*Days)-(3600*Hours)-(Minutes*60))
local str = (Days > 0 and (Days.." day"..(Days == 1 and ", " or "s, ")) or "") .. (Hours > 0 and (Hours.." hour"..(Hours == 1 and ", " or "s, ")) or "") .. (Minutes > 0 and (Minutes.." minute"..(Minutes == 1 and ", " or "s, ")) or "") .. (Seconds > 0 and (Seconds.." second"..(Seconds == 1 and "" or "s ")) or "")
print(str)
3 Likes

From what I understand, you don’t want the commas to remain before and after (avoiding something like , 1 hour, 1 min, )?

What you can do is simply use anchors in string pattern to target commas and spaces at the front and end of the string to remove them only, not any other commas or spaces:

-- clear up trailing commas / spaces
Cal = Cal:gsub("^,* *", "")       -- start
Cal = Cal:gsub(",* *$", "")       -- end
3 Likes

I already implemented this, to check if days, hours, mins and secs is 1 or higher, otherwise display (time)'s. Thanks anyways, yet not really useful to me now.

2 Likes

You asked for a solution to your problem, my code fixes your problem :slight_smile:

3 Likes

No, that won’t work the way I want it to. It will still display everything, even if the times are 0.

1 Like

Yes, I understand!
However… I found a solution (and commented) before you posted.

Also, you forgot a space after the comma, but no worries! I gave your suggestion a heart.

Okay, just trying to be helpful! That solves the commas, as well as condensing formatting and checking if the days exist. Glad you found a solution, anyways!

1 Like

Actually it seems like I don’t have a solution to this yet, for some reason hours will display 0 even when it shouldn’t be in the string.

Edit - Nevermind, forgot I commented out my code and were using @TheCarbyneUniverse’s code, which doesn’t work the way I want.

Anyways, thanks for the replies!

Yeah! Definitely, however I do it in a more readable way as the line is already long enough (the inline expression).

What I’m doing instead is:

local CheckDays = Days == 1 and 'day' or 'days'
local CheckHours = Hours == 1 and 'hour' or 'hours'
local CheckMinutes = Minutes == 1 and 'min' or 'mins'
local CheckSeconds = Seconds == 1 and 'sec' or 'secs'
1 Like

I made something that does something similar but it’s really inefficient so I decided I’d rework it to the best of my abilities. Turns out you don’t even need gsub either. Judging by your replies, you know about ternary operators so here you go:

local function formatTime(seconds)
	local days = math.floor(seconds / 86400)
	seconds -= (days * 86400)
	local hours = math.floor(seconds / 3600)
	seconds -= (hours * 3600)
	local minutes = math.floor(seconds / 60)
	seconds -= (minutes * 60)
	return string.format(
		'%s%s%s%s', -- 4 sections: days, hours, minutes, seconds
		string.format('%s%s%s', -- 3 sub-sections: numerical time value, unit of time and the comma
			days > 0 and tostring(days) or '',
			(days <= 0 and '') or (days == 1 and ' day') or (' days'),
			(days > 0 and (hours > 0 or minutes > 0 or seconds > 0) and ', ') or ''
		),
		string.format('%s%s%s',
			hours > 0 and tostring(hours) or '',
			(hours <= 0 and '') or (hours == 1 and ' hour') or (' hours'),
			(hours > 0 and (minutes > 0 or seconds > 0) and ', ') or ''
		),
		string.format('%s%s%s',
			minutes > 0 and tostring(minutes) or '',
			(minutes <= 0 and '') or (minutes == 1 and ' minute') or ' minutes',
			(minutes > 0 and (seconds > 0) and ', ') or ''
		),
		string.format('%s%s',
			seconds > 0 and tostring(seconds) or '',
			seconds <= 0 and '' or seconds == 1 and ' second' or ' seconds'
		)
	)
end;

print(formatTime(1)) --> 1 second
print(formatTime(31536000)) --> 365 days
print(formatTime(70)) --> 1 minute, 60 seconds

I appreciate that! However I don’t quite need it as I am determining days and stuff myself.
However I ran into an issue with my original script if you’d like to help.

Apparently, the comma appears if there’s only 1 thing that can be displayed at a time, ex days, hours, mins or secs, but it don’t happen if there’s two (ex, days and secs).

Could you show your script? I don’t think I see it in the thread

((Days > 0 and Days .. ' ' .. CheckDays .. ', ') or '') .. ((Hours > 0 and Hours .. ' ' .. CheckHours .. ', ') or '') .. ((Minutes > 0 and Minutes .. ' ' .. CheckMinutes .. ', ') or '') .. ((Seconds > 0 and Seconds .. ' ' .. CheckSeconds) or '')

Example image:
Comma issue: https://gyazo.com/567fbcda7c6a676bc9474e42507bb522
No comma issue: https://gyazo.com/fe2ecb6cacd09c295728738e480018b5

And now it doesn’t show minutes apparently, only the hour and seconds.

Well, I guess I could just use gsub and remove the comma if there’s any in case there’s only 1 item that can be displayed (?) :thinking:

I don’t know if this still applies, but the main post said your string looked like:
1 day, 1 hour, 1 min, 1 sec.

Try this:

local function Clean(str)
    local Continue = false
    
    str = str:gsub("[%d%a]+,?",function(response)
        if tonumber(response) == 0 then
            Continue = true
            return ""
        elseif Continue then
            Continue = false
            return ""
        end
    end)
    
    str = str:gsub(",* *$", "") --Thanks to Carbyne
    return str
end
Cal = Clean(Cal)

I am sure there is a better way to do this, but this is what I could come up with.

It seems like it’s working, however I believe it’s easier to just remove the comma afterwards instead of a function. Not really sure what ,* *$ is, but I’m assuming it removes the comma then a space.

If anyone else has suggestions, I’m still open for them!

If your current issue is just “1 hour,”, then you can just use the second part of that function. It removes trailing commas and spaces if they exist.

local Cal = "1 hour,"
Cal = Cal:gsub(",* *$", "")

This was posted already by @TheCarbyneUniverse.

Yeah it seems like I need to do what I didn’t want…
I’m pretty sure there’s a better method though, that doesn’t require you to “clean up” after.

Seems like I’ll stick with it though, unfortunately, but so far it works.