How to Create a LIVE Countdown for your games!

Hello! Today we are going to be creating a Live Countdown across all servers. Without further a do, lets get to it!

About Epoch

Epoch is the amount of time that has passed since January 1st, 1970. We use Epoch to tell our code how much time is left. Epoch is extremely useful, and you might use it a lot in the future.

Scripting!

the First variable, will be our time that we want the Countdown to hit 0! Go to https://www.epochconverter.com/ to convert to your time! For this example, Im going to make the countdown, countdown to December 25th, 2020. (Christmas). Here is my input: image
Make sure that its set to Local Time and not GMT. And after setting it to Local time, just insert the date! Now that you know how to get your date, lets begin coding.

local StartTime = 1608879600

The variable is the epoch that we want it to Countdown to! And now, we want to add another variable. That variable, will be Tween Service. This is totally up to you, but if you want a cool fade away effect once the timer hits 0, you might want to add this variable for reference! Like So:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

Okay, now that we have our basic variables, inside a Server Script inside of Workspace, we will begin designing our Countdown! Here is a basic countdown Model Link I Created: https://www.roblox.com/library/6048982565/Countdown-Model
Once you grab that, Insert it into your game, and put it into Workspace. Your workspace should look something like this: image
Now, you can simply drag the Script into the Countdown. Like so: image
Wonderful! Now, we can continue with our code!
Open up the script, and now we can create the Loop that counts down! Inside your script, add a simple While loop. Like this:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do

end

Now, we have to add a wait(1) Inside our loop. This will make it wait 1 second before updating the countdown! Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	wait(1)
end

Simple. Now, we will create a Variable that is the current time epoch! Make sure this variable is inside the Loop. Why inside the loop? Because if its outside the loop… It will only tell us the current time when the script runs, and not every second like we want it to. So simply use os.time() to tell the current epoch time! Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	wait(1)
end

Cool, huh? Now, we will code the part that has the script check if the currentTime is equal to StartTime. And if it is, then we will tell the script what to do. (What happens once it hits 0.)
We will do this by using a simple if statement. Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	end
	wait(1)
end

Simple, but works well! Now, to test out if everything is in order, we will put an else inside the if statement. So if the countdown isn’t done counting down to 0, then it will print how much time is left!
First, lets make a Variable Inside the loop. This will be how much time is left! We will subtract the StartTime variable, to the currentTime variable. Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft =  StartTime - currentTime
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	end
	wait(1)
end

Now, we will add the else inside the if statement, with the print! The else is checking if the countdown is NOT done counting down, then it will print how much time is left. Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft = StartTime - currentTime
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	else
		print(timeLeft)
	end
	wait(1)
end

And if you check the Output, it will print how much time (in seconds) is left until your chosen epoch time!
Now, we will begin making the actual countdown text change.
We will need to use a LOT of big brain math to determine how many days that are left, hours that are left, ect. First, lets a variable for math.Floor! We use math.Floor for the big brain math. Add the variable like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft = StartTime - currentTime
	local mFloor = math.floor
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	else
		print(timeLeft)
	end
	wait(1)
end

Epic. Now, we will add the big brian math. This will divide the epoch until it gets the days, hours, minutes, and seconds. Add the big brain math like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft = StartTime - currentTime
	local mFloor = math.floor
	local DaysLeft = mFloor((timeLeft / 60 / 60 / 24) % (365 + 0.2425)) 
	local HoursLeft = mFloor((timeLeft / 60 / 60) % 24) 
	local MinutesLeft = mFloor((timeLeft / 60) % 60) 
	local SecondsLeft = mFloor(timeLeft % 60) 
	
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	else
		print(timeLeft)
	end
	wait(1)
end

Isnt that some awesome math? Now… We will make the Timer Text update! We will simply get the decendants of the Billboard GUI. And then we will check if it has the className of, “Frame” Then if it does, then we will set its child’s text depending on the Frames Name! We will also make sure that it is inside the else statement. We will do this Like so:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft = StartTime - currentTime
	local mFloor = math.floor
	local DaysLeft = mFloor((timeLeft / 60 / 60 / 24) % (365 + 0.2425)) 
	local HoursLeft = mFloor((timeLeft / 60 / 60) % 24) 
	local MinutesLeft = mFloor((timeLeft / 60) % 60) 
	local SecondsLeft = mFloor(timeLeft % 60) 
	
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
	else
		print(timeLeft)
		local decendants = script.Parent.BillboardGui:GetDescendants()
		for i,v in pairs(decendants) do
			if v.ClassName == "Frame" then
				if v.Name == "Days" then
					v.Number.Text = DaysLeft
				end
				if v.Name == "Hours" then
					v.Number.Text = HoursLeft
				end
				if v.Name == "Minutes" then
					v.Number.Text = MinutesLeft
				end
				if v.Name == "Seconds" then
					v.Number.Text = SecondsLeft
				end
			end
		end
	end
	wait(1)
