Working with “time” in any programming language can be initially challenging, since it’s not always obvious what various time/date functions mean and how they behave. This tutorial discusses some of these issues and shows you how to work with things like date calculations, time zones, and date formatting.
Before we get to code, there are several important concepts to understand. To a computer, the basic unit of time is a second. From that one value, we can determine any date, or with fractions, even smaller measures of time.
While one second is a short period of time to a human, it’s actually a very long period of time to a computer. Just as humans break down long periods of time into smaller blocks, computers do as well. Besides seconds, many functions work in milliseconds
In Corona, the system.getTimer() call returns the amount of time since your app started running, and the value is typically returned in milliseconds, where the fractional part represents microseconds if the hardware supports it. Most devices will return a fractional time like:
1839.3949
For Corona apps where we deal with frame rates of 30 or 60 frames per second (1/30th to 1/60th of a second), having resolution more accurate than 1/1000th of a second isn’t important. That leaves us with functions that run on milliseconds, including:
Since each of these accepts time in milliseconds, you would, for example, provide a value of 5000
to the function for an equivalent of 5 seconds. ½ second would be 500
, 2½ seconds would be 2500
, and so on. Basically, just multiply the time you want in seconds by 1000
to get the time for any of these functions.
As stated above, the basic unit of time for a computer is one second. Both iOS and Android, as well as macOS, are based on operating systems derived from Unix, and in those operating systems the standard “time” function returns the number of seconds since
In Corona, to get the current number of seconds since
print( os.time() )
This outputs something like 1497577639
. That number by itself is somewhat meaningless and you can’t really determine the “real world” time that it represents, but it is very useful to calculate date math. For example, imagine that you’re building a
player[1].lastMove = os.time()
Then, if you want to check if they’ve made a move in the last hour, you could write:
local now = os.time() if ( now > ( player[1].lastMove + 3600 ) ) then -- Nudge the player end
Where did the 3600
come from? Simple! 3600
is 60 seconds multiplied by 60 minutes, thus one hour.
Essentially, with time in seconds as an integer value, it becomes very easy to do date math:
86400
24 * 60 * 60
)604800
7 * 24 * 60 * 60
)2592000
30 * 24 * 60 * 60
)Dates are a bit more challenging because they are strings which can be formatted in seemingly infinite ways, including:
April 1, 2010 4:53pm
April 1, 2010 4:53 P.M.
4/1/10 16:53 MT
Sun Jan 13 15:17:32 EST 2013
13-JAN-13 15:17
As a developer, you need to parse dates into their
One common date format is the
2017-01-12T12:04:35.03-0400
This may look a bit menacing, so let’s separate out the elements for further inspection:
2017-01-12 T 12:04:35.03 -0400
Much better! Now it’s clearer what each element represents:
The part before the T
is a date in a very predictable and
The letter T
indicates that the time part is starting; afterward there’s another series of positive integers which indicate hour, minutes, and seconds, separated by colons. Note that the seconds value may be a floating point number which represents a whole plus a fractional amount of seconds.
Finally, the string may have an optional time zone string indicating the zone the time originated in. We’ll talk more about the time zone format in a bit.
To convert an string:match()
function and, once again, the os.time()
function. Above, we simply called os.time()
without parameters to retrieve the number of seconds since os.time()
also accepts an optional table parameter containing values which represent each of the date components.
local dateString = "2017-01-12T12:04:35.03-0400" local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)" local year, month, day, hour, minute, seconds, tzOffset, offsetHour, offsetMin = dateString:match( pattern ) local timestamp = os.time( { year=year, month=month, day=day, hour=hour, min=minute, sec=seconds } )
We now have a value that is in seconds since
local timestamp = os.time( { year=year, month=month, day=day, hour=hour, min=minute, sec=seconds } ) local offset = 0 if ( tzOffset ) then if ( tzOffset == "+" or tzOffset == "-" ) then -- We have a timezone offset = offsetHour * 60 + offsetMin if ( tzOffset == "-" ) then offset = offset * -1 end timestamp = timestamp + offset end end
It’s important to consider time zones because, by default, os.time()
returns its value local to the time zone you’re in. You can easily compare “now” to a timestamp and determine if something is in the past or future, or if a given amount of time has elapsed.
In a date string, an ending character Z
indicates that the time is in Coordinated Universal Time or UTC. If it’s in UTC or local time, you don’t need to make any time zone adjustments.
Here is a complete function which combines the aspects discussed in this section:
local function makeTimeStamp( dateString ) local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)" local year, month, day, hour, minute, seconds, tzOffset, offsetHour, offsetMin = dateString:match( pattern ) local timestamp = os.time( { year=year, month=month, day=day, hour=hour, min=minute, sec=seconds } ) local offset = 0 if ( tzOffset ) then if ( tzOffset == "+" or tzOffset == "-" ) then -- We have a timezone offset = offsetHour * 60 + offsetMin if ( tzOffset == "-" ) then offset = offset * -1 end timestamp = timestamp + offset end end return timestamp end
Despite its power and convenience, this code does not compensate for Daylight Savings Time, a topic that’s beyond the scope of this tutorial.
At some point, you will probably need to go the “other way” with your time and convert it to a date string in a format that users can understand. This is the purpose of the os.date() function.
The os.date()
function takes two optional parameters. Calling it with no parameters will return the current date/time for the time zone you’re in (or the zone your device thinks you’re in). This will be in a format like:
Sat Jan 12 14:07:30 2013
Fortunately, you can specify various formatting parameters to build dates as you like. These format parameters are based on the Unix/C++ library function strftime, and with that you can format the date/time in many different ways.
print( os.date("%A") ) -- Outputs a string representing the weekday print( os.date("%A %l:%M%p") ) -- Outputs something like "Saturday 2:30PM"
By default, the values returned by os.date() will be based on your locale or time zone. This is crucial to understand if you want to create the type of date above, possibly for a multiplayer game with players in different time zones. In cases like these, you need to output the time based on UTC by placing a single !
in front of the format
parameter:
print( os.date("!%Y-%m-%dT%XZ") ) -- In UTC; outputs something like "2017-06-16T19:32:23Z"
Dealing with time and dates can be heavy on syntax, but hopefully this tutorial has exhibited that it’s not an insurmountable task. With a little effort and the convenient functions/methods above, you can “wield time” in ways that you might not have considered in the past.