end

Cool huh? Now if you give it a test, it will display how many days, hours, minutes and seconds are left until your chosen time! Now as promised, I will teach a simple fade System. We will use Tween Service to tween the TextTransparency. We will also use Get Decendants here! We will make it fade once the timer hits 0. So we will put it inside the if statement. Like So:

local StartTime = 1608879600
local TweenService = game:GetService("TweenService")

while true do
	local currentTime = os.time()
	local timeLeft = StartTime - currentTime
	local mFloor = math.floor
	local DaysLeft = mFloor((timeLeft / 60 / 60 / 24) % (365 + 0.2425)) 
	local HoursLeft = mFloor((timeLeft / 60 / 60) % 24) 
	local MinutesLeft = mFloor((timeLeft / 60) % 60) 
	local SecondsLeft = mFloor(timeLeft % 60) 
	
	if currentTime >= StartTime then
		-- Whatever is inside here is what happens when the countdown hits 0! -- 
		local dec = script.Parent.BillboardGui:GetDescendants()
		for i,v in pairs(dec) do
			if v.ClassName == "TextLabel" then
				TweenService:Create(v,TweenInfo.new(2,Enum.EasingStyle.Linear),{TextTransparency = 1}):Play()
				TweenService:Create(v,TweenInfo.new(2,Enum.EasingStyle.Linear),{TextStrokeTransparency = 1}):Play()
			end
		end
		break
	else
		print(timeLeft)
		local decendants = script.Parent.BillboardGui:GetDescendants()
		for i,v in pairs(decendants) do
			if v.ClassName == "Frame" then
				if v.Name == "Days" then
					v.Number.Text = DaysLeft
				end
				if v.Name == "Hours" then
					v.Number.Text = HoursLeft
				end
				if v.Name == "Minutes" then
					v.Number.Text = MinutesLeft
				end
				if v.Name == "Seconds" then
					v.Number.Text = SecondsLeft
				end
			end
		end
	end
	wait(1)
end

And boom, a Countdown has been created! If you want the sources for learning more about Tween Service, Epoch, and strings for the 0’s in front of single digits, then here are the links!

https://developer.roblox.com/en-us/api-reference/class/Tween
https://developer.roblox.com/en-us/api-reference/lua-docs/os
If you notice anything wrong, please tell me by leaving a Reply, or messaging me! Thank you.

108 Likes

This seems really useful, I’ll definitely try it out in the future!

4 Likes

While I don’t need to make one, I use this for a Halloween event we do yearly. It’s a good method so I can vouch for it. I like the tutorial.

1 Like

No need to read scroll along :smile:

Just a little correction, Epoch starts at January 1st 1970 not 1972

Great tutorial! :slight_smile:

8 Likes

If you’re going to work with your machine’s local time zone to get an Epoch timestamp for a certain date but os.time on the server, then you’re going to have some serious time discrepancies because the server can only work in UTC time and every machine will return a different date. The counter will be fairly inaccurate and depending on your time zone, (significantly) far from the target.

Best thing to do instead is to fit a date table into os.time to get the Epoch timestamp in UTC of that date for the server and then work with os.time without the date table to get the current time, also in UTC. This will provide you with a more accurate counter. It’s also easy to work with and requires no external conversion because you’d know exactly what you want.

Alternatively, and even better, just cut out the server from this system entirely. Have every client do the timer themselves. You will still need the date table pattern I mentioned earlier to make a better countdown system because you want to accommodate for a local player’s time zone though.

Simple example:

-- The indices in this key are case sensitive and should be left lowercase
local TARGET_DATE = {
    year = 2020,
    month = 12,
    day = 25
}

local TARGET_TIME = os.time(TARGET_DATE)

local currentTime = os.time()
local timeUntilTarget = math.max(TARGET_TIME - currentTime, 0)

timeUntilTarget gives you the seconds remaining until the time is met. You can then use this time and format it accordingly. Of course you would need to update the currentTime and timeUntilTarget variables in some kind of active operation but that can be figured out by the developer according to the system they’re developing.

15 Likes

Allthough the Epoch is local, Its just easier for inserting it. It works for other players as well.

2 Likes

Hey buddy, ever heard of DateTime?

Could be included to this tutorial as much more precise and easier writing.

9 Likes

Sorry I don’t follow - I don’t quite understand what you mean here.

Irrespective of the time zone, the Unix Epoch is fixed as January 1st, 1970. The only difference you may experience by not accounting for time zones is a time discrepancy where the time left may be closer or further from what one would usually expect (e.g. the countdown says December 25th is in ~3 hours when your time zone would reach December 25th in ~4 hours).

If you’re working with the os library from the server, then all times are in or should be UTC. You can offset this time by adding a set number of seconds. For example, if you wanted the server to use EST times (not EDT), EST is UTC-5 so you would use os.time() - (3600*5) (one hour in seconds, multiplied by 5 for 5 hours in seconds, subtracted from os.time to get an offset of UTC-5).

Yes it works with other players but that’s not the point I was making. The point I’m primarily trying to highlight is your system facing potential time discrepancies as a result of relying on that external time converter with a local time setting. There’s an easier way to do that right from the engine without any external resources… passing a date table to os.time.

Additionally, I also said that doing it from the client is better. The client can actually, unlike the server, work with time in terms of the machine’s time zone, making for a more accurate counter. That way, everyone can appropriately see how long there is until December 25th - some may be a few minutes away, while others may be a few hours way or even past December 25th 00:00.

The DateTime object also helps a lot with stuff like this - forgot it was added and activated.

6 Likes

It works fine. I tested it with multiple people before posting on the dev fourms, and it worked great! Having the epoch local and putting it on the server worked fine. No issues.

2 Likes

It’d be awesome if you actually read the replies I’m writing. My responses have nothing to do with whether your code will break or not. Issues refers to unresolved problems.

What I’m talking about here with my replies is that some of the things you’re doing are wrong and/or unnecessary and the practical implications about using this code as is. I’m telling you that there are certain things you haven’t taken into account that make this not very useful and I’m pitching ways to improve your system so it can be better.

Again, the Unix Epoch isn’t “local”. It’s a fixed date of January 1st, 1970. When I’m talking about the local time issues, I’m referring to the lack of accommodating for time zones that your code fails to do.

  • You used an external site to get the number of seconds a certain date is. There’s an easier and better method which is just passing a date table to os.time. No need to rely on external services when the engine fully supports your use cases.

  • You used a “local time” option on said website, which works according to your time zone. Roblox game servers work in UTC. You need to understand this; that the server is working on a different time zone than what you got the time from. There will be discrepancies. This is problematic because your system will give you incorrect timing to December 25th.

4 Likes

…Why are you localizing the math functions every loop?

3 Likes

It has to update the current time so It can detect how much time is left, ect.

2 Likes

That doesn’t answer my question.

3 Likes

Because they have to update …

Do you even know what localization is? I don’t think you understood my question.

3 Likes

Why not explain the problem instead of asking in a roundabout way? :upside_down_face:

@DomDerpRBLX What avozzo is referring to is that you’re unnecessarily declaring a variable from the math library in each iteration when you can leave it as one of your outside variables. What you are doing right now which is localising a math function:

while true do
    local mFloor = math.floor
    -- ...

And what you should do:

local mFloor = math.floor

while true do
    -- ...

Yes your variables do need to update but it’s not those numbers that they’re talking about.

You really, really, please need to listen to the feedback people are giving you as you’ve requested at the end of your thread, properly understand that feedback or ask for clarification and try incorporating it into your code so that you can form a tutorial that teaches proper practice about something with utility for many developers.

The feedback that has been provided so far has not been taken very well.

12 Likes

How can the counter be “00:00:00:00” And not just “0:0:0:0”

string.Format can be used to check if It is a single digit number

do you know how to make the music turn on in an exact minute? How does the countdown reach “00:00:02:25” and the music is activated?

check if the time left is the same value as a music’s length and if it is then it plays the